さて、今回は編集可能にしたTableに関数を実装していく前に循環参照を検出しようというところ。
編集中のセルからフォーカスアウトした時に発火するイベントにチェックを仕込みます。
循環参照のチェックを開始する部分
$("#edit_target").focus().blur(function (){ var adrs = $(this).attr("data-address"); var edit_val = $(this).val().toString(); var rollback = editable_table_val[adrs].value; editable_table_val[adrs]["value"] = edit_val; if (/^=.*([A-Z]+\d+).*/.test(edit_val)) { if (editable_table.LoopCheck(this)) { alert("循環参照を検出しました。\r\nセルの内容を修正し直して下さい。"); $(this).focus(); editable_table_val[adrs] = rollback; return } } $(this).remove(); $("td[data-address='" + adrs + "']").toggleClass("editing", false); editable_table.Recalculation(); });
行 7-13のif分のところですね。
Excelと同じように使いたいから、最初の文字が"="(イコール)でかつセルアドレスに合致するパターンの文字列が入っていたら循環参照のチェックをしたい。
んで、循環参照だとわかったら事前によっこしておいたロールバック値にセルの値を戻してフォーカスを強制的にテキスト編集エリアに戻してしまう訳だ。
editable_table.LoopCheckの実装部分
LoopCheck: function(cell) { var roots = []; var parent_adrs = $(cell).attr("data-address"); return editable_table.RootExists(parent_adrs, roots); },
まずは、渡された編集元のセルのアドレスを控えつつ、ここからは再起的に同じセルアドレスへの参照が含まれていないか検索し続ける訳だ。
editable_table.RootExistsの実装部分
RootExists: function(adrs, roots) { if (roots.indexOf(adrs) == -1) { roots.push(adrs); var cell_val = editable_table_val[adrs].value; if (/^=.*([A-Z]+\d+).*/.test(cell_val)) { var mtch_adrs = cell_val.match(/([A-Z]+\d+)/g); if (mtch_adrs.length == 0) { return false } for (idx in mtch_adrs){ console.log(idx); if (editable_table.RootExists(mtch_adrs[idx], roots)) { return true } } } else { return false; } } else { return true } }
渡された配列にセルの値に含まれてるセルアドレスを追加しながら、その新たなセルの値にも同じ検索を繰り返していって、すでに追加済みのセルアドレスが検出されてしまったら循環参照とみなしてfalseを返すようにすると。
これを事前にやっておかないと、関数的にセルの書式を処理し始めたらループしちゃう可能性がありそうですからね。
まだ関数の実装については細かく考えてないんだけども。
で、裏の機能は実装したけども、いざ表を編集しようとしてみるとExcelと違って行と列にインデックスがないからなんて指定したらセル参照になるのかわかりませんね(@_@;)
次は、編集モードの時だけ各セルのセルアドレスがフワッと出現する様にしてみますかね。