1 /**
  2  *  @auther kiyo@nekomataya.info (ねこまたや)
  3  *  @fileoverview ServiceAgent モジュール
  4 
  5     サービスエージェント
  6     一旦このモジュールを通すことで異なる種別のリポジトリの操作を統一する
  7     サービスエージェントは、ログイン管理を行う
  8     
  9 test data:
 10     var username = kiyo@nekomataya.info
 11     var password = 'devTest'
 12     var client_id = "b115aead773388942473e77c1e014f4d7d38e4d4829ae4fd1fa0e48e1347b4cd";
 13     var client_secret = "54c0f02c38175436df16a058cc0c0e037038c82d3cc9ce4c212e3e4afe0449dd";
 14 
 15 http://remaping.scivone-dev.com/oauth/token?
 16 Object ServiceAgent
 17     .servers    サーバコレクション
 18     .repositories リポジトリコレクション
 19     .currentRepository  現在選択されているリポジトリへの参照
 20 
 21 
 22     サーバは複数のリポジトリを持つことができる
 23     リポジトリは、いずれかのサーバに属しそのサーバへの参照を持つ
 24     ローカルリポジトリはアプリケーション自身がサーバの代用をする
 25 
 26 アプリケーションはリポジトリセレクタでリポジトリを選ぶ
 27 
 28 ドキュメントリストにはリポジトリから取得した内容と、実際にオープンした内容の履歴を表示する
 29 履歴は、ローカルリポジトリにするか?
 30 そうする場合は、ローカルリポジトリはセレクタに入れずに表示にマーキングをする
 31 特に現在開いている(又は開いていない)リポジトリのカットとカブっている場合
 32 
 33     リポジトリ分類
 34 以下のような段階的な差を付けてアカウントを取得してもらう+制作会社に対する有料サービスを販売しやすくしたい
 35 
 36     ローカルリポジトリ
 37 オフライン作業用のリポジトリ
 38 常に使用可能、このリポジトリのデータは対応する作品を管理するサーバと同期可能にする?    
 39 作業中に認証を失ったり、ネットワーク接続が切れた作業はこのリポジトリに保存することが可能
 40 サービスノード(サーバ)としてはダミーの値を持たせる
 41 (作業バックアップ領域とは別 作業バックアップは常時使用可能)
 42 ローカルリポジトリは容量が制限されるので保存できるカット数に制限がある(現在5カット 2016.11.15)
 43 この部分は作業履歴や作業キャッシュとして扱うべきかも
 44 履歴として扱う場合は、「最終5カットのリングバッファ」という風に
 45 
 46     ホームリポジトリ
 47 ログインしたサーバ上でデフォルトで提供されるリポジトリ
 48 ログイン時は常に使用可能
 49 =チーム
 50 
 51     追加リポジトリ
 52 個人用のリポジトリとは別に設定される共同制作用リポジトリ
 53 ある程度の管理サービスが追加される
 54 
 55     プロダクションリポジトリ(有料サービス)
 56 個人用のリポジトリとは別に設定される業務用リポジトリ
 57 会社単位での作品制作のための管理サービスが追加される
 58 
 59 こんな感じか?
 60 
 61 同時にアクセス可能なリポジトリの数を制限したほうが良い
 62 あまり多くのリポジトリを開いて一律に表示すると混乱する
 63 
 64 とくに、ローカルリポジトリはバックアップの性格が強くなるので、他のリポジトリのタイトルを表示することになる
 65 リポジトリセレクタで選んだ単一のリポジトリのみにアクセスするように設定する
 66 
 67 
 68 
 69 サーバのメニュー上でリポジトリにプロダクト(制作管理単位)を登録すると、そのプロダクトに対してカットの読み書きが可能になる
 70 
 71 プロダクト毎にアクセス可能な(リポジトリ共有=スタッフ)グループにユーザを登録することができる
 72 
 73 登録されたユーザはそのリポジトリにスタッフとしてアクセスしてデータを編集又は閲覧することが可能
 74 Repository.ouganization
 75 Repository.pmdb.orgnizations
 76 Repository.pmdb.users           当該リポジトリ内の基礎ユーザDB
 77 Repository.pmdb.staff           同基礎スタッフDB(ここにユーザを含む必要はないツリー下位のDBが優先)
 78 Repository.pmdb.lines           同ラインテンプレート(テンプレート ツリー下位のDB優先)
 79 Repository.pmdb.stages         同ステージテンプレート(同上)
 80 Repository.pmdb.jobNames        同ジョブテンプレート (同上)
 81 Repository.pmdb.assets          アセット定義テーブル
 82 Repository.pmdb.medias          同メディアテンプレート
 83 Repository.pmdb.workTitles      作品タイトルコレクション
 84 Repository.pmdb.opuses          プロダクトコレクション
 85 
 86 Repository.users    アクセスの可能性がある全ユーザのリスト
 87 Repository.productsData.staff    アクセス可否情報 リポジトリに対するユーザとその所属・役職のDB
 88      Repository.productsData[px].staff    アクセス可否情報 リポジトリに対するユーザとその所属・役職のDB
 89         Repository.productsData[px].episodes[ex].staff    アクセス可否情報 リポジトリに対するユーザとその所属・役職のDB
 90             Repository.productsData[px].episodes[ex].cuts[cx].staff    アクセス可否情報 リポジトリに対するユーザとその所属・役職のDB
 91          
 92 エントリごとにスタッフに対して以下の権利を設定することができる
 93 
 94 true    (アクセス可)
 95 false   (アクセス不可)
 96 
 97 内部状態として、以下の状態があるがユーザが設定可能なのはtrue/falseの2状態のみとする
 98     X    リスト
 99     R    読出
100     W    チェックイン
101 
102 権利に対して レイヤー構造がある
103 リポジトリは組織として プロダクト(workTitle)を含む
104 プロダクト(workTitle)はエピソード(opus)に分割される
105 opusは制作話数であり、個々のドキュメント(pmunit)を含む
106 
107 各ドキュメントはライン/ステージ/ジョブの構造を持つ
108 
109 このアトリビュート毎・ユーザ毎に 権限が異なるケースがある
110 
111                 
112 リポジトリ   *
113 プロダクト   *
114 各話         *
115 カット       *
116 ライン       *
117 ステージ     * 
118 ジョブ       *
119 
120     サーバごとに権限グループを設ける基本は作業部毎
121 制作管理部 
122 演出部     
123 撮影部     
124 美術部     
125 原画部     
126 動画部     
127 仕上部     
128 
129130 
131 グループ・ユーザの権限は、エントリ毎に設定可能に
132 基本はグループに対する権限設定
133 イレギュラー処理が必要なケースのみユーザごとの権限をエントリの設定に追記する
134 
135 イレギュラーや変更がなければエントリの権限設定は上位のレイヤから継承するので記載は無くとも良い
136 
137 グループ権限・作品データ・管理基礎データ等の保存管理
138 
139 基本的なDBへの登録等は、リポジトリ内に設定データを置いてその読み書きで対処する
140 RDBMのサポートのないファイル(ストレージ)上でリポジトリを築く際に必要
141 サービスエージェントを介してのDBへの情報請求をここで解決
142 これらの権利関連を別紙にまとめる
143 
144 サービスエージェントはこのまま拡張 pmdb,xMap,Xpstを統合する
145 
146 */
147 /**
148     @constractor
149     
150     サービスノードオブジェクト
151     複数あるサーバ(ログイン先)の必要な情報を保持するオブジェクト
152     複数のサービス情報をプログラム内に保持しないようにドキュメント内の属性として監理する
153     同時に記録する認証は一つ、複数のログイン情報を抱える必要はない
154     最期に認証したノード一つで運用 トークンはログイン毎に再取得
155 */
156 ServiceNode=function(serviceName,serviceURL){
157     this.name = serviceName ;//識別名称
158     this.url  = serviceURL  ;//ベースになるURL localStorageの際は"localStorage:"
159     this.type = "scivon"    ;//localStrage/scivon/localfilesystem/dropbox/googleDrive/oneDrive 等のキーワード(外部ストレージは未サポート2017.11)
160 //    this.uid  = '';//uid ログインユーザID パスワードは控えない 必要時に都度請求
161 //    this.lastAuthorized = "";//最期に認証したタイミング
162 //    this.accessToken="";//アクセストークン
163 //    this.username = kiyo@nekomataya.info
164 //    this.password = 'devTest'
165 //  以下の情報は、テスト用に埋め込み あとで分離処置
166     this.client_id = "b115aead773388942473e77c1e014f4d7d38e4d4829ae4fd1fa0e48e1347b4cd";
167     this.client_secret = "54c0f02c38175436df16a058cc0c0e037038c82d3cc9ce4c212e3e4afe0449dd";
168 }
169 /**
170     リクエストのヘッダにトークンを載せる
171     トークンの期限が切れていた場合は、再度のトークン取得(再ログイン)を促す
172     v1向けのコードは考慮しない
173 */
174 ServiceNode.prototype.setHeader=function(xhr){
175 //  if (this.type=="sivon"){}
176     var oauth_token = (xUI.onSite)? 
177     $('#backend_variables').attr('data-user_access_token'):$('#server-info').attr('oauth_token');
178 //console.log("setHeader :: ");
179 //console.log(oauth_token);
180 //console.log(xhr);
181     var organizationToken = (typeof serviceAgent.currentRepository.token != 'undefined')? serviceAgent.currentRepository.token:'';
182     if(oauth_token.length==0) return false;
183         xhr.setRequestHeader('Access-Control-Allow-Origin', '*' );
184         xhr.setRequestHeader('Authorization', ( "Bearer " + oauth_token));    
185         xhr.setRequestHeader('OrganizationToken', organizationToken );
186     return true;
187 }
188 /**
189     @undocumented
190     データ取得
191     参考コード 実際にはコールされない
192 */
193 ServiceNode.prototype.getFromServer=function getFromServer(url, msg){
194 //V1
195     $.ajax({
196         url: this.url + url,
197         type: 'GET',
198         dataType: 'json',
199         success: function(res) {
200 if(dbg) console.log(msg);
201 if(dbg) console.log(res);
202         },
203         beforeSend: this.setHeader
204     });
205 //V2    
206         $.ajax({
207           url: this.url + url,
208           type: 'GET',
209           dataType: 'json',
210           success: function(res) {
211 if(dbg) console.log(msg);
212 if(dbg) console.log(res);
213 
214             if( url == '/api/v2/organizations.json' ){
215               organization_token = res.data.organizations[0]["token"];
216               $('#organization_needed').fadeIn("slow");
217               $('#organization_name').text(res.data.organizations[0]["name"]);
218             }else if ( msg == '作品一覧取得'){
219               product_token = res.data.products[0]["token"]
220             }else if (msg == 'エピソード一覧取得'){
221               episode_token = res.data.episodes[0]["token"]
222             }else if (msg == 'カット一覧取得'){
223               cut_token = res.data.cuts[0]["token"]
224             }
225 
226 
227           },
228           beforeSend: setHeader
229         });
230 }
231 
232 /**
233     認証手続きはサービスノードのメソッド ノード自身が認証と必要なデータの記録を行う
234     パスワードは記録しない
235     認証毎にパスワードをユーザに要求する
236         myService.authorize()
237     パスワードとUIDは、ページ上のフォームから読む
238 */
239 ServiceNode.prototype.authorize=function(callback){
240 if(dbg) console.log("authorize::execute");
241     var noW =new Date();
242     var myUserId   = document.getElementById('current_user_id').value;
243     var myPassword = document.getElementById('current_user_password').value;
244     if ((myUserId.length<1) || (myPassword.length<1)) return false;
245     var data = {
246         username: myUserId,
247         password: myPassword,
248         client_id: this.client_id,
249         client_secret: this.client_secret,
250         grant_type: 'password'
251     };
252     var oauthURL=serviceAgent.currentServer.url+"/oauth/token.json";//.split('/').slice(0,3).join('/');
253 if(dbg) console.log(oauthURL);
254     $.ajax({
255         type: "POST",
256         url: oauthURL,
257         data: data,
258 		success : function(result) {
259 //console.log(serviceAgent.currentServer.name + ": success")
260 //console.log(result.access_token)
261             $('#server-info').attr('oauth_token'  , result.access_token);
262             $('#server-info').attr('last_authrized' , new Date().toString());
263             serviceAgent.authorized('success');
264             serviceAgent.currentServer.getRepositories(callback);
265 		},
266 		error : function(result) {
267 		    /**
268 		        認証失敗 エラーメッセージ表示 トークンと必要情報をクリアして表示を変更する
269 		    */
270 		    alert(localize(nas.uiMsg.dmAlertFailAuthorize));
271             $('#server-info').attr('oauth_token'  , '');
272             $('#server-info').attr('last_authrized' , '');
273             serviceAgent.authorized('false');
274 		}
275 	});
276 }
277 /** @undocumented
278     errorhandle
279 */
280 ServiceNode.prototype.errorhandle=function(obj){
281     console.log(obj);
282     return;
283 }
284 /**
285     リポジトリ(TEAM)一覧を取得してUIを更新する
286     
287 */
288 ServiceNode.prototype.getRepositories=function(callback){
289 // console.log(serviceAgent.currentServer.setHeader);
290         var myURL = serviceAgent.currentServer.url + '/api/v2/organizations.json';
291 //console.log(myURL);
292         $.ajax({
293           url : myURL,
294           type : 'GET',
295           dataType : 'json',
296           success : function(result) {
297 //            if(result.res != "200"){this.errorhandle(result);}else{};
298 // result replace result.data
299             serviceAgent.repositories.splice(1); // ローカルリポジトリを残してクリア(要素数1)
300 //console.log(result);
301             for( var rix=0 ; rix<result.data.organizations.length ; rix ++){
302                 serviceAgent.repositories.push(new NetworkRepository(
303                     result.data.organizations[rix].name,
304                     serviceAgent.currentServer
305                 ));
306                 serviceAgent.repositories[serviceAgent.repositories.length - 1].token = result.data.organizations[rix].token;
307     if(result.data.organizations[rix].owner_name){;//オーナ情報が全てのサーバにに行き渡るまでは判定して避ける
308                 serviceAgent.repositories[serviceAgent.repositories.length - 1].owner = new nas.UserInfo(
309                     result.data.organizations[rix].owner_name,
310                     {'token':result.data.organizations[rix].owner_token}
311                 );
312     }else{
313                 serviceAgent.repositories[serviceAgent.repositories.length - 1].owner = new nas.UserInfo();
314     };//
315             };
316             var myContents="";
317     myContents += '<option selected value=0> = local Repository =</option>' ;
318     for(var idr=1; idr < serviceAgent.repositories.length;idr ++){
319         myContents +='<option value="'+idr+'" >'+serviceAgent.repositories[idr].name+'</option>'; 
320     };
321     document.getElementById('repositorySelector').innerHTML = myContents;
322     document.getElementById('repositorySelector').disabled  = false;
323     if(callback instanceof Function){setTimeout(callback,10)};
324           },
325           error : function(result){
326 //console.log("getRepositories::fail");
327 //console.log(JSON.stringify(result));
328           },
329           beforeSend: serviceAgent.currentServer.setHeader
330         });
331 }
332 /*
333     履歴構造の実装には、XPSのデータを簡易パースする機能が必要
334     プロパティを取得するのみ?
335     
336     サーバは自身でXPSをパースしない
337     
338     アプリケーションがパースした情報を識別情報として記録してこれを送り返す
339     
340 (タイトル)[##№](番号)[(サブタイトル)]//S##C####(##+##)/S##C####(##+##)/S##C####(##+##)/不定数…//lineID//stageID//jobID//documentStatus
341     例:
342 ももたろう#SP-1[鬼ヶ島の休日]//SC123 ( 3 + 12 .)//0//0//1//Hold
343  
344 タイトル/話数/サブタイトル/カット番号等の文字列は、少なくともリポジトリ内/そのデータ階層でユニークであることが要求される
345 例えば現存のタイトルと同じと判別されるタイトルが指定された場合は、新規作品ではなく同作品として扱う
346 似ていても、別のタイトルと判別された場合は別作品として扱われるので注意
347 
348 *判定時に
349 
350     タイトル内のすべての空白を消去
351     半角範囲内の文字列を全角から半角へ変換
352     連続した数字はparseInt
353 
354 等の処置をして人間の感覚に近づける操作を行う(比較関数必要)
355 
356 
357 ラインID ステージID 及びジョブIDはカット(管理単位)毎の通番 同じIDが必ずしも同種のステージやジョブを示さない。
358 管理工程の連続性のみが担保される
359 識別子に管理アイテム識別文字列を加えても良い
360 
361 第4要素は作業状態を示す文字列
362 
363     例:
364 0//0//0//Stratup
365 0:本線//1:レイアウト//2:演出検査//Active
366  
367     ラインID
368 ラインが初期化される毎に通番で増加 整数
369 0   本線trunkライン
370 1   本線から最初に分岐したライン
371 1-1 ライン1から分岐したライン
372 2   本線から分岐した2番めのライン
373 
374     ステージID
375 各ラインを結んで全通番になる作業ステージID
376 0//0
377 0//1
378 0//2    1//2
379 0//3    1//3
380 
381     ジョブID
382 ステージごとに初期化される作業ID
383 0//0//0
384 0//0//1
385 0//0//2
386 0//1//0
387 0//1//1
388 
389     ステータス
390 作業状態を表すキーワード
391 Startup/Active/Hold/Fixed/Aborted (開始/作業/保留/終了/削除) の5態
392 floating/Finished (浮動/完了) の2態を追加
393 
394     エントリの識別子自体にドキュメントの情報を埋め込めばサーバ側のパースの必要がない。
395     ファイルシステムや一般的なネットワークストレージ、キー/値型のDBをリポジトリとして使う場合はそのほうが都合が良い
396     管理DBの支援は受けられないが、作業の管理情報が独立性を持ち、アプリケーションからの管理が容易
397 
398 ステータスは それぞれのキーワードで始まり サブプロパティを含む
399 
400     Startup:{asignment:yuid,message:text}
401 
402  //現状
403  var myXps= XPS;
404     [encodeURIComponent(myXps.title)+"#"+encodeURIComponent(myXps.opus)+"["+encodeURIComponent(myXps.subtitle)+"]",encodeURIComponent("S"+((myXps.scene)?myXps.scene:"-")+"C"+myXps.cut)+"("+myXps.time()+")",myXps.xMap.currentLine,myXps.xMap.currentStage,myXps.xMap.currentJob].join(" // ");
405  //将来は以下で置き換え予定 CSオブジェクト未実装
406     myXps.sci.getIdentifier();
407  //Xpsオブジェクトのクラスメソッドとして仮実装済み オブジェクトメソッドとして同名の機能の異なる関数があるので要注意
408   Xps.getIdentifier(myXps);
409   
410 */
411 /**
412 比較関数 管理情報 3要素の管理情報配列 issuesを比較して先行の管理ノード順位を評価する関数
413 ライン拡張時は追加処理が必要
414 */
415 issuesSorter =function(val1,val2){
416     if(typeof val1 == 'undefined'){ return -1 } 
417     if(typeof val2 == 'undefined'){ return  1 } 
418 
419     return (parseInt(String(val1[0]).split(':')[0]) * 10000 + parseInt(String(val1[1]).split(':')[0]) * 100 + parseInt(String(val1[2]).split(':')[0])) - ( parseInt(String(val2[0]).split(':')[0]) * 10000 + parseInt(String(val2[1]).split(':')[0]) * 100 + parseInt(String(val2[2]).split(':')[0]));
420 };
421 
422 /**
423     ソート比較関数
424     カット番号(文字列内の最初の整数クラスタ)を整数化して比較
425 */
426 numSorter =function(val1,val2){ return (nas.parseNumber(val1) - nas.parseNumber(val2))};
427 
428 /**
429 listEntry オブジェクト
430 初期化引数:カット識別子[タイトルID,話数ID,カットID]
431 
432 タイトルID、話数IDは将来的にタイトル話数エントリへのアクセスキーを入力
433 カットIDは自身のDBエントリへのアクセスキーを設定する
434 現在は省略可だがDB整備され次第必須
435 
436 ローカルリポジトリの場合はそれぞれのエントリのキーを省略なしに入力
437     ドキュメントリストにエントリされるオブジェクト
438     parent  リポジトリへの参照
439     product 作品と話数
440     sci     カット番号(兼用情報含む)
441     issues  管理情報 4要素一次元配列 [line,stage,job,status]
442     実際のデータファイルはissueごとに記録される
443     いずれも URIエンコードされた状態で格納されているので画面表示の際は、デコードが必要
444     issues には オリジナル(初期化時)の識別子を保存する
445     ネットワークリポジトリに接続する場合は以下のプロパティが設定される
446     listEntry.titleID   /string token
447     listEntry.episodeID /string token
448     listEntry.issues[#].cutID  /string token
449     listEntry.issues[#].versionID  /string token
450     
451     オブジェクトメソッド
452     listEntry.toString(Index)   
453     listEntry.push(Identifier)
454     listEntry.getStatus()
455     
456 listEntry は listEntryCollection に格納される
457 listEntryCollection はデフォルトで this.parent.entryListとして参照される。
458 */
459 listEntry=function(myIdentifier){
460     this.dataInfo = Xps.parseIdentifier(myIdentifier);//dataInfoそのものを拡張すればプロパティが不要となる?
461     this.parent;//初期化時にリポジトリへの参照を設定
462     this.product = encodeURIComponent(this.dataInfo.product.title)+"#"+encodeURIComponent(this.dataInfo.product.opus);
463     this.sci     = encodeURIComponent(this.dataInfo.sci[0].cut);
464 if(typeof this.dataInfo.line == 'undefined'){
465 //識別子にバージョン情報が含まれない場合は初期バーションで補填(nullとかのほうが良いかも)
466     this.issues  = [[
467         new XpsLine(nas.pmdb.pmTemplate.members[0].line).toString(true),
468         new XpsStage(nas.pmdb.pmTemplate.members[0].stages[0]).toString(true),
469         new XpsStage(nas.pmdb.jobNames.getTemplate(nas.pmdb.pmTemplate.members[0].stages[0],"init")[0]).toString(true),
470         "Startup"
471     ]];
472 }else{
473     this.issues  = [[
474         encodeURIComponent(this.dataInfo.line.toString(true)),
475         encodeURIComponent(this.dataInfo.stage.toString(true)),
476         encodeURIComponent(this.dataInfo.job.toString(true)),
477         this.dataInfo.currentStatus.toString(true)
478     ]];
479 }
480     this.issues[0].identifier=myIdentifier;
481     this.issues[0].time=nas.FCT2Frm(this.dataInfo.sci[0].time);
482 if(arguments.length>1) {
483         this.titleID             = arguments[1];
484         this.episodeID           = arguments[2];
485         this.issues[0].cutID     = arguments[3];
486         this.issues[0].versionID = null;
487     }
488 }
489 /**
490     エントリは引数が指定されない場合、管理情報を除いたSCI情報分のみを返す
491     引数があれば引数分の管理履歴をさかのぼって識別子を戻す
492     このメソッド全体がIssues配列の並びが発行順であることを期待している
493     リスト取得の際にソートをかけることで解決
494     ライン拡張後はソートで解決できなくなるので要注意
495     方針としては、各ラインをまたがずに開始点まで遡れるように設定する
496 */
497 listEntry.prototype.toString=function(myIndex){
498     if(typeof myIndex == "undefined"){myIndex = -1;}
499     if(myIndex < 0){
500         return [this.product,this.sci].join("//");
501     }else{
502         if(myIndex<this.issues.length){
503             return this.issues[this.issues.length - 1 - myIndex].identifier;
504 //            return [this.product,this.sci].join("//")+"//"+ this.issues[this.issues.length - 1 - myIndex].join("//");
505             //この部分の手続はラインをまたぐと不正な値を戻すので要修正 11.23
506         }else{
507             return this.issues[this.issues.length - 1].identifier;
508 //            return [this.product,this.sci].join("//")+"//"+ this.issues[this.issues.length - 1].join("//");
509         }
510     }
511 }
512 /**
513 */
514 listEntry.prototype.dumpIssues=function(form){
515     var myResult="";
516     switch(form){
517     case 'html':
518     break;
519     case 'dump':
520     default:
521         for (var idx=0;idx < this.issues.length ;idx++){
522             myResult += decodeURIComponent(this.issues[idx].toString());
523             myResult += '\n';
524         }
525     }
526     return myResult;
527 }
528 /**
529     識別子を引数にして管理情報をサブリストにプッシュする
530     管理情報のみが与えられた場合は無条件で追加
531     フルサイズの識別子が与えられた場合は SCI部分までが一致しなければ操作失敗
532     追加成功時は管理情報部分を配列で返す
533     
534     
535     SCI部分のみでなく ラインとステージが一致しないケースも考慮すること(今回の実装では不用)
536     
537     ネットワークリポジトリ・DB接続用にIDを増設
538 */
539 listEntry.prototype.push=function(myIdentifier){
540     if(Xps.compareIdentifier(this.issues[0].identifier,myIdentifier) < 1){return false;}
541     var dataInfo=Xps.parseIdentifier(myIdentifier);
542     if(dataInfo.currentStatus){
543         var issueArray = [
544             encodeURIComponent(dataInfo.line.toString(true)),
545             encodeURIComponent(dataInfo.stage.toString(true)),
546             encodeURIComponent(dataInfo.job.toString(true)),
547             dataInfo.currentStatus.toString(true)
548         ];
549     }else{
550         var issueArray = [];
551     }
552         issueArray.identifier=myIdentifier;
553         issueArray.time=nas.FCT2Frm(dataInfo.sci[0].time);
554     if(arguments.length>1) {
555 //        this.titleID         = arguments[1];
556 //        this.episodeID       = arguments[2];
557         issueArray.cutID     = arguments[3];
558         issueArray.versionID = arguments[4];
559     }
560         for (var iid = 0 ; iid < this.issues.length ; iid ++ ){
561             if(this.issues[iid].join('//')==issueArray.join('//')) return false;
562         }
563         this.issues.push(issueArray);
564         this.issues.sort(issuesSorter);
565         return this.issues;
566 }
567 /**
568 A=new listEntry("%E3%81%8B%E3%81%A1%E3%81%8B%E3%81%A1%E5%B1%B1Max#%E3%81%8A%E3%81%9F%E3%82%81%E3%81%97[%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%AB%E3%83%83%E3%83%88]//S-C10(72)//0:trunk//1:layout//1://");
569 A
570 */
571 /**
572     エントリのステータスを取得する
573     記録位置は、最終ジョブ
574     エントリにステータスを設定する機能は設けない
575     と、思ったけどやはり設定機能を作る
576     新規の状況更新はすべてリポジトリ本体からの再読出で行う
577     リポジトリ本体からの読み出しは冗長にすぎる
578     戻り値をオブジェクトに変更 0809
579 */
580 listEntry.prototype.getStatus=function(){
581     var currentStatusDescription = this.issues[this.issues.length-1][3];
582     if((! currentStatusDescription)&&(this.issues[this.issues.length-1].identifier)){
583         var currenEntryInfo = Xps.parseIdentifier(this.issues[this.issues.length-1].identifier);
584         return currenEntryInfo.currentStatus;
585     }
586     return new JobStatus(currentStatusDescription);
587 }
588 /**
589     エントリのステータスを設定する
590     記録対象は最終ジョブのエントリ
591     先のデータによって設定可能データは制限される
592     管理権限がある場合はAbortedに変更可能
593     いったんAbortedになったエントリは基本的に変更不可
594 Startup > Active
595 Active  > Hold/Fixed:assignment:comment
596 Hold   > Active
597 Fixed   > Active/Aborted(要権限)
598 
599 フロート化・シンクの >> サインは状態の遷移ではなくコピーして登録であり。
600 逆方は、全てのステータスからの複製が可能
601 移行時にもともとのステータスは保存されない
602 
603 現状 >> 遷移先 > 遷移後のサーバ上のデータのステータス
604 Float   >> Startup:assignment:comment/Fixed:assignment:comment > (元データはサーバ上には無い)
605 Startup >>Float > 変わらず
606 Active  >>Float > Hold
607 Hold    >>Float > 変わらず
608 Fixed   >>Float > 変わらず
609 
610 ドキュメントはFloat化する際に必ず複製されて安定化遷移を行う。
611 リポジトリ上には決してFloat状態のエントリを持たない
612 エラー等により、Float状態のデータをリポジトリ上に確認した場合は、同ジョブのStartup、またはFixed状態に自動で遷移する?
613 
614     戻り値は現在のステータス
615     ステータスオブジェクトが多分必要
616     あとswitch文でない方がヨサゲ
617     必要に従ってissuesを更新または追加する
618       Jobが進まないときは更新
619       Jobが進む際に追加 ただし追加時は listEntry.push(Idf)で追加なので注意
620     まだステータスの副次情報は実装しないので配列のまま保存しないように注意
621   ステータスを複合オブジェクト JobStatusとして実装0809
622 
623 
624     issues.identifier/.time の設定が抜けている 2017.0429 早急に要修正!!!!!
625     timeは基本的に変更が無いがidentifierは,
626     statusの変更に従って必ず変わる
627     setStatusの引数がJobStatus  であれば変換は行わない
628     
629     引数にFloatステータスが入った場合は不正引数とする
630     サーバ上のエントリのステータスがFloatになることは無い
631 */
632 listEntry.prototype.setStatus=function(myStatus){
633     var currentIssue  = this.issues[this.issues.length-1];
634 //    var currentStatus = currentIssue[3].split(":");
635     var currentStatus = new JobStatus(currentIssue[3]);//オブジェクト化
636     if(myStatus instanceof JobStatus){
637      var newStatus = myStatus;
638     }else{
639      var newStatus = new JobStatus(myStatus);//オブジェクト化
640     }
641     if (newStatus.content.indexOf("Float")>=0){return false;}
642     if (currentStatus.content=="Hold"){
643         switch (newStatus.content){
644             case "Active":
645                 currentIssue[3] = newStatus.toString(true);
646                 currentIssue.identifier=currentIssue.identifier.replace(/\/\/Hold.*$/,"//"+currentIssue[3]);
647             break;
648             case "Hold":
649             case "Fixed":
650             case "Aborted":
651             default:
652             return new JobStatus(currentIssue[3]);
653         }
654     } else if(currentStatus.content=="Startup"){
655         switch (newStatus.content){
656             case "Active":
657                 this.push(currentIssue.slice(0,3).concat(newStatus.toString(true)).join("//"));
658                 this.issues[this.issues.length-1].identifier=currentIssue.identifier.replace(/\/\/Startup.*$/,"//"+newStatus.toString(true));
659 
660                 this.issues[this.issues.length-1].time=currentIssue.time;
661             break;
662             case "Hold":
663             case "Fixed":
664             case "Aborted":
665             default:
666             return new JobStatus(currentIssue[3]);
667         }
668     } else if(currentStatus.content=="Active"){
669         switch (newStatus.content){
670             case "Hold":
671             case "Fixed":
672                 currentIssue[3] = newStatus.toString(true);
673                 currentIssue.identifier=currentIssue.identifier.replace(/\/\/Active.*$/,"//"+currentIssue[3]);
674             break;
675             case "Active":
676             case "Aborted":
677             default:
678             return new JobStatus(currentIssue[3]);
679         }
680     } else if(currentStatus.content=="Aborted"){
681         switch (newStatus.content){
682             case "Hold":
683             case "Fixed":
684             case "Active":
685             case "Aborted":
686             default:
687             return currentStatus;
688         }
689     } else if(currentStatus.content=="Fixed"){
690         switch (newStatus.content){
691             case "Active":
692                 currentIssue[3] = newStatus.toString(true);
693                 currentIssue.identifier=currentIssue.identifier.replace(/\/\/Fixed.*$/,"//"+currentIssue[3]);
694             break;
695             case "Hold":
696             case "Fixed":
697             case "Aborted":
698             default:
699             return new JobStatus(currentIssue[3]);
700         }
701     }
702 if(dbg) console.log(currentIssue[3]);
703     return new JobStatus(currentIssue[3]);
704 }
705 
706 /**
707     エントリが自分自身を削除する。
708     parentが存在しない場合は削除に失敗する
709 */
710 listEntry.prototype.remove=function(){
711     if(! this.parent) return false;
712     for (var ix=0;ix<this.parent.entryList.length;ix++){
713         if(this.parent.entryList[ix].issues[0].cutID == this.issues[0].cutID){
714             this.parent.entryList.splice(ix,1);
715             return true;
716         };
717     }
718 //この下実行されない…はず されたらヤダ
719 //console.log(this);
720 //console.log(this.parent.entryList.length);
721     return false;
722 }
723 /**
724     listEntryから識別子を抽出するメソッド
725     自己の情報を組み上げて最も正しいと思われる識別子で戻す
726 */
727 listEntry.prototype.getIdentifier=function(issueOffset){
728     if(typeof issueOffset == 'undefined') issueOffset =-1;
729     var myTitle = this.parent.title (this.titleID);
730     var myOpus  = this.parent.opus  (this.episodeID);
731 
732     var myResult = [
733         encodeURIComponent(myTitle.name),
734         '#',encodeURIComponent(myOpus.name),'[',encodeURIComponent(myOpus.description),']//',
735         this.dataInfo.cut,'(',this.dataInfo.time,')'
736     ].join('');
737     if(issueOffset>=0){
738         if (issueOffset > (this.issues.length-1)) issueOffset = (this.issues.length-1);
739         var targetIssue = this.issues[this.issues.length-1-issueOffset];
740         myResult+='//'+targetIssue.join('//');
741     }
742     return myResult;
743 }
744 /**
745     エントリリストコレクション
746     配列ベースで以下のメソッドを持つ
747 
748 .put(entry)       ;エントリ追加
749 .remove(idf)        ;idf指定でエントリ削除
750 .getByIdf(idf)      ;idf指定でエントリを返す
751 .getByToken(token)  ;ネットワークのみ
752 */
753 function listEntryCollection (){
754 /*
755     コレクションにエントリを追加する
756     同識別子のエントリが存在する場合は上書き(置換)
757     存在しなかった場合は新規に追加する
758 */
759      this.put=function (myEntry){
760         for (var ix = 0 ; ix < this.length ; ix ++){
761             if(this[ix].toString().split('//')[0] != myEntry.toString().split('//')[0]) continue;
762             if (Xps.compareIdentifier(this[ix].toString(true),myEntry.toString(true)) >= 1 ) {
763                 this[ix]=myEntry;
764                 return myEntry;
765             }
766         }
767         return this.push(myEntry);
768     }
769 /*
770     識別子指定でコレクションからエントリを削除する
771     存在しなかった場合はfalse
772 */
773     this.remove=function (myIdentifier){
774         for (var ix = 0 ; ix < this.length ; ix ++){
775             if(String(this[ix]).split('//')[0] != myIdentifier.split('//')[0]) continue;
776             if (Xps.compareIdentifier(this[ix].toString(true),myIdentifier) >= 1 ) {
777                 return this.splice(ix,1);
778             }
779         }
780         return false;
781     }
782 /*
783     識別子指定でエントリを取得
784     第二引数で一致レベルを指定
785     指定がない場合は、カットNo一致
786     -2  <NO-match>
787     -1  title
788     0   opus    (プロダクト一致)
789     1   カットNo
790     2   ライン
791     3   ステージ
792     4   ジョブ
793     Repository.entry の基底メソッド
794 */
795     this.getByIdf=function (myIdentifier,opt){
796         if(typeof opt == 'undefined') opt = 1;
797         for (var ix = 0 ; ix < this.length ; ix ++){
798 // 高速化スキップは文字列比較だと取りこぼしが多いので禁止 それよりはcompareIdentifier自体を高速化すること
799             if (Xps.compareIdentifier(this[ix].toString(true),myIdentifier) >= opt ) return this[ix];
800         }
801         return null;
802     }
803 /*
804     トークンでエントリを取得
805     バージョンの指定は不能
806 */
807     this.getByToken=function (myToken){
808         for (var ix = 0 ; ix < this.length ; ix ++){
809             if (this[ix].issues[0].cutID == myToken) return this[ix];
810         }
811         return null;
812     }
813 };
814 listEntryCollection.prototype = Array.prototype;
815 /**
816     ローカルリポジトリ
817     主に最近の作業データをキャッシュする役目
818     カットのデータを履歴付きで保持できる
819     複数カットを扱う 制限カット数内のリングバッファ動作
820     xUIから見るとサーバの一種として働く
821     ローカルストレージを利用して稼働する
822 
823 保存形式
824 info.nekomataya.remaping.dataStore
825 内部にオブジェクト保存
826 リポジトリのデータ取得メソッド
827 Repository.title(myIdentifier) 
828 Repository.opus(myIdentifier)
829 Repository.cut(myIdentifier)
830 Repository.entry(myIdentifier)
831 
832 productsData追加
833     プロダクトデータは DBに直接接続して情報ストアするオブジェクト
834     JSONで通信を行う場合に必須
835     entryList(listEntryコレクション)はこのデータから生成するように変更される?
836     またはentryListからproductsDataを生成する 同時か?
837     動作試験のため maxEntryを増やしてある 10>32 170705
838 */
839 localRepository={
840     name:'localStrageStore',
841     url:'localStorage:',
842     owner:new nas.UserInfo(),
843 //    owner:xUI.currentUser,
844 //    currentProduct:"",
845 //    currentSC:"",
846 //    currentLine:"",
847 //    currentStage:"",
848 //    currentJob:"",
849     productsData:[],
850     entryList:new listEntryCollection(),
851     keyPrefix:"info.nekomataya.remaping.dataStore.",
852     maxEntry:32
853 };
854 /**
855     TITLE取得
856 引数:
857     myIdentifier    識別子またはトークン
858     searchDepth     検索深度 0:タイトルのみ 1:エピソードからもタイトルを探す 2:カットからも
859 
860     エピソードやカットのトークンからもタイトルノードを返す
861     深度指定省略時は 0
862     該当するオブジェクトがない場合はnullを戻す
863 */
864 _title=function(myIdentifier,searchDepth){
865     if(! searchDepth) searchDepth = 0;
866     var myIdf= Xps.parseIdentifier(myIdentifier);
867     for ( var idx = 0 ;idx <this.productsData.length;idx ++){
868         if(
869             (myIdf.title == this.productsData[idx].name)||
870             (myIdentifier == this.productsData[idx].token)
871         ) return this.productsData[idx];
872         if((searchDepth > 0)&&(this.productsData[idx].episodes)){
873             for(var epx = 0 ;epx <this.productsData[idx].episodes[0].length;epx++){
874                 if(
875                     (myIdf.opus == this.productsData[idx].episodes[0][epx].name)||
876                     (myIdentifier == this.productsData[idx].episodes[0][epx].token)
877                 ) return this.productsData[idx];
878             }
879             if(searchDepth >1){
880                  for(var ctx = 0 ;ctx <this.productsData[idx].episodes[0][epx].cuts[0].length;ctx++){
881                     if(
882                         (myIdf.sci[0].cut == this.productsData[idx].episodes[0][epx].cuts[0][ctx].name)||
883                         (myIdentifier == this.productsData[idx].episodes[0][epx].cuts[0][ctx].token)
884                     ) return this.productsData[idx];
885                 }
886             }
887         }
888     };
889     return null;
890 }
891 
892 localRepository.title=_title;
893 /**
894     OPUS取得
895 引数:
896     myIdentifier    識別子またはトークン
897 
898     識別子またはトークンからエピソードノードを戻す
899     該当するオブジェクトがない場合はnullを戻す
900 */
901 _opus=function(myIdentifier,searchDepth){
902 //console.log([myIdentifier,searchDepth])
903     if(! searchDepth) searchDepth = 0;
904     var currentOpus = Xps.parseProduct(myIdentifier);
905     var isTkn = ((currentOpus.opus =='')&&(currentOpus.title == myIdentifier))? true:false;
906 //console.log([searchDepth,currentOpus,isTkn])
907     for ( var idx = 0 ;idx <this.productsData.length;idx ++){
908       if(
909         ((! isTkn ) &&
910         (String(currentOpus.title).indexOf(this.productsData[idx].name) < 0))||
911         (! this.productsData[idx].episodes )
912       ) continue;
913       for (var eid = 0 ;eid < this.productsData[idx].episodes[0].length; eid ++){
914         if (
915             (currentOpus.opus == this.productsData[idx].episodes[0][eid].name)||
916             (myIdentifier == this.productsData[idx].episodes[0][eid].token)
917         ) return this.productsData[idx].episodes[0][eid];
918       };
919     };
920     return null;
921 }
922 
923 localRepository.opus=_opus;
924 /**
925     CUT取得
926     myIdentifier は識別子またはトークンからカットノードを戻す
927 */
928 _cut=function(myIdentifier){
929     var target = Xps.parseIdentifier(myIdentifier);
930     var isTkn = ((target.cut =='')&&(target.title == myIdentifier))? true:false;
931 //console.log([myIdentifier,target,isTkn]);
932 //console.log(this.productsData)
933     for ( var idx = 0 ;idx <this.productsData.length;idx ++){
934     if(
935         (! this.productsData[idx].episodes )||(
936          (! isTkn )&&
937          (String(target.title).indexOf(this.productsData[idx].name) < 0)
938         )
939       ) continue;
940 //console.log(this.productsData[idx].episodes[0]);
941       for (var eid = 0 ;eid < this.productsData[idx].episodes[0].length; eid ++){
942         if (
943             (! this.productsData[idx].episodes[0][eid].cuts )||(
944              (! isTkn ) &&
945              (String(target.opus).indexOf(this.productsData[idx].episodes[0][eid].name) < 0)
946             )
947         ) continue;
948 //console.log(this.productsData[idx].episodes[0][eid].cuts[0]);
949         for (var cid = 0 ; cid < this.productsData[idx].episodes[0][eid].cuts[0].length ; cid ++) {
950             if (
951                 (Xps.compareCutIdf(target.sci[0].cut,this.productsData[idx].episodes[0][eid].cuts[0][cid].name))||
952                 (myIdentifier == this.productsData[idx].episodes[0][eid].cuts[0][cid].token)
953             ) return this.productsData[idx].episodes[0][eid].cuts[0][cid];
954         };
955       };
956     };
957     return null;
958 }
959 
960 localRepository.cut=_cut;
961 
962 /**
963     プロダクト(タイトル)データを更新
964     タイトル一覧をクリアして更新する エピソード更新を呼び出す
965     受信したデータを複合させてサービス上のデータ構造を保持する単一のオブジェクトに
966     getXx で概要(一覧)を取得
967     xxUpdateが詳細を取得して this.productsData を上書きしてゆく
968     プロダクト詳細は、各個に取得するように変更
969     引き続きの処理を行う際はコールバック渡し
970     コールバックがない場合は、全プロダクトの詳細を取得?
971     プロダクトデータ取得のみの場合は 空動作のコールバックを渡す必要あり
972     myToken 引数がある場合はtokenが一致したエントリのみを処理する
973     myToken は配列でも良い
974 */
975 localRepository.getProducts=function(callback,callback2,myToken){
976         if(typeof myToken == 'undefined') myToken =[];
977         if(!(myToken instanceof Array)) myToken = [myToken];
978     try{
979         var keyCount=localStorage.length;
980         for (var kid=0;kid<keyCount;kid++){
981             if(localStorage.key(kid).indexOf(this.keyPrefix)==0){
982                 var currentIdentifier=localStorage.key(kid).slice(this.keyPrefix.length);
983 //タイトルリストにすでに登録されているか検査 未登録エントリをDBに追加
984 //token指定がある場合は、登録タイトルを抹消して新しい情報で上書き?
985                 var currentTitle = this.title(currentIdentifier);
986                 if(! currentTitle){
987 // console.log(currentIdentifier);
988                 if((myToken.indexOf(localStorage.key(kid)) >= 0)||(! myToken.length)){
989                     var myData=Xps.parseIdentifier(currentIdentifier);
990                     localRepository.productsData.push({
991                         token:localStorage.key(kid),
992                         name:myData.title,
993                         description:"",
994                         created_at:null,
995                         updated_at:null,
996                         episodes:[[]]
997                     });
998                 }};
999             };
1000         };
1001         if(callback instanceof Function){
1002             callback();
1003         }else{
1004 //console.log('get Episodes###')
1005             for(var ix =0;ix < localRepository.productsData.length; ix ++){
1006 //console.log(this.productsData[ix].token)
1007                 this.getEpisodes(false,false,this.productsData[ix].token);
1008             }    
1009         };
1010     }catch(err){
1011         if(callback2 instanceof Function){callback2();}
1012     }
1013 }
1014 /**
1015     opusデータ更新
1016 引数:成功時コールバック,失敗時コールバック,タイトルキー
1017 myOpusToken 引数がある場合は、引数で制限された処理を行う
1018 */
1019 localRepository.getEpisodes=function(callback,callback2,myProductToken,myOpusToken){
1020     var allOpus =false
1021     if(typeof myOpusToken == 'undefined'){
1022         myOpusToken = [];
1023         allOpus     = true;
1024         var myProduct=this.title(myProductToken);
1025 //console.log(myProduct);
1026         if(! myProduct){console.log('stop'); return false;}
1027         for (var px = 0 ;px < myProduct.episodes[0].length;px ++){myOpusToken.push(myProduct.episodes[0][px].token);}
1028     }
1029     if(!(myOpusToken instanceof Array)) myOpusToken = [myOpusToken];
1030 //console.log(myOpusToken);
1031 //console.log(documentDepot.currentProduct);
1032     try{
1033         var myProduct=localRepository.title(myProductToken);
1034         var keyCount     = localStorage.length;
1035         for (var kid = 0;kid < keyCount; kid++){
1036 //console.log(myProduct.name);
1037             if(localStorage.key(kid).indexOf(this.keyPrefix)==0){
1038                 var currentIdentifier=localStorage.key(kid).slice(this.keyPrefix.length);
1039                 var myData = Xps.parseIdentifier(currentIdentifier);
1040                 if(myData.title != myProduct.name) continue;//タイトル違いを排除
1041 //OPUSリストにすでに登録されているか検査 未登録エントリはDBに追加 tokenは初出のkey
1042                 var currentOpus = localRepository.opus(currentIdentifier);
1043                 if(! currentOpus){
1044                 if((! myOpusToken.length)||(myOpusToken.indexOf(localStorage.key(kid)) >= 0)){
1045                     var Ex = myProduct.episodes[0].push({
1046                         token:localStorage.key(kid),
1047                         name:myData.opus,
1048                         description:myData.subtitle,
1049                         created_at:null,
1050                         updated_at:null,
1051                         cuts:[[]]
1052                     });
1053                     currentOpus = myProduct.episodes[0][Ex-1];
1054                     if(!(callback instanceof Function)){
1055                         localRepository.getSCi(false,false,currentOpus.token);
1056                     };
1057                 }};
1058             };
1059         };
1060 //エピソード1取得毎に実行したほうが良いかも?
1061 //このままだと必ずタイトル内の全エピソード取得になる
1062         if(callback instanceof Function){ callback();}   
1063     } catch(err) {
1064         if(callback2 instanceof Function){ callback2();}
1065     }
1066 }
1067 /**
1068     エピソード毎にカットリストを取得
1069     エピソード詳細の内部情報にコンバート    
1070 引数
1071     myOpusToken   ターゲットの話数キー(識別子で与える)
1072     pgNo      リストのページID 1 origin
1073     ppg       ページごとのエントリ数
1074  */
1075 localRepository.getSCi=function (callback,callback2,myOpusToken,pgNo,ppg) {
1076 //現在、pgNo,ppgは意味を持たない引数
1077     try{
1078         var myOpus = this.opus(myOpusToken);
1079         if(! myOpus){console.log('noOpus');return false;}
1080 //console.log('prcessing : '+myOpus.name);
1081         var keyCount=localStorage.length;
1082         for (var kid = 0; kid < keyCount; kid ++){
1083             if(localStorage.key(kid).indexOf(this.keyPrefix)==0){
1084                 var currentIdentifier=localStorage.key(kid).slice(this.keyPrefix.length);
1085                 var myData = Xps.parseIdentifier(currentIdentifier);
1086                 if(myOpus.name != myData.opus) continue;
1087                 var myCut = this.cut(currentIdentifier);
1088                 var currentEntry= this.entry(currentIdentifier);
1089                 if(myCut){
1090                 //登録済みカットなのでissues追加
1091 //console.log("push version :" + decodeURIComponent(currentIdentifier));
1092                     myCut.versions.push({
1093                         updated_at:null,
1094                         description:currentIdentifier,
1095                         version_token:localStorage.key(kid)
1096                     });
1097                     if(currentEntry){
1098         //登録済みプロダクトなのでエントリに管理情報を追加
1099                         currentEntry.push(currentIdentifier);
1100                     }else{
1101                 //情報不整合                            
1102                     }
1103                 }else{
1104                 //未登録カット  新規登録
1105                 //エントリが既に登録済みなので不整合 消去
1106                     if(currentEntry) currentEntry.remove();
1107 //console.log("add :: "+decodeURIComponent(currentIdentifier));
1108                     var myCut = myOpus.cuts[0].push({
1109                         token:localStorage.key(kid),
1110                         name:myData.cut,
1111                         description:currentIdentifier,
1112                         created_at:null,
1113                         updated_at:null,
1114                         versions:[{
1115                             updated_at:null,
1116                             description:currentIdentifier,
1117                             version_token:localStorage.key(kid)
1118                         }]
1119                     });
1120                 //未登録新規プロダクトなのでエントリ追加
1121                     //ここにローカルストレージのキーIDを置く タイトルとエピソードの情報取得キーは現在エントリなし
1122                     //初出エントリのキーか? 0524
1123                     var newEntry = new listEntry(currentIdentifier,null,null,localStorage.key(kid));
1124                     newEntry.parent = this;
1125                     this.entryList.push(newEntry);
1126                 }
1127             };
1128         };
1129         if(callback instanceof Function){ callback();}   
1130     } catch(err) {
1131         if(callback2 instanceof Function){ callback2();}
1132     }
1133 }
1134 /**
1135     getListメソッドは、entryList/productsData の更新を行う
1136     メンバー初期化を行わない
1137     ローカルストレージ内のデータを走査してリストを更新
1138     既に存在するエントリは上書き//新規のエントリは追加//存在しないエントリは削除する
1139     getList関数自体が非同期動作になるように調整
1140 引数:
1141   force /Bool
1142   callback  /Function
1143 forceオプションは、引数の統一のために存在する NetworkRepositoryでのみ必要なオプション
1144 localStorageでは意味を持たないダミーオプションとなる
1145 callBack関数が指定された場合 処理終了直前に実行される 存在しない場合は ドキュメントセレクタの更新が行われる
1146 
1147 戻値: なし
1148     実際のデータ・エントリリストが必要な場合は、localRepository.entryList を参照すること
1149 */
1150 localRepository.getList=function(force,callback){
1151 //console.log('localRepository getList');
1152 //    if(callback instanceof Function){callback();}else{documentDepot.documentsUpdate(this.entryList);}
1153     if(!(callback instanceof Function)){documentDepot.documentsUpdate(this.entryList);}
1154         return;
1155     var keyCount=localStorage.length;//ローカルストレージのキー数を取得
1156     this.entryList.length=0;//配列初期化
1157     for (var kid=0;kid<keyCount;kid++){
1158         if(localStorage.key(kid).indexOf(this.keyPrefix)==0){
1159             var currentIdentifier=localStorage.key(kid).slice(this.keyPrefix.length);
1160             //エントリリストにすでに登録されているか検査
1161             var currentEntry = this.entry(currentIdentifier);
1162             if(currentEntry){
1163 if(dbg) console.log("push issues :" + decodeURIComponent(currentIdentifier));
1164                 //登録済みプロダクトなのでエントリに管理情報を追加
1165                 currentEntry.push(currentIdentifier);
1166             }else{
1167 if(dbg) console.log("add :: "+decodeURIComponent(currentIdentifier));
1168                 //未登録新規プロダクトなのでエントリ追加
1169                 var newEntry = new listEntry(currentIdentifier);
1170                 newEntry.parent = this;
1171                 this.entryList.push(newEntry);
1172             }
1173         }
1174     }
1175     //コールバックがない場合はデフォルト動作としてエントリをドキュメントブラウザに送る
1176     if(callback instanceof Function){
1177         callback();
1178     }else{
1179         documentDepot.documentsUpdate(this.entryList);
1180     }
1181 //    return this.entryList.length;//no use
1182 }
1183 /**
1184     ローカルリポジトリにエントリを追加
1185     引数:Xpsオブジェクト
1186     与えられたXpsオブジェクトから識別子を自動生成
1187     識別子にkeyPrefixを追加してこれをキーにしてデータを格納する
1188     ここでステータスの解決を行う?
1189     キーが同名の場合は自動で上書きされるのでクリアは行わない
1190     エントリ数の制限を行う
1191     エントリ数は、キーの総数でなく識別子の第一、第二要素を結合してエントリとして認識する
1192 
1193     Floating ステータスが新設
1194     Floating ステータスのドキュメントは書込み不可とする。
1195     リポジトリメソッドに渡す前にステータスの解決を行い適切なステータスを持たせること。
1196     このメソッドはステータス変更をサポートしない。
1197 */
1198 localRepository.pushEntry=function(myXps,callback,callback2){
1199     var msg='';
1200     if(String(myXps.cut).match(/^\s*$/)){
1201         msg += localize({
1202             en:"you can't save entry without cutNo.",
1203             ja:"カット番号のないエントリは記録できません。"
1204         });
1205     };
1206     if(myXps.currentStatus.content.indexOf('Floating')>=0){
1207         msg += '\n'+localize({
1208             en:"you can't save entry of Flating status.",
1209             ja:"Floatingエントリは記録できません。"
1210         });
1211     }
1212     if(msg.length){
1213         alert(msg);
1214         return false;
1215     };
1216 //クラスメソッドで識別子取得
1217     var myIdentifier=Xps.getIdentifier(myXps);
1218 //識別子に相当するアイテムがローカルストレージ内に存在するかどうかを比較メソッドで検査
1219     for (var pid=0;pid<this.entryList.length;pid++){
1220         if(Xps.compareIdentifier(this.entryList[pid].toString(),myIdentifier) > 3){
1221             //既存のエントリが有るのでストレージとリストにpushして終了
1222             try{
1223                 this.entryList[pid].push(myIdentifier);
1224                 localStorage.setItem(this.keyPrefix+myIdentifier,myXps.toString());
1225                 if (xUI.XPS === myXps){
1226                     xUI.setStored('current');
1227                     sync();
1228                 }
1229             }catch(err){
1230                 if(callback2 instanceof Function){callback2();}                
1231             }
1232             sync();
1233             documentDepot.updateDocumentSelector();
1234             if(callback instanceof Function){callback();}
1235             return this.entryList[pid];
1236         };
1237     };
1238 // console.log(myXps)
1239 //console.log("既存エントリなし :追加処理");
1240 //既存エントリが無いので新規エントリを追加
1241 //設定制限値をオーバーしたら、警告する。 OKならばローカルストレージから最も古いエントリを削除して実行
1242     try{
1243         if ( this.entryList.length >= this.maxEntry ){
1244             var msg=localize({en:"over limit!\n this entry will remove [%1]\n ok?",ja:"制限オーバーです!\nこのカットを登録するとかわりに[%1]が消去されます。\nよろしいですか?"},decodeURIComponent(this.entryList[0].toString()));
1245             if(confirm(msg)){
1246 //console.log("removed Item !");
1247                 for (var iid=0; iid < this.entryList[0].issues.length ; iid++ ){
1248                     localStorage.removeItem( this.keyPrefix + this.entryList[0].issues[iid].identifier );
1249                 };
1250                 this.entryList[0].remove();//アイテムメソッドで削除
1251                 localStorage.setItem(this.keyPrefix+myIdentifier,myXps.toString());
1252                 this.entryList.put(new listEntry(myIdentifier));//Collectionメソッドで追加
1253 //console.log(this.entryList.length +":entry/max: "+ this.maxEntry)
1254             }
1255         }else{
1256             localStorage.setItem(this.keyPrefix+myIdentifier,myXps.toString());
1257 //            this.entryList.put(new listEntry(myIdentifier)); 
1258         }
1259     }catch(err){
1260 //console.log('localRepositoty.pushEntry');
1261 //console.log(err);
1262         if(callback2 instanceof Function){callback2();}                
1263     }
1264     sync();
1265     documentDepot.updateDocumentSelector();
1266     if(callback instanceof Function){callback();}
1267     return this.entryList[this.entryList.length-1];
1268 }
1269 
1270 /**
1271     識別子を引数にしてリスト内を検索
1272     一致したデータをローカルストレージから取得してXpsオブジェクトで戻す
1273     識別子に管理情報があればそれをポイントして、なければ最も最新のデータを返す
1274     コールバック渡し可能
1275     引数は、Object
1276     読み出し直後は必ず書き込み禁止のモードとなる
1277 */
1278 localRepository.getEntry=function(myIdentifier,isReference,callback){
1279     if(typeof isReference == 'undefined'){isReference = false;}
1280     //識別子をパース
1281     var targetInfo = Xps.parseIdentifier(myIdentifier);//根底としてここで解釈に問題が発生している
1282 
1283     var myIssue = false;
1284     var refIssue = false;
1285 
1286     var myEntry = this.entry(myIdentifier);
1287     if(! myEntry){
1288 if(dbg) console.log("noProduct : "+ decodeURIComponent(myIdentifier));//プロダクトが無い
1289         return false;
1290     }
1291     if(! targetInfo.currentStatus){
1292    //引数に管理部分がないので、最新のissueとして補う
1293         var cx = myEntry.issues.length-1;//最新のissue
1294         myIssue = myEntry.issues[cx];//配列で取得
1295     } else {
1296     //指定管理部分からissueを特定する 連結して文字列比較(後方から検索) リスト内に指定エントリがなければ失敗
1297         checkIssues:{
1298             for (var cx = (myEntry.issues.length-1) ; cx >= 0 ;cx--){
1299                 if ( Xps.compareIdentifier(myEntry.issues[cx].identifier,myIdentifier) > 4){
1300                     myIssue = myEntry.issues[cx];
1301                     break checkIssues;
1302                 }
1303             }
1304             if (! myIssue){
1305 console.log( 'no target data :'+ decodeURIComponent(myIdentifier) );//ターゲットのデータが無い
1306                 return false;
1307             }
1308         }
1309     }
1310 
1311     // 構成済みの情報を判定 (リファレンス置換 or 新規セッションか)
1312     // ソースデータ取得
1313 if(dbg) console.log("readIn XPS");
1314 if(dbg) console.log(decodeURIComponent(myIssue.identifier));
1315 
1316     var myXpsSource=localStorage.getItem(this.keyPrefix+myIssue.identifier);
1317 //識別子を再結合してもキーが得られない場合があるのでエントリから対応キーの引き出しを行う
1318 
1319     if(myXpsSource){
1320         if(isReference){            
1321         //データ単独で現在のセッションのリファレンスを置換
1322             documentDepot.currentReference = new Xps();
1323             documentDepot.currentReference.readIN(myXpsSource);
1324             xUI.resetSheet(undefined,documentDepot.currentReference);
1325         }else{
1326         //新規セッションを開始する
1327             documentDepot.currentDocument = new Xps();
1328             documentDepot.currentDocument.readIN(myXpsSource);
1329             documentDepot.currentReference = new Xps(5,144);//カラオブジェクトをあらかじめ新規作成
1330            //自動設定されるリファレンスはあるか?
1331             //指定管理部分からissueを特定する 文字列化して比較
1332             if ( cx > 0 ){
1333                 if(parseInt(decodeURIComponent(myIssue[2]).split(':')[0]) > 0 ){    
1334                 //ジョブIDが1以上なので 単純に一つ前のissueを選択する
1335                 //必ず先行jobがある  =  通常処理の場合は先行JOBが存在するが、単データをエントリした場合そうでないケースがあるので対処が必要 2016 12 29
1336                     refIssue = myEntry.issues[cx-1];
1337                 }else if(decodeURIComponent(myIssue[1]).split(':')[0] > 0 ){
1338                 //第2ステージ以降前方に向かって検索
1339                 //最初にステージIDが先行IDになった要素が参照すべき要素
1340                     for(var xcx = cx-1 ;xcx >= 0 ; xcx --){
1341                         if (parseInt(decodeURIComponent(myEntry.issues[xcx][1]).split(':')[0]) == (parseInt(decodeURIComponent(myIssue[1]).split(':')[0])-1)){
1342                             refIssue = myEntry.issues[xcx];
1343                             break;
1344                         }
1345                     }
1346                 };//cx==0 のケースでは、デフォルトで参照すべき先行ジョブは無い
1347                 if(refIssue){
1348 //if(dbg) console.log(this.keyPrefix + refIssue.identifier);
1349                     myRefSource=localStorage.getItem(this.keyPrefix + refIssue.identifier);//リファレンスソースとる
1350                     if(myRefSource){
1351 //if(dbg) console.log('myRefSource:');
1352 //if(dbg) console.log(myRefSource);
1353                         documentDepot.currentReference.readIN(myRefSource);
1354                     }
1355                 }
1356             }
1357 // if(dbg) console.log(documentDepot.currentReference);//単エントリで直前のエントリ取得不能の可能性あり
1358             xUI.resetSheet(documentDepot.currentDocument,documentDepot.currentReference);
1359             xUI.sessionRetrace = myEntry.issues.length-cx-1;
1360             xUI.setUImode('browsing');sync("productStatus");
1361             xUI.flushUndoBuf();sync('undo');sync('redo');
1362             if(callback instanceof Function){setTimeout(callback,10)};
1363         }
1364     } else { 
1365         return false;
1366     }
1367 }
1368 /**
1369     DBにタイトルを作成する。
1370     confirmなし 呼び出し側で済ませること
1371     必要あれば編集UI追加
1372 引数
1373     タイトル(必須)
1374     備考テキスト
1375     Pmオブジェクト
1376     コールバック関数2種
1377 識別子は受け入れない 必要に従って前段で分解のこと
1378 */
1379 localRepository.addTitle=function (myTitle,myDescription,myPm,callback,callback2){
1380 //現在ローカルリポジトリ側で行う処理は存在しない コールバックの実行のみを行う
1381 //タイトルDBが実装された場合はDBにエントリを加える
1382 console.log(['localRepository.addTitle',myTitle,myDescription,myPm].join(':'));
1383  if(callback instanceof Function) callback();
1384     return true;
1385 }
1386 /**
1387     DBにOPUS(エピソード)を作成する。
1388 引数
1389     タイトルを含む識別子 カット番号は求めない
1390     コールバック関数2種
1391     識別子のみ受け入れ
1392     このルーチンを呼び出す時点で、タイトルは存在すること
1393 */
1394 localRepository.addOpus=function (myIdentifier,prodIdentifier,callback,callback2){
1395 //console.log(['localRepository.addOpus',myIdentifier,prodIdentifier].join(':'));
1396 //現在ローカルリポジトリ側で行う処理は存在しない コールバックの実行のみを行う
1397 //タイトルDBに加えて、documetntDepotのプロダクト更新が必要
1398 //タイトルDB側のイベント処理とするか、または追加後にdocumentDepot側でのデータ要求処理に振替
1399     
1400  if(callback instanceof Function) callback();
1401     return true;
1402 }
1403 /**
1404     識別子を指定してローカルリポジトリから相当エントリを消去する
1405     リストは再構築
1406     ローカルリポジトリに関しては、各ユーザは編集権限を持つ
1407     
1408     また、ステータス変更のため内部ルーチンがこのメソッドを呼ぶ
1409     直接要素編集をしても良い?
1410 */
1411 localRepository.removeEntry=function(myIdentifier){
1412     var myEntry = this.entry(myIdentifier);
1413     if(myEntry){
1414 //エントリに関連するアイテムをすべて削除
1415         for (var iid=0;iid < myEntry.issues.length;iid++){
1416             localStorage.removeItem(this.keyPrefix+myEntry.issues[iid].identifier);
1417         };
1418 //エントリ自身を削除
1419         var res = myEntry.remove();
1420         if(! res ){console.log('fail removed : ' + res)}
1421 //ドキュメントブラウザ更新
1422     documentDepot.updateDocumentSelector();
1423 //        documentDepot.rebuildList();//ドキュメントブラウザの再ビルド
1424         return true;
1425     };
1426     return myEntry;    
1427 };
1428 /**
1429     識別子でエントリリストを検索して該当するリストエントリを返す操作をメソッド可
1430     issuesは受取先で評価
1431     NetroekRepositoryにも同メソッドを
1432     引数 opt を加えると プロダクトまで一致で最初のエントリを返す
1433  */
1434 localRepository.entry=function(myIdentifier,opt){
1435     if(! opt) {opt = 1}else{opt = 0};
1436     return this.entryList.getByIdf(myIdentifier,opt);
1437     
1438     if(! opt) {opt = 0}else{opt = -1};
1439     for (var pid=0;pid<this.entryList.length;pid++){
1440         if(Xps.compareIdentifier(this.entryList[pid].toString(),myIdentifier) > opt){
1441             return this.entryList[pid]
1442         }
1443     }
1444     return null;        
1445 }
1446 /**
1447     以下、ステータス操作コマンドメソッド
1448     serviceAgentの同名メソッドから呼び出す下位ファンクション
1449 
1450 */
1451 /**
1452     現在のドキュメントをアクティベートする
1453 */
1454 localRepository.activateEntry=function(callback,callback2){
1455     var currentEntry = this.entry(Xps.getIdentifier(xUI.XPS));
1456     var currentCut   = this.cut(currentEntry.toString());
1457 //    var currentCut   = this.cut(currentEntry.issues[0].cutID);
1458         var newXps = new Xps();
1459         var currentContents = localStorage.getItem(this.keyPrefix+currentEntry.toString(0));
1460         if (currentContents) { newXps.readIN(currentContents); }else {return false;}
1461         //ここ判定違うけど保留 あとでフォーマット整備 USERNAME:uid@domain(mailAddress)  型式で暫定的に記述
1462         //':'が無い場合は、メールアドレスを使用
1463         if ((newXps)&&(xUI.currentUser.sameAs(newXps.update_user))){
1464              //同内容でステータスを変更したエントリを作成 新規に保存して成功したら先のエントリを消す
1465             newXps.currentStatus = new JobStatus('Active');
1466             localStorage.setItem(this.keyPrefix+Xps.getIdentifier(newXps),newXps.toString());
1467             var result = (localStorage.getItem(this.keyPrefix+Xps.getIdentifier(newXps)) == newXps.toString())?true:false;
1468             if(result){
1469                 localStorage.removeItem (this.keyPrefix+currentEntry.toString(0));
1470                 currentEntry.setStatus(newXps.currentStatus);
1471                 var myVersion=currentCut.versions[currentCut.versions.length-1];
1472                   myVersion.updated_at=new Date().toString();
1473                   myVersion.description=currentEntry.toString(0);
1474                   myVersion.version_token=this.keyPrefix+myVersion.description;
1475                 xUI.XPS.currentStatus=new JobStatus('Active');//ドキュメントステータスを更新
1476 			    xUI.setStored("current");//UI上の保存ステータスをセット
1477 			    sync();//保存ステータスを同期
1478                 selectSCi();//カレントデータを再セレクトして情報更新
1479                 sync('historySelector');//履歴セレクタ更新
1480             }else{
1481 //console.log('ステータス変更失敗 :');
1482                 delete newXps ;
1483                 if(callback2 instanceof Function) {setTimeout(callback2,10);}
1484                 return false;
1485             }
1486             xUI.setUImode('production');
1487             xUI.sWitchPanel();//パネルクリア
1488             if(callback instanceof Function){ setTimeout (callback,10);}
1489             return true;
1490         }else{
1491 //console.log('ステータス変更不可 :'+ Xps.getIdentifier(newXps));
1492             if(callback2 instanceof Function) {setTimeout(callback2,10);}
1493             return false
1494         }
1495 }
1496 //作業を保留する リポジトリ内のエントリを更新してステータスを変更 
1497 localRepository.deactivateEntry=function(callback,callback2){
1498     var currentEntry = this.entry(Xps.getIdentifier(xUI.XPS));
1499     var currentCut   = this.cut(currentEntry.toString());
1500 //    var currentCut   = this.cut(currentEntry.issues[0].cutID);
1501             //Active > Holdへ
1502         var newXps = new Xps();
1503         var currentContents = xUI.XPS.toString();
1504         newXps.readIN(currentContents);
1505         //ユーザ判定は不用
1506         if (newXps){
1507              //同内容でステータスを変更したエントリを作成 新規に保存して成功したら先のエントリを消す
1508             newXps.currentStatus = new JobStatus('Hold');//(ジョブID等)status以外の変更はない
1509             localStorage.setItem(this.keyPrefix+Xps.getIdentifier(newXps),newXps.toString());
1510             var result = (localStorage.getItem(this.keyPrefix+Xps.getIdentifier(newXps)) == newXps.toString())?true:false;
1511             if(result){
1512 if(dbg) console.log('deactivated');
1513                 localStorage.removeItem(this.keyPrefix+currentEntry.toString(0));
1514                 currentEntry.setStatus(newXps.currentStatus);
1515                 var myVersion=currentCut.versions[currentCut.versions.length-1];
1516                   myVersion.updated_at=new Date().toString();
1517                   myVersion.description=currentEntry.toString(0);
1518                   myVersion.version_token=this.keyPrefix+myVersion.description;
1519                 documentDepot.rebuildList();
1520                 xUI.XPS.currentStatus=new JobStatus('Hold');//ドキュメントステータスを更新
1521 			    xUI.setStored("current");//UI上の保存ステータスをセット
1522 			    sync();//保存ステータスを同期
1523                 selectSCi();//カレントデータを再セレクトして情報更新
1524                 sync('historySelector');//履歴セレクタの更新
1525             }else{
1526             //保存に失敗
1527 //console.log('保留失敗')
1528                 delete newXps ;
1529 			    if(callback2 instanceof Function) setTimeout(callback2,10);
1530                 return false;
1531             }
1532             //データをホールドしたので、リストを更新 編集対象をクリアしてUIを初期化
1533             xUI.setUImode('browsing');
1534             xUI.sWitchPanel();//パネルクリア
1535 			if(callback instanceof Function) setTimeout(callback,10);
1536         }else{
1537 //console.log('保留可能エントリが無い :'+ Xps.getIdentifier(newXps));
1538 			if(callback2 instanceof Function) setTimeout(callback2,10);
1539             return false ;
1540         }
1541 }
1542 /** 
1543     作業にチェックイン
1544     リポジトリ種別にかかわらないので
1545     このメソッドを呼ぶ前段でジョブ名称は確定しておくこと
1546     ジョブ名指定のない場合は操作失敗    
1547 */
1548 localRepository.checkinEntry=function(myJob,callback,callback2){
1549     if( typeof myJob == 'undefined') return false;
1550     myJob = (myJob)? myJob:xUI.currentUser.handle;
1551     var currentEntry = this.entry(Xps.getIdentifier(xUI.XPS));
1552     var currentCut   = this.cut(currentEntry.toString());
1553 //    var currentCut   = this.cut(currentEntry.issues[0].cutID);
1554     if(! currentEntry){
1555 if(dbg) console.log ('noentry in repository :' +  decodeURIComponent(currentEntry))
1556         //当該リポジトリにエントリが無い
1557          return false;
1558       }
1559             //次のJobへチェックイン 読み出したデータでXpsを初期化 
1560         var newXps = new Xps();
1561         var currentContents = localStorage.getItem(this.keyPrefix+currentEntry.toString(0));
1562         if (currentContents) {
1563             newXps.readIN(currentContents);
1564         } else {
1565 if(dbg) console.log('読み出し失敗')
1566             return false;
1567         }
1568         // ユーザ判定は不用(権利チェックは後ほど実装)
1569         if (newXps){
1570             newXps.job.increment(myJob);
1571             newXps.update_user = xUI.currentUser;
1572             newXps.currentStatus = new JobStatus('Active');
1573              //引数でステータスを変更したエントリを作成 新規に保存 JobIDは必ず繰り上る
1574             localStorage.setItem(this.keyPrefix+Xps.getIdentifier(newXps),newXps.toString());
1575             var resultData = localStorage.getItem(this.keyPrefix+Xps.getIdentifier(newXps));
1576             var result = ( resultData == newXps.toString()) ? true:false;
1577             if(result){
1578                 currentEntry.push(Xps.getIdentifier(newXps));
1579                 currentCut.versions.push({
1580                     updated_at:new Date().toString(),
1581                     description:currentEntry.toString(0),
1582                     version_token:this.keyPrefix+currentEntry.toString(0)
1583                 });
1584                 xUI.setReferenceXPS();
1585                 xUI.XPS.job.increment(myJob);
1586                 xUI.XPS.currentStatus=new JobStatus('Active');//ドキュメントステータスを更新
1587                 xUI.XPS.update_user=xUI.currentUser;//ユーザ更新
1588                 xUI.setStored("current");//UI上の保存ステータスをセット
1589 			    sync();//保存ステータスを同期
1590                 selectSCi();//カレントデータを再セレクトして情報更新
1591                 xUI.setUImode('production');//モードをproductionへ
1592                 xUI.sWitchPanel();//ドキュメントパネルが表示されていたらパネルクリア
1593                 sync('historySelector');//履歴セレクタ更新
1594                 if(callback instanceof Function){ setTimeout(callback,10)};
1595                 return result;
1596             }else{
1597 if(dbg) console.log(result);
1598             }
1599         }
1600 //console.log('編集権利取得失敗');
1601         // すべてのトライに失敗
1602         if(callback2 instanceof Function){ setTimeout(callback2,10)};
1603         return false ;
1604 }
1605 /**
1606     作業終了
1607 */
1608 localRepository.checkoutEntry=function(assignData,callback,callback2){
1609     var currentEntry = this.entry(Xps.getIdentifier(xUI.XPS));
1610     var currentCut   = this.cut(currentEntry.toString());
1611 //    var currentCut   = this.cut(currentEntry.issues[0].cutID);
1612     if(! currentEntry) {
1613 //console.log ('noentry in repository :' +  decodeURIComponent(currentEntry))
1614         return false;
1615     }
1616             //Active > Fixed
1617         var newXps = new Xps();
1618         var currentContents = xUI.XPS.toString();
1619         newXps.readIN(currentContents);
1620         //ユーザ判定は不用 JobID変わらず
1621         if (newXps){
1622              //同内容でステータスを変更したエントリを作成 新規に保存して成功したら先のエントリを消す
1623 //            newXps.currentStatus = ['Fixed',assignData].join(":");
1624             newXps.currentStatus = new JobStatus('Fixed');
1625             newXps.currentStatus.assign = assignData;
1626             //いったん元に戻す assignData は宙に保留(ここで消失)
1627             localStorage.setItem(this.keyPrefix+Xps.getIdentifier(newXps),newXps.toString());
1628 
1629             var result = (localStorage.getItem(this.keyPrefix+Xps.getIdentifier(newXps))==newXps.toString())? true:false;
1630             if(result){
1631 //console.log(result);
1632 //console.log(currentCut)
1633                 localStorage.removeItem(this.keyPrefix+currentEntry.toString(0));
1634                 xUI.XPS.currentStatus=newXps.currentStatus;//ドキュメントステータスを更新
1635                 currentEntry.setStatus(newXps.currentStatus);
1636                 var myVersion=currentCut.versions[currentCut.versions.length-1];
1637                   myVersion.updated_at=new Date().toString();
1638                   myVersion.description=currentEntry.toString(0);
1639                   myVersion.version_token=this.keyPrefix+myVersion.description;
1640 			    xUI.setStored("current");//UI上の保存ステータスをセット
1641 			    sync();//保存ステータスを同期
1642                 selectSCi();//カレントデータを再セレクトして情報更新
1643                 xUI.sWitchPanel();//ドキュメントパネルが表示されていたらパネルクリア
1644                 xUI.setUImode('browsing');//モードをbrousingへ
1645                 sync('historySelector');//履歴セレクタ更新
1646                 if(callback instanceof Function){ setTimeout('callback()',10)};
1647                 return result;
1648             }else{
1649 //console.log("fail checkout store")
1650             }
1651         }
1652 //console.log('終了更新失敗');
1653         delete newXps ;
1654         if(callback2 instanceof Function){ setTimeout(callback2,10)};
1655         return false ;
1656 }
1657 /**
1658     検収処理receiptEntry/receiptEntry
1659 */
1660 localRepository.receiptEntry=function(stageName,jobName,callback,callback2){
1661     if( typeof stageName == 'undefined') return false;
1662     var myStage = nas.pmdb.stages.getStage(stageName) ;//ステージDBと照合 エントリが無い場合はエントリ登録
1663     /*  2016-12 の実装では省略して エラー終了
1664         2017-07 最小限の処理を実装 ステージの存在を確認して続行
1665     */
1666     if(! myStage) return false;
1667     var currentEntry = this.entry(Xps.getIdentifier(xUI.XPS));
1668     if(! currentEntry){
1669         console.log ('noentry in repository :' +  decodeURIComponent(currentEntry))
1670         //当該リポジトリにエントリが無い
1671          return false;
1672       }
1673     var currentCut   = this.cut(currentEntry.toString());//= this.cut(currentEntry.issues[0].cutID);
1674     if(! currentCut) return false;
1675 //次のステージを立ち上げるため 読み出したデータでXpsを初期化 
1676         var newXps = new Xps();
1677         var currentContents = localStorage.getItem(this.keyPrefix+currentEntry.toString(0));
1678         if (currentContents) {
1679             newXps.readIN(currentContents);
1680         } else {
1681 if(dbg) console.log('読み出し失敗')
1682             return false;
1683         }
1684         // ユーザ判定は不用(権利チェックは後ほど実装)
1685         if (newXps){
1686             newXps.stage.increment(stageName);
1687             newXps.job.reset(jobName);
1688             newXps.update_user = xUI.currentUser;
1689             newXps.currentStatus = new JobStatus('Startup');
1690 if(dbg) console.log(newXps.toString());//
1691              //引数でステータスを変更したエントリを作成 新規に保存 stageIDは必ず繰り上る jobは0リセット
1692             localStorage.setItem(this.keyPrefix+Xps.getIdentifier(newXps),newXps.toString());
1693             var resultData = localStorage.getItem(this.keyPrefix+Xps.getIdentifier(newXps));
1694 if(dbg) console.log(resultData);
1695             var result = ( resultData == newXps.toString()) ? true:false;
1696             if(result){
1697 if(dbg) console.log('receipt');
1698                 //delete newXps ;
1699 if(dbg) console.log(newXps.currentStatus);
1700 //                this.getList();//リストステータスを同期
1701                 currentEntry.push(Xps.getIdentifier(newXps));
1702                 currentCut.versions.push({
1703                     updated_at:newXps.update_time,
1704                     description:currentEntry.toString(0),
1705                     version_token:this.keyPrefix+currentEntry.toString(0)
1706                 });
1707                 xUI.XPS.stage.increment(stageName);
1708                 xUI.XPS.job.reset(jobName);
1709                 xUI.XPS.currentStatus=new JobStatus('Startup');//ドキュメントステータスを更新
1710                 xUI.XPS.update_user=xUI.currentUser;//ユーザ更新
1711                 xUI.setStored("current");//UI上の保存ステータスをセット
1712 			    sync();//保存ステータスを同期
1713                 selectSCi();//カレントデータを再セレクトして情報更新
1714 //                xUI.setUImode('browsing');//モードをbrowsingへ  <<領収処理の後はモード遷移なし
1715                 xUI.sWitchPanel();//ドキュメントパネルが表示されていたらパネルクリア
1716                 sync('historySelector');//履歴セレクタ更新
1717                 if(callback instanceof Function){ setTimeout(callback,10)};
1718                 return result;
1719             }else{
1720 if(dbg) console.log(result);
1721             }
1722         }
1723 if(dbg) console.log('編集権利取得失敗');
1724         // すべてのトライに失敗
1725         if(callback2 instanceof Function){ setTimeout(callback2,10)};
1726         return false ;
1727 }
1728 /**
1729     作業中断処理
1730 */
1731 localRepository.abortEntry=function(myIdentifier,callback,callback2){
1732     var currentEntry = this.entry(myIdentifier);
1733     if(! currentEntry) return false;
1734     var currentStatus=currentEntry.getStatus();
1735 
1736     if(String(currentStatus.content).indexOf('Fixed')<0){return false;}
1737     var currentCut   = this.cut(currentEntry.toString());
1738     if(! currentCut) return false;
1739     
1740 //中断エントリを作成するために、読み出したデータで新規Xpsを初期化 
1741         var newXps = new Xps();
1742         var currentContents = localStorage.getItem(this.keyPrefix+currentEntry.toString(0));
1743         if (currentContents) {
1744             newXps.parseXps(currentContents);
1745         } else {
1746 //console.log('abort entry:読み出し失敗')
1747             return false;
1748         }
1749         // ユーザ判定は不用(権利チェックは後ほど実装)
1750         if (newXps){
1751             newXps.job.increment('Abort');
1752             newXps.update_user = xUI.currentUser;
1753             newXps.currentStatus = new JobStatus('Aborted');
1754 //console.log('abort entry:');
1755 //console.log(newXps.toString());//
1756              //引数でステータスを変更したエントリを作成 新規に保存 stageIDは変わらず、jobIDは繰り上る
1757             localStorage.setItem(this.keyPrefix+Xps.getIdentifier(newXps),newXps.toString());
1758             var resultData = localStorage.getItem(this.keyPrefix+Xps.getIdentifier(newXps));
1759 //console.log(resultData);
1760             var result = ( resultData == newXps.toString()) ? true:false;
1761             if(result){
1762 //console.log('aborted');
1763 //console.log(newXps.currentStatus);
1764                 this.getList();//リストステータスを同期
1765 //                currentEntry.push(Xps.getIdentifier(newXps));
1766                 currentEntry.remove(Xps.getIdentifier(newXps));
1767                 currentCut.versions.push({
1768                     updated_at:newXps.update_time,
1769                     description:currentEntry.toString(0),
1770                     version_token:this.keyPrefix+currentEntry.toString(0)
1771                 });
1772 //                xUI.XPS.stage.increment(stageName);
1773 //                xUI.XPS.job.reset(jobName);
1774 //                xUI.XPS.currentStatus=new JobStatus('Startup');//ドキュメントステータスを更新
1775 //                xUI.XPS.update_user=xUI.currentUser;//ユーザ更新
1776 //                xUI.setStored("current");//UI上の保存ステータスをセット
1777 			    sync();//保存ステータスを同期
1778                 selectSCi();//カレントデータを再セレクトして情報更新
1779                 xUI.setUImode('floating');//モードをfloatingへ  <<領収処理の後はモード遷移なし
1780                 xUI.sWitchPanel();//ドキュメントパネルが表示されていたらパネルクリア
1781                 sync('historySelector');//履歴セレクタ更新
1782                 if(callback instanceof Function){ setTimeout(callback,10)};
1783                 return result;
1784             }else{
1785 if(dbg) console.log(result);
1786             }
1787         }
1788 if(dbg) console.log('編集権利取得失敗');
1789         // すべてのトライに失敗
1790         if(callback2 instanceof Function){ setTimeout(callback2,10)};
1791         return false ;
1792 }
1793 /**
1794     リポジトリの情報をダイアログで表示
1795 
1796 */
1797 localRepository.showInformation=function (){
1798     var ownerString = (xUI.currentUser)? xUI.currentUser.toString(): nas.localize({en:"(Could not acquire.)",ja:"(取得できません)"});
1799     var title = nas.localize(nas.uiMsg.aboutOf,this.name);
1800     var msg = "";
1801     msg += nas.localize(nas.uiMsg.serviceNode) +" : "+ "localRepository<br>";
1802     msg += nas.localize(nas.uiMsg.repositoryName) +" : " + this.name +"<br>";
1803     msg += nas.localize(nas.uiMsg.repositoryOwner) + " : " + ownerString + "<br>";
1804     msg += nas.localize({
1805 en:"<hr>** This is the area where temporary files are stored using local storage of the browser. Data can not be shared between users in this repository.<br>",
1806 ja:"<hr>** ブラウザのローカルストレージを使用した、一時ファイルを保存する領域です。<br>ユーザ間のデータ共有はできません。<br>"
1807 });
1808     nas.showModalDialog("alert",msg,title);
1809 }
1810 /*  test data 
1811     localRepository.currentProduct = "ももたろう#12[キジ参戦!ももたろう地獄模様!!]";
1812     localRepository.currentSC      = "S-C005 (12+00)/011(3+00)/014(3+00)";
1813     localRepository.currentLine    = 0;
1814     localRepository.currentStage   = 0;
1815     localRepository.currentJob     = 0;
1816 
1817 JSON.stringify(localRepository);
1818 
1819 localRepository.pushStore(XPS);
1820 localRepository.getList();
1821 //localRepository.entryList[0];
1822 localRepository.getEntry(localRepository.entryList[0]);
1823 
1824 localRepository.showInformation();
1825 */
1826 /**
1827     最終作業の破棄
1828     バックアップ作業これを呼び出す側で処理
1829     ここを直接コールした場合はバックアップは実行されない
1830     ユーザメッセージはここでは処理されない
1831     
1832 */
1833 localRepository.destroyJob=function(callback,callback2){
1834     if(xUI.XPS.currentStatus.content != 'Active'){return false}
1835 //   カレントの作業に対応するストレージ上のキーを消去
1836 //   成功すれば
1837     var currentEntry = this.entry(Xps.getIdentifier(xUI.XPS));
1838     if(! currentEntry){
1839 if(dbg) console.log ('noentry in repository :' +  decodeURIComponent(currentEntry))
1840         //当該リポジトリにエントリが無い
1841          return false;
1842     }
1843     try {
1844         localStorage.removeItem(this.keyPrefix+currentEntry.toString(0));
1845 		currentEntry.issues.pop();
1846 //        xUI.resetSheet(new Xps(5,144),new Xps(5,144));
1847         xUI.resetSheet();
1848         if(callback instanceof Function) callback();
1849     }catch(er){
1850 //console.log(er) 
1851         if(callback2 instanceof Function) callback2();
1852     }
1853 }
1854 
1855 /**
1856     ネットワーク上のリポジトリオブジェクト
1857     識別名とサーバを与えて初期化する
1858         リポジトリとしての共通メソッド
1859     .getList()
1860     .title(myIdentifier)
1861     .opus(myIdentifier)
1862     .cut(myIdentifier)
1863     .entry(myIdentifier)
1864 
1865     .push(myXps)
1866         
1867 リポジトリに 相当する構造は Team
1868 チームごとにリポジトリが設定される
1869 Teamへアクセスするためのトークンは、アクセス毎に設定される
1870 リポジトリは、複数の同一名称のリポジトリが想定されるため、補助的にオーナー情報を保持する仕様を追加
1871 特に同作品の複製を見分けるために必須 APIに追加
1872 */
1873 //NetworkRepository=function(repositoryName,repositoryOwner,myServer,repositoryURI){}
1874 NetworkRepository=function(repositoryName,myServer,repositoryURI){
1875     this.name = repositoryName;
1876 //    this.owner = new nas.UserInfo(repositoryOwner);//リポジトリオーナー情報
1877     this.service = myServer;//リポジトリの所属するサーバ
1878     this.url=(typeof repositoryURI == 'undefined')?this.service.url:repositoryURI;//サーバとurlが異なる場合は上書き
1879     this.token=null;//nullで初期化
1880 //サーバ内にTeamが実装 Teamをリポジトリとして扱うのでその切り分けを作成 12/13
1881 //リストは素早いリポジトリの切り替えやリポジトリ同士のマージ処理に不可欠なのでここで保持
1882 //    this.currentProduct;
1883 //    this.currentSC;
1884 //    this.currentLine;
1885 //    this.currentStage;
1886 //    this.currentJob;
1887 //    this.product_token      = $('#server-info').attr('product_token');
1888 //    this.episode_token      = $('#server-info').attr('episode_token');
1889 //    this.cut_token          = $('#server-info').attr('cut_token');
1890 // ?idの代替なので要らないか? 
1891     this.pmd={};//制作管理データキャリア 機能クラスオブジェクト化?
1892     this.currentIssue;
1893     this.productsData=[];//workTitleCollectionで置換?タイトルキャリアでノードルートになる
1894     this.entryList = new listEntryCollection();
1895 }
1896 /**
1897     リポジトリ情報表示メソッド
1898     引数:なし
1899     リポジトリのオーナー情報を表示してリポジトリ(共有・チーム)へのアクセスリンクを提供する
1900     リポジトリへのリンクはリポジトリ名を使用
1901     
1902 */
1903 NetworkRepository.prototype.showInformation = function(){
1904     var ownerString = (this.owner.handle)? this.owner.handle: nas.localize({en:"(Could not acquire.)",ja:"(取得できません)"});
1905     var title = nas.localize(nas.uiMsg.aboutOf,this.name);
1906     var msg = "";
1907     msg += nas.localize(nas.uiMsg.serviceNode) +" : <a href='" +this.service.url+"' target='_blank'>"+ this.service.name + "("+this.service.url +")</a><br>";
1908     msg += nas.localize(nas.uiMsg.repositoryName) +" : " + this.name +"<br>";
1909     msg += nas.localize(nas.uiMsg.repositoryOwner) + " : " + ownerString + "<br>";
1910 //    msg += "    アクセス先 : " + this.owner.token + "<br>";
1911     nas.showModalDialog("alert",msg,title);
1912 }
1913 /**
1914 各層のエントリを識別子で取得
1915     TITLE取得
1916 */
1917 NetworkRepository.prototype.title=_title;
1918 /**
1919     OPUS取得
1920 */
1921 NetworkRepository.prototype.opus=_opus;
1922 /**
1923     CUT取得
1924 */
1925 NetworkRepository.prototype.cut=_cut;
1926 /**
1927     タイトル一覧を取得して情報を更新する エピソード更新を呼び出す
1928     受信したデータを複合させてサービス上のデータ構造を保持する単一のthis.productsDataオブジェクトにする
1929     getXx で概要(一覧)を取得
1930     xxUpdateが詳細を取得して this.productsData を上書きしてゆく
1931     プロダクト詳細は、各個に取得できるように変更
1932     引き続きの処理を行う際はコールバック渡し
1933     トークン指定がない場合は、全プロダクトの詳細を取得
1934     プロダクトデータ取得のみの場合は 空動作のコールバックを渡す必要あり
1935 */
1936 NetworkRepository.prototype.getProducts=function (callback,callback2,prdToken){
1937     if(typeof prdToken == 'undefined'){prdToken = [];}
1938     if(!(prdToken instanceof Array)) prdToken=[prdToken];
1939     $.ajax({
1940         url: serviceAgent.currentRepository.url+'/api/v2/products.json',
1941         type: 'GET',
1942         dataType: 'json',
1943         success: function(result) {
1944             //resultにデータが無いケース{}があるので分離が必要
1945            //権限等で
1946            //この時点でタイトルに付属のメンバーシップをs同時に取得してオブジェクトに設定する(プロパティオブジェクト未実装20171116)
1947 		    serviceAgent.currentRepository.productsData = result.data.products;
1948 		    if(prdToken.length){
1949 		    //引数があれば引数のプロダクトを順次処理
1950 		        for (var tId = 0 ; tId < prdToken.length ; tId ++ ){
1951 		            serviceAgent.currentRepository.productsUpdate(callback,callback2,prdToken[tId]);
1952 		        }
1953 		    }else{
1954 		    //引数がない場合はすべてのプロダクトの詳細を取得更新
1955 		        for (var tId = 0 ; tId < serviceAgent.currentRepository.productsData.length ; tId ++ ){
1956 		            serviceAgent.currentRepository.productsUpdate(callback,callback2,serviceAgent.currentRepository.productsData[tId].token);
1957 		        }
1958 		    }
1959         },
1960         error : function(result){
1961             if(dbg) console.log('fail productsData::');
1962             if(dbg) console.log(result);
1963 		    if(callback2 instanceof Function){callback2()}
1964         },
1965         beforeSend: serviceAgent.currentRepository.service.setHeader
1966     });
1967 }
1968 /**
1969     タイトルごとの詳細(エピソードリスト含む)を取得してタイトルに関連付ける
1970     myToken 引数がない場合はすべてのプロダクトを更新
1971     必要に従ってエピソードリストの更新を行う
1972     コールバック引数がない場合はタイトルのエピソード毎に情報を取得
1973 */
1974 NetworkRepository.prototype.productsUpdate=function(callback,callback2,myToken){
1975     if(typeof myToken == 'undefined'){
1976             myToken = [];
1977         for(var tknId = 0 ;tknId < serviceAgent.currentRepository.productsData.length ;tknId ++){
1978             myToken.push(serviceAgent.currentRepository.productsData[tknId].token);
1979         }
1980     }else{
1981         if(!(myToken instanceof Array)) myToken=[myToken];
1982     }
1983     for(var ix = 0 ;ix < myToken.length ;ix ++){
1984     $.ajax({
1985         url: serviceAgent.currentRepository.url+'/api/v2/products/'+myToken[ix]+'.json' ,
1986         type: 'GET',
1987         dataType: 'json',
1988         success: function(result) {
1989             var productUpdated=false;
1990             for(var idx = 0 ;idx < serviceAgent.currentRepository.productsData.length ;idx ++){
1991 		        if(result.data.product.token != serviceAgent.currentRepository.productsData[idx].token) continue;
1992                 //プロダクトデータを詳細データに「入替」エピソードの概要を取得する
1993 if(dbg) console.log("update product data detail:"+serviceAgent.currentRepository.productsData[idx].name) ;
1994 //console.log(serviceAgent.currentRepository.productsData);
1995 		                serviceAgent.currentRepository.productsData[idx] = result.data.product ;
1996 		                if(! (serviceAgent.currentRepository.productsData[idx].episodes)){serviceAgent.currentRepository.productsData[idx].episodes=[[]];};//episodes/cutsの配列整理が終了したら変更
1997 		                serviceAgent.currentRepository.productsData[idx].episodes[0] = result.data.episodes ;
1998 		                productUpdated=true;
1999 		                break;
2000 		    };
2001 //console.log("updated : "+serviceAgent.currentRepository.productsData[idx].name);
2002 		    if(productUpdated) {
2003 		            serviceAgent.currentRepository.updateEpisodes(callback,callback2,serviceAgent.currentRepository.productsData[idx].token);
2004 		    }else{
2005 //console.log('fail productsData update no entry in Repository::');
2006 //console.log(result);
2007 		        //指定されたトークンが ,リポジトリ内に存在しないのでエラー
2008 		        if(callback2 instanceof Function){callback2();}
2009 		    };
2010         },
2011         error : function(result){
2012 //console.log('fail productsData update::');
2013 //console.log(result);
2014 		    if(callback2 instanceof Function){callback2();}
2015         },
2016         beforeSend: (serviceAgent.currentRepository.service.setHeader)
2017     });
2018     }
2019 }
2020 /**
2021     プロダクトごとにエピソード一覧を再取得してデータ内のエピソード一覧を更新
2022     引数 product_tokenが存在する場合は、指定のプロダクト以外の処理をスキップ
2023 */
2024 NetworkRepository.prototype.updateEpisodes=function (callback,callback2,prdToken) {
2025 //       var myProduct = serviceAgent.currentRepository.getNodeElementByToken(prdToken);
2026        var myProduct = serviceAgent.currentRepository.title(prdToken);
2027         if(! myProduct) return false;
2028 //console.log("getEpisodeList : "+myProduct.token+' : '+myProduct.name) ;
2029     $.ajax({
2030         url: serviceAgent.currentRepository.url+'/api/v2/episodes.json?product_token='+myProduct.token ,
2031         type: 'GET',
2032         dataType: 'json',
2033         success: function(result) {
2034                 //プロダクトデータのエピソード一覧を「入替」
2035 
2036 		    if(result){
2037 		        if(! myProduct.episodes) {myProduct.episodes=[[]];}
2038 		        //後ほどエピソードレベルのユーザ情報取得
2039 		        myProduct.episodes[0] = result.data.episodes;
2040 //console.log('success getting episodes :'+myProduct.name);
2041 //console.log(myProduct);
2042                 if(callback instanceof Function){
2043 		            callback();
2044 		        }else{
2045 		            serviceAgent.currentRepository.getEpisodes(callback,callback2,myProduct.token);
2046 //                    for(var eid=0;eid<myProduct.episodes[0].length;eid++){
2047 //		                serviceAgent.currentRepository.getEpisodes(callback,callback2,myProduct.token,myProduct.episodes[0][eid].token);
2048 //		            }
2049 		        }
2050 		    }else{
2051 //console.log('fail get no episodes::');
2052 //console.log(result);
2053 		        if(callback2 instanceof Function){callback2();}		        
2054 		    }
2055         },
2056         error : function(result){
2057 //console.log('fail getting Episodes::');
2058 //console.log(result);
2059 		    if(callback2 instanceof Function){callback2();}
2060         },
2061         beforeSend: serviceAgent.currentRepository.service.setHeader
2062     });
2063 }
2064 /**
2065     episode_token を指定して詳細を取得 内部リストにコンバート
2066     コールバックリンクのためこのあたりの機能は統合かける
2067 リポジトリ取得 server.getRepositories()
2068     サーバ指定=引数なしでサーバの管理するリポジトリの情報を取得
2069 
2070 プロダクト取得 repositoriy.getProducts(識別子)
2071 
2072 下位ファンクションを組み合わせてプロダクトレベルの情報取得を行う
2073 識別子でコントロール
2074 識別子を引数にするとその配下を取得
2075 
2076     タイトル取得  repositoriy.getTitle(myTitel,callback,callback2)
2077         リポジトリ指定=引数なし で全タイトル
2078         タイトル指定で特定タイトルの情報を更新
2079         引数が配列の場合は配列内のタイトルを更新
2080         下位情報には踏み込まないコールバックリレーは行わない
2081 
2082     エピソード取得 repository.getOpus(myTitle,myOpus,callback,callback2)
2083         タイトル指定で、そのタイトル配下のエピソード
2084         OPUS指定があれば、そのOPUSのみを更新
2085     カット取得       repository.getSCi(myOpus,pgNo,ppg,callback,callback2)
2086         プロダクト指定,エピソード指定,ページ数,単位
2087     エピソードの指定が存在する場合は、指定エピソードの処理を行う 配列OK
2088     それ以外は指定プロダクトのすべてを更新
2089  */
2090 NetworkRepository.prototype.getEpisodes=function (callback,callback2,prdToken,epToken) {
2091     var allEpisodes=false;
2092     if(typeof epToken == 'undefined'){
2093         epToken     = [];
2094         allEpisodes = true;
2095 console.log(prdToken)
2096         var myProduct=this.title(prdToken);
2097 console.log(myProduct)
2098         if((! myProduct)||(! myProduct.episodes)||(myProduct.episodes[0].length == 0)){console.log('stop'); return false;}
2099         for (var px = 0 ;px < myProduct.episodes[0].length;px ++){epToken.push(myProduct.episodes[0][px].token);}
2100     }
2101     if(!(epToken instanceof Array)) epToken = [epToken];
2102     for(var ex = 0;ex < epToken.length ;ex ++){
2103         var myEpisode=this.opus(epToken[ex]);
2104         if(! myEpisode) continue;
2105         if((allEpisodes)&&(myEpisode.cuts)){console.log('skip'+myEpisode.name) ;continue;}
2106         //対象が全エピソードで、エピソードがすでにカット情報を持っているケースでは処理スキップ
2107 console.log("get episodes details for : "+myEpisode.name) ;
2108 console.log("Token : "+myEpisode.token) ;
2109 	            // /api/v2
2110                 var targetURL = serviceAgent.currentRepository.url+ '/api/v2/episodes/'+myEpisode.token +'.json';
2111 	    $.ajax({
2112             url: targetURL,
2113             type: 'GET',
2114             dataType: 'json',
2115             success: function(result) {
2116 console.log('success : episode details for:'+result.data.episode.name);//リザルト不正 調整中20190129
2117 console.log(result);
2118         var  updateTarget = serviceAgent.currentRepository.opus(result.data.episode.token);
2119         if(! updateTarget){console.log('erroe###');console.log(updateTarget);};   
2120 //非同期処理中に変数を共有するのでmyEpisodeが変動するためターゲットをリザルトから再キャプチャ
2121 //オブジェクト入れ替えでなくデータの追加アップデートに変更
2122 //内容は等価だがAPIの変更時は注意
2123 //この時点でカットの総数が取得されるのでカット一覧詳細取得時総数を参照して分割取得
2124 //console.log('update target episode###');console.log(updateTarget);
2125                 if(!(updateTarget.cuts)){updateTarget.cuts=[[]];}
2126                 updateTarget.cuts[0] = result.data.cuts;
2127                 updateTarget.created_at = result.data.episode.created_at;
2128                 updateTarget.updated_at = result.data.episode.updated_at;
2129 //console.log(updateTarget);
2130                 if(callback instanceof Function){
2131                     callback();
2132                 }else{
2133                     //標準処理
2134                     serviceAgent.currentRepository.getSCi(false,false,myEpisode.token);
2135                 }
2136             },
2137             error : function(result){
2138 //console.log('fail getting episode details::');
2139 //console.log(result);
2140 		        if(callback2 instanceof Function){callback2();}
2141             },
2142             beforeSend: serviceAgent.currentRepository.service.setHeader
2143         });
2144     }
2145 };
2146 /**
2147     エピソード毎にカットリストを再取得
2148     エピソード詳細の内部情報にコンバート
2149     カット一覧にdescriptionを出してもらう
2150     取得時にentryListを同時更新する
2151 引数
2152     epToken   ターゲットの話数キーまたは、カットトークン
2153     pgNo      リストのページID 1 origin
2154     ppg       ページごとのエントリ数
2155 
2156     epToken のかわりにカットトークンが与えられた場合は、カット1つのみのリスト作成して高速に処理を完了する。
2157     
2158  */
2159 NetworkRepository.prototype.getSCi=function (callback,callback2,epToken,pgNo,ppg) {
2160     var myEpisode = this.opus(epToken);
2161 //console.log('getSCi :');console.log(myEpisode);
2162     if((! myEpisode)||(! myEpisode.cuts)) return false;
2163 /*
2164     if(! myEpisode){
2165          if(myEpisode == null) {
2166             var targetURL='/api/v2/cuts/'+epToken+'.json';// epToken ascutToken
2167             $.ajax({
2168                 url: this.url + targetURL,
2169                 type: 'GET',
2170                 dataType: 'json',
2171                 success: function(result) {
2172                     console.log(result);
2173                 },
2174                 beforeSend: this.service.setHeader
2175             })
2176          }else{
2177             return false;
2178          }
2179             return true;
2180     }
2181 */
2182     if(typeof pgNo == 'undefined') pgNo = '1';
2183     if(typeof ppg  == 'undefined')  ppg = myEpisode.cuts[0].length;
2184 //console.log(arguments);
2185 //console.log([pgNo,ppg]);
2186     var targetURL = serviceAgent.currentRepository.url+ '/api/v2/cuts.json?episode_token='+myEpisode.token+'&page_no='+parseInt(pgNo)+'&per_page='+parseInt(ppg);
2187 	            $.ajax({
2188                     url: targetURL,
2189                     type: 'GET',
2190                     dataType: 'json',
2191                     success: function(result){
2192 //console.log(result);console.log(myEpisode);
2193                                       myEpisode.cuts[0]=result.data.cuts;
2194 //カット登録数1以上の場合のみ処理
2195 if(myEpisode.cuts[0].length){
2196 if (! myEpisode.cuts[0][0].description) console.log(myEpisode.token)
2197                                         var currentTitle = (! myEpisode.cuts[0].description)?
2198                                             serviceAgent.currentRepository.title(myEpisode.token,1):
2199                                             serviceAgent.currentRepository.title(myEpisode.cuts[0].description);
2200 if(! currentTitle){console.log(currentTitle)}
2201 /**
2202 エントリ取得タイミングで仮にcutのdescription を追加するcuts[1][cid].description を作成して調整に使用する
2203 本番ではデータ比較ありで、入替えを行う サーバ側のプロパティ優先
2204 */
2205     var myIdentifier_opus =
2206         encodeURIComponent(currentTitle.name) +
2207         '#'+encodeURIComponent(myEpisode.name) +
2208         ((myEpisode.description)?
2209             '['+encodeURIComponent(myEpisode.description) +']':''
2210         );
2211     if(! myEpisode.cuts){console.log(myEpisode.cuts);}
2212     for ( var cid = 0 ; cid < result.data.cuts.length ; cid ++){
2213         var myCut = myEpisode.cuts[0][cid];
2214         if(myCut.name == null) myCut.name = "";//この状態は実際にはエラー
2215         var myIdentifier_cut = encodeURIComponent(myCut.name);
2216 // デスクリプションに識別子がない場合issuen部の無い識別子を補う
2217 // 他はDB側の識別子を優先して識別子を更新する
2218         if(! myCut.description){
2219             myCut.description=[myIdentifier_opus,myIdentifier_cut].join('//');
2220         } else {
2221             var currentIssue = myCut.description.split("//").slice(2);
2222             myCut.description=([myIdentifier_opus,myIdentifier_cut].concat(currentIssue)).join('//');
2223         }
2224 //============エントリ更新 
2225 /*
2226     管理情報は識別子から取得する
2227 APIの情報は、識別子と一致しているはずだが 照合の上異なる場合はAPIの情報で上書きを行う
2228 識別子として cut.description を使用 上位情報は、エントリから再作成
2229 サブタイトルは episode.discriptionを使用
2230 兼用カット情報はペンディング
2231 */
2232 //console.log(myCut)
2233                 var myCutToken = myCut.token;
2234                 var myCutLine  = (myCut.line_id)?
2235                     myCut.line_id:
2236                     (new XpsLine(nas.pmdb.pmTemplates.members[0].line.toString())).toString(true);
2237                 var myCutStage = (myCut.stage_id)?
2238                     myCut.stage_id:
2239                     (new XpsStage(nas.pmdb.pmTemplates.members[0].stages.getStage())).toString(true);
2240                 var myCutJob   = (myCut.job_id)?
2241                     myCut.job_id:
2242                     (new XpsStage(nas.pmdb.jobNames.members[0].toString())).toString(true);
2243                 var myCutStatus= (myCut.status)?
2244                     myCut.status:new JobStatus('Startup');
2245 // myCut.status new JobStatus('Startup');
2246 //管理情報が不足の場合は初期値で補う description情報が未登録の場合は、APIの情報からビルドする?
2247 
2248                 var entryArray = (
2249                     String(myCut.description).split('//').concat([
2250                         encodeURIComponent(myCutLine),
2251                         encodeURIComponent(myCutStage),
2252                         encodeURIComponent(myCutJob),
2253                         myCutStatus
2254                     ])
2255                 ).slice(0,6);//
2256                 var myEntry=entryArray.slice(0,2).join( "//" );//管理情報を外してSCi部のみ抽出
2257                 var currentEntry=serviceAgent.currentRepository.entry(myCut.description);//既登録エントリを確認
2258                 if(currentEntry) {console.log(decodeURIComponent(myCut.description));console.log(currentEntry);console.log(currentEntry.remove());console.log('current entry removed')}
2259                     //登録されていた場合はあらかじめ削除しておく
2260                 var newEntry = new listEntry(entryArray.join('//'),currentTitle.token,myEpisode.token,myCutToken);
2261                 newEntry.parent = serviceAgent.currentRepository;
2262                 serviceAgent.currentRepository.entryList.push(newEntry);
2263                 // エントリ配下にversionsがあればそのままpush
2264                 if(! myCut.versions) myCut.versions=[];
2265                 for (var vid = 0;vid<myCut.versions.length;vid++){
2266                     var myVersionString=(myCut.versions[vid].description)?
2267                     myCut.versions[vid].description:entryArray.join("//");
2268                     var myVersionToken = myCut.versions[vid].version_token;
2269                     newEntry.push(myVersionString,currentTitle.token,myEpisode.token,myCut.token,myVersionToken);
2270                 }
2271 //============エントリ更新
2272     }
2273 }
2274                         if(callback instanceof Function){
2275                             callback();
2276                         }else{
2277                             documentDepot.documentsUpdate();
2278                         }
2279                     },
2280                     error : function(result){
2281 if(dbg) console.log('getSCi ::');
2282 if(dbg) console.log(result);
2283 		                if(callback2 instanceof Function){callback2();}
2284                     },
2285                     beforeSend: serviceAgent.currentRepository.service.setHeader
2286                 });
2287 };
2288 
2289 /**
2290 リポジトリ内のentryListを更新する
2291 documentsDataの更新が必要なケースでは、force スイッチを置く
2292 force スイッチが与えられるかまたはプロダクトリストに値がない場合はプロダクトの取得から処理が開始され
2293 現在のリストはクリアされずに(バッファリストを作って比較)更新が行われる
2294 
2295 このメソッド自体 サービス内のエントリを取得してentryListを更新するのが目的なので
2296 一括取得をやめるまたはこのメソッドの再帰的な呼び出しをやめるかいずれかの処置が必要
2297 getProductsからの再呼び出しは再クリアがあるのでOK
2298 それ以外のメソッドからの再呼び出しは厳禁
2299 代わりに entryListに編集メソッドを設けて出力はそちらにつなぐものとする
2300 entryList.put(entry)    エントリを加える 同じidfのエントリは上書きする
2301 entryList.remove(idf)    エントリを削除する idf指定
2302 entryList.get(idf)    エントリを加える 同じトークンのエントリは上書きする
2303 
2304 
2305 documentDepot.documents は serviceAgent.currentRepository.entryList への参照
2306 
2307 更新時点の新規データを常に参照可能 リポジトリを切り替える際に参照を切り替えれば、特に問題はない?
2308 
2309 オブジェクトをクリアしなければ問題ないはず
2310 entryListの機能性を高めてスタティックオブジェクト化する
2311 
2312 getList等のentryListを更新するメソッドはリストのメソッドを使ってリストを更新する
2313 
2314 store(listEntry)
2315 
2316 
2317 */
2318 NetworkRepository.prototype.getList_=function (force,callback){
2319 //console.log("clear entryList \n rebuild entryList from documentsData"); console.log(this.productsData); console.log('++==%%');
2320 
2321     this.entryList.length=0;//エントリリスト初期化
2322     var newList = []; //新規配列作成    
2323     if((force)||(serviceAgent.currentRepository.productsData.length==0)) {
2324 // forceオプションが指定されるかまたはプロダクトエントリがまだ無いケース
2325 //プロダクト取得を設定して手続を一旦終了
2326         serviceAgent.currentRepository.getProducts();
2327         return;//一旦処理を中断 getProductsの最終工程でgetListが再度呼び出される
2328     }else{
2329 //プロダクト情報更新
2330        for(var idx = 0 ;idx < serviceAgent.currentRepository.productsData.length ;idx ++){
2331             var currentTitle = serviceAgent.currentRepository.productsData[idx];//Object取得
2332             if(typeof currentTitle.episodes == "undefined"){
2333                 if(! force){console.log('skip :'+ currentTitle.name) ;continue;}
2334                 serviceAgent.currentRepository.productsUpdate(function(){
2335                     serviceAgent.currentRepository.getEpisodes(false,false,currentTitle.token);
2336                 },false,currentTitle.token);
2337                 return;
2338             }
2339             if( currentTitle.episodes[0].length == 0 ) continue;
2340             for(var eid = 0 ;eid < serviceAgent.currentRepository.productsData[idx].episodes[0].length ; eid ++){
2341                 var currentEpisode = currentTitle.episodes[0][eid];
2342                 if(typeof currentEpisode.cuts == "undefined"){
2343                 if(! force){console.log('skip :'+currentEpisode.name) ;continue;}
2344                     serviceAgent.currentRepository.episodesUpdate(false,false,currentEpisode.token);
2345                     return;//中断
2346                 }
2347 //console.log('products check clear');console.log(currentEpisode);
2348 //                if( currentEpisode.cuts.length==1){serviceAgent.currentRepository.getSCi(false,false,currentEpisode.token);return;}
2349                 if( currentEpisode.cuts[0].length == 0 ) continue;
2350                 for(var cid = 0 ; cid < currentEpisode.cuts[0].length ;cid ++){
2351 /*
2352     管理情報は識別子から取得する
2353 APIの情報は、識別子と一致しているはずだが 照合の上異なる場合はAPIの情報で上書きを行う
2354 識別子として cut.description を使用 上位情報は、エントリから再作成
2355 サブタイトルは episode.discriptionを使用
2356 兼用カット情報はペンディング
2357 */
2358                 var myCutToken = currentEpisode.cuts[0][cid].token;
2359                 var myCutLine  = (currentEpisode.cuts[0][cid].line_id)?
2360                     currentEpisode.cuts[0][cid].line_id:
2361                     (new XpsLine(nas.pmdb.pmTemplate.members[0].line.toString())).toString(true);
2362                 var myCutStage = (currentEpisode.cuts[0][cid].stage_id)?
2363                     currentEpisode.cuts[0][cid].stage_id:
2364                     (new XpsStage(nas.pmdb.pmTemplate.members[0].stages[0].toString())).toString(true);
2365                 var myCutJob   = (currentEpisode.cuts[0][cid].job_id)?
2366                     currentEpisode.cuts[0][cid].job_id:
2367                     (new XpsStage(nas.pmdb.jobNames.members[0].toString())).toString(true);
2368                 var myCutStatus= (currentEpisode.cuts[0][cid].status)?
2369                     currentEpisode.cuts[0][cid].status:'Startup';
2370 
2371 //管理情報が不足の場合は初期値で補う description情報が未登録の場合は、APIの情報からビルドする?
2372 if(! currentEpisode.cuts[0][cid].description){
2373     currentEpisode.cuts[0][cid].description="";
2374 if(dbg)    console.log(currentEpisode.cuts[0][cid]);
2375 };
2376                 var entryArray = (
2377                     String(currentEpisode.cuts[0][cid].description).split('//').concat([
2378                         encodeURIComponent(myCutLine),
2379                         encodeURIComponent(myCutStage),
2380                         encodeURIComponent(myCutJob),
2381                         myCutStatus
2382                     ])
2383                 ).slice(0,6);//
2384 
2385                 var myEntry=entryArray.slice(0,2).join( "//" );//管理情報を外してSCi部のみ抽出
2386                 var currentEntry=serviceAgent.currentRepository.entry(currentEpisode.cuts[0][cid].description);//既登録エントリを確認
2387                 if(currentEntry){
2388                     //データ構造上このパートが実行されるケースは無い…はず versionIDがつかない=エラーエントリになる
2389                     currentEntry.push(entryArray.slice(2).join("//"),currentTitle.token,currentEpisode.token,myCutToken);
2390                 }else{
2391                     var newEntry = new listEntry(entryArray.join('//'),currentTitle.token,currentEpisode.token,myCutToken);
2392                     newEntry.parent = serviceAgent.currentRepository;
2393                     serviceAgent.currentRepository.entryList.push(newEntry);
2394                     // エントリ配下にversionsがあればそのままpush
2395 if(! currentEpisode.cuts[0][cid].versions){
2396     currentEpisode.cuts[0][cid].versions=[];//
2397 //    console.log(currentEpisode.cuts[0][cid]);
2398 };
2399                     for (var vid = 0;vid<currentEpisode.cuts[0][cid].versions.length;vid++){
2400                         var myVersionString=(currentEpisode.cuts[0][cid].versions[vid].description)?
2401                             currentEpisode.cuts[0][cid].versions[vid].description:entryArray.join("//");
2402                         var myVersionToken = currentEpisode.cuts[0][cid].versions[vid].version_token;
2403 if(dbg) console.log("push entry : "+ myVersionString);
2404                        newEntry.push(myVersionString,currentTitle.token,currentEpisode.token,myCutToken,myVersionToken);
2405                     }
2406                 }
2407             };//エピソード更新ループ終了
2408         };//プロダクト更新ループ終了
2409     }
2410     }
2411     documentDepot.documentsUpdate();
2412     //現在すべてのデータを取得後にドキュメントブラウザの更新を行っているためレスポンスが途絶える
2413     //これを解消するためにドキュメントブラウザが逐次更新を行えるように改装を行う
2414     if(callback instanceof Function) callback();
2415 //    return serviceAgent.currentRepository.entryList.length;
2416 }
2417 /*
2418     サーバから情報を取得してproductsDataを更新する
2419     entryListの更新は行わない
2420 */
2421 NetworkRepository.prototype.getList=function (force,callback){
2422 //console.log('networkRepository getList');
2423     alert('getList');return false;
2424     if(callback instanceof Function){callback();}else{documentDepot.documentsUpdate(this.entryList);}
2425         return;
2426 
2427     if((force)||(serviceAgent.currentRepository.productsData.length==0)) {
2428 // forceオプションが指定されるかまたはプロダクトエントリがまだ無いケース
2429 //プロダクト取得を呼び出して手続を一旦終了
2430         setTimeout(function (){serviceAgent.currentRepository.getProducts(callback)},10);
2431         return;
2432     }else{
2433 //プロダクト情報更新
2434 //getProductsメソッドの一部として同様の処理が行わるれる?
2435        for(var idx = 0 ;idx < serviceAgent.currentRepository.productsData.length ;idx ++){
2436             var currentTitle = serviceAgent.currentRepository.productsData[idx];//Object取得
2437             if(typeof currentTitle.episodes == "undefined"){
2438                 if(! force){console.log('skip :'+ currentTitle.name) ;continue;}
2439                 serviceAgent.currentRepository.productsUpdate(function(){
2440                     serviceAgent.currentRepository.getEpisodes(false,false,currentTitle.token);
2441                 },false,currentTitle.token);
2442                 return;
2443             }
2444             if( currentTitle.episodes[0].length == 0 ) continue;
2445             for(var eid = 0 ;eid < serviceAgent.currentRepository.productsData[idx].episodes[0].length ; eid ++){
2446                 var currentEpisode = currentTitle.episodes[0][eid];
2447                 if(typeof currentEpisode.cuts == "undefined"){
2448                 if(! force){console.log('skip :'+currentEpisode.name) ;continue;}
2449                     serviceAgent.currentRepository.episodesUpdate(false,false,currentEpisode.token);
2450                     return;//中断
2451                 }
2452 //console.log('products check clear');console.log(currentEpisode);
2453 //                if( currentEpisode.cuts.length==1){serviceAgent.currentRepository.getSCi(false,false,currentEpisode.token);return;}
2454                 if( currentEpisode.cuts[0].length == 0 ) continue;
2455                 for(var cid = 0 ; cid < currentEpisode.cuts[0].length ;cid ++){
2456                 }
2457             };//エピソード更新ループ終了
2458             documentDepot.updateOpusSelector();
2459         };//プロダクト更新ループ終了
2460     }
2461     //現在すべてのデータを取得後にドキュメントブラウザの更新を行っているためレスポンスが途絶える
2462     //これを解消するためにドキュメントブラウザが逐次更新を行えるように改装を行う
2463     if(callback instanceof Function){
2464         callback();
2465     }else{
2466         documentDepot.documentsUpdate();
2467     };
2468 }
2469 /**
2470     productsDataをentryListに変換するプロシジャ
2471    独立してなるべく高速に処理
2472    変換のみリスト取得は試みない
2473 */
2474 NetworkRepository.prototype.convertPDEL=function (){
2475 console.log("clear entryList \n rebuild entryList from documentsData"); console.log(this.productsData); console.log('++==%%');
2476     this.entryList.length=0;//エントリリスト初期化
2477     var newList = []; //新規配列作成
2478 //プロダクト情報構築
2479     for(var idx = 0 ;idx < serviceAgent.currentRepository.productsData.length ;idx ++){
2480         var currentTitle = serviceAgent.currentRepository.productsData[idx];//Object取得
2481 console.log(currentTitle);
2482         if((typeof currentTitle.episodes == "undefined")||( currentTitle.episodes[0].length == 0 )) continue;
2483         //エピソード未登録タイトルはカットが存在しないので処理スキップ
2484         for(var eid = 0 ;eid < serviceAgent.currentRepository.productsData[idx].episodes[0].length ; eid ++){
2485 console.log(serviceAgent.currentRepository.productsData[idx].episodes[0][eid]);
2486             var currentEpisode = currentTitle.episodes[0][eid];
2487             if((typeof currentEpisode.cuts == "undefined")||( currentEpisode.cuts[0].length == 0 )) continue;
2488                 for(var cid = 0 ; cid < currentEpisode.cuts[0].length ;cid ++){
2489 /*
2490     管理情報をビルド
2491 APIの情報は、識別子と一致しているはずだが 照合の上異なる場合はAPIの情報で上書きを行う
2492 識別子として cut.description を使用 上位情報は、エントリから再作成
2493 サブタイトルは episode.discriptionを使用
2494 兼用カット情報はペンディング
2495 */
2496 
2497                 var myCut = currentEpisode.cuts[0][cid];
2498 console.log(myCut);
2499 console.log(xUI.XPS.currentStatus);
2500     if((! myCut.currentStatus)&&(myCut.description)){
2501         console.log('no currentStatus');
2502         myCut.line_id="0:trunk";
2503         myCut.stage_id="0:noname";
2504         myCut.job_id="0:init";
2505         myCut.status="Startup";
2506     }
2507 //管理情報が不足の場合は初期値で補う   初期値の設定はダミー
2508   
2509     if (myCut.description){
2510 console.log(decodeURIComponent(myCut.description));
2511                 var myIdentifier = myCut.description;
2512 
2513 
2514 
2515     }else{
2516 
2517                 if(! myCut.line_id) myCut.line_id  =(new XpsLine(nas.pmdb.pmTemplate.members[0])).toString(true);
2518                 if(! myCut.stage_id) myCut.stage_id =(new XpsStage(nas.pmdb.pmTemplate.members[0].stages.getStage())).toString(true);
2519                 if(! myCut.job_id)   myCut.job_id   =(new XpsStage(nas.pmdb.jobNames.getTemplate(nas.pmdb.pmTemplate.members[0].stages.getStage(),"init")[0])).toString(true);
2520                 if(! myCut.status)   myCut.status   =(new JobStatus("Startup")).toString(true);
2521 
2522                 var myIdentifier =  encodeURIComponent(currentTitle.name);
2523                 myIdentifier += '#' + encodeURIComponent(currentEpisode.name);
2524                 myIdentifier += (currentEpisode.description)? '['+encodeURIComponent(currentEpisode.description) + ']':'';
2525                 myIdentifier += '//'+encodeURIComponent(myCut.name);
2526                 myIdentifier += '//'+([
2527                     encodeURIComponent(myCut.line_id),
2528                     encodeURIComponent(myCut.stage_id),
2529                     encodeURIComponent(myCut.job_id),
2530                     encodeURIComponent(myCut.status)
2531                 ]).join('//');
2532                 myCut.description=myIdentifier;//description情報が未登録の場合は、APIの情報からビルドした識別子を登録
2533     }
2534 console.log(myCut.description);
2535 console.log(decodeURIComponent(myCut.description));                
2536 
2537                 var entryArray = myIdentifier.split('//').slice(0,6);//
2538                 var myEntry=entryArray.slice(0,2).join( "//" );//管理情報を外してSCi部のみ抽出
2539                 var currentEntry=serviceAgent.currentRepository.entry(currentEpisode.cuts[0][cid].description);//既登録エントリを確認
2540 console.log(currentEntry);
2541 console.log(entryArray.slice(2).join("//"),currentTitle.token,currentEpisode.token,myCut.token);
2542                if(currentEntry){
2543 /*
2544 ネットワークリポジトリの場合は、同カットが二重にプロダクトデータに存在することはない。
2545 存在した場合はエラーを記録してNOP
2546 */
2547 console.log('error : エントリ重複検出');console.log(currentEntry);
2548                 }else{
2549 //当該カットが無いので新規にエントリ
2550 console.log([entryArray.join('//'),currentTitle.token,currentEpisode.token,myCut.token]);              
2551 //                    var newEntry = new listEntry(entryArray.slice(0,2).join('//'),currentTitle.token,currentEpisode.token,myCut.token);
2552                     var newEntry = new listEntry(myCut.description,currentTitle.token,currentEpisode.token,myCut.token);
2553                     newEntry.parent = serviceAgent.currentRepository;
2554                     serviceAgent.currentRepository.entryList.put(newEntry);
2555 console.log(newEntry.issues[0].toString());
2556 console.log(serviceAgent.currentRepository.entryList);
2557                 };
2558 // エントリ配下にversionsがあるはずなのでissuesをクリアしてデータをpush
2559 // versionsが存在しない場合のみissuesに規定値が残る
2560                 if(! myCut.versions){ myCut.versions=[]};
2561                 for (var vid = 0;vid < myCut.versions.length;vid++){
2562                     var myVersionString=(myCut.versions[vid].description)?
2563                         myCut.versions[vid].description:entryArray.join("//");
2564                     var myVersionToken = myCut.versions[vid].version_token;
2565                     if(vid == 0){
2566 //第一要素のみ 上書き処理
2567 console.log('既存のissuesを上書き');
2568 console.log(entryArray);
2569 console.log(myCut);
2570 console.log(newEntry.issues[0]);
2571 //                        newEntry.issues[0][0]='';
2572 //                        newEntry.issues[0][1]='';
2573 //                        newEntry.issues[0][2]='';
2574 //                        newEntry.issues[0][3]='';
2575 //                        newEntry.issues[0].time='';
2576                         newEntry.issues[0].cutID=myCut.token;
2577                         newEntry.issues[0].identifier=myVersionString;
2578                         newEntry.issues[0].versionID=myVersionToken;
2579                     }else{
2580 console.log("push entry : "+ myVersionString);
2581                         newEntry.push(
2582                             myVersionString,
2583                             currentTitle.token,
2584                             currentEpisode.token,
2585                             myCut.token,
2586                             myVersionToken
2587                         );
2588                     };
2589                 };//バージョンループ
2590             };//カットループ
2591         };//エピソードループ
2592     };//プロダクトループ
2593 console.log('XXXXXX');
2594 console.log(serviceAgent.currentRepository.entryList)
2595     documentDepot.documentsUpdate();
2596 }
2597 /**
2598 online-sigleモード用にエントリを一つだけ(高速に)作って固定する
2599 
2600 以下の構造の productsDataを組む
2601 
2602 タイトル/詳細は、該当タイトル一つのみ
2603 エピソード/詳細も当該カットの親一つのみ
2604 カットも当該カット一つのみ
2605 
2606 NetworkRepository.prototype.buildProducts=function(){
2607     serviceAgent.currentServer.getRepositories(function(){
2608         serviceAgent.currentRepository.getEntry();
2609     });
2610 }    
2611 */
2612 /**
2613 識別子(ユーザの選択)を引数にして実際のデータを取得
2614 識別子に管理情報が付いている場合はそれを呼ぶ
2615 管理情報なしの場合は、当該エントリの最新ジョブの内容を呼ぶ
2616 管理情報が未fixの場合は編集エリア、既fixの場合はリファレンスエリアに読み込む
2617 
2618 ターゲットジョブに先行するジョブがある場合は、そのジョブをリファレンスとしてコールバック内で呼ぶ
2619 
2620 動作仕様調整
2621 識別子引数は同じだが、完全型式の引数+動作を渡してここでは判定を行わないように変更
2622 判定はブラウザ又はサービスエージェントの同名関数が行う
2623 引数の自動補完もしない
2624 
2625 サーバからの読み出し後に、データ照合を行ってデータから生成される識別子がサーバの識別子と一致するように調整
2626 サーバ側指定を優先してデータは自動更新される
2627 詳細情報を受け取った際に補助情報又は受け取ったオブジェクトそのものをバックアップすること
2628 
2629 UATサーバに対する請求の際に、識別子のかわりにtokenを使用可能にする。
2630 (targetInfo.title == myIdentifier)であった場合のみcutTokenとして扱う?
2631 
2632 */
2633 NetworkRepository.prototype.getEntry=function (myIdentifier,isReference,callback,callback2){
2634     if(typeof isReference == 'undefined'){isReference = false;}
2635     //識別子をパース
2636     var targetInfo     = Xps.parseIdentifier(myIdentifier);//?
2637     var myIssue = false;
2638     var refIssue = false;
2639 /*
2640   if (targetInfo.title == myIdentifier){
2641 //トークンが直接与えられたものと判断する
2642     var targetURL='/api/v2/cuts/'+myIdentifier+'.json'; 
2643   } else {}
2644 */
2645 //識別子からトークンを得る
2646     var myEntry = this.entry(myIdentifier);
2647     var myCut   = this.cut(myEntry.issues[0].cutID);
2648 console.log(myEntry);
2649     if((! myEntry)||(! myCut)){
2650             var msg=localize({en:"no entry %1 in DB",ja:"DBからエントリ%1の取得に失敗しました"},decodeURIComponent(myIdentifier));
2651         alert(msg);
2652 console.log(myEntry);
2653 console.log(myCut);
2654 console.log(serviceAgent.currentRepository);
2655 console.log("noEntry : "+ decodeURIComponent(myIdentifier));//プロダクトが無い
2656         return false;
2657     }
2658     if(! targetInfo.currentStatus){
2659     //ターゲットに管理部分がないので、最新のissueとして補う
2660         var cx = myEntry.issues.length-1;
2661         myIssue = myEntry.issues[cx];
2662     }else{
2663     //指定管理部分からissueを特定する 連結して比較(後方から検索)リスト内に指定エントリがなければ失敗
2664         checkIssues:{
2665             for (var cx = (myEntry.issues.length-1) ; cx >= 0 ;cx--){
2666                if ( Xps.compareIdentifier(myEntry.issues[cx].identifier,myIdentifier) > 4){
2667                     myIssue = myEntry.issues[cx];
2668                     break checkIssues;
2669                 }
2670             }
2671             if (! myIssue){
2672 if(dbg) console.log( 'no target data :'+ decodeURIComponent(myIdentifier) );//ターゲットのデータが無い
2673                 return false;
2674             }
2675         }
2676     }
2677 // 構成済みの情報を判定 (リファレンス置換 or 新規セッションか)
2678 //      myIssue; これがカットへのポインタ episode.cuts配列のエントリ myIssue.url にアドレスあり
2679 //      urlプロパティが無い場合はid があるのでidからurlを作成する
2680     if(! myIssue.versionID){
2681         var targetURL=(myIssue.url)? myIssue.url: '/api/v2/cuts/'+myIssue.cutID.toString()+'.json';
2682     }else{
2683         var targetURL=(myIssue.url)? myIssue.url: '/api/v2/cuts/'+myIssue.cutID.toString()+'/'+String(myIssue.versionID)+'.json';
2684 if(dbg) console.log(targetURL);
2685     }
2686 
2687     if(! isReference){
2688 /**
2689 暫定補助情報フォーマット
2690     product.name
2691         decoded name
2692     product.description
2693     episode.name
2694         decoded name
2695     episode.description
2696         decoded subtitle 
2697     cut.name
2698         decoded name
2699     cut.description
2700         identifier-fullformat
2701 */
2702     $.ajax({
2703         url: this.url + targetURL,
2704         type: 'GET',
2705         dataType: 'json',
2706         success: function(result) {
2707 //console.log(result);
2708 //データ請求に成功したので、現在のデータを判定して処理の必要があれば処理
2709         	var myContent=result.data.cut.content;//XPSソーステキストをセット
2710         	var currentXps = new Xps();
2711 	        if(myContent){
2712 	            currentXps.parseXps(myContent);
2713 	        }else{
2714 /*
2715     サーバリザルトにタイムシートの内容が含まれない場合は、登録直後の空白データ
2716     以下の情報を取得して空のタイムシートをビルドする
2717     タイトル、エピソード
2718     タイトルのフレームレート(ない場合はシステムデフォルト)
2719     識別子に含まれるカット番号 あれば カット尺(ない場合はシステムデフォルト)
2720 */
2721 //console.log('contents :'+ myContent);
2722 	            var myParseData = Xps.parseSCi(result.data.cut.name);
2723 	            currentXps.cut = myParseData.cut;
2724 	            currentXps.setDuration(nas.FCT2Frm(String(myParseData.time)));
2725 	        }
2726 //myContent==nullのケースは、サーバに空コンテンツが登録されている場合なので単純にエラー排除してはならない
2727 //currentXpsのプロパティをリザルトに同期させる
2728 //エラーではなく初期化時点の初期状態のXpsのままで処理を継続する
2729             //xUI.userPermissions=result.data.cut.permissions;
2730 //読み込んだXPSが識別子と異なっていた場合識別子優先で同期する
2731 	            xUI.resetSheet(currentXps);
2732 	            var durationChange=xUI.XPS.duration();
2733 //console.log(xUI.XPS);
2734 //console.log(myIssue.identifier);
2735 //                xUI.XPS.syncIdentifier(myIssue.identifier,false);
2736                 xUI.XPS.syncIdentifier(myIssue.identifier,true);
2737                 durationChange = (durationChange == xUI.XPS.duration())? false:true;
2738 	            if(myEntry.issues.length>1){
2739                     documentDepot.currentReference = new Xps(5,144);//空オブジェクトをあらかじめ新規作成
2740                     //自動設定されるリファレンスはあるか?
2741                     //指定管理部分からissueを特定する 文字列化して比較
2742                     if ( cx > 0 ){
2743                         if(parseInt(decodeURIComponent(myIssue[2]).split(':')[0]) > 0 ){
2744                     //ジョブIDが1以上なので 単純に一つ前のissueを選択する
2745                     //必ず先行jobがある  =  通常処理の場合は先行JOBが存在するが、単データをエントリした場合 そうではないケースがあるので対処が必要 2016 12 29
2746                         refIssue = myEntry.issues[cx-1];
2747                         }else if(decodeURIComponent(myIssue[1]).split(':')[0] > 0 ){
2748                     //第2ステージ以降前方に向かって検索
2749                     //最初にステージIDが先行IDになった要素が参照すべき要素
2750                             for(var xcx = cx-1 ;xcx >= 0 ; xcx --){
2751                                 if (parseInt(decodeURIComponent(myEntry.issues[xcx][1]).split(':')[0]) == (parseInt(decodeURIComponent(myIssue[1]).split(':')[0])-1)){
2752                                     refIssue = myEntry.issues[xcx];
2753                                     break;
2754                                 }
2755                             }
2756                         };//cx==0 のケースでは、デフォルトで参照すべき先行ジョブは無い
2757 	                }
2758 	                if(refIssue) serviceAgent.currentRepository.getEntry(refIssue.identifier,true);
2759 	            }
2760 	            //xUI.resetSheet(XPS);
2761                 xUI.sessionRetrace = myEntry.issues.length-cx-1;
2762                 xUI.setUImode('browsing');sync("productStatus");
2763                 xUI.flushUndoBuf();sync('undo');sync('redo');
2764                 if(durationChange) xUI.resetSheet();
2765                 if(callback instanceof Function) callback();
2766         },
2767         error:function(result){
2768 if(dbg) console.log(result);
2769             if(callback2 instanceof Function) callback2();
2770         },
2771         beforeSend: this.service.setHeader
2772     });
2773 
2774 }else{
2775     //データ単独で現在のセッションのリファレンスを置換
2776     $.ajax({
2777         url: this.url + targetURL,
2778         type: 'GET',
2779         dataType: 'json',
2780         success: function(result) {
2781             var myContent=result.data.cut.content;//XPSソーステキストをセット
2782 if(dbg) console.log('import Reference'+myContent);
2783 	        documentDepot.currentReference=new Xps();
2784 	        documentDepot.currentReference.readIN(myContent);
2785 	        xUI.setReferenceXPS(documentDepot.currentReference);
2786             if(callback instanceof Function) callback();
2787         },
2788         error: function(result){
2789 if(dbg) console.log(result);
2790             if(callback2 instanceof Function) callback2();
2791         },
2792         beforeSend: this.service.setHeader
2793     });
2794 }
2795   return null;
2796 }
2797 /**
2798     標準コールバックを作る
2799     コールバック関数が引数で与えられなかった場合は xUIに新規Xpsとして与えて読み込ませる
2800     読み出したエントリに前方のジョブがあれば、それをリファレンスとして与えるルーチンも必要
2801 
2802 function(result){
2803 	var myContent=result.data.cut.content;//XPSソーステキストをセット
2804 //以下が標準の読み込み時の初期化
2805 	if(xUI.XPS.readIN(myContent)){xUI.resetSheet(xUI.XPS);}
2806     if(that.
2807 }
2808 function(result){
2809 	var myContent=result.data.cut.content;//XPSソーステキストをセット
2810 	myXps=new Xps();
2811     xUI.setReferenceXPS(myXps)
2812 }
2813     外部からコールバックを与える場合は、以下のようなケース
2814 未fixのXPSをリファレンスペインに読み込む操作(これに関してはオプションをつけたほうが良さそう?)
2815  */
2816 /**
2817     DBにタイトルを作成する。
2818     confirmなし 呼び出し側で済ませること
2819     必要あれば編集UI追加
2820 引数:
2821     タイトル(必須)
2822     備考テキスト
2823     Pmオブジェクト
2824     コールバック関数2種
2825 戻値:
2826      なし
2827 
2828 識別子は受け入れない 必要に従って前段で分解のこと
2829 */
2830 NetworkRepository.prototype.addTitle=function (myTitle,myDescription,myPm,callback,callback2){
2831 /*
2832     識別子を検出(呼び出し側で)このルーチンまで来た場合は、引数を分解しておくこと
2833     2017.01.28時点でAPIにtemplateが出ていないのでpmの処理は省略 遅延で詳細編集を行っても良い
2834     serviceAgent.currentRepository.addTitle("tST2","testTitlewith API")
2835     作成時に検査を行い、既存タイトルならば処理を中断する(呼び出し側で)
2836     タイトル作成前に確認メッセージを出す(これも呼び出し側)
2837     現在はPmオブジェクトは機能していない 2/9 2017
2838 */
2839     if(! myTitle) return false;
2840     if(! myDescription) myDescription="";
2841 //      var parseData = Xps.parseIdentifier(myTitle);
2842 //      if(parseData){myTitle=parseData.title};
2843     var data = {
2844         product: {
2845           name          : myTitle,
2846           description   : myDescription,
2847           framerate     : nas.FRATE,
2848         } 
2849     };
2850 
2851 	$.ajax({
2852 		type : 'POST',
2853 		url : serviceAgent.currentRepository.url+"/api/v2/products.json",
2854 		data : data,
2855 		success : function(result) {
2856 if(dbg) console.log('success');
2857 if(dbg) console.log(result);
2858             if(callback instanceof Function) callback();
2859 		},
2860 		error:function(result) {
2861 if(dbg) console.log('error');
2862 if(dbg) console.log(result);
2863             if(callback2 instanceof Function) callback2();
2864 		},
2865 		beforeSend: serviceAgent.currentRepository.service.setHeader
2866 	});
2867 }
2868 /**
2869     DBにOPUS(エピソード)を作成する。
2870 引数
2871     タイトルを含む識別子 カット番号は求めない
2872     コールバック関数2種
2873     識別子のみ受け入れ
2874     このルーチンを呼び出す時点で、タイトルは存在すること
2875     手続的には、カット作成を主眼にして
2876     Title/Opus 等の上位のオブジェクトが存在しない時点で自動でコールされるように調整する?
2877     
2878 */
2879 NetworkRepository.prototype.addOpus=function (myIdentifier,prodIdentifier,callback,callback2){
2880 /*
2881     listEntry.titleID
2882 */
2883     var parseData = Xps.parseIdentifier(myIdentifier);
2884     if(! parseData){
2885         if(callback2 instanceof Function) callback2;
2886         return;
2887     }
2888     var myProduct = parseData.product;
2889     
2890     var myEntry=false;
2891     if(typeof prodIdentifier == 'undefined'){
2892         for (var pid=0;pid<documentDepot.products.length;pid ++){
2893         //productsのメンバをオブジェクト化したほうが良いかも
2894             var prdInfo=Xps.parseProduct(documentDepot.products[pid]);
2895             if(prdInfo.title==myProduct.title) {
2896                  myEntry = serviceAgent.currentRepository.entry(documentDepot.products[pid]);
2897                 break;
2898             }
2899         };
2900     }else{
2901         myEntry = serviceAgent.currentRepository.entry(prodIdentifier+"//",true);
2902     }
2903     if(!myEntry){
2904         if(callback2 instanceof Function) callback2;
2905         return;
2906     }
2907     var data = {
2908         episode: {
2909           product_token : myEntry.productID,
2910           name          : myProduct.opus,
2911           description   : myProduct.subtitle
2912         } 
2913     };
2914 
2915 	$.ajax({
2916 		type : 'POST',
2917 		url : serviceAgent.currentRepository.url+"/api/v2/episodes.json",
2918 		data : JSON.stringify(data),
2919 		success : function(result) {
2920 		    if( callback instanceof Function) callback();
2921 		},
2922 		error:function(result) {
2923 		    if( callback2 instanceof Function) callback2();
2924 		},
2925 		beforeSend: serviceAgent.currentRepository.service.setHeader
2926 	});
2927 }
2928 /**
2929 データオブジェクトを渡してリポジトリにプッシュする
2930 一致エントリがあれば上書き
2931 一致エントリで先行の管理情報がロックされている場合はリジェクト
2932 管理情報の世代が上がっていれば追加の管理情報を添えて保存
2933 タイトルがDBに登録されていない場合は、ユーザの確認をとってタイトルを作成
2934 エピソードがDBに登録されていない場合も同様
2935 
2936 これは保存系のAPIが出てから調整
2937 */
2938 
2939 NetworkRepository.prototype.pushEntry=function (myXps,callback,callback2){
2940 //識別子取得(全要素で取得)
2941     var myIdentifier=Xps.getIdentifier(myXps,true);
2942 //識別子に相当するアイテムがリポジトリに存在するかどうかをチェック
2943     var currentEntry = this.entry(myIdentifier);
2944 //    var currentCut   = this.cut(myIdentifier);
2945     var currentCut   = this.cut(currentEntry.issues[0].cutID);
2946     if(currentEntry){
2947             //既存のエントリが有るのでストレージとリストにpushして処理終了
2948         this.pushData('PUT',currentEntry,myXps,callback,callback2)
2949     }else{
2950 /**
2951     currentEntry==null なので、ターゲットのエピソードtokenを再取得して引数で渡す必要あり 12・21
2952 */
2953             //新規エントリなので新たにPOSTする (空エントリを引数に付ける)
2954 //        var tmpEntry= new listEntry(Xps.getIdentifier(myXps));
2955         var tmpEntry= this.entry(Xps.getIdentifier(myXps),true);
2956         this.pushData('POST',tmpEntry,myXps,callback,callback2)    
2957     }
2958 };
2959 /**
2960     サーバにデータを送信する(メンテナンスメソッド)
2961     引数:
2962         メソッド
2963         myProduct
2964         Xpsオブジェクト
2965         成功時コールバック関数
2966         失敗時コールバック関数
2967 
2968 リポジトリ上に既存エントリはPUT 新規エントリはPOSTで 送信
2969 タイトルや、エピソードが存在しないデータはリジェクト
2970 オンサイト時は 各種データをbackend_variablesから取得
2971 それ以外の場合は、documentDepotから取得をトライする
2972 取得に失敗した場合は送信失敗
2973 
2974 エントリリスト等の内部操作は行わない
2975 
2976 <span id="backend_variables" data-user_access_token="4dcb5a249c94aa21529a522e23de730f176d032d8e1e1bf621c8f09b0d733566"
2977                                data-user_token="aWWMWNKW2HAfuRHWANZKbETy"
2978                                data-user_name="ねこまたや"
2979                                data-user_email="kiyo@nekomataya.info"
2980                                data-episode_id="17"
2981                                data-cut_id="24" 
2982                                data-episode_token="mfjVjBUuG6Q8GHu7u6nzJTa2"
2983                                data-cut_token="73o16nRYK7oqNNmeGDHWizLV"
2984                                data-line_id="0:(trunk)"
2985                                data-stage_id="0:Startup"
2986                                data-job_id="1:work"
2987                                data-status="Active"
2988   ></span>
2989   myEntry を myProduct に換装
2990   listEntry > productsData.episodes[0]
2991 */
2992 NetworkRepository.prototype.pushData=function (myMethod,myEntry,myXps,callback,callback2){
2993 //console.log(myEntry);
2994 if (myEntry instanceof listEntry){
2995 //エントリオブジェクト渡し
2996 	var lastIssue   = myEntry.issues[myEntry.issues.length-1];
2997 
2998     var title_name     = myEntry.product.split('#')[0];
2999     var episode_name   = myEntry.product.split('#')[1];
3000     var cut_name       = (myMethod == 'PUT')? myEntry.sci:'s'+((myXps.scene)? myXps.scene:'-')+'c'+myXps.cut+"("+nas.Frm2FCT(myXps.time(),3,0,myXps.framerate)+")";
3001     var line_id        = myXps.line.toString(true);
3002     var stage_id       = myXps.stage.toString(true);
3003     var job_id         = myXps.job.toString(true);
3004     var status         = myXps.currentStatus.toString(true);
3005 /*
3006     var line_id        = lastIssue[0];
3007     var stage_id       = lastIssue[1];
3008     var job_id         = lastIssue[2];
3009     var status         = lastIssue[3];
3010 */
3011 }else{
3012 //プロダクト(該当カットはなし)
3013 	var lastIssue   = ['0:','0:','0:','Startup'];
3014 
3015     var title_name     = myEntry.product.split('#')[1];
3016     var episode_name   = encodeURIComponent(myEntry.name);
3017     var cut_name       = (myMethod == 'PUT')? myEntry.sci:'s'+((myXps.scene)? myXps.scene:'-')+'c'+myXps.cut+"("+nas.Frm2FCT(myXps.time(),30,myXps.framerate)+")";
3018     var line_id        = myXps.line.toString(true);
3019     var stage_id       = myXps.stage.toString(true);
3020     var job_id         = myXps.job.toString(true);
3021     var status         = myXps.currentStatus.toString(true);
3022 /*
3023     var line_id        = lastIssue[0];
3024     var stage_id       = lastIssue[1];
3025     var job_id         = lastIssue[2];
3026     var status         = lastIssue[3];
3027 */
3028     
3029 }
3030 //オンサイト・シングルドキュメントバインドの場合はbackend_variablesから情報を取得
3031   if(serviceAgent.currentStatus=="online-single"){
3032 //  if(document.getElementById('backend_variables')){}
3033 	var episode_token   = $('#backend_variables').attr('data-episode_token');
3034 	var cut_token       = $('#backend_variables').attr('data-cut_token');
3035   }else{
3036 	var episode_token   = myEntry.episodeID;
3037 	var cut_token       = (myMethod == 'PUT')? lastIssue.cutID:false;
3038   }
3039 //console.log(serviceAgent.currentStatus);
3040 //console.log("epToken : "+episode_token);
3041 //console.log("ctToken : "+cut_token +" :: "+ lastIssue.cutID);
3042 //console.log("ctName  : "+decodeURIComponent(cut_name));
3043 //return
3044 /**
3045 	保存時に送り出すデータに
3046 		タイトル・エピソード番号(文字列)・サブタイトル
3047 		カット番号+カット尺
3048 	を加えて送出する
3049 	型式をきめこむ
3050 	サーバ側では、これが保存状態と異なる場合は、エラーを返すか又は新規タイトルとして保存する必要がある。
3051 	アプリケーション側は、この文字列が異なる送出を抑制して警告を出す?
3052 このメソッドは、既存のエピソードに対しての追加機能のみ
3053 タイトル作成及びエピソード作成は別に用意する	
3054 */
3055 if(myMethod=='POST'){
3056 //新規エントリ作成 POST
3057 	json_data = {cut:{
3058 		     		episode_token   : episode_token,
3059 	                name            : decodeURIComponent(cut_name),
3060 	                description     : Xps.getIdentifier(myXps,true),
3061 			 		status          : myXps.currentStatus.toString(true),
3062 			 		job_id          : decodeURIComponent(myXps.job.toString(true)),
3063 			 		stage_id        : decodeURIComponent(myXps.stage.toString(true)),
3064 			 		line_id         : decodeURIComponent(myXps.line.toString(true)),
3065 			 		content         : myXps.toString()
3066 				}};
3067 		method_type = 'POST';
3068 		target_url = '/api/v2/cuts.json';
3069 }else{
3070 //エントリ更新 PUT
3071 	json_data = {
3072 		     		token: cut_token,
3073 		     		cut:{
3074 	                   name         : decodeURIComponent(cut_name),
3075 //	                   description  : myEntry.toString(true),
3076 	                   description  : Xps.getIdentifier(myXps,true),
3077 			 		   content      : myXps.toString(),
3078 //			 		 cut_token   : cut_id,
3079 //			 		 title_name  : title_name,
3080 //			 		 episode_name: episode_name,
3081 //			 		 cut_name    : cut_name,
3082 			 		   line_id     : decodeURIComponent(line_id),
3083 			 		   stage_id    : decodeURIComponent(stage_id),
3084 			 		   job_id      : decodeURIComponent(job_id),
3085 			 		   status      : status
3086 				}};
3087 		method_type = 'PUT';
3088 		target_url = '/api/v2/cuts/' + cut_token + '.json'
3089 }
3090 if((typeof cut_token == 'undefined')||(cut_token == 'undefined')){console.log(myXps);console.log(myEntry);}
3091 /*
3092 開発中の 制作管理DB/MAP/XPS で共通で使用可能なnas.SCInfoオブジェクトを作成中
3093 これに一意のIDを持たせる予定です。
3094 */
3095 if(dbg) console.log(method_type+' :'+serviceAgent.currentRepository.url+target_url +'\n' +JSON.stringify(json_data));
3096 	$.ajax({
3097 		type : method_type,
3098 		url : serviceAgent.currentRepository.url+target_url,
3099 		data : JSON.stringify(json_data),
3100 		contentType: 'application/JSON',
3101 		dataType : 'JSON',
3102 		scriptCharset: 'utf-8',
3103 		success : function(result) {
3104                 if (xUI.XPS === myXps) xUI.setStored('current');
3105 			sync();//保存ステータスを同期
3106 			if( method_type == 'POST'){
3107 if(dbg) console.log("new cut!");
3108 //console.log(result);
3109 				$('#backend_variables').data('cut_token', result.data.cut['token']);
3110 			}else{
3111 if(dbg) console.log('existing cut!');
3112 			}
3113 // リストプッシュ 等の内部 DB操作は前段で適用を済ませるかまたはコールバック渡しにする
3114             if(callback instanceof Function){callback();}
3115 		},
3116 		error : function(result) {
3117             if(callback2 instanceof Function){callback2();}
3118 			// Error
3119 //console.log("error");
3120 //console.log(result);
3121 		},
3122 		beforeSend: serviceAgent.currentRepository.service.setHeader
3123 	});
3124 };
3125 /**
3126     ネットワークリポジトリのエントリをアプリケーションから削除することは無いので以下のメソッドは不用?
3127     APIにハードデリートとソフトデリートを出してもらう?
3128     削除が可能な条件は
3129     自分自身が開始した作業セッションであること && 最終の作業セッションであること
3130     エントリをすべて削除するにはオプションが必要
3131     識別子がフル()
3132 */
3133 NetworkRepository.prototype.removeEntry=function (myIdentifier){
3134 //
3135 //識別子 からエントリを特定して削除する?
3136 };
3137 /**
3138     エントリリストを検索して該当するリストエントリを返す操作をメソッド可
3139 引数:
3140     識別子
3141     プロダクト検索オプション
3142 戻値:
3143     識別子に該当するlistEntry
3144     または episode情報
3145     データ照合に失敗した場合はnull
3146     
3147     issues他のプロパティは受取先で評価
3148     指定の識別子との比較は
3149     title,opus,scene,cut の4点の比較で行う(秒数とサブタイトルは比較しない)
3150     optを加えるとtitle,opus(= product)のみを比較
3151     現在カットが0(未登録)の登録済みプロダクトの場合 true/-1 false/0 を戻す
3152 */
3153 NetworkRepository.prototype.entry=function(myIdentifier,opt){
3154 //    if(! opt) {opt = 1}else{opt = 0};
3155 //    return this.entryList.getByIdf(myIdentifier,opt);
3156 /*--以下検証要
3157 --*/
3158     opt = (opt)? -1 : 0;
3159   if(serviceAgent.currentRepository.entryList.length){
3160     for (var pid=0;pid<serviceAgent.currentRepository.entryList.length;pid++){
3161         if(Xps.compareIdentifier(serviceAgent.currentRepository.entryList[pid].toString(),myIdentifier) > opt){
3162                 return serviceAgent.currentRepository.entryList[pid]
3163         }
3164     }
3165   }else if(opt){
3166     for (var pid=0;pid<serviceAgent.currentRepository.productsData.length;pid++){
3167       for (var oid=0;oid<serviceAgent.currentRepository.productsData[pid].episodes[0].length;oid++){
3168         var checkTitle = serviceAgent.currentRepository.productsData[pid].name;
3169         var checkOpus  = serviceAgent.currentRepository.productsData[pid].episodes[0][oid].name;
3170         if(
3171     Xps.compareIdentifier([decodeURIComponent(checkTitle),decodeURIComponent(checkOpus)].join('#')+'//',myIdentifier) > opt
3172         ){
3173                 return opt;
3174         }
3175       }
3176     }
3177   }
3178     return null;        
3179 }
3180 /**
3181     カットトークン又はエピソードトークンから識別子を取得する
3182     エピソードトークンで得られた識別子はカット番号を自動で補う
3183     タイトル等の文字列が必要な場合はダミーのカット番号を捨てる必要あり
3184 */
3185 NetworkRepository.prototype.getIdentifierByToken=function(myToken){
3186     search_loop:
3187     for (var pid=0;pid<this.productsData.length;pid++){
3188         //先にカットの照合を行う
3189         //要素内に情報の不足がある場合はそのブロックをスキップする
3190         if(! this.productsData[pid].episodes) continue;
3191         for (var eid=0;eid<this.productsData[pid].episodes[0].length;eid++){
3192             if(! this.productsData[pid].episodes[0][eid].cuts) continue;
3193             for (var cid=0;cid<this.productsData[pid].episodes[0][eid].cuts[0].length;cid++){
3194                 if(this.productsData[pid].episodes[0][eid].cuts[0][cid].token==myToken){
3195                     return this.productsData[pid].episodes[0][eid].cuts[0][cid].description;
3196                 }
3197             if(this.productsData[pid].episodes[0][eid].token==myToken){
3198                 var lastName = (this.productsData[pid].episodes[0][eid].cuts[0].length)?
3199                 this.productsData[pid].episodes[0][eid].cuts[0][this.productsData[pid].episodes[0][eid].cuts[0].length-1].name:'0';
3200                 var myIdentifier = encodeURIComponent(this.productsData[pid].name)+'#'+
3201                     encodeURIComponent(this.productsData[pid].episodes[0][eid].name)+
3202                     ((this.productsData[pid].episodes[0][eid].description)?
3203                     '['+encodeURIComponent(this.productsData[pid].episodes[0][eid].description)+']':''
3204                 )+'//'+nas.incrStr(lastName);
3205                 return myIdentifier;   
3206             }
3207             }
3208         }
3209     }
3210     return null;        
3211 }
3212 /**
3213     tokenの一致するproductsData内のノードエレメントを戻す
3214     product>episodes>cut の順で検索 種別指定があればそのエレメントを先に検索
3215 */
3216 NetworkRepository.prototype.getNodeElementByToken=function(myToken,myKind){
3217     if(! myToken) return null;
3218     if(! myKind) myKind = '*';
3219 products:
3220     for (var pid=0;pid<this.productsData.length;pid++){
3221         if(((myKind=="product")||(myKind=="*"))&&(this.productsData[pid].token == myToken))
3222         return this.productsData[pid];
3223 episodes:
3224         if(this.productsData[pid].episodes){
3225         for (var eid=0;eid<this.productsData[pid].episodes[0].length;eid++){
3226             // episodes[0]がトレーラー配列なので注意
3227             if(((myKind=="episode")||(myKind=="*"))&&(this.productsData[pid].episodes[0][eid].token == myToken))
3228             return this.productsData[pid].episodes[0][eid];
3229 cuts:
3230             if(this.productsData[pid].episodes[0][eid].cuts){
3231             for (var cid=0;cid<this.productsData[pid].episodes[0][eid].cuts[0].length;cid++){
3232 //                if(this.productsData[pid].episodes[0][eid].cuts[0].token == myToken) return this.productsData[pid].episodes[0][eid].cuts[0];
3233                 if(((myKind=="cut")||(myKind=="*"))&&(this.productsData[pid].episodes[0][eid].cuts[0].token == myToken))
3234                 return this.productsData[pid].episodes[0][eid].cuts[0];
3235                 //cuts[0]がトレーラーオブジェクト cuts[1] を廃止する準備中
3236             }}
3237         }}
3238     }
3239     return null;
3240 }
3241 /**
3242     エントリステータス操作コマンドメソッド群
3243     ServiceAgentの同名メソッドから呼び出す下位ファンクション
3244 
3245     caller側のルーチンで判定基準にしたデータが最新である保証が無いので
3246     各メソッドの書込み前にステータスの再確認が必要
3247     読み出しを前段に置いて成功時の関数内で再判定か?
3248     または、サービス側で変更要求に対する処理基準を明確にしてリジェクトを実装してもらう 2016.12.23
3249 */
3250 /**
3251     現在のドキュメント XPS に対応するリポジトリ上のエントリをアクティベートする
3252     アクティベート前に、データの現状を取得してコールバックでアクティベート処理を渡す
3253 
3254     引数:処理成功時と失敗時のコールバック関数
3255 */
3256 NetworkRepository.prototype.activateEntry=function(callback,callback2){
3257     var currentEntry = this.entry(Xps.getIdentifier(xUI.XPS));
3258 //    var currentCut   = this.cut(currentEntry.toString());
3259     var currentCut   = this.cut(currentEntry.issues[0].cutID);
3260     if((!currentEntry)||(!currentCut)){
3261 //console.log('noentry');
3262 //console.log(serviceAgent.currentRepository);
3263         return false;
3264     }
3265 /*
3266     サーバからデータの最新状況を取得する
3267     ステータスを確認してから、新規のステータスを設定する
3268  */
3269 	    $.ajax({
3270 		    type : 'GET',
3271 		    url : this.url+'/api/v2/cuts/'+currentEntry.issues[0].cutID+'.json',
3272 		    success : function(result){
3273 //console.log(result);
3274 /*
3275 手順 GET serverURL(token).json
3276 サーバリザルトのdescriptionから状態と内容を確認して
3277 Activate可能な場合は新しいコンテンツとdescriptionを送信             
3278 それ以外は失敗
3279 */
3280                 currentCut.versions = result.data.versions;
3281                 var currentServerXps=new Xps();
3282                     currentServerXps.parseXps(result.data.cut.content);
3283 //cut.description にidentifierがセットされないケースがある(サービス的には正常)
3284 //cut.descriptionがヌルまたはundefinedの際はXps本体から情報を構築する
3285 
3286                 var currentDataInfo=Xps.parseIdentifier(result.data.cut.description);
3287 //ディスクリプションがcutに付属していないのは APIの変更によるので 調整 0211
3288                 if(! currentDataInfo) currentDataInfo = Xps.parseIdentifier(Xps.getIdentifier(currentServerXps));
3289 //書き込み権限の判定からスタッフの判定になる 
3290 //                    (result.data.permissions.write)&&
3291 //                    (result.data.permissions.read)&&
3292                 if(
3293                     ((currentDataInfo.currentStatus.content == "Fixed")|| (currentDataInfo.currentStatus.content == "Hold"))&&
3294                     (xUI.currentUser.sameAs(currentServerXps.update_user))
3295                 ){
3296 //同内容でステータスを変更したエントリを作成 新規に保存して成功したら先のエントリを消す
3297                     var newXps = Object.create(xUI.XPS);//現在のデータの複製をとる
3298 //console.log('activate : '+decodeURIComponent(Xps.getIdentifier(newXps)));
3299                         newXps.currentStatus = new JobStatus('Active');
3300                         newXps.update_time   = new Date().toNASString();
3301                     var data = {
3302                         token: currentCut.token,
3303                         cut: {
3304                             name:   decodeURIComponent(currentEntry.toString().split('//')[1]),
3305                             content:    newXps.toString(),
3306                             description: Xps.getIdentifier(newXps)
3307                         }
3308                     };
3309 if(dbg) console.log(data);
3310                     $.ajax({
3311 		                type : 'PUT',
3312 		                url : serviceAgent.currentRepository.url+'/api/v2/cuts/'+currentEntry.issues[0].cutID+'.json',
3313 		                data : data,
3314 		                success : function(result){
3315 //console.log('success activated :' + decodeURIComponent(currentEntry.toString()));
3316 //console.log(result)
3317                             currentEntry.setStatus(newXps.currentStatus);
3318 
3319                             currentCut.versions[currentCut.versions.length-1].description = Xps.getIdentifier(newXps);
3320                             currentCut.versions[currentCut.versions.length-1].updated_at  = newXps.update_time;
3321 //PUTのリザルトは200コードのみ
3322 //PUT時点でアサイン可能リスト等をとったほうが良いか?
3323                             xUI.XPS.currentStatus=newXps.currentStatus;//ドキュメントステータスを更新
3324 			                xUI.setStored("current");//UI上の保存ステータスをセット
3325 			                sync();//保存ステータスを同期
3326                             selectSCi();//カレントデータを再セレクトして情報更新
3327                             sync('historySelector');//履歴セレクタ更新
3328                 
3329                             xUI.setUImode('production');
3330                             xUI.sWitchPanel();//パネルクリア
3331                             if(callback instanceof Function) {setTimeout(callback,10);}
3332                         },
3333                         error : function(result){
3334 //console.log('fail activate :'+ decodeURIComponent(currentEntry.toString()));
3335 //console.log(result);
3336 //console.log('ステータス変更失敗 :');
3337                             if(callback2 instanceof Function) {setTimeout(callback2,10);}
3338                                 return false;
3339                         },
3340 		                beforeSend: serviceAgent.currentRepository.service.setHeader
3341                     });                    
3342                 }else{
3343 //console.log('fail activate :'+ decodeURIComponent(currentEntry.toString()));
3344 //console.log(result);
3345 //console.log('ステータス変更できません :');
3346                     if(callback2 instanceof Function) {setTimeout(callback2,10);}
3347                         return false;
3348                 }
3349             },
3350 		    error : function(result) {
3351 			// Error
3352 if(dbg) console.log("error");
3353 if(dbg) console.log(result);
3354 if(dbg) console.log('ステータス変更不可 :'+ Xps.getIdentifier(newXps));
3355                 if(callback2 instanceof Function) {setTimeout(callback2,10);}
3356 		    },
3357 		    beforeSend: this.service.setHeader
3358 	    });
3359 /**
3360     サービス側での排他が完了したら下の簡単な処理でOKのはず
3361         var data = {
3362                 token: currentEntry.issues[0].cutID,
3363                 cut: {
3364                     name:   decodeURIComponent(currentEntry.toString().split('//')[1]),
3365                     description: Xps.getIdentifier(newXps)
3366                 }
3367         };
3368 if(dbg) console.log(data);
3369 	    $.ajax({
3370 		    type : 'PUT',
3371 		    url : this.url+'/api/v2/cuts/'+currentEntry.issues[0].cutID+'.json',
3372 		    data : data,
3373 		    success : function(result) {
3374 //console.log('network repository activated :'+ decodeURIComponent(currentEntry.toString()));
3375 //console.log(result);
3376                 currentEntry.setStatus(newXps.currentStatus);
3377                 currentCut.versions[currentCut.versions.length-1]=result.data.versions[currentCut.versions.length-1];
3378 
3379                 xUI.XPS.currentStatus=newXps.currentStatus;//ドキュメントステータスを更新
3380 			    xUI.setStored("current");//UI上の保存ステータスをセット
3381 			    sync();//保存ステータスを同期
3382                 selectSCi();//カレントデータを再セレクトして情報更新
3383                 sync('historySelector');//履歴セレクタ更新
3384 
3385                 xUI.setUImode('production');
3386                 xUI.sWitchPanel();//パネルクリア
3387                 if(callback instanceof Function){ setTimeout (callback,10);}
3388 		    },
3389 		    error : function(result) {
3390 			// Error
3391 if(dbg) console.log("error");
3392 if(dbg) console.log(result);
3393 if(dbg) console.log('ステータス変更不可 :'+ Xps.getIdentifier(newXps));
3394                 if(callback2 instanceof Function) {setTimeout(callback2,10);}
3395 		    },
3396 		    beforeSend: this.service.setHeader
3397 	    });
3398 */
3399 }
3400 /**
3401     作業を保留する リポジトリ内の対応エントリデータを更新してステータスを変更
3402     基本的にデータはActiveなので、変更権利は取得済みとみなして操作を行う
3403     失敗の可能性はあり
3404 */
3405 NetworkRepository.prototype.deactivateEntry=function(callback,callback2){
3406     var currentEntry = this.entry(Xps.getIdentifier(xUI.XPS));
3407 //    var currentCut   = this.cut(currentEntry.toString());
3408     var currentCut   = this.cut(currentEntry.issues[0].cutID);
3409         //Active > Holdへ
3410     var newXps = Object.create(xUI.XPS);//現在のデータの複製をとる
3411         //ユーザ判定は不用
3412         if (newXps){
3413              //同内容でステータスを変更したエントリを作成 新規に上書き保存(先行データは上書きされる)
3414             newXps.currentStatus = new JobStatus('Hold');//(ジョブID等)status以外の変更はない
3415     //ここでサーバに現在のエントリへのステータス変更要求を送信する 成功時と失敗時の処理を渡し、かつcallback を再度中継
3416     //カットの name,description のみを送信してステータスを変更
3417         var data = {
3418                 token: currentCut.token,
3419                 cut: {
3420                     name        : decodeURIComponent(currentEntry.toString().split('//')[1]),
3421                     description : Xps.getIdentifier(newXps),
3422                     content     : newXps.toString(),
3423 			 		line_id     : newXps.line.toString(true),
3424 			 		stage_id    : newXps.stage.toString(true),
3425 			 		job_id      : newXps.job.toString(true),
3426 			 		status      : newXps.currentStatus.toString(true)
3427                 }
3428         };
3429 if(dbg) console.log(data);
3430 	    $.ajax({
3431 		    type : 'PUT',
3432 		    url : this.url+'/api/v2/cuts/'+currentEntry.issues[0].cutID+'.json',
3433 		    data : data,
3434 		    success : function(result) {
3435 //console.log('network repository deactivated :'+decodeURIComponent(currentEntry.toString().split('//')[1]));
3436 //console.log(result);
3437                 currentEntry.setStatus(newXps.currentStatus);
3438 //                currentCut.versions[currentCut.versions.length-1]=result.data.versions[currentCut.versions.length-1];
3439                 currentCut.versions[currentCut.versions.length-1].description = Xps.getIdentifier(newXps);
3440                 currentCut.versions[currentCut.versions.length-1].updated_at= newXps.update_time;
3441                 xUI.XPS.currentStatus=newXps.currentStatus;//ドキュメントステータスを更新
3442 			    xUI.setStored("current");//UI上の保存ステータスをセット
3443 			    sync();//保存ステータスを同期
3444                 selectSCi();//カレントデータを再セレクトして情報更新
3445                 sync('historySelector');//履歴セレクタ更新
3446 
3447                 xUI.setUImode('browsing');
3448                 xUI.sWitchPanel();//パネルクリア
3449                 if(callback instanceof Function){ setTimeout (callback,10);}
3450 		    },
3451 		    error : function(result) {
3452 			// Error
3453 //console.log("error");
3454 //console.log(result);
3455 //console.log('保留失敗 :'+ Xps.getIdentifier(newXps));
3456                 if(callback2 instanceof Function) {setTimeout(callback2,10);}
3457                 delete newXps;
3458 		    },
3459 		    beforeSend: this.service.setHeader
3460 	    });
3461     }else{
3462 //console.log('保留可能エントリ無し :'+ decodeURIComponent(Xps.getIdentifier(newXps)));
3463              return false ;
3464     }
3465 }
3466 /** 
3467     作業にチェックイン
3468     リポジトリ種別にかかわらないので
3469     このメソッドを呼ぶ前段でジョブ名称は確定しておくこと
3470     ジョブ名指定のない場合は操作失敗    
3471 */
3472 NetworkRepository.prototype.checkinEntry=function(myJob,callback,callback2){
3473     if( typeof myJob == 'undefined') return false;
3474     var currentEntry = this.entry(Xps.getIdentifier(xUI.XPS));
3475     var currentCut   = this.cut(currentEntry.issues[0].cutID);
3476     if(! currentEntry){
3477 if(dbg) console.log ('noentry in repository :' +  decodeURIComponent(currentEntry))
3478         //当該リポジトリにエントリが無い
3479          return false;
3480       }
3481             //次のJobへチェックイン 
3482             //リポジトリのステータスを変更する XPSの内容は変更不用
3483         var newXps = new Xps();
3484         var currentContents = xUI.XPS.toString();
3485         newXps.readIN(currentContents);
3486         // ユーザ判定は不用(権利チェックは後ほど実装)
3487     if (newXps){
3488         newXps.job.increment(myJob);
3489         newXps.update_user = xUI.currentUser;
3490         newXps.currentStatus = new JobStatus('Active');
3491 if(dbg) console.log(newXps.toString());//
3492     //引数でステータスを変更したエントリを作成 新規に保存 JobIDは必ず繰り上げる
3493     //ここでサーバに現在のエントリへのステータス変更要求を送信する 
3494     //成功時と失敗時の処理を渡し、かつcallback を再度中継
3495     //カットを送信してステータスを変更(ステータスのみの変更要求は意味が無い・内部データと不整合を起こすので却下)
3496     //descriptionのステータスを優先するならその方法も可能だが、バックアップタイミングを逃す?
3497 
3498         var data = {
3499                 token: currentEntry.issues[0].cutID,
3500                 cut: {
3501                     name:   decodeURIComponent(currentEntry.toString(true).split('//')[1]),
3502                     description: Xps.getIdentifier(newXps),
3503                     content: newXps.toString()
3504                 }
3505         };
3506 //console.log('networkRepository checkinEntry')
3507 //console.log(data);
3508 	    $.ajax({
3509 		    type : 'PUT',
3510 		    url : this.url+'/api/v2/cuts/'+currentEntry.issues[0].cutID+'.json',
3511 		    data : data,
3512 		    success : function(result) {
3513 //console.log('check-in :'+decodeURIComponent(currentEntry.toString()));
3514 //console.log(result);//PUTなので200番のみ
3515 //リザルトに含まれるカットのデータでリストを更新する(ムリ)
3516 
3517 //                currentEntry.push(result.cut.description,currentEntry.titleID,currentEntry.episodeID,result.cut.token);
3518                 currentEntry.push(Xps.getIdentifier(newXps),currentEntry.titleID,currentEntry.episodeID,currentEntry.issues[0].cutID);
3519 
3520                 if(! currentCut.versions) currentCut.versions = [];
3521                 currentCut.versions.push({
3522                     updated_at:newXps.updated_time,
3523                     description:Xps.getIdentifier(newXps),
3524                     version_token:null
3525                 });
3526 
3527                 xUI.setReferenceXPS()
3528                 xUI.XPS.job.increment(myJob);
3529 
3530                 xUI.XPS.currentStatus=newXps.currentStatus;//ドキュメントステータスを更新
3531                 xUI.XPS.update_user=xUI.currentUser;//ユーザ更新
3532                 xUI.setStored("current");//UI上の保存ステータスをセット
3533 			    sync();//保存ステータスを同期
3534                 selectSCi();//カレントデータを再セレクトして情報更新
3535                 sync('historySelector');//履歴セレクタ更新
3536 
3537                 xUI.setUImode('production');
3538                 xUI.sWitchPanel();//パネルクリア
3539                 if(callback instanceof Function){ setTimeout (callback,10);}
3540 		    },
3541 		    error : function(result) {
3542 			// Error
3543 if(dbg) console.log("error");
3544 if(dbg) console.log(result);
3545 if(dbg) console.log('ステータス変更不可 :'+ Xps.getIdentifier(newXps));
3546                 if(callback2 instanceof Function) {setTimeout(callback2,10);}
3547 		    },
3548 		    beforeSend: this.service.setHeader
3549 	    });
3550     }
3551 }
3552 /**
3553     作業終了
3554 */
3555 NetworkRepository.prototype.checkoutEntry=function(assignData,callback,callback2){
3556     var currentEntry = this.entry(Xps.getIdentifier(xUI.XPS));
3557 //    var currentCut   = this.cut(currentEntry.toString());
3558     var currentCut   = this.cut(currentEntry.issues[0].cutID);
3559     if(! currentEntry) {
3560 if(dbg) console.log ('noentry in repository :' +  decodeURIComponent(currentEntry))
3561         return false;
3562     }
3563             //Active > Fixed
3564 if(true){
3565         var newXps = Object.create(xUI.XPS);//現在のデータの複製をとる
3566 }else{
3567         var newXps = new Xps();
3568         var currentContents = xUI.XPS.toString();
3569         newXps.readIN(currentContents);
3570 };//下の複製のほうが安全?
3571         //ユーザ判定は不用 JobID変わらず
3572     if (newXps){
3573              //同内容でステータスを変更したエントリを作成 新規に保存して成功したら先のエントリを消す
3574             newXps.currentStatus = new JobStatus('Fixed');//(ジョブID等)status以外の変更はない
3575             
3576 //            newXps.currentStatus = ['Fixed',assignData].join(":"); アサインデータはまだUIのみでペンディング
3577 
3578     //ここでサーバに現在のエントリへのステータス変更要求を送信する 成功時と失敗時の処理を渡し、かつcallback を再度中継
3579     //カットの name,description のみを送信してステータスを変更
3580         var data = {
3581                 token: currentEntry.issues[0].cutID,
3582                 cut: {
3583                     name        : decodeURIComponent(currentEntry.toString().split('//')[1]),
3584                     description : Xps.getIdentifier(newXps),
3585                     content     : newXps.toString(),
3586  			 		line_id     : newXps.line.toString(),
3587 			 		stage_id    : newXps.stage.toString(),
3588 			 		job_id      : newXps.job.toString(),
3589 			 		status      : newXps.currentStatus.toString(true)
3590                }
3591         };
3592 if(dbg) console.log(data);
3593 	    $.ajax({
3594 		    type : 'PUT',
3595 		    url : this.url+'/api/v2/cuts/'+currentEntry.issues[0].cutID+'.json',
3596 		    data : data,
3597 		    success : function(result) {
3598 //console.log('check out entry :' + decodeURIComponent(currentEntry.toString()));
3599 //console.log(result);
3600                 currentEntry.setStatus(newXps.currentStatus);//result.data.cut.status も可
3601                 if(result.data.versions)
3602                 currentCut.versions[currentCut.versions.length-1] = result.data.versions[currentCut.versions.length-1];
3603 //?
3604                 xUI.XPS.currentStatus=newXps.currentStatus;//ドキュメントステータスを更新
3605 			    xUI.setStored("current");//UI上の保存ステータスをセット
3606 			    sync();//保存ステータスを同期
3607                 selectSCi();//カレントデータを再セレクトして情報更新
3608                 sync('historySelector');//履歴セレクタ更新
3609 
3610                 xUI.setUImode('browsing');
3611                 xUI.sWitchPanel();//パネルクリア
3612 //                if(serviceAgent.currentStatus=="online-single"){backToDocumentList('cut');} ;//コレだ! ダメだ!!
3613                 if(callback instanceof Function){ setTimeout (callback,10);}
3614 		    },
3615 		    error :function(result) {
3616 			// Error
3617 if(dbg) console.log("error");
3618 if(dbg) console.log(result);
3619 if(dbg) console.log('終了更新失敗 :'+ Xps.getIdentifier(newXps));
3620                 if(callback2 instanceof Function) {setTimeout(callback2,10);}
3621                 delete newXps;
3622 		    },
3623 		    beforeSend: this.service.setHeader
3624 	    });
3625     }
3626 if(dbg) console.log('終了更新失敗');
3627         delete newXps ;
3628         if(callback2 instanceof Function){ setTimeout('callback2()',10)};
3629         return false ;
3630 }
3631 /**
3632     検収処理
3633 引数:
3634     nas.Pm.ProductionStageオブジェクト
3635     Xps.Stage
3636     または
3637     Stage名文字列("layout"等)
3638 *上二つのオブジェクトからの処理は未実装2016-1230
3639     初期化用Job名文字列
3640     現在の工程(作業は既Fixed)を閉じて次の工程を開始する手続き
3641     現在のデータのステータスを変更
3642         ステージを新規オブジェクトでincrement = Jobは初期状態にリセット
3643         
3644     
3645 */
3646 NetworkRepository.prototype.receiptEntry=function(stageName,jobName,callback,callback2){
3647     if( typeof stageName == 'undefined') return false;
3648     var myStage = nas.pmdb.stages.getStage(stageName) ;//ステージDBと照合 エントリが無い場合はエントリ登録
3649     /*  2106-12 の実装では省略して エラー終了
3650         2017-07 最小限の処理を実装 ステージの存在を確認して続行
3651     */
3652     if(! myStage) return false;
3653     var currentEntry = this.entry(Xps.getIdentifier(xUI.XPS));
3654 //    var currentCut   = this.cut(currentEntry.toString());
3655     var currentCut   = this.cut(currentEntry.issues[0].cutID);
3656     if((!currentEntry)||(!currentCut)){
3657 //console.log('noentry');
3658 //console.log(serviceAgent.currentRepository);
3659         console.log('noentry in repository :' +  decodeURIComponent(currentEntry))
3660         //当該リポジトリにエントリが無い
3661          return false;
3662       }
3663             //次のステージを立ち上げるため 読み出したデータでXpsを初期化 
3664 if(false){
3665         var newXps = Object.create(xUI.XPS);//現在のデータの複製をとる
3666 }else{
3667         var newXps = new Xps();
3668         var currentContents = xUI.XPS.toString();
3669         newXps.parseXps(currentContents);
3670 };//下の複製のほうが安全?
3671         // ユーザ判定は不用(権利チェックは後ほど実装)
3672     if (newXps){
3673         newXps.stage.increment(stageName);
3674         newXps.job.reset(jobName);
3675         newXps.update_user = xUI.currentUser;
3676         newXps.currentStatus = new JobStatus('Startup');
3677 if(dbg) console.log(newXps.toString());//
3678              //引数でステータスを変更したエントリを作成 新規に保存 stageIDは必ず繰り上る jobは0リセット
3679     //ここでサーバに現在のエントリへのステータス変更要求を送信する 成功時と失敗時の処理を渡し、かつcallback を再度中継
3680     //カットの name,description のみを送信してステータスを変更
3681     //明示的なエントリ変更の要求が必要ならば処理
3682         var data = {
3683                 token: currentEntry.issues[0].cutID,
3684                 cut: {
3685                     name:   decodeURIComponent(currentEntry.toString().split('//')[1]),
3686                     description: Xps.getIdentifier(newXps),
3687                     content: newXps.toString()
3688                 }
3689         };
3690 if(dbg) console.log(data);
3691 	    $.ajax({
3692 		    type : 'PUT',
3693 		    url : this.url+'/api/v2/cuts/'+currentEntry.issues[0].cutID+'.json',
3694 		    data : data,
3695 		    success : function(result) {
3696 //console.log('check-in');
3697 //console.log(result);
3698                 currentEntry.push(result.data.cut.description);
3699                 documentDepot.documentsUpdate();
3700 //                serviceAgent.currentRepository.getList(true);//リストステータスを同期
3701 //                currentEntry.push(Xps.getIdentifier(newXps));
3702 //                documentDepot.updateDocumentSelector();
3703 //                documentDepot.rebuildList();
3704                 xUI.XPS.stage.increment(stageName);
3705                 xUI.XPS.job.reset(jobName);
3706                 xUI.XPS.currentStatus= new JobStatus('Startup');//ドキュメントステータスを更新
3707                 xUI.XPS.update_user=xUI.currentUser;//ユーザ更新
3708                 xUI.setStored("current");//UI上の保存ステータスをセット
3709 			    sync();//保存ステータスを同期
3710                 xUI.setUImode('browsing');
3711                 xUI.sWitchPanel();//パネルクリア
3712                 if(callback instanceof Function){ setTimeout (callback,10);}
3713 		    },
3714 		    error : function(result) {
3715 			// Error
3716 if(dbg) console.log("error");
3717 if(dbg) console.log(result);
3718 if(dbg) console.log('ステータス変更不可 :'+ Xps.getIdentifier(newXps));
3719                 if(callback2 instanceof Function) {setTimeout(callback2,10);}
3720 		    },
3721 		    beforeSend: this.service.setHeader
3722 	    });
3723     }
3724 }
3725 /**
3726     作業中断処理
3727 */
3728 NetworkRepository.prototype.abortEntry=function(myIdentifier){
3729     var currentEntry = this.entry(myIdentifier);
3730     if(! currentEntry) return false;
3731     var currentStatus=currentEntry.getStatus();
3732 
3733     if(String(currentStatus.content).indexOf('Fixed')<0){return false;}
3734 
3735     switch (currentStatus.content){
3736         case 'Startup':
3737         case 'Hold':
3738         case 'Fixed':
3739         case 'Active':
3740             //管理モード下でのみ処理 このメソッドのコール自体が管理モード下でのみ可能にする
3741             //リポジトリに対して
3742         break;
3743     }
3744 }
3745 
3746 /**
3747     最終作業の破棄
3748     バックアップ作業これを呼び出す側で処理
3749     ここを直接コールした場合はバックアップは実行されない
3750     ユーザメッセージはここでは処理されない
3751     
3752 */
3753 NetworkRepository.prototype.destroyJob=function(callback,callback2){
3754     if(xUI.XPS.currentStatus.content != 'Active'){return false}
3755  //    カレントのtokenを添えてdestroyコマンドを発行する
3756     var currentEntry = this.entry(Xps.getIdentifier(xUI.XPS));
3757     if(! currentEntry){
3758 if(dbg) console.log ('noentry in repository :' +  decodeURIComponent(currentEntry))
3759         //当該リポジトリにエントリが無い
3760          return false;
3761     }
3762     
3763         //currentEntry.issues[0].cutID 
3764         // debug : change PATCH to PUT
3765 if(dbg) console.log(currentEntry.issues[0].cutID);
3766 	    $.ajax({
3767 		    type : 'PUT',
3768 		    url : this.url+'/api/v2/cuts/'+currentEntry.issues[0].cutID+'/discard',
3769 		    success : function(result) {
3770 		        currentEntry.issues.pop();
3771                 xUI.resetSheet(new Xps(5,144),new Xps(5,144)) ;
3772                 documentDepot.updateDocumentSelector();
3773 //                documentDepot.rebuildList();
3774                 xUI.setStored("current");//UI上の保存ステータスをセット
3775 			    sync();//保存ステータスを同期
3776                 xUI.setUImode('browsing');
3777                 if(callback instanceof Function){ setTimeout (callback,10);}
3778 		    },
3779 		    error : function(result) {
3780 			// Error
3781 if(dbg) console.log("error");
3782 if(dbg) console.log(result);
3783                 if(callback2 instanceof Function) {setTimeout(callback2,10);}
3784 		    },
3785 		    beforeSend: this.service.setHeader
3786 	    });
3787 
3788 }
3789 //serviceAgent.currentRepository.destroyJob();
3790 /**
3791 サービスエージェントオブエジェクト
3792 
3793 ログインするサーバを選んでログイン処理をする
3794 ログイン情報を保持して
3795 状態は offline/onnline/online-single の3状態
3796 モードによっては機能が制限される
3797 UI上モードを表示するシンボルが必要
3798 
3799 servers ServiceNode
3800 */
3801 
3802 
3803 serviceAgent = {
3804     servers     :[],
3805     repositories:[],
3806     currentStatus       :'offlline',
3807     currentServer       :null,
3808     currentRepository   :null
3809 };
3810 /**
3811     サービスエージェントの初期化(テスト版)
3812  リポジトリの初期化は 最終的には作業記録とサーバからの受信情報でアプリケーション初期化のタイミングで組む
3813 */
3814 serviceAgent.init= function(){
3815     this.servers=[]; //サーバコレクション初期化
3816     this.repositories=[localRepository]; //ローカルリポジトリを0番として加える
3817     if(document.getElementById('backend_variables')){
3818     ;//本番用
3819       if($("#backend_variables").attr("data-server_url")){
3820 //ローカルテスト時はこちらで
3821        var myUrl = $("#backend_variables").attr("data-server_url");
3822       }else{
3823         var loc = String(window.location).split('/');//
3824         var locOffset = (loc[loc.length-1]=="edit")? 3:2;
3825         var myUrl = loc.splice(0,loc.length-locOffset).join('/');
3826 //      var myUrl = 'http://remaping.scivone-dev.com';//テスト用決め打ち
3827 //      var myUrl = 'https://remaping-stg.u-at.net';//テスト用に決め打ち
3828 //      var myUrl = 'https://u-at.net';//テスト用決め打ち
3829       }
3830       this.servers.push(new ServiceNode("CURRENT",myUrl));
3831     }else{
3832       var myServers={
3833         UAT: {name:'U-AT',url:'https://u-at.net'},
3834         Srage:{name:'Stage',url:'https://remaping-stg.u-at.net'},
3835         devFront:{name:'devFront',url:'https://remaping.scivone-dev.com'}
3836       };
3837       for(svs in myServers){this.servers.push(new ServiceNode(myServers[svs].name,myServers[svs].url));}
3838     }
3839 /*
3840     仮のサーバセレクタを設定
3841 */
3842     var mylistContents="";
3843     mylistContents +='<option selected value="-1" > +no server selected+';
3844     for(var ids=0; ids < this.servers.length;ids ++){
3845         mylistContents +='<option value="'+ids+((ids==0)? '" selected >':'" >')+this.servers[ids].name; 
3846     }
3847     document.getElementById('serverSelector').innerHTML = mylistContents;
3848 
3849 //    var Home=new NetworkRepository("HOME",serviceA);
3850 //    this.repositories.push(Home);
3851 /**
3852     組んだリポジトリでリポジトリリストを更新する
3853     ローカルリポジトリはすべての状況で利用可能
3854 */
3855     var myContents="";
3856     myContents +='<option selected value=0> = local Repository =';
3857     for(var idr=1; idr < this.repositories.length;idr ++){
3858         myContents +='<option value="'+idr+'" >'+this.repositories[idr].name; 
3859     }
3860     document.getElementById('repositorySelector').innerHTML = myContents;
3861 
3862 //    this.switchRepository(0);
3863     this.switchService(0);
3864 //    this.currentServer = this.severs[0];//初期化時点のサーバは 最初のサーバ(U-AT);
3865 }
3866 /**
3867     ユーザ認証
3868 カレントサービスを認証又は解除する
3869 
3870 カレントサービスが"0:=no selected="の場合は,
3871 単純にすべてのサービスからログアウトする
3872 
3873  */
3874 serviceAgent.authorize=function(){
3875 if(dbg) console.log("authorize!::");
3876     switch (this.currentStatus){
3877     case 'online-single':
3878         return false;
3879     break;
3880     case 'online':
3881         if(xUI.onSite){return 'online'};
3882 //            this.currentServer     = null;
3883 //            this.currentRepository = null;
3884             this.authorized(false);
3885         return 'offline';
3886     break;
3887     case 'offline':
3888     default:
3889         if(this.currentServer) this.currentServer.authorize();
3890         return 'online';
3891     }
3892 }
3893 /**
3894     認証/解除時の画面処理
3895 */
3896 serviceAgent.authorized=function(status){
3897     if (status == 'success'){
3898         this.currentStatus = 'online';
3899 //二回目以降のUI初期化時は ローカルリポジトリにフォーカスが移ってカレントサーバがないケースがあるので注意
3900           if(serviceAgent.currentServer){
3901             document.getElementById('serverurl').innerHTML = serviceAgent.currentServer.url.split('/').slice(0,3).join('/');//?
3902             document.getElementById('loginuser').innerHTML = document.getElementById('current_user_id').value;
3903             document.getElementById('loginstatus_button').innerHTML = "=ONLINE=";
3904             document.getElementById('login_button').innerHTML = "SIGNOUT";
3905             document.getElementById('serverSelector').disabled  = true;
3906           };//二度目以降の表示更新はサーバの切り替えが無い限り特に不用
3907     }else{
3908         this.currentStatus = 'offline';
3909             document.getElementById('serverurl').innerHTML = localize(nas.uiMsg.noSigninService);//?
3910             document.getElementById('loginuser').innerHTML = '';
3911             document.getElementById('loginstatus_button').innerHTML = "=OFFLINE=";
3912             document.getElementById('login_button').innerHTML = "SIGNIN";
3913             document.getElementById('serverSelector').disabled  = false;
3914 
3915         serviceAgent.switchRepository(0);//ローカルレポジトリセット
3916         serviceAgent.switchService();
3917         serviceAgent.init();
3918     }
3919 }
3920 /**
3921     サーバを切り替える
3922 引数: myServer / Object ServiceNode
3923 サーバ名・URL・ID またはキーワードで指定
3924 キーワードと同名のサーバは基本的に禁止?
3925 サーバにログインしていない場合は、各サーバごとの認証を呼ぶ
3926 既にサービスにログインしている場合は、その認証を解除してから次のサービスを認証する
3927 
3928 内部的にはともかくユーザ視点での情報の輻輳を避けるため サーバ/リポジトリを多層構造にせず 
3929 リポジトリに対する認証のみをUIで扱う
3930 リポジトリの切り替えに対してログイン/ログアウトを行うUI仕様とする。
3931 サービスの切り替えは内部での呼び出しのみになるので引数は整理する
3932 */
3933 serviceAgent.switchService=function(myServer){
3934     var newServer = null;
3935 //引数とカレントのサービスが一致 切替不能(不用)
3936     if (myServer === this.currentServer){
3937         return myServer;
3938     }
3939     
3940     if((myServer instanceof ServiceNode )&&(myServer !== this.currentServer)) {
3941 //引数がノードオブジェクト
3942         newServer = myServer;
3943     }else if((myServer >= 0)&&(myServer<this.servers.length)){
3944 //引数がサーバID
3945         newServer = this.servers[myServer];
3946     }else if(myServer instanceof String){
3947 //引数が文字列
3948         for (var ix = 0 ;ix < this.servers.length ; ix ++){
3949             if((myServer == this.servers[ix].url)||(myServer == this.servers[ix].name)){
3950                 newServer = this.servers[ix];break;
3951             }
3952         }
3953     }
3954 //オンラインであった場合は切替前にオフライン化して
3955 //エントリリスト クリア ドキュメントセレクタ リセット
3956     this.switchRepository(0);
3957     this.currentServer = newServer;
3958     sync();
3959 
3960 return this.currentServer;
3961 };
3962 /**
3963     リポジトリを切り替える
3964     UIから直接呼び出されるのはこちら
3965     カレントのリポジトリを切り替え、
3966     リポジトリに関連付けられたサービスをカレントにする
3967     サービスが現在のログイン先と異なる場合も認証は実際のアクセスまで保留
3968     (解除前にもとのサービスに戻った際に再ログインを行わないため)
3969     引数は、現在のリポジトリID
3970     リポジトリIDは以下のように決定
3971     
3972     0:ローカルリポジトリ固定
3973     1~ 以降登録順 現在同時に処理できるサーバは1つ サーバ内のリポジトリは複数
3974     
3975      リポジトリ切替時にドキュメントリストの更新をバックグラウンドで行う
3976      
3977 */
3978 serviceAgent.switchRepository=function(myRepositoryID,callback){
3979     if(this.currentRepository === this.repositories[myRepositoryID]){
3980         //同オブエジェクトに切り替える必要はないのでそのままリターン
3981         return this.currentRepository;
3982     }else{
3983     if(typeof myRepositoryID == 'undefined')  myRepositoryID = 0;
3984 //切り替え前に現在のデータの状態を確認して必要ならば編集状態を解除 その後自身を再度呼び出し
3985     if((xUI.uiMode=='production')&&(xUI.XPS.currentStatus.content=='Active')){
3986 //console.log("deactivate current document");
3987             if(xUI.edchg) xUI.put(document.getElementById('iNputbOx').value);
3988                 serviceAgent.currentRepository.deactivateEntry(function(){
3989                 serviceAgent.switchRepository(myRepositoryID,callback);
3990             });
3991         return;
3992 }else{
3993         serviceAgent.currentRepository = serviceAgent.repositories[myRepositoryID];
3994         if((myRepositoryID > 0)&&(myRepositoryID<this.repositories.length)){
3995 //            serviceAgent.currentServer=serviceAgent.currentRepository.service;
3996             serviceAgent.switchService(serviceAgent.currentRepository.service);
3997         } else {
3998 //            serviceAgent.currentServer     = null;
3999 //console.log('reset');console.log(serviceAgent.currentServer);
4000             //serviceAgent.switchService();
4001         };
4002 }
4003         if(document.getElementById('repositorySelector').value != myRepositoryID){
4004             document.getElementById('repositorySelector').value = myRepositoryID;
4005         }
4006         if(callback instanceof Function){ callback(); }else{
4007 //OPUSセレクタを停止
4008         document.getElementById( "opusSelect" ).disabled=true;
4009 //ドキュメントセレクタを停止
4010         document.getElementById( "cutList" ).disabled=true;
4011         /*== ドキュメントリスト更新 ==*/
4012 //console.log("change repository :"+ myRepositoryID);
4013         serviceAgent.currentRepository.getProducts(function(){
4014 
4015                         documentDepot.getProducts();
4016                         documentDepot.currentProduct=null;
4017                         documentDepot.currentSelection=null;
4018                         documentDepot.updateOpusSelector();
4019                         documentDepot.updateDocumentSelector();
4020 /*          for(var ix=0;ix<serviceAgent.currentRepository.productsData.length;ix ++){
4021             var myProduct = serviceAgent.currentRepository.productsData[ix];
4022             serviceAgent.currentRepository.getEpisodes(function(){
4023 //                documentDepot.documentsUpdate();//クリア
4024 //                for(var ex =0 ;ex < myProduct.episodes[0].length;ex++){
4025 //                    var myEpisode = myProduct.episodes[0][ex];
4026 //                    serviceAgent.currentRepository.getSCi(function(){},false,myEpisode.token);
4027 //                }
4028  //             console.log(myEpisode.name);
4029                         documentDepot.getProducts();
4030                         documentDepot.currentProduct=null;
4031                         documentDepot.currentSelection=null;
4032                         documentDepot.updateOpusSelector();
4033                         documentDepot.updateDocumentSelector();
4034                     
4035             },false,myProduct.token);//getEpisode
4036           }
4037 */
4038         },false);//getProduct
4039         
4040 //        this.currentRepository.getList()
4041 //                documentDepot.updateDocumentSelector();
4042 //            documentDepot.rebuildList(callback);
4043         }
4044     };
4045     sync('server-info')
4046     return this.currentRepository;
4047 };
4048 /**
4049     title-token  又は episode-token が含まれるRepositoryをカレントに切り替えて返す
4050 */
4051 serviceAgent.getRepsitoryIdByToken=function(myToken){
4052     var RIX=0;
4053     search_loop:
4054     for (var rix=1;rix<this.repositories.length;rix++){
4055         if(myToken==this.repositories[rix].token){
4056             RIX=rix;
4057             break search_loop;            
4058         }
4059         //リポジトリ内のプロダクトデータを検索(エントリ総当りはしない)
4060         for (var pix=0;pix<this.repositories[rix].productsData.length;pix++){
4061             if(myToken==this.repositories[rix].productsData[pix].token){
4062                 RIX=rix;
4063                 break search_loop;
4064             };
4065             for (var eix=0;eix<this.repositories[rix].productsData[pix].episodes[0].length;eix++){
4066                 if(myToken == this.repositories[rix].productsData[pix].episodes[0][eix].token){
4067                     RIX=rix;
4068                     break search_loop;                    
4069                 };
4070             };
4071         };
4072     };
4073     if(RIX)  {return RIX}else{return false}
4074     
4075 };
4076 
4077 /**
4078     引数を判定して動作を決定 カレントリポジトリの操作を呼び出す
4079     myIdentifier    カット識別子 完全状態で指定されなかった場合は、検索で補う
4080     isReference    リファレンスとして呼び込むか否かのフラグ 指定がなければ自動判定
4081     callback    コールバック関数指定が可能 コールバックは以下の型式で
4082     コールバックの指定がない場合は指定データをアプリケーションに読み込む
4083     コールバック関数以降の引数はコールバックに渡される
4084     リファレンス取得の際にアプリケーションステータスをリセットする場合があるので注意
4085     
4086     ネットワークリポジトリからエントリを取得の際コンテンツが空のケースがある。
4087     これはエントリ登録直後のデータで、アプリケーション上でタイトル/エピソードに従ったデータをさくせいる必要があるので注意
4088     
4089     リポジトリ共通の機能としてタイトル/エピソードからデフォルトプロパティの取得を行い、その後、各リポジトリごとに記述子による指定データのパースを行って新規データのビルドを行う。
4090 */
4091 serviceAgent.getEntry=function(myIdentifier,isReference,callback,callback2){
4092 if(dbg) console.log('getEntry ::' + decodeURIComponent(myIdentifier));
4093     if(typeof isReference == 'undefined'){isReference = false;}
4094     //識別子をパース
4095     var targetInfo = Xps.parseIdentifier(myIdentifier);
4096     var myIssue = false;
4097     var refIssue = false;
4098 
4099     var myEntry = serviceAgent.currentRepository.entry(myIdentifier);
4100     if(! myEntry){
4101 if(dbg) console.log("noProduct : "+ decodeURIComponent(myIdentifier));//プロダクトが無い
4102         return false;
4103     }else{
4104 //pmdbからプロダクトごとのデフォルト値を取得する
4105         
4106     }
4107     if(! targetInfo.currentStatus){
4108    //引数に管理部分がないので、最新のissueとして補う
4109         var cx = myEntry.issues.length-1;//最新のissue
4110         myIssue = myEntry.issues[cx];//配列で取得
4111     } else {
4112     //指定管理部分からissueを特定する 連結して文字列比較(後方から検索) リスト内に指定エントリがなければ失敗
4113         checkIssues:{
4114             for (var cx = (myEntry.issues.length-1) ; cx >= 0 ;cx--){
4115 //if(dbg) console.log ( String(myEntry.issues[cx].identifier)+'\n'+String(myIdentifier));
4116 //if(dbg) console.log ( Xps.compareIdentifier(myEntry.issues[cx].identifier,myIdentifier))
4117                 if ( Xps.compareIdentifier(myEntry.issues[cx].identifier,myIdentifier) > 4){
4118                     myIssue = myEntry.issues[cx];
4119                     break checkIssues;
4120                 }
4121             }
4122             if (! myIssue){
4123 if(dbg) console.log( 'no target data :'+ decodeURIComponent(myIdentifier) );//ターゲットのデータが無い
4124                 return false;
4125             }
4126         }
4127     }
4128 //console.log(decodeURIComponent(myEntry.issues[cx].identifier));
4129 if((! isReference)&&(Xps.compareIdentifier(myEntry.issues[cx].identifier,Xps.getIdentifier(xUI.XPS)) > 3)){
4130 //console.log(decodeURIComponent(Xps.getIdentifier(xUI.XPS)))
4131 //console.log('ジョブ一致 ロードスキップ');
4132 }
4133 //読み込み前に現在のデータの状態を確認して必要ならば編集状態を解除
4134 //その後読み込み
4135 //読込の前にカーソル位置を 1_0 にリセット
4136 //参照読み込みに際しては、編集状態を維持
4137     if((! isReference ) && ( xUI.uiMode=='production' )&&( xUI.XPS.currentStatus.content=='Active' )){
4138 //console.log("need deactivate");
4139             if(xUI.edchg) xUI.put(document.getElementById('iNputbOx').value);
4140             serviceAgent.currentRepository.deactivateEntry(function(){
4141             serviceAgent.currentRepository.getEntry(myIdentifier,isReference,callback,callback2)
4142             return;
4143 /*                
4144                 serviceAgent.currentRepository.getEntry(myIdentifier,isReference,function(){
4145 //console.log("get ");
4146                     sync('historySelector');
4147                     if (callback instanceof Function) callback();
4148 }
4149 */
4150             },function(){
4151 //console.log("fail getting ");
4152                     if (callback2 instanceof Function) callback2();
4153             });
4154     }else{
4155         xUI.selectCell([1,0]);
4156         this.currentRepository.getEntry(myIdentifier,isReference,function(){
4157             sync('historySelector');
4158             if (callback instanceof Function) callback();
4159         },function(){
4160 //console.log("fail getting ");
4161             if (callback2 instanceof Function) callback2();
4162         });
4163     }
4164     if($("#optionPanelFile").is(':visible')) xUI.sWitchPanel('File');
4165 };
4166 
4167 /**
4168     ドキュメント操作メソッド群
4169     実行時に実際の各リポジトリに対してコマンドを発行して エントリのステータスを更新する
4170     ステータスによっては、ジョブ名引数を必要とする
4171     変更をトライして成功時/失敗時に指定のコールバック関数を実行する
4172     
4173     操作対象ドキュメントは、必ずUI上でオープンされている (xUI.XPS が対象)
4174     引数で識別子を与えることは無い
4175     
4176     activate(callback,callback2)
4177                 Active > Active  例外操作 作業セッションの回復時のみ実行 データにチェック機能が必要
4178         ステータス変更なし
4179                 Hold   > Active
4180                 Fixed  > Active (Fixedの取り消し操作)
4181         ステータスのみ変更(カレントユーザのみが可能)
4182     
4183     deactivate(callback,callback2)
4184                 Active > Hold
4185         現データをpush
4186         ステータス変更(カレントユーザのみが可能)
4187     
4188     checkin(ジョブ名,callback,callback2)
4189                 Startup > Active
4190                 Fixed   > Active
4191 
4192     checkout/fix/close(callback,callback2)
4193                 Active  > Fixed
4194         現データをpush
4195         ステータス変更(カレントユーザのみが可能)
4196     
4197 
4198         引数としてJob名が必要
4199     
4200 Abort > 最終JobのステータスをAbortに変更して保存する
4201 ストレージタイプの場合、同内容でステータスの異なるデータを保存して成功時に先行データを削除して更新する
4202 サービス型の場合は変更リクエストを発行して終了
4203 
4204 この場合の違いを吸収するためにRepositry.changeStatus() メソッドを実装する
4205 エントリステータスの変更は単純な変更にならない かつ リポジトリ通信先のデータ更新にかかわるのでエントリのメソッドにはしない
4206 
4207     checkin(開く)
4208 Startup/Fixed/(Active) > Active
4209 新規ジョブを開始
4210 
4211     checkout(fix)(閉じる)
4212 Active > Fixed
4213 カレントジョブを終了
4214 
4215     activate(再開)
4216 Hold/Fixed > Active
4217 カレントジョブの状態を変更
4218 データをActiveにできるのは、updateユーザのみ
4219 
4220     deactivate(保留)
4221 Acive > Hold   
4222 カレントジョブの状態を変更
4223 
4224 =================== ここまでproductionMode での操作
4225 ドキュメントブラウザパネルの表示は
4226 [ CHECKIN][CHECKOUT][ACTIVATE][DEACTIVATE]
4227 [作業開始][作業終了][作業保留][作業再開]
4228 となる
4229 =================== 
4230 
4231     receipt(検収)
4232 fixed > Startup
4233 新規ステージを開始(管理者権限)
4234 
4235 *管理者権限での作業時はステータスの遷移を抑制する
4236 =読み出してもactiveにならない?
4237 
4238     abort(中断)
4239 *   > Aborted
4240 エントリの制作を中断(管理者権限)
4241 すべての状態から移行可能性あり
4242 
4243 状況遷移を単純化するために、読み込まれていないデータの状況遷移を抑制する。
4244 基本的にユーザのドキュメント操作の際に状況の遷移が自動で発生する。
4245 
4246 プログラム上の手続きとして
4247 
4248 リポジトリのステータス更新
4249 ドキュメントリスト上のステータスを同期
4250 バッファ上のデータのステータス同期
4251 を上の順序で行う必要があるので注意
4252 
4253 */
4254 /**
4255     現在のドキュメントをアクティベートする
4256     
4257 */
4258 serviceAgent.activateEntry=function(callback,callback2){
4259     var currentEntry = this.currentRepository.entry(Xps.getIdentifier(xUI.XPS));
4260 //    var currentCut   = this.currentRepository.cut(currentEntry.toString());
4261     var currentCut   = this.currentRepository.cut(currentEntry.issues[0].cutID);
4262     if(! currentEntry) {
4263 //console.log ('noentry in this repository :' +  decodeURIComponent(currentEntry))
4264         return false;
4265     }
4266 //console.log('activateEntry :'+decodeURIComponent(currentEntry.toString()));
4267 //console.log(currentEntry);
4268 //console.log(currentEntry.getStatus());
4269     switch (xUI.XPS.currentStatus.content){
4270         case 'Active':
4271 /**     例外処理 ロストセッションの回復
4272     編集中に保存せずに作業セッションを失った場合 この状態となる
4273     また同一ユーザで編集中に他のブラウザからログインした場合も可能性あり
4274     ここに確認用のメッセージが必要
4275     checkinの手続内でここがコールされている?
4276 */
4277 //console.log('recover acitivate');
4278             if(xUI.currentUser.sameAs(xUI.XPS.update_user)){
4279                 var msg = localize(nas.uiMsg.alertAbnomalPrccs);
4280                 msg += '\n'+localize(nas.uiMsg.dmPMrecoverLostSession);
4281                 msg += '\n\n'+localize(nas.uiMsg.confirmOk);
4282                if(confirm(msg)){
4283                     xUI.setUImode('production');
4284 //  必要があればここでリポジトリの操作(基本不用)
4285                     xUI.sWitchPanel();//パネルクリア
4286                     return true;
4287                 }
4288             }
4289         break;
4290         case 'Aborted':case 'Startup':
4291             alert(localize(nas.uiMsg.dmAlertCantActivate));//アクティベート不可 
4292             //NOP return
4293             return false;
4294         break;
4295         case 'Hold':case 'Fixed':
4296             //ユーザが同一の場合のみ 再アクティベート可能(activate)
4297             //Fixed>Active の場合は、通知が必要かも
4298         if (xUI.currentUser.sameAs(xUI.XPS.update_user)){
4299 //console.log('call repository.activateEntry :')
4300             this.currentRepository.activateEntry(callback,callback2);//コールバックはリレーする
4301             return true;
4302         } else {
4303             alert(localize(nas.uiMsg.dmAlertDataBusy));//ユーザ違い
4304             return false;
4305         }
4306         break;
4307     }
4308 }
4309 /**
4310     作業を保留する リポジトリ内のエントリを更新してステータスを変更
4311     ユーザ判定は不用
4312     現データの送信(保存)後にリスト更新とドキュメントブラウザの更新
4313 */
4314 serviceAgent.deactivateEntry=function(callback,callback2){
4315     var currentEntry = this.currentRepository.entry(Xps.getIdentifier(xUI.XPS));
4316 //    var currentCut   = this.currentRepository.cut(currentEntry.toString());
4317     var currentCut   = this.currentRepository.cut(currentEntry.issues[0].cutID);
4318     if(! currentEntry) {
4319 if(dbg) console.log ('noentry in repository :' +  decodeURIComponent(currentEntry))
4320         return false;
4321     }
4322 //console.log(currentEntry);
4323     var currentStatus=currentEntry.getStatus();
4324     switch (currentStatus.content){
4325         case 'Aborted': case 'Startup': case 'Hold': case 'Fixed':
4326             //NOP
4327 //            if(dbg) console.log('fail deactivate so :'+ currentEntry.getStatus());
4328             alert(localize(nas.uiMsg.dmAlertCantDeactivate));//アクティブでない
4329             return false;
4330             break;
4331         case 'Active':
4332             //編集を確認して Active > Holdへ
4333             if(xUI.edchg) xUI.put(document.getElementById('iNputbOx').value);
4334             this.currentRepository.deactivateEntry(callback,callback2);
4335         break;
4336     }
4337 }
4338 /** 
4339     作業にチェックイン
4340     リポジトリ種別にかかわらないので
4341     このメソッド内でジョブ名称を確定しておく  
4342 */
4343 serviceAgent.checkinEntry=function(myJob,callback,callback2){
4344 //  ここで処理前にリストを最新に更新する
4345 /*リストの更新では、ムダに待ち時間が長いので「エントリ情報の更新」に変更したい 後ほど処理 今は保留 0422*/
4346 //    this.currentRepository.getList(true);
4347 //console.log(Xps.getIdentifier(xUI.XPS))
4348     var currentEntry = this.currentRepository.entry(Xps.getIdentifier(xUI.XPS));
4349 //console.log(currentEntry);
4350     var currentCut   = this.currentRepository.cut(currentEntry.issues[0].cutID);
4351 //console.log(currentCut)
4352     if(! currentEntry){
4353         alert(localize(nas.uiMsg.dmAlertNoEntry));//対応エントリが無い
4354 //        if(dbg) console.log ('noentry in repository :' +  decodeURIComponent(currentEntry))
4355         //当該リポジトリにエントリが無い
4356          return false;
4357       }
4358     var currentStatus=currentEntry.getStatus();
4359     switch (currentStatus.content){
4360         case 'Aborted': case 'Active': case 'Hold':
4361             alert(localize(nas.uiMsg.dmAlertCheckinFail)+"\n>"+currentEntry.getStatus(myJob,callback,callback2));
4362             //NOP return
4363 if(dbg) console.log('fail checkin so :'+ currentEntry.getStatus(myJob,callback,callback2));
4364             return false;
4365         break;
4366         case 'Fixed':case 'Startup':
4367             //次のJobへチェックイン
4368             //ジョブ名称を請求
4369             var title   = localize(nas.uiMsg.pMcheckin);//'作業開始 / チェックイン';
4370             var msg     = localize(nas.uiMsg.dmPMnewItemSwap,localize(nas.uiMsg.pMjob));
4371             //'新規作業を開始します。\n新しい作業名を入力してください。\nリストにない場合は、作業名を入力してください。';
4372 //            var msg2    = '<br> <input id=newJobName class=mdInputText type=text list=newJobList></input><datalist id=newJobList>';
4373             var msg2    = '<br> <input type="text" list="newJobList" id=newJobName class=mdInputText  autocomplete=on ></input><datalist id="newJobList">';
4374 //console.log(xUI.XPS.stage.name +","+ ((xUI.XPS.job.id == 0) ? 'primary':'*'));
4375 //console.log(nas.pmdb.jobNames.getTemplate(xUI.XPS.stage.name,((xUI.XPS.job.id == 0) ? 'primary':'*')));
4376             var newJobList = nas.pmdb.jobNames.getTemplate(xUI.XPS.stage.name,((xUI.XPS.job.id == 0) ? 'primary':'*'));//ここは後ほどリポジトリ個別のデータと差替
4377 //console.log(newJobList)
4378             for(var idx = 0 ; idx < newJobList.length;idx ++){
4379                 msg2   += '<option value="';
4380                 msg2   += newJobList[idx];
4381                 msg2   += '">'+newJobList[idx]+'</option>';
4382             };
4383                 msg2 += '</datalist>';
4384 //console.log(msg);
4385 
4386 //console.log(newJobList);
4387 //console.log(msg2);
4388             nas.showModalDialog('confirm',[msg,msg2],title,false,function(){
4389                 var newJobName=document.getElementById('newJobName').value;
4390                 if((this.status == 0)&&(newJobName)){
4391                     serviceAgent.currentRepository.checkinEntry(newJobName,function(){
4392                         //成功時は現在のデータをリファレンスへ複製しておく
4393                         //putReference(); このタイミングで行うと ステータス変更後のデータがリファレンスへ入るので ダメ 各メソッド側に実装
4394 //                        sync('productStatus');//ここで ステータスの更新を行う
4395 //                        sync('historySelector');//ここで 履歴セレクタの更新を行う
4396                         if(callback instanceof Function) callback();
4397                     },
4398                     function(){
4399                         alert(localize(nas.uiMsg.dmAlertCheckinFail));//チェックイン失敗
4400 //                        sync('productStatus');//ここで ステータスの更新を行う
4401 //                        sync('historySelector');//ここで 履歴セレクタの更新を行う
4402                         if(callback2 instanceof Function) callback2();
4403                     });
4404                 }
4405             });
4406         break;
4407     }
4408 }
4409 /**
4410     作業を終了する リポジトリ内のエントリを更新してステータスを変更
4411     ユーザ判定は不用
4412     現データの送信(保存)後にリスト更新とドキュメントブラウザの更新
4413 */
4414 serviceAgent.checkoutEntry=function(callback,callback2){
4415     var currentEntry = this.currentRepository.entry(Xps.getIdentifier(xUI.XPS));
4416 //    var currentCut   = this.currentRepository.cut(currentEntry.toString());
4417     var currentCut   = (currentEntry.issues[0].cutID) ?this.currentRepository.cut(currentEntry.issues[0].cutID):this.currentRepository.cut(currentEntry.toString());
4418 //console.log('checkoutEntry'+decodeURIComponent(currentEntry));
4419 //console.log(currentEntry.toString(true));
4420 //console.log(currentEntry.getStatus());
4421     if(! currentEntry) {
4422 //console.log ('noentry in repository :' +  decodeURIComponent(currentEntry))
4423         //当該リポジトリにエントリが無い
4424         alert(localize(nas.uiMsg.dmAlertNoEntry)+'\n>'+decodeURIComponent(currentEntry));//対応エントリが無い
4425         return false;
4426     }
4427     var currentStatus=currentEntry.getStatus();
4428     switch (currentStatus.content){
4429         case 'Startup': case 'Hold': case 'Fixed':
4430             //NOP
4431 //            if(dbg) console.log('fail checkout so :'+ currentEntry.getStatus());
4432             alert(localize(nas.uiMsg.dmAlertCantCheckout));//作業データでない
4433             return false;
4434         break;
4435         case 'Active':
4436             //編集状態を確認の上 Active > Fixed
4437             if(xUI.edchg) xUI.put(document.getElementById('iNputbOx').value);
4438             //Jobチェックアウト
4439             //アサイン情報を請求
4440             //このあたりはアサイン関連のDB構成が済むまで保留 要調製
4441             var title   = localize(nas.uiMsg.pMcheckout);//'作業終了 / チェックアウト';
4442 //            var msg     = localize(nas.uiMsg.dmPMnewAssign,xUI.XPS.cut);
4443 var msg = localize({
4444   en:"",
4445   ja:"%1\n作業終了します"  
4446 },decodeURIComponent(Xps.getIdentifier(xUI.XPS)))
4447 
4448             var msg2    = '<br>';
4449 
4450             msg2   += localize(nas.uiMsg.toPrefix);
4451             msg2   += ' <input id=assignNextUser autocomplete=yes class=mdInputText type=text list=assignUserList></input> ';
4452             msg2   += localize(nas.uiMsg.toPostfix);
4453             msg2   += '<datalist id=assignUserList>';
4454             var assignUserList = ["演出","作画監督","監督","美術監督","美術","原画","動画","仕上","特効"];//ここは後ほどタイトル個別のデータを請求して差替
4455             for(var idx = 0 ; idx < assignUserList.length;idx ++){
4456                 msg2   += '<option value="';
4457                 msg2   += assignUserList[idx];
4458                 msg2   += '"></option>';
4459             };
4460                 msg2 += '</datalist><br>';
4461                 msg2 += '<textarea id=assignNoteText class=mdInputArea >指名及び申し送りは開発中のダミー画面です。\n指名データを選択または入力して先に進めてください。</textarea>'
4462 
4463             nas.showModalDialog('confirm',msg,title,false,function(){
4464 //            nas.showModalDialog('confirm',[msg,msg2],title,false,function(){
4465 //                var assignUserName=document.getElementById('assignNextUser').value;
4466 //                var assignNoteText=document.getElementById('assignNoteText').value;
4467                 var assignUserName=true
4468                 var assignNoteText="";
4469                 if((this.status == 0)&&(assignUserName)){
4470                     var assignData=encodeURIComponent(JSON.stringify([assignUserName,assignNoteText]));
4471                     serviceAgent.currentRepository.checkoutEntry(assignData,callback,callback2);
4472 //                        alert(localize(nas.uiMsg.dmAlertCheckoutFail));//チェックアウト失敗
4473                 }
4474             });
4475         break;
4476     }
4477 }
4478 /**
4479      新規カットを追加登録
4480      現在のリポジトリに存在しないタイトル・エピソードを指定する場合は、必ずXpsオブジェクトを指定すること
4481      マネジメントモード下で引数無しで呼び出された場合に限り、ドキュメントブラウザの入力情報をベースに新規のエントリを作成する。
4482      その際は、規定のコールバック関数を利用して、指定のコールバックは使用されない
4483      引数なしのケースではデータ内容の指定は不可
4484      尺(識別子情報)のみ指定可能 最小テンプレートでカット番号のある空エントリのみが処理対象
4485      
4486      現在のTitle+Opus(product)の既存カットに対する衝突は排除
4487      
4488      初期状態の、ライン/ステージ/ジョブの指定が可能
4489      引数で与えられるXpsのステータスは、"Floating"である必要がある
4490 */
4491 serviceAgent.addEntry=function(myXps,callback,callback2){
4492     if(!myXps){
4493 //console.log(documentDepot);
4494          if(xUI.uiMode!='management')   return false;         
4495         var myIdentifier = documentDepot.buildIdentifier();
4496 //console.log(decodeURIComponent(myIdentifier));
4497         var entryInfo = Xps.parseIdentifier(myIdentifier);
4498                 myXps = new Xps(5,entryInfo.time);
4499                 myXps.title      = entryInfo.title;
4500                 myXps.opus       = entryInfo.opus;
4501                 myXps.subtitle   = entryInfo.subtitle;
4502                 myXps.cut        = entryInfo.cut;
4503                 myXps.createUser = xUI.currentUser;
4504                 myXps.updateUser = xUI.currentUser;
4505                 myXps.currentStatus =  new JobStatus();
4506         var productIdf = Xps.getIdentifier(myXps,'product');
4507 //新規エントリを判定
4508         if((String(myXps.cut).length==0)||(serviceAgent.currentRepository.entry(myIdentifier))){
4509             var msg = "";
4510             if (String(myXps.cut).length==0){
4511 //console.log(String(myXps.cut));
4512                 msg += localize(nas.uiMsg.alertCutIllegal);//"カット番号不正"
4513             }else{
4514                 msg += localize(nas.uiMsg.alertCutConflict);//"カット番号衝突"
4515             }
4516             alert(msg+': can not addEntry');
4517             return false;
4518         }else{
4519 //限定条件下なのでコールバックを規定値で行う
4520             serviceAgent.addEntry(myXps,function(){
4521                 serviceAgent.currentRepository.getSCi(false,false,Xps.getIdentifier(myXps));
4522             },function(){
4523 //console.log('error: addEntry')
4524             });
4525         };
4526         return;
4527     }else{
4528         var myIdentifier = Xps.getIdentifier(myXps);
4529 //既存カットと一致(排除)
4530         if(this.currentRepository.entry(myIdentifier)){
4531             alert(localize(nas.uiMsg.alertCutConflict));
4532             return false;
4533         }
4534 
4535 //既存プロダクトあり(プロダクト作成処理不用)
4536         if(this.currentRepository.entry(myIdentifier,true)){
4537             serviceAgent.pushEntry(myXps,callback,callback2);
4538         }else{
4539 //既存のタイトルがあるか?あればエピソードのみ新作
4540 //なければタイトルを作成後にエピソードを新作して処理続行
4541 // confirmあり
4542              var hasTitle = false;
4543              var hasOpus  = false;
4544              for (var pid=0;pid<documentDepot.products.length;pid ++){
4545                 //productsのメンバをオブジェクト化したほうが良いかも
4546                 var prdInfo=Xps.parseProduct(documentDepot.products[pid]);
4547                 if(prdInfo.title== myXps.title) {
4548                     hasTitle = documentDepot.products[pid];
4549                     if(prdInfo.opus == myXps.opus) {hasOpus  = documentDepot.products[pid];break;}
4550                 }
4551              }
4552              if((hasTitle)&&(! hasOpus)){
4553                 var msg=localize({
4554                     en:"The specified episode #%1[%2] is not registered in this sharing.\nwould you like to create a new episode #%1[%2]?\nTo change sharing please cancel once and try the procedure again.",
4555                     ja:"この共有には指定の制作話数 #%1[%2] が登録されていません。\n新規に制作話数 #%1[%2] を登録しますか?\n共有を変更する場合は一旦キャンセルして手続をやり直してください。"},myXps.opus,myXps.subtitle);
4556                 if(confirm(msg))
4557                 serviceAgent.currentRepository.addOpus(myIdentifier,productIdf,function(){
4558                     serviceAgent.pushEntry(myXps,callback,callback2);
4559                 });
4560               }else if((! hasTitle)&&(! hasOpus)){
4561                 var msg=localize({
4562                     en:"The specified production %1#%2[%3] is not registered in this sharing.\nwould you like to create a new production %1#%2[%3]?\nTo change sharing please cancel once and try the procedure again.",
4563                     ja:"この共有には指定された作品 %1#%2[%3] が登録されていません。\n新規に %1#%2[%3] を登録しますか?\n\n共有を変更する場合は一旦キャンセルして手続をやり直してください。"},myXps.title,myXps.opus,myXps.subtitle);
4564                 if(confirm(msg))
4565                 serviceAgent.currentRepository.addTitle(myXps.title,"","",function(){
4566                     serviceAgent.currentRepository.addOpus(myIdentifier,myIdentifier,function(){
4567                         serviceAgent.pushEntry(myXps,callback,callback2);
4568                     });
4569                 });
4570              }else{
4571                 serviceAgent.pushEntry(myXps,callback,callback2);
4572              };
4573                        
4574         };
4575     };
4576 };
4577 /**
4578      工程を閉じて次の工程を開始する手続き
4579      逆戻り不能なのでチェックを厳重に
4580      
4581 */
4582 serviceAgent.receiptEntry=function(){
4583     var currentEntry = this.currentRepository.entry(Xps.getIdentifier(xUI.XPS));
4584 //    var currentEntry = (typeof myIdentifier == 'undefined')?this.currentRepository.entry(Xps.getIdentifier(xUI.XPS)):this.currentRepository.entry(myIdentifier);
4585     if(! currentEntry) return false;
4586     var currentStatus=currentEntry.getStatus();
4587 //console.log(currentStatus);
4588     switch (currentStatus.content){
4589         case 'Startup': case 'Active': case 'Hold':case 'Floating':
4590 console.log("not Fixed :"+currentEntry.toString());
4591             return false;
4592         break;
4593         case 'Fixed':
4594             //Fixedのみを処理
4595             var newStageList = nas.pmdb.stages.getTemplate(xUI.XPS.stage.name);
4596             var newJobList   = nas.pmdb.jobNames.getTemplate(xUI.XPS.stage.name);
4597             var title = localize(nas.uiMsg.pMreseiptStage);//'作業検収 / 工程移行';
4598             var msg   = localize(nas.uiMsg.dmPMnewStage);//'現在の工程を閉じて次の工程を開きます。\n新しい工程名を入力してください。\nリストにない場合は、工程名を入力してください。';
4599             var msg2  = '<br><span>'
4600                       + localize(nas.uiMsg.pMcurrentStage)
4601                       + ' : %currentStage% <br>'
4602                       + localize(nas.uiMsg.pMnewStage) + ' : '+ nas.incrStr(xUI.XPS.stage.id)
4603                       + ':</span><input id=newStageName type=text list=taragetStageList onChange="serviceAgent.updateNewJobName(this.value);"></input><datalist id=taragetStageList>';
4604                 msg2 = msg2.replace(/%currentStage%/,xUI.XPS.stage.toString(true));
4605             for(var idx = 0 ; idx < newStageList.length;idx ++){
4606                 msg2   += '<option value="';
4607                 msg2   += newStageList[idx];
4608                 msg2   += '"></option>';
4609             };
4610                 msg2   += '</datalist>';
4611                 msg2   += '<br><span>'
4612                        + localize(nas.uiMsg.pMnewJob) + ' : 0'
4613                        +':</span><input id=newJobName type=text list=taragetJobList ></input><datalist id=taragetJobList></datalist>';
4614             nas.showModalDialog('confirm',[msg,msg2],title,false,function(){
4615                 var newStageName = document.getElementById('newStageName').value;
4616                 var newJobName = document.getElementById('newJobName').value;
4617                 if ((this.status == 0)&&(newStageName)&&(newJobName)){
4618                     //if(dbg) console.log([newStageName,newJobName]);
4619                     serviceAgent.currentRepository.receiptEntry(newStageName,newJobName);
4620                 }
4621             });
4622             
4623         break;
4624     }
4625 }
4626 /**
4627     当該エントリの制作を中断する。
4628     以降は複製のみ可能となる
4629 */
4630 serviceAgent.abortEntry=function(myIdentifier,callback,callback2){
4631     var currentEntry = (typeof myIdentifier == 'undefined')? this.currentRepository.entry(Xps.getIdentifier(xUI.XPS)):this.currentRepository.entry(myIdentifier);
4632 //console.log(currentEntry)
4633     if(! currentEntry) return false;
4634     var currentStatus=currentEntry.getStatus();
4635 //console.log(currentStatus)
4636     switch (currentStatus.content){
4637         case 'Startup':
4638         case 'Hold':
4639         case 'Active':
4640         case 'Floating':
4641             //管理モード下でのみ処理 このメソッドのコール自体が管理モード下でのみ可能にする
4642             //リポジトリに対して
4643         break;
4644         case 'Fixed':
4645         default:
4646 //console.log('serviceAgent abort entry');
4647             return this.currentRepository.abortEntry(myIdentifier,callback,callback2);
4648     }
4649     return currentStatus.content;
4650 }
4651 /**
4652     閉じる
4653     開いているエントリが、ActiveならばHoldに変更
4654     XPSをカラ(初期状態=float)する
4655     ドキュメントの状態をFloatingにセット
4656     
4657     現状のドキュメントをフロート化する際はfloatEntryメソッドを使用
4658 
4659 */
4660 serviceAgent.closeEntry=function(callback,callback2){
4661     //  ドキュメントがアクティブで変更フラグが立っている場合 holdしてカレントリポジトリにプッシュ
4662      if((xUI.XPS.currentStatus.content=="Active")&&(! xUI.isStored())){
4663     //  成功したらカレントドキュメントをクリアしてロック
4664          serviceAgent.currentRepository.deactivateEntry(function(){
4665             serviceAgent.closeEntry(callback,callback2);
4666         },function(){
4667             xUI.errorCode=9;
4668             if(callback2 instanceof Function) callback2();
4669         }
4670         );
4671     }else{
4672         xUI.resetSheet(new Xps(5,144),new Xps(5,144));
4673         xUI.XPS.currentStatus= new JobStatus("Floating");
4674         xUI.setUImode('floating');    
4675         if(callback instanceof Function) callback();
4676     }
4677 }
4678 /**
4679     フロート化
4680     ドキュメントを複製してFloating状態にする
4681     開いているエントリが、ActiveならばHoldに変更する
4682     XPSはそのままの状態でステータスをフロート化する
4683     レポジトリ上のエントリーは変更なし
4684     これは単純なエクスポートであり、管理情報はここで切れる
4685 */
4686 serviceAgent.floatEntry=function(callback,callback2){
4687     //  ドキュメントがアクティブで変更フラグが立っている場合 holdしてカレントリポジトリにプッシュ
4688      if((xUI.XPS.currentStatus.content=="Active")&&(! xUI.isStored())){
4689     //  成功したらカレントドキュメントをクリアしてロック
4690          serviceAgent.currentRepository.deactivateEntry(function(){
4691             serviceAgent.floatEntry();
4692         },function(){
4693             xUI.errorCode=9;
4694             if(callback2 instanceof Function) callback2();
4695         }
4696         );
4697     }else{
4698         xUI.XPS.currentStatus.content='Floating';
4699         xUI.setUImode('floating');
4700         if(callback instanceof Function) callback();
4701     }
4702 }
4703 /**
4704     最終ジョブを破棄する(巻き戻し)
4705     現在のジョブ内容を、保存含めて破棄する
4706     破棄可能な条件は、
4707     現在作業中のジョブまたは作業可能なジョブであること(Activeドキュメントのみに適用)
4708     closeに手順がにているが、ハードデリートを伴う点が異なる
4709     ハードデリートを伴うため バックアップコピーを作成して保険として使うべき
4710      
4711 */
4712 serviceAgent.destroyJob=function(callback,callback2){
4713     if(xUI.XPS.currentStatus.content !="Active"){alert("this entry is not active.");return false;}
4714 //ドキュメントがアクティブでない場合は操作不能
4715     var currentEntry = serviceAgent.currentRepository.entry(Xps.getIdentifier(xUI.XPS));
4716     var currentOpus  = serviceAgent.currentRepository.opus(currentEntry.toString(0));
4717 //console.log(currentOpus)
4718     var currentWork = [
4719         xUI.XPS.line,
4720         xUI.XPS.stage,
4721         xUI.XPS.job       
4722     ].join("//"); 
4723     var msg=currentEntry.toString() + "\n" +localize({
4724         en:"%1 : Discard the current work and return it to the state of the previous work. Is it OK?",
4725         ja:"%1 : 現在の作業を廃棄して、一つ前の作業の状態にもどします。よろしいですか?"
4726     },currentWork);
4727     if(confirm(msg)){
4728 //console.log('bkup');
4729         xUI.setBackup();//自動でバックアップをとる(undoではない)
4730         serviceAgent.currentRepository.destroyJob(function(){
4731             alert("destroyed job :" +currentWork);
4732             serviceAgent.currentRepository.getSCi(false,false,currentOpus.token);
4733             documentDepot.documentsUpdate();
4734 //console.log(serviceAgent.currentRepository);
4735             alert(currentEntry.toString(1));
4736             serviceAgent.getEntry(currentEntry.toString(1),function(){
4737                 xUI.setUImode("browsing");sync("productStatus");                
4738             })
4739         },function(result){
4740 //console.log(result)
4741             alert("作業取り消しに失敗しました。");
4742         });
4743     }
4744 }
4745 /**
4746     選択可能な参考ジョブリストの更新
4747     更新されたリスト以外のジョブ名称も認められる
4748 */
4749 serviceAgent.updateNewJobName=function(stageName,type){
4750     var targetList=document.getElementById("taragetJobList");
4751     if(! targetList) return false;
4752     for (var i = targetList.childNodes.length-1; i>=0; i--) {
4753         targetList.removeChild(targetList.childNodes[i]);
4754     }
4755     if(!type) type='init';
4756     var newJobList = nas.pmdb.jobNames.getTemplate(stageName,type);
4757     for(var idx = 0 ; idx < newJobList.length;idx ++){
4758         var option = document.createElement('option');
4759         option.id = idx;
4760         option.value = newJobList[idx];
4761         targetList.appendChild(option);
4762     };
4763 //    if(dbg) console.log(newJobList);
4764 }
4765 //Test code
4766 /**
4767     サーバエージェントを経由してリポジトリにデータを送出する
4768     保存データが最新のissueでない場合はリジェクト
4769     この場合はデータの更新があるかないかは問わない
4770     ステータスがFloatingの場合は、複製をとってStartup状態でプッシュする
4771 */
4772 serviceAgent.pushEntry=function(myXps,callback,callback2){
4773 //console.log('serviceAgent.pushEntry');
4774     if (typeof myXps == 'undefined') myXps = xUI.XPS;
4775     if((xUI.XPS === myXps)&&(xUI.sessionRetrace > 0)){
4776         xUI.errorCode=8;//確定済データを更新することはできません
4777         alert(localize(xUI.errorMsg[xUI.errorCode]));
4778         return false;
4779     }
4780     if (!( myXps instanceof Xps)){
4781         if(callback2 instanceof Function){callback2();}
4782         return false;
4783     }
4784     var newXps = new Xps();
4785     newXps.parseXps(myXps.toString());
4786     
4787     if(myXps.currentStatus.content.indexOf('Floating')>=0){
4788 /*プッシュ条件
4789 タイトルが存在する、エピソードが存在する
4790 カット番号がある
4791 ユーザ情報が存在する
4792 ここでユーザアサインメントを付加することが可能ーーー未実装 201802
4793 */
4794         var msg=localize({
4795         en:"Add the current cut: %1 :\nto the share : %2 :.\n Is it OK?",
4796         ja:"現在のカット: %1 :を\n共有: %2 :に追加します。\nよろしいですか?"
4797     },myXps.getIdentifier(),serviceAgent.currentRepository.name)
4798         // "TEST push Entry :"+myXps.getIdentifier();
4799         var go=confirm(msg);
4800         if(go){
4801 /*  データステータスをチェック
4802     カレントタイトルがない場合は新作
4803     カレントのopusが無い場合は新作
4804     いずれも コールバック処理渡し
4805     データステータスがFloatingなので、Startupへ変更
4806 */
4807        newXps.currentStatus = new JobStatus('Startup');
4808         }else{
4809             return false;//処理中断
4810         }
4811     }
4812 // console.log(newXps);
4813     this.currentRepository.pushEntry(newXps,callback,callback2);
4814 }
4815 /**
4816 
4817 Repos.getProducts();//一度初期化する
4818 if(dbg) console.log(Repos.productsData);
4819 Repos.getList();
4820 
4821 */
4822 /**
4823     入力テキストをパースしてカットを集計した配列を返す
4824     入力書式は別紙
4825 */
4826 function parseCutText(sourceText){
4827     var sepChar      = '\t';//セパレータ初期値H-TAB
4828     var commentRegex = new RegExp('(#|;|//)');
4829     var cutRegex     = new RegExp('cut(#|#|no\.|№)?','i');
4830     var timeRegex    = new RegExp('(time|duration|seconds|秒|時間|尺)','i');
4831     var sourceArray=sourceText.split('\n');
4832     var dataStartLine     = -1;
4833     var namePosition = -1;var timePosition= -1;
4834     
4835     for (var lid=0;lid<sourceArray.length;lid++){
4836         if(String(sourceArray[lid]).match(/^\s*$/)||String(sourceArray[lid]).match(commentRegex)){
4837             continue;
4838         }else{
4839             dataStartLine = lid;
4840             if(String(sourceArray[lid]).match(/,/)){sepChar = ','};
4841             var myFields = String(sourceArray[lid]).split(sepChar);
4842             for (var fid=(myFields.length-1);fid>=0;fid--){
4843                 if(myFields[fid].match(cutRegex)) {namePosition=fid};
4844                 if(myFields[fid].match(timeRegex)){timePosition=fid};
4845             }
4846             break;
4847         }
4848     }
4849 // console.log(dataStartLine)
4850     
4851     if(namePosition==-1){
4852         namePosition=0;
4853         if(timePosition != -1){dataStartLine ++};
4854         timePosition=1;
4855     }else{
4856         dataStartLine ++;
4857     }
4858 
4859 //console.log(dataStartLine)
4860     var resultArray=[];
4861     var cutName="";var cutTime="";
4862     var currentName="";var currentTime=0;
4863     
4864     for (var lid=dataStartLine;lid<sourceArray.length;lid++){
4865         if(String(sourceArray[lid]).match(/^\s*$/)||String(sourceArray[lid]).match(commentRegex)){
4866             continue;
4867         }else{
4868             var myFields = String(sourceArray[lid]).split(sepChar);
4869             cutName = (myFields[namePosition])? String(myFields[namePosition]) :currentName;
4870                 if (cutName.match(/^"([^"]*)"$/)){cutName=RegExp.$1};//"
4871             cutTime = (timePosition < 0)? "":String(myFields[timePosition]);
4872                 if (cutTime.match(/^"([^"]*)"$/)){cutTime=RegExp.$1};//"
4873                 cutTime = parseInt(nas.FCT2Frm(decodeURI(cutTime)),10);
4874 //            console.log(cutName+":"+currentName);
4875             if((cutName != currentName)&&(currentName.length>0)){
4876                 resultArray.push([currentName,currentTime]);
4877                 currentTime = cutTime;
4878             }else{
4879                 currentTime+=cutTime;
4880             }
4881             currentName = cutName;
4882         }
4883     }
4884     resultArray.push([currentName,currentTime]);
4885     return resultArray;
4886 }
4887 // test
4888 //sourceText="1,24\n2,48\n3,12\n,12";
4889 /**
4890 sourceText=([
4891 "cut\tb\ttime\td\te",
4892 "1\tX\t30\tA\tA",
4893 "2\tW\t30\tA\tA",
4894 "3\tZ\t30\tA\tA",
4895 "\t''\t30\tA\tA",
4896 ]).join("\n");
4897 sourceText=document.getElementById('data_well').value;
4898 parseCutText(sourceText);
4899 */
4900 
4901 /*
4902      インポート/エクスポートウェルに置いたカット登録テキストを識別子に変換して
4903      entryQueueを作成
4904      これを引数にしてpushEntryを順次コールする。
4905      
4906 */
4907     serviceAgent.entryQueue = [];
4908     serviceAgent.entryQueue.select = 0;
4909 
4910 makeNewEntriesFromFormatedText=function(ix){
4911     if(typeof ix == 'undefined'){
4912         var sourceText=document.getElementById('data_well').value;
4913         serviceAgent.entryQueue = parseCutText(sourceText);
4914         for (var qid=0;qid<serviceAgent.entryQueue.length;qid++){
4915             var cutNo   = serviceAgent.entryQueue[qid][0];
4916             var cutTime = serviceAgent.entryQueue[qid][1];//整数化が済んでいるものとする
4917             if((String(cutNo).length > 0)&&(cutTime > 0)){
4918                 // ビルドの際にXPSを参照するのはあまり良くない これは引数で与えるか、またはdocumentDepotのプロパティから取得する
4919                 var myXps = new Xps(5,cutTime);
4920                 myXps.title     = xUI.XPS.title;
4921                 myXps.opus      = xUI.XPS.opus;
4922                 myXps.subtitle  = xUI.XPS.subtitle;
4923                 myXps.cut       = cutNo;//sciはやく
4924                 myXps.create_user = xUI.currentUser;
4925             }else{
4926                 myXps=null;
4927             }
4928                 serviceAgent.entryQueue[qid] = myXps;
4929         }
4930         serviceAgent.entryQueue.select = 0;//エントリ用のキューを初期化
4931         ix = 0;
4932     }
4933         //カット番号が空・カット尺が0 の場合は処理スキップ
4934         
4935 //console.log("queue entry : "+ix);
4936 
4937         if(serviceAgent.entryQueue[ix]){
4938 //console.log(serviceAgent.entryQueue[ix]);
4939 //console.log(decodeURIComponent(Xps.getIdentifier(serviceAgent.entryQueue[ix])));
4940             serviceAgent.currentRepository.pushEntry(serviceAgent.entryQueue[ix],function(){
4941                 serviceAgent.entryQueue.select ++;
4942                 if(serviceAgent.entryQueue.select < serviceAgent.entryQueue.length){
4943                     makeNewEntriesFromFormatedText(serviceAgent.entryQueue.select);
4944                 }else{
4945                     //終了
4946                     alert('エントリ終了だと思われるナリ :' + serviceAgent.entryQueue.select+"/"+serviceAgent.entryQueue.length)
4947                 }
4948             });
4949         }else{
4950                  //エントリ不正の場合は、処理スキップ
4951                 serviceAgent.entryQueue.select ++;
4952                 if(serviceAgent.entryQueue.select < serviceAgent.entryQueue.length)
4953                     makeNewEntriesFromFormatedText(serviceAgent.entryQueue.select);
4954         }
4955 };
4956 
4957 //makeNewEntriesFromFormatedText();
4958 
4959 /**
4960     プロダクトデータDB
4961 object Pm.Title={
4962     token:token-string, //ローカルリポジトリのキー
4963     name:title-short-name,
4964     description:title-string,
4965     created_at:time-created,
4966     updated_at:time-updated,
4967     episodes:[[array of Pm.Opus]]
4968 }
4969 object Pm.Opus={
4970     token:token-string,
4971     name:opus-name
4972     description:opus-string-long(ex subtitle)
4973     created_at:time-created,
4974     updated_at:time-updated,
4975     cuts:[[array of SCi]]    
4976 }
4977 object Pm.SCi={
4978     token:token-string,
4979     name:opus-name
4980     description:entry-identifier-string,
4981     versions:[]    
4982 }
4983 
4984 object PM.SCiVersion={
4985     content:[line,stage,Job]
4986     updated_at:time-updated,
4987     description:entry-identifier-string,
4988     version_token:59    
4989 }
4990 
4991 productsData=[
4992 {
4993     "token":"fTkqAmVz8ZEfrctW7JrrJ66g",
4994     "name":"mns2_r",
4995     "description":"モンスターストライク2",
4996     "created_at":"2017-01-20T09:42:30.000+09:00",
4997     "updated_at":"2017-01-20T11:59:02.000+09:00",
4998     "episodes":[
4999      [
5000       {
5001         "token":"CKmnhS6iu3Hw8Jh2nZyNBWtB",
5002         "name":"00",
5003         "description":"",
5004         "created_at":"2017-01-20T09:43:01.000+09:00",
5005         "updated_at":"2017-01-20T11:28:12.000+09:00",
5006         "cuts":[[
5007         {
5008             "token":"aDZn4cteVMUSvAsuJa3hmZGW",
5009             "name":"001",
5010             "description":"mns2_r#00//001",
5011             "versions":[]
5012         },
5013         {
5014             "token":"tCEpSnz9BanvwKrCdtqc5fSs",
5015             "name":"1",
5016             "description":"mns2_r#00//1",
5017             "versions":[]
5018         },
5019         {
5020             "token":"85c5q2NsNbdXmqkFrMS6jyJy",
5021             "name":"s-c2",
5022             "description":"mns2_r#00//s-c2//0%3A(undefined)//0%3A//0%3Aundefined//Fixed",
5023             "versions":[
5024             {
5025                 "updated_at":"2017-01-20T11:52:12.000+09:00",
5026                 "description":null,
5027                 "version_token":59
5028             },
5029             {
5030                 "updated_at":"2017-01-20T12:13:58.000+09:00",
5031                 "description":"mns2_r#00//s-c2(144)//0%3A(undefined)//%3Aundefined//undefined%3ALO//Active",
5032                 "version_token":115
5033             }]
5034         }]],
5035        }
5036       ]
5037      ]
5038     }
5039 ]
5040 //listEntrオブジェクトプロパティ
5041 
5042 object listEntry={
5043     dataInfo    : 識別子情報オブジェクト,
5044     parent      : リポジトリへの参照,
5045     product     : プロダクト識別子-encoded,
5046     sci         : 代表カット番号 -encoded,
5047     issues      : [[
5048         ライン情報 -encoded,
5049         ステージ情報-encoded,
5050         ジョブ情報 -encoded,
5051         ステータス -encoded
5052     ]],
5053         issues[0].identifier :カット識別子,
5054         issues[0].time       :代表カット尺,
5055         issues[0].cutID      :DBアクセスキー,
5056         issues[0].versionID  :DBアクセスキー,
5057     titleID             : DBアクセスキー,
5058     episodeID           : DBアクセスキー
5059 }
5060 
5061 アクティブなカットがある状態で、カレントのリポジトリを切り替えると問題が発生するので対応を考えること
5062 編集中のエントリをキャッシュするか、切替前のリポジトリをキャッシュ?
5063 または 切替時にエントリを強制クローズ < これで対処
5064 
5065 リポジトリ切り替えのタイミングで強制的にアクティブなドキュメントをディアクティベートすることで処理
5066 
5067 切り替えのタイミングでセレクタが使えなくなる(表示データの信頼性が無くなる)タイミングでセレクタを不活性化
5068 
5069 データ更新が終了した時点で再活性化するように変更(済)
5070 
5071 ネットワーク上でのDB更新にまだ問題あり dev に適用して調整
5072 
5073 */