gridy_ajax


var Gridy = {};
(function($, Gridy) {
  /**
   * 現在のモジュールのルートパスを返します
   * @returns string
   */
  Gridy.getModulePath = function() {
    var url = $.url();
    var path = url.attr("path");
    if (!path || path.length == 0 || path == "/") {
      return "/";
    }
    if (path.charAt(0) == '/') {
      path = path.substring(1);
    }
    var paths = path.split('/');
    var module = '';
    for(var i = 0; i < Gridy.getModulePath.defaults.appPaths.length; i++) {
      if (paths[0] == Gridy.getModulePath.defaults.appPaths[i]) {
        module = paths[0] + '/' + paths[1];
        break;
      }
    }
    if (module == '') {
      module = paths[0];
    }
    return '/' + module;
  };
  Gridy.getModulePath.defaults = {
    appPaths: ['frontend_dev.php', 'index.php']
  };

  /**
   * フラグメント識別子内に埋め込まれたquery文字列を取得します
   * @param url string フラグメント識別子 nullの場合は現在のURLから取得
   * @return string
   */
  Gridy.getHashQuery = function(url) {
    if (url == null) {
      url = location.href;
    }
    var urls = url.split("#");
    var hash = null;
    if (urls.length == 2) {
      hash = urls[1];
    }
    if (!hash || hash.indexOf("?") == -1 || hash.indexOf("?") == hash.length - 1) {
      return null;
    }
    hash = hash.split("?");
    hash = hash[1];
    return hash;
  };

  /**
   * 引数・または現在のURLのquery文字列から連想配列を作成します
   * @param query 対象となるquery文字列 nullの場合は現在のURLから取得
   * @param hash trueの場合、URL上のフラグメント文字列内に埋め込まれたquery文字列を対象とします default true
   * @returns Object
   */
  Gridy.getQueryParams = function(query, hash) {
    hash = hash !== undefined ? hash : true;
    if (!query) {
      if (hash) {
        query = Gridy.getHashQuery();
      } else {
        var url = $.url();
        query = url.attr("query");
        if (query) {
          // $.urlがdecodeURIしているのでencodeし直す
          query = encodeURI(query);
        }
      }
    }
    if (query) {
      if (query.indexOf("?") == 0) {
        query = query.substring(1);
      }
      query = query.split('&');
      var params={};
      for(var i = 0; i < query.length; i++) {
        var pair = query[i].split('=');
        if (pair[0]) {
          var _key = decodeURIComponent(pair[0]);
          var _val = decodeURIComponent(pair[1]);
          if (params[_key] != undefined) {
            if ($.isArray(params[_key])) {
              params[_key].push(_val);
            } else {
              params[_key] = [params[_key], _val];
            }
          } else {
            params[_key] = _val;
          }
        }
      }
      return params;
    }
    return null;
  };

  /**
   * 現在のqueryを含むhash値を引数の配列値で更新します
   * @param updateParams 更新する値を保持する連想配列
   * @param returnParams trueの場合URLのhash値を更新せず、hash文字列を返す default false
   */
  Gridy.updateHashQuery = function(updateParams, returnParams) {
    var params = Gridy.getQueryParams() || {};
    $.extend(params, updateParams);
    for (var key in params) {
      if (key == null || key == "" || params[key] == null || params[key] == "") {
        delete params[key];
      }
    }
    if (returnParams) {
      return $.param(params);
    } else {
      var url = $.url();
      var hash = url.attr("fragment");
      if (hash && hash.indexOf("?") != -1) {
        hash = hash.split("?")[0];
      } else {
        hash = "";
      }
      location.href = url.attr("path") + "#" + hash + "?" + $.param(params);
    }
  };

  /**
   * window.onhashcangeイベントが使用可能かどうかを返します
   * @return boolean
   */
  Gridy.isHashChangeEnable = function() {
    return 'onhashchange' in window && (document.documentMode === undefined || document.documentMode >= 8);
  };
  
  /**
   * クライアントがタッチパネル端末かどうかを返します
   * @return boolean
   */
  Gridy.isTouchPanel = function() {
    return "ontouchend" in document;
  }

  /**
   * 共通エラーハンドラ
   * @param jqXHR XmlHttpRequest
   * @param textStatus
   * @param errorThrown
   */
  Gridy.errorHandler = function(jqXHR, textStatus, errorThrown) {
    if (jqXHR.status == 401) {
      return;
    }

    var message = "サーバ通信中にエラーが発生しました。";
    if (jqXHR.status == 404) {
      message = "指定のID、またはページが存在しません。";
    }

    if (jqXHR && jqXHR.getResponseHeader("Content-Type") == 'application/json') {
      var json = eval("(" + jqXHR.responseText + ")");
      if (json.message) {
        message = json.message;
      }
    }
    Gridy.errorDialog(message);
  };

  /**
   * エラーメッセージを表示するダイアログを表示
   * @param message string ダイアログに表示する文字列
   */
  Gridy.errorDialog = function(message) {
    $("<div></div>")
      .dialog("close")
      .append($("<p></p>").addClass("message_component").text(message))
      .dialog({
        'title': 'エラー',
        'close': function(){
          $(this).remove();
        }
      });
  };

  /**
   * プロトタイプ型継承処理を行います。
   * @param parent Function or Object 親クラスのコンストラクタまたはインスタンス
   * @param child Function 子クラスのコンストラクタ
   * @returns Function or Object childが指定されなかった場合、parentを継承したObject
   *          childを指定した場合、継承処理を行ったchildを返す
   */
  Gridy.extend = function(parent, child) {
    var F = function() {};
    if (parent instanceof Function) {
      parent = new parent();
    }
    if (!child) {
      F.prototype = parent;
      return new F();
    }
    for (var prop in parent) {
      if (parent.hasOwnProperty(prop)) {
        delete parent[prop];
      }
    }
    child.prototype = parent;
    child.prototype.constructor = child;
    return child;
  };

  // jQuery拡張
  /**
   *  ハッシュ用リンク作成
   *  例 
   *  pathname = /defaultpath の場合
   *  /defaultpath/method?key=value
   *  ↓
   *  /defaultpath#/method?key=value
   *  
   *  /defaultpath?key=value
   *  ↓
   *  /defaultpath#?key=value
   *
   * @param opts オプションを定義した連想配列
   *   pathname: string モジュールのルートパス等、アプリのルートを示す文字列
   *   selector: mixed
   *             default: a.hash_link
   *             実行したオブジェクト配下の対象リンクを指定するselector
   *             falseを指定すると実行したオブジェクトに対して処理を行います
   * @returns jQueryオブジェクト
   */
  $.fn.hashLink = function(opts) {
    opts = $.extend({}, $.fn.hashLink.defaults, opts);
    var $this = opts.selector ? this.find(opts.selector) : this;
    $this.each(function() {
      var url = $(this).url();
      if (!url || !url.attr("path")) {
        return;
      }
      if (url.attr("path").match("^" + opts.pathname + "$") ||
        url.attr("path").match("^" + opts.pathname + "/.*") ||
        (opts.pathname == "/" && url.attr("path").match(/^\/.*/))) {
        var hash = "";
        var pathHash = url.attr("path");
        if (pathHash != opts.pathname) {
          pathHash = pathHash.substring(opts.pathname.length);
          hash += pathHash;
        }
        var query = this.search;
        if (query) {
          hash += query;
        }
        this.href = opts.pathname + "#" + hash;
      }
    });
    return this;
  };
  $.fn.hashLink.defaults = {
    pathname: Gridy.getModulePath(),
    selector: "a.hash_link"
  };


  /**
   * 営業報告の顧客検索の部署選択などで使っている
   * 複数表示を可能にする
   * @param name hiddenのname属性
   * @param text 表示テキスト
   * @param value hiddenのvalue属性
   * @param conditionElm spanを埋め込む、側になるhtml
   * @returns jQueryオブジェクト
   */
  $.fn.createSelectedData2 = function(name, text, value, conditionElm) {
    return $("<span></span>")
      .append(
          $("<a></a>")
          .attr("href", "#")
          .text(text)
          .addClass("delete_link")
          )
      .append(
          $("<input type='hidden' />")
          .attr("name", name)
          .val(value)
          )
      .appendTo(conditionElm);
  }

  /**
   * selectorで指定したElementに対してイベントハンドラをセットします
   * 引数のactionオブジェクトが呼び出されて画面描写を行った後にbind処理が実施されます
   * 別のactionによって対象画面が置き換わる前にunbind処理が実施されます。
   * @param eventType string
   * @param handler Function
   * @param action Gridy.Action イベントのbind、unbindを実行するGridy.Actionオブジェクト
   *               引数が配列の場合、中のAction全体が適用対象になる
   * @param eventData Object
   * @returns jQueryオブジェクト
   */
  $.fn.bindToAction = function(eventType, handler, action, eventData) {
    var _bindAction = function (action) {
      if (action instanceof Gridy.Action) {
        action.addHandler(new Gridy.Handler(
          this.selector,
          eventType,
          handler,
          eventData
        ));
      }
    };
    if (action instanceof Gridy.Action) {
      _bindAction.call(this, action);
    } else if (action instanceof Array) {
      for (var i = 0; i < action.length; i++) {
        _bindAction.call(this, action[i]);
      }
    }
    return this;
  };
  
  /**
   * Drag & Dropイベント用にtoucheイベントを追加
   */
  (function() {
    var dispatchMouseEvent = function(eventType, touchEvent) {
      var simulatedEvent = document.createEvent("MouseEvent");
      simulatedEvent.initMouseEvent(
      eventType, true, true, window, 1,
      touchEvent.screenX, touchEvent.screenY,
      touchEvent.clientX, touchEvent.clientY, false,
      false, false, false, 0, null);
      touchEvent.target.dispatchEvent(simulatedEvent);
    };

    var touchHandler = function(event) {
      if (event.originalEvent.touches.length > 1){
        return;
      }
      var touches = event.originalEvent.changedTouches;
      var first = touches[0];
      var type = "";
      switch(event.type){
        case "touchstart":
          type = "mousedown";
          break;
        case "touchend":
          type = "mouseup";
          break;
        case "touchmove":
          type = "mousemove";
          event.preventDefault();
          break;
        default:
          return;
      }
      dispatchMouseEvent(type, first);
    };
    
    $.fn.addTouchEvent = function() {
      if (Gridy.isTouchPanel()) {
        this.on("touchstart touchmove touchend", touchHandler);
      }
      return this;
    };
    $.fn.removeTouchEvent = function() {
      if (Gridy.isTouchPanel()) {
        this.off("touchstart touchmove touchend", touchHandler);
      }
      return this;
    };
  })();

  //絞り込み設定
  (function() {
    /**
     *  一覧検索FORM用イベントハンドラを定義します
     *  
     * @param opts オプションを定義した連想配列
     *   filterForm: String 検索項目を設定したdivを示すselector
     *   link: String 検索フォーム開閉ボタンを示すselector
     *   linkImg: String 検索フォーム開閉img画像を示すselector
     *   itemForm: String 画面項目設定フォームを示すselector
     *   itemLink: String 画面項目設定フォーム開閉ボタンを示すselector
     *   formSelector: String 対象となるform要素を示すselector
     *                        default: #main_contents .filter_form_input
     *   reset: String 検索項目クリアボタンを示すselector
     *   submit: String 検索実行ボタンを示すselector
     *   linkClickFunc: 検索フォーム開閉ボタンのイベントハンドラ
     *   searchResetFunc: リセットボタンのイベントハンドラ
     *   searchSubmitFunc: 検索実行ボタンのイベントハンドラ
     */
    Gridy.filterFormBindHandler = function(opts) {
      opts = $.extend({}, Gridy.filterFormBindHandler.defaults, opts);
      $(opts.link).click(opts, opts.linkClickFunc);
      $(opts.reset).click(opts, opts.searchResetFunc);
      if (opts.submit) {
        $(opts.submit).click(opts, opts.searchSubmitFunc);
      } else {
        $(opts.formSelector)
          .filter(":text, :password, :file, textarea, select")
            .change(opts, opts.searchSubmitFunc).end()
          .filter(":radio, :checkbox")
            .click(opts, opts.searchSubmitFunc).end();
        }
      return opts;
    };
    Gridy.filterFormBindHandler.defaults = {
        filterForm: "#filter_form",
        link: "#filter_form_link",
        linkImg: "#filter_form_link_btn", 
        itemForm: "#view_item_form",
        itemLink: "#view_item_edit_link",
        formSelector: "#main_contents .filter_form_input",
        reset: "#filter_form_search_reset",
        submit: "#filter_form_search_submit",
        linkClickFunc: function(event) {
          var data = event.data;
          event.preventDefault();
          if ($(data.itemForm).css("display") != "none") {
            $(data.itemLink).click();
          }
          $(data.filterForm).toggle("fast", function() {
            var btn = $(data.linkImg);
            var src = btn.attr("src");
            if ($(this).css("display") == 'none') {
              btn.attr("src", src.replace("minus\.png", "plus\.png"));
            } else {
              btn.attr("src", src.replace("plus\.png", "minus\.png"));
            }
          });
        },
        searchResetFunc: function(event) {
          event.preventDefault();
          $(event.data.formSelector)
            .filter(":text, :password, :file, input[type='hidden'], textarea, select").val("").end()
            .filter(":checked").prop("checked", false).end();
          $(event.data.filterForm + " a.cancel").click();
        },
        searchSubmitFunc: function(event) {
          if (event.data.submit) {
            event.preventDefault();
          }
          var params = {};
          $(event.data.formSelector).each(function() {
            var val = $(this).val();
            if (val != '' && val != null) {
              var formType = this.tagName.toLowerCase() == "input" ? $(this).attr("type") : "";
              if ((formType != "checkbox" && formType != "radio") || this.checked) {
                params[this.name] = val;
              }
            }
          });
          if (event.data.beforeSearchFunc != undefined) {
            event.data.beforeSearchFunc.call(this, params);
          }
          params = $.param(params);
          var url = $.url();
          var hash = url.attr("fragment") || "";
          if (hash.indexOf("?") != -1) {
            hash = hash.split("?")[0];
          }
          if (params && params.length >= 1) {
            hash += "?" + params;
          }
          location.href = url.attr("path") + "#" + hash;
        }
    };
    
    /**
     * 一覧検索FORM用イベントハンドラをunbindします。
     */
    Gridy.filterFormUnbindHandler = function(opts) {
      opts = $.extend({}, Gridy.filterFormBindHandler.defaults, opts);
      $(opts.link).off("click", opts.linkClickFunc);
      $(opts.reset).off("click", opts.searchResetFunc);
      if (opts.submit) {
        $(opts.submit).off("click", opts.searchSubmitFunc);
      } else {
        $(opts.formSelector)
          .filter(":text, :password, :file, textarea, select")
            .off("change", opts.searchSubmitFunc).end()
          .filter(":radio, :checkbox")
            .off("click", opts.searchSubmitFunc).end();
      }
      return opts;
    };
  })();
  
  // カスタム項目用検索フォーム定義
  (function() {
    /**
     * @param Array opts オプションを定義した連想配列
     *   filterDivClass: String 検索条件のグループ(1行)を保持するdivクラス名
     *     
     */
    $.fn.customForm = function(opts) {
      opts = $.extend(
        {},
        $.fn.customForm.defaults,
        this.data("custom_form_options"),
        opts);
      opts.form = this;
      this
        .find("#" + opts.filterAddButtonId)
          .click(opts, opts.addFilterEvent)
          .end()
        .find("#" + opts.clearButtonId)
          .click(opts, opts.clearFilterEvent)
          .end()
        .on("change", "select." + opts.itemSelectClass, opts, opts.changeItemEvent)
        .on("change", "select." + opts.dateChoiceClass, opts, opts.changeDateChoiceEvent)
        .on("click", "a." + opts.deleteLinkClass, opts, opts.deleteLinkEvent)
        .on("click", "a.delete_link", opts, opts.deleteItemLinkEvent)
        .find("#" + opts.searchButtonId)
          .click(opts, opts.searchEvent)
          .end();
      if (opts.useSaveSearchCondition) {
        this.find("#" + opts.saveSearchConditionButtonId)
          .click(opts, opts.saveSearchConditionEvent)
          .end();
      }
      if (opts.useSaveCompanySearchCondition) {
        this.find("#" + opts.saveCompanySearchConditionButtonId)
          .click(opts, opts.saveCompanySearchConditionEvent)
          .end();
      }
      opts._initPrevFilters();
      $("#" + opts.openLinkId).click(opts, opts.openLinkEvent);
      return this;
    };
    $.fn.unbindCustomForm = function(opts) {
      opts = $.extend({}, $.fn.customForm.defaults, opts);
      this.data("custom_items", null)
        .data("custom_form_options", null)
        .find("#" + opts.filterAddButtonId)
          .off("click", opts.addFilterEvent)
          .end()
        .find("#" + opts.clearButtonId)
          .off("click", opts.clearFilterEvent)
          .end()
        .off("change", "select." + opts.itemSelectClass, opts.changeItemEvent)
        .off("change", "select." + opts.dateChoiceClass, opts, opts.changeDateChoiceEvent)
        .off("click", "a." + opts.deleteLinkClass, opts.deleteLinkEvent)
        .off("click", "a.delete_link", opts.deleteItemLinkEvent)
        .find("#" + opts.searchButtonId)
          .off("click", opts.searchEvent)
          .end();
      if (opts.useSaveSearchCondition) {
        this.find("#" + opts.saveSearchConditionButtonId)
          .off("click", opts.saveSearchConditionEvent)
          .end();
      }
      if (opts.useSaveCompanySearchCondition) {
        this.find("#" + opts.saveCompanySearchConditionButtonId)
          .off("click", opts.saveCompanySearchConditionEvent)
          .end();
      }
      $("#" + opts.openLinkId).off("click", opts.openLinkEvent);
      $.fn.customForm.dataSelects.splice(0);
      return this;
    };
    $.fn.customForm.defaults = {
      maxFilterSize: 10,
      filterFormId: "filter_form",
      openLinkId: "filter_form_link",
      openLinkImgId: "filter_form_link_btn", 
      itemLinkId: "view_item_edit_link",
      filterDivClass: "custom_filter_div",
      itemSelectClass: "custom_filter_item_select",
      dateChoiceClass: "custom_filter_date_choice",
      dateChoiceMethodClass: "custom_filter_date_choice_method",
      conditionClass: "custom_filter_condition",
      deleteLinkClass: "custom_filter_delete",
      multiSelectClass: "custom_filter_multiselect",
      valueSelectClass: "custom_filter_value_select",
      defaultFilterSelector: ".default_filter_div .filter_form_input",
      openLinkEvent: function(event) {
        event.preventDefault();
        var $this = $(this);
        var data = event.data;
        if ($("#" + event.data.itemLinkId).data("form_open")) {
          $("#" + event.data.itemLinkId).click();
        }
        event.data.form.toggle("fast", function() {
          var btn = $("#" + data.openLinkImgId);
          var src = btn.attr("src");
          if ($(this).css("display") == 'none') {
            btn.attr("src", src.replace("minus\.png", "plus\.png"));
            $this.data("form_open", false);
          } else {
            btn.attr("src", src.replace("plus\.png", "minus\.png"));
            $this.data("form_open", true);
            if ($("div." + data.filterDivClass, data.form).size() == 0) {
              $("#" + data.filterAddButtonId, data.form).click();
            }
          }
        });
      },
      addFilterEvent: function(event) {
        event.preventDefault();
        if (!event.data.form.data("custom_items")) {
          event.data._getSelectList(
            event.data.moduleId,
            event.data.viewType,
            $.proxy(event.data._addFilter, event.data)
          );
        } else {
          event.data._addFilter();
        }
        
      },
      changeItemEvent: function(event, filter) {
        var $this = $(this);
        var item = $(this.options[this.selectedIndex]).data("item_def");
        if (item == undefined) {
          return;
        }
        var conditionSpan =
          $("<span></span>")
          .addClass(event.data.conditionClass);
        $("." + event.data.conditionClass, $this.parent()).remove();
        var itemType = item["ITEM_TYPE"];
        var cmnMSelectId = item['CMN_M_SELECT_ID'];
        var itemId = item['ITEM_ID'];
        var index = $this.parent().data("filter_index");
        if (cmnMSelectId != null &&
            item['INPUT_TYPE'] != event.data.INPUT_TYPE_CHECKLIST &&
            itemType != event.data.ITEM_TYPE_BOOLEAN) {
          event.data._getSelectInput(item, index, conditionSpan, filter);
        } else {
          switch (parseInt(itemType)) {
            case event.data.ITEM_TYPE_BOOLEAN:
              event.data._createSelectInput(item, index, conditionSpan,
                [
                  $("<option></option>")
                    .val("1")
                    .text("ON"),
                  $("<option></option>")
                    .val("0")
                    .text("OFF")
                ], 2, filter);
              break;
            case event.data.ITEM_TYPE_INT:
            case event.data.ITEM_TYPE_DECIMAL16_0:
            case event.data.ITEM_TYPE_DECIMAL17_2:
              event.data._createBetweenInput(item, index, conditionSpan);
              break;
            case event.data.ITEM_TYPE_DATETIME:
            case event.data.ITEM_TYPE_DATE:
              event.data._createDateChoiceInput(item, index, conditionSpan, filter);
              break;
            case event.data.ITEM_TYPE_VARCHAR:
            case event.data.ITEM_TYPE_TEXT:
              conditionSpan
                .append(
                  $("<input type='text' />")
                    .attr("name", "f[" + index + "][l]")
                );
              break;
            case event.data.ITEM_TYPE_RECORD:
              switch (parseInt(itemId)) {
                case event.data.ITEM_ID_PROJECT_ID:
                case event.data.ITEM_ID_CREATED_BY:
                case event.data.ITEM_ID_UPDATED_BY:
                case event.data.ITEM_ID_CMN_T_RECORD_ID:
                  event.data._getSearchInput(item, index, conditionSpan, filter);
                  break;
                case event.data.ITEM_ID_CREATED_AT:
                case event.data.ITEM_ID_UPDATED_AT:
                  event.data._createDateChoiceInput(item, index, conditionSpan, filter);
                  break;
                default:
                  break;
              }
              break;
            case event.data.ITEM_TYPE_REF:
              event.data._getSearchInput(item, index, conditionSpan, filter);
              break;
            default:
              break;
          }
        }
        conditionSpan.insertBefore($("." + event.data.deleteLinkClass, $this.parent()));
      },
      changeDateChoiceEvent: function(event) {
        var index = $(this).parent().parent().data("filter_index");
        var choice = parseInt($(this.options[this.selectedIndex]).val());
        var choiceMethodSpan =
          $("<span></span>")
          .addClass(event.data.dateChoiceMethodClass);
        $("." + event.data.dateChoiceMethodClass, $(this).parent()).remove();
        event.data._createDateChoiceMethodInput(index, choice, choiceMethodSpan);
        $(this).parent().append(choiceMethodSpan);
      },
      deleteLinkEvent: function(event) {
        event.preventDefault();
        $(this).parent().remove();
        var divSize = $("div." + event.data.filterDivClass, event.data.form).each(function(i) {
          var div = $(this);
          $(":input[name^='f\[" + div.data("filter_index") + "\]']")
            .each(function() {
              this.name = "f[" + i + "]" + this.name.substring(("f[" + div.data("filter_index") + "]").length);
              div.data("filter_index", i);
            });
        }).size();
        if (divSize < event.data.maxFilterSize) {
          $("#" + event.data.filterAddButtonId).show();
        }
      },
      clearFilterEvent: function(event) {
        event.preventDefault();
        event.data.form.find("div." + event.data.filterDivClass).remove();
        $("#" + event.data.filterAddButtonId).show();
        $(event.data.defaultFilterSelector)
          .filter(":text, :password, :file, input[type='hidden'], textarea, select").val("").end()
          .filter(":checked").prop("checked", false).end();
        // 営業報告の「報告者」を初期化する
        $("#owner_login_user").hide();
        $("#owner_last_name").removeAttr("disabled");
        $("#owner_first_name").removeAttr("disabled");
        $("#owner_id").val('');
        $("#" + event.data.filterFormId + " a.cancel").click();
      },
      deleteItemLinkEvent: function(event) {
        event.preventDefault();
        $(this).parent().remove();
      },
      searchEvent: function(event) {
        event.preventDefault();
        var form = $("<form></form>")
          .attr("method", "get");
        event.data.form
          .find(":input[name^='f\[']")
            .each(function() {
              var $this = $(this);
              var clone = $this.clone()
                .val($this.val())
                .appendTo(form);
              // IE7でnameがコピーされない
              if (clone.attr("name") == "") {
                clone.attr("name", $this.attr("name")); 
              }
            }).end()
          .find(event.data.defaultFilterSelector)
            .each(function() {
              var $this = $(this);
              if ($this.val() != undefined && $this.val() != "") {
                var clone = $this.clone()
                  .val($this.val())
                  .appendTo(form);
                // IE7でnameがコピーされない
                if (clone.attr("name") == "") {
                  clone.attr("name", $this.attr("name")); 
                }
              }
            }).end();
        if (event.data.useHashLink) {
          var url = location.href;
          url = url.replace(/#/, "");
          var _index = url.indexOf("?");
          if (_index >= 1) {
            url = url.split("?")[0];
          } else if (_index == 0) {
            url = url.substring(1);
          }
          var query = form.serialize();
          //serializeを利用すると半角空白は+にエンコードされるが、                                        
          //デコードにはdecodeURIComponentが使用されるのでそのままだと半角空白は正常にデコードされない
          //なので事前にencodeURIComponentで半角空白に対応する"%20"に変更しておく              
          query = query.replace(/\+/g, '%20');
          if (query != null && query.length >= 1) {
            url += "?" + query;
//            $("#" + event.data.saveSearchConditionButtonId).show();
//          } else {
//            $("#" + event.data.saveSearchConditionButtonId).hide();
          }
          location.href = 
            $("<a></a>").attr("href", url)
              .hashLink({selector: false})
              .attr("href");
        } else  {
          var url = event.data.sendUrl;
          if (url == undefined) {
            url = Gridy.getModulePath();
          }
          form
            .attr("action", url)
            .appendTo("body")
            .submit();
        }
        form.remove();
      },
      saveSearchConditionEvent: function(event) {
        event.preventDefault();
        $(window).saveSearchCondition({
          formId: event.data.form.attr("id"),
          messageId: event.data.messageId,
          navigationId: event.data.navigationId
        });
      },
      saveCompanySearchConditionEvent: function(event) {
        event.preventDefault();
        $(window).saveCompanySearchCondition({
          formId: event.data.form.attr("id"),
          messageId: event.data.messageId,
          navigationId: event.data.navigationId
        });
      },
      _initPrevFilters: function() {
        if (this.prevFilters == undefined) {
          return;
        }
//        $("#" + this.saveSearchConditionButtonId).show();
        this._getSelectList(
          this.moduleId,
          this.viewType,
          $.proxy(function() {
            for (var i = 0; i < this.prevFilters.length; i++) {
              var filter = this.prevFilters[i];
              var selectOption = null;
              var selectItem = null;
              var div = this._addFilter(i)
                .find("select[name='f\[" + i + "\]\[g\]']")
                  .val(filter['g'] != undefined ? filter['g'] : "")
                  .end()
                .find("select[name='f\[" + i + "\]\[i\]']")
                  .val(filter['i'])
                  .trigger("change", filter)
                  .each(function() {
                    selectOption = this.options[this.selectedIndex];
                  })
                  .end()
                .find("select[name='f\[" + i + "\]\[n\]']")
                  .val(filter['n'])
                  .end()
                .find("input[name='f\[" + i + "\]\[l\]']")
                  .val(filter['l'] != undefined ? filter['l'] : "")
                  .end()
                .find("input[name='f\[" + i + "\]\[f\]']")
                  .val(filter['f'] != undefined ? filter['f'] : "")
                  .end()
                .find("input[name='f\[" + i + "\]\[t\]']")
                  .val(filter['t'] != undefined ? filter['t'] : "")
                  .end()
                .find("input[name='f\[" + i + "\]\[d\]']")
                  .val(filter['d'] != undefined ? filter['d'] : "")
                  .end();
              if (filter['v'] != undefined) {
                if (selectOption != null) {
                  selectItem = $(selectOption).data("item_def");
                }
                if (selectItem != null && 
                    selectItem['ITEM_TYPE'] != this.ITEM_TYPE_BOOLEAN &&
                    (selectItem['CMN_M_SELECT_ID'] == null ||  selectItem['INPUT_TYPE'] == this.INPUT_TYPE_CHECKLIST)) {
                  for (var j = 0; j < filter['v'].length; j++) {
                    this._createSelectedData(
                      i, 
                      filter['v_name'] != undefined && filter['v_name'][j] != undefined ? filter['v_name'][j] : filter['v'][j],
                      filter['v'][j],
                      $("span." + this.conditionClass, div));
                  }
                }
              }
            }
          }, this)
        );
      },
      _getSelectList: function(moduleId, viewType, afterSuccess) {
        $.ajax({
          url: "/common/getCustomItems",
          data: {
            'module_id': moduleId,
            'view_type': viewType
          },
          dataType: "json",
          success: $.proxy(function(data) {
            this.form.data("custom_items", data);
            if (afterSuccess) {
              afterSuccess();
            }
          }, this)
        });
      },
      _addFilter: function(index) {
        // maxFilterSize以上は追加できない
        var cnt = $("." + this.filterDivClass, this.form).size();
        if (cnt >= this.maxFilterSize) {
          return;
        }
        var opts = this;
        if (index == undefined) {
          index = cnt;
        }
        var div = $("<div></div>")
          .addClass(this.filterDivClass)
          .data("filter_index", index)
          .insertBefore("#" + this.filterAddButtonId, this.form)
          .append(this._createOrGroupSelect(index))
          .append(
            $("<select></select")
              .addClass(this.itemSelectClass)
              .attr("name", "f[" + index + "][i]")
              .each(function() {
                opts._createItemOptions(this);
              })
          )
          .append(
            $("<select></select>")
              .attr("name", "f[" + index + "][n]")
              .append(
                $("<option></option>")
                  .val(0)
                  .text("含む")
              )
              .append(
                $("<option></option>")
                  .val(1)
                  .text("含めない")
              )
          )
          .append(
            $("<a></a>")
              .attr("href", "#")
              .text("削除")
              .addClass(this.deleteLinkClass)
              .addClass("btn_blue_f")
          )
          .find("." + this.itemSelectClass)
            .change()
            .end();
        if (cnt >= this.maxFilterSize - 1) {
          $("#" + this.filterAddButtonId).hide();
        }
        return div;
      },
      _createOrGroupSelect: function(index) {
        var select = $("<select></select>")
          .attr("name", "f[" + index + "][g]")
          .attr("title", "同じ数字を指定した検索項目がある場合、or指定でグループ化します");
        for (var i = 0; i <= 10; i++) {
          $("<option></option>")
            .val(i == 0 ? "" : i)
            .text(i== 0 ? "" : i)
            .appendTo(select);
        }
        return select;
      },
      _createItemOptions: function(select) {
        var items = this.form.data("custom_items")["items"];
        for (var i = 0; i < items.length; i++) {
          if (items[i]['REF_ID_FLAG'] == 1) {
            continue;
          }
          var text = items[i]["VIEW_DISP_NAME"];
          if (text == null) {
            text = items[i]["ITEM_DISP_NAME"];
          }
          $("<option></option>")
            .val(items[i]["COLUMN_NAME"].substring("column_".length))
            .text(text)
            .data("item_def", items[i])
            .appendTo(select);
        }
      },
      _useDatepicker: function(item) {
        return item['ITEM_TYPE'] == this.ITEM_TYPE_DATETIME || item['ITEM_TYPE']  == this.ITEM_TYPE_DATE ||
          (item['ITEM_TYPE'] == this.ITEM_TYPE_RECORD && (item['ITEM_ID'] == this.ITEM_ID_CREATED_AT || item['ITEM_ID'] ==this.ITEM_ID_UPDATED_AT));
      },
      _createBetweenInput: function(item, index, conditionSpan) {
        var opts = this;
        conditionSpan
        .append(
          $("<input type='text' />")
            .attr("name", "f[" + index + "][f]")
            .filter(function() {
              return opts._useDatepicker(item);
            }).datepicker().end()
        )
        .append(" ~ ")
        .append(
          $("<input type='text' />")
          .attr("name", "f[" + index + "][t]")
          .filter(function() {
            return opts._useDatepicker(item);
          }).datepicker().end()
        );
        
      },
      _getSearchInput: function(item, index, conditionSpan, filter) {
        var componentId = this.form.attr("id") + "_" + index;
        $.fn.customForm.dataSelects[componentId] = 
          $.proxy(
            this._dataSelect,
            $.extend({
              componentId: componentId,
              index: index,
              selectedItem: item,
              conditionSpan: conditionSpan
            }, this)
          );
        var tableId = item["REF_TABLE_ID"];
        if (item["ITEM_ID"] == this.ITEM_ID_CMN_T_RECORD_ID) {
          tableId = item["TABLE_ID"];
        }
        $.ajax({
          url: '/common/searchInput',
          data: {
            index: index,
            ref_table_id: tableId,
            componentId: componentId,
            profileModuleId: this.profileModuleId
          },
          success: function(data) {
            conditionSpan.prepend(data);
            if (filter != undefined && filter['l'] != undefined) {
              $("input[name='f\[" + index + "\]\[l\]']")
                .val(filter['l']);
            }
          }
        });
      },
      _createSelectInput: function(item, index, conditionSpan, options, size, filter) {
        var select = $("<select></select>")
          .addClass(this.valueSelectClass)
          .attr("name", "f[" + index + "][v][]")
          .attr("multiple", "multiple")
          .attr("size", size);
        for (var i = 0; i < options.length; i++) {
          select.append(options[i]);
        }
        if (filter != undefined && filter['v'] != undefined) {
          for (var i = 0; i < filter['v'].length; i++) {
            $("option[value='" + filter['v'][i] + "']", select)
              .prop("selected", true);
          }
        }
        conditionSpan.append(select);
      },
      _createDateChoiceInput: function(item, index, conditionSpan, filter) {
        // 【関連先】CmnTRecordPeer::getDateRangeFromParameter()
        
        // 日付指定方法取得
        var choice = 0;
        if (filter != undefined && filter['c'] != undefined) {
          choice = parseInt(filter['c']);
        }
        
        // 日付指定方法選択
        var select = $("<select></select>")
          .addClass(this.dateChoiceClass)
          .attr("name", "f[" + index + "][c]")
          .append($("<option></option>").val(0).text("日付範囲指定"))
          .append($("<option></option>").val(1).text("n日以前指定"))
          .append($("<option></option>").val(2).text("n日以降指定"))
          .append($("<option></option>").val(3).text("n日前指定"))
          .append($("<option></option>").val(4).text("n日後指定"))
          .append($("<option></option>").val(5).text("nヶ月前指定"))
          .append($("<option></option>").val(6).text("nヶ月後指定"))
          .append($("<option></option>").val(7).text("四半期指定"))
          .append($("<option></option>").val(8).text("半期指定"))
          .append($("<option></option>").val(9).text("年度指定"));
        $("option[value='" + choice + "']", select)
          .prop("selected", true);
        conditionSpan.prepend(select);
        
        // 日付指定方法生成
        var choiceMethodSpan =
          $("<span></span>")
          .addClass(this.dateChoiceMethodClass);
        $("." + this.dateChoiceMethodClass, $(this).parent()).remove();
        switch (choice) {
        case 5: // nヶ月前指定
          this._createDateChoiceMethodInput(index, choice, choiceMethodSpan);
          if (filter != undefined && filter['m'] != undefined) {
            $("select[name='f[" + index + "][m]'] option[value='" + filter['m'] + "']", choiceMethodSpan)
              .prop("selected", true);
          }
          break;
        case 6: // nヶ月後指定
          this._createDateChoiceMethodInput(index, choice, choiceMethodSpan);
          if (filter != undefined && filter['m'] != undefined) {
            $("select[name='f[" + index + "][m]'] option[value='" + filter['m'] + "']", choiceMethodSpan)
              .prop("selected", true);
          }
          break;
        case 7: // 四半期指定
          this._createDateChoiceMethodInput(index, choice, choiceMethodSpan);
          if (filter != undefined && filter['y'] != undefined) {
            $("select[name='f[" + index + "][y]'] option[value='" + filter['y'] + "']", choiceMethodSpan)
              .prop("selected", true);
          }
          if (filter != undefined && filter['q'] != undefined) {
            $("select[name='f[" + index + "][q]'] option[value='" + filter['q'] + "']", choiceMethodSpan)
              .prop("selected", true);
          }
          break;
        case 8: // 半期指定
          this._createDateChoiceMethodInput(index, choice, choiceMethodSpan);
          if (filter != undefined && filter['y'] != undefined) {
            $("select[name='f[" + index + "][y]'] option[value='" + filter['y'] + "']", choiceMethodSpan)
              .prop("selected", true);
          }
          if (filter != undefined && filter['h'] != undefined) {
            $("select[name='f[" + index + "][h]'] option[value='" + filter['h'] + "']", choiceMethodSpan)
              .prop("selected", true);
          }
          break;
        case 9: // 年度指定
          this._createDateChoiceMethodInput(index, choice, choiceMethodSpan);
          if (filter != undefined && filter['y'] != undefined) {
            $("select[name='f[" + index + "][y]'] option[value='" + filter['y'] + "']", choiceMethodSpan)
              .prop("selected", true);
          }
          break;
        default: // その他(日付範囲指定、n日前指定など)
          this._createDateChoiceMethodInput(index, choice, choiceMethodSpan);
          break;
        }
        conditionSpan.append(choiceMethodSpan);
      },
      _createDateChoiceMethodInput: function(index, choice, choiceMethodSpan) {
        // 年度選択
        var years = $("<select></select>")
          .addClass(this.valueSelectClass)
          .attr("name", "f[" + index + "][y]")
          .append($("<option></option>").val(-1).text("前"))
          .append($("<option></option>").val(0).text("今"))
          .append($("<option></option>").val(1).text("次"));
        $("option[value='0']", years).prop("selected", true);
        
        // 日付指定方法生成
        switch (choice) {
        case 1: // n日以前指定
          choiceMethodSpan
            .append("今日から")
            .append(
              $("<input type='text' />")
                .attr("name", "f[" + index + "][d]")
                .attr("size", 5)
            )
            .append("日以前");
          break;
        case 2: // n日以降指定
          choiceMethodSpan
            .append("今日から")
            .append(
              $("<input type='text' />")
                .attr("name", "f[" + index + "][d]")
                .attr("size", 5)
            )
            .append("日以降");
          break;
        case 3: // n日前指定
          choiceMethodSpan
            .append("今日から")
            .append(
              $("<input type='text' />")
                .attr("name", "f[" + index + "][d]")
                .attr("size", 5)
            )
            .append("日前まで");
          break;
        case 4: // n日後指定
          choiceMethodSpan
            .append("今日から")
            .append(
              $("<input type='text' />")
                .attr("name", "f[" + index + "][d]")
                .attr("size", 5)
            )
            .append("日後まで");
          break;
        case 5: // nヶ月前指定
          var month = $("<select></select>")
            .addClass(this.valueSelectClass)
            .attr("name", "f[" + index + "][m]");
          for (var m = 1; m <= 60; m++) {
            month.append(
              $("<option></option>")
                .val(m)
                .text(m)
            );
          }
          choiceMethodSpan
            .append("今月を含む")
            .append(month)
            .append("ヶ月");
          break;
        case 6: // nヶ月後指定
          var month = $("<select></select>")
            .addClass(this.valueSelectClass)
            .attr("name", "f[" + index + "][m]");
          for (var m = 1; m <= 60; m++) {
            month.append(
              $("<option></option>")
                .val(m)
                .text(m)
            );
          }
          choiceMethodSpan
            .append("今月を含む")
            .append(month)
            .append("ヶ月");
          break;
        case 7: // 四半期指定
          var quarter = $("<select></select>")
            .addClass(this.valueSelectClass)
            .attr("name", "f[" + index + "][q]");
          for (var q = 1; q <= 4; q++) {
            quarter.append(
              $("<option></option>")
                .val(q)
                .text("第" + q)
            );
          }
          choiceMethodSpan
            .append(years)
            .append("年度")
            .append(quarter)
            .append("四半期");
          break;
        case 8: // 半期指定
          var half = $("<select></select>")
            .addClass(this.valueSelectClass)
            .attr("name", "f[" + index + "][h]")
            .append($("<option></option>").val(1).text("上"))
            .append($("<option></option>").val(2).text("下"));
          choiceMethodSpan
            .append(years)
            .append("年度")
            .append(half)
            .append("半期");
          break;
        case 9: // 年度指定
          choiceMethodSpan
            .append(years)
            .append("年度");
          break;
        default: // 日付範囲指定
          choiceMethodSpan
            .append(
              $("<input type='text' />")
                .attr("name", "f[" + index + "][f]")
                .datepicker()
            )
            .append(" ~ ")
            .append(
              $("<input type='text' />")
                .attr("name", "f[" + index + "][t]")
                .datepicker()
            );
          break;
        }
        
      },
      _getSelectInput: function(item, index, conditionSpan, filter) {
        $.ajax({
          url: '/common/getSelectItems',
          data: {
            cmnMSelectId: item['CMN_M_SELECT_ID']
          },
          success: $.proxy(function(data) {
            if(item['ITEM_TYPE'] == 11){
              var allflg = $("<select></select>")
                .attr("name", "f[" + index + "][a]")
                .append(
                  $("<option></option>")
                    .val(0)
                    .text("完全一致")
                )
                .append(
                  $("<option></option>")
                    .val(1)
                    .text("部分一致")
                );
              conditionSpan.append(allflg);
              
              var _options = [];
            }else{
              var _options = [
                $("<option></option")
                  .val("null")
                  .text("(未選択)")
                  .css("color", "gray")
              ];
            }
            for (var i = 0; i < data.length; i++) {
              _options.push(
                $("<option></option>")
                  .val(data[i]['value'])
                  .text(data[i]['name'])
              );
            }
            this._createSelectInput(item, index, conditionSpan, _options, 4, filter);

            // 連携プルダウン
            if(item['ITEM_TYPE'] == 12){
              var _option = $('<option>').text("(未選択)").css("color", "gray").val('null');

              // セレクトを三つにする
              this._createSelectInput(item, index, conditionSpan, _option.clone(), 4, filter);
              this._createSelectInput(item, index, conditionSpan, _option.clone(), 4, filter);

              var selects = conditionSpan.find('select');
              // nameとindexをつける
              selects.each(function (i) {
                i++;
                $(this).attr('name', "f[" + index + "][p]["+i+"][]").data('index', i);
              });

              // 保存された検索条件がある場合,selectedを設定し
              // 右隣のセレクトの中身を取りに行かせる
              if (filter && filter.p[1]) {
                selects.eq(0).val(filter.p[1]);
                $.fn.customForm.defaults._ajaxGetOption(item, selects.eq(0), filter, _option);
              };

              // changeイベントをバインド
              conditionSpan.on('change mouseleave', 'select', function (e) {
                // 左クリックを押したままマウスをセレクトから離し、クリックを話した時にイベントが走らなかったので
                if ( e.type == 'change' || (e.type == 'mouseleave' && e.buttons == 1) ) {
                  $.fn.customForm.defaults._ajaxGetOption(item, this, false, _option); 
                };
              });
            }
          }, this)
        });
      },
      _ajaxGetOption: function (item, elem, params, _option) {
        var _option = _option || $('<option>').text("(未選択)").css("color", "gray").val('null');
        // 再帰処理判定用
        var params = params || false;

        // 選択を解除された場合
        if ( ! $(elem).val()) {
          $(elem).nextAll().children().remove().end().append(_option.clone());
        }
        // 複数選択されていた場合
        else if ($(elem).val().length > 1) {
          // 以降のセレクトの中身を選択不可にする
          $(elem).nextAll().children().remove().end().append(_option.clone().text("(選択不可)").attr('disabled', 'disabled'));
        }
        else {
          $.ajax ({
            type: 'POST',
            url: '/ks_common2/getMultipleSelectForAjax',
            data: {
              'table_id': item['TABLE_ID'],
              'item_id': item['ITEM_ID'],
              'parent_id': $(elem).val()[0]
            },
            success: $.proxy(function(data){
              data = $.parseJSON(data);
              $(elem).nextAll().children().remove().end().append(_option.clone());

              // changeイベント対象thisを格納
              var next = $(elem).next();
              // optionタグを入れる
              $.each(data['data'], function (k, v) {
                next.append("<option value="+ k +">"+ v +"</option>");
              });

              // 保存された検索条件がある場合,selectedを設定し
              // 右隣のセレクトの中身を取りに行かせる
              if (params && params.p[next.data('index')]) {
                next.val(params.p[next.data('index')]);
                $.fn.customForm.defaults._ajaxGetOption(item, next[0], params, _option);
              };
            }, elem)
          });
        }
      },
      _dataSelect: function() {
        var tableId = this.selectedItem['REF_TABLE_ID'];
        var nameJq = null;
        var idJq = null;
        if (parseInt(tableId) == this.TABLE_ID_PROJECT) {
          idJq = $("#" + this.componentId + "_project_id");
          nameJq = {
            org: $("span.groupName", idJq.parent()),
            val: function(text) {
              if (arguments.length == 0) {
                return this.org.text();
              } else {
                this.org.text(text);
                return this;
              }
            }
          };
        } else {
          nameJq = $("#ksDataId_" + this.componentId + "_name");
          idJq = $("#ksDataId_" + this.componentId);
        }
        if ($("input[name='f\[" + this.index + "\]\[v\]\[\]'][value='" + idJq.val() + "']", this.conditionSpan).size() == 0) {
          this._createSelectedData(this.index, nameJq.val(), idJq.val(), this.conditionSpan);
        }
        nameJq.val("");
        idJq.val("");
      },
      _createSelectedData: function(index, name, value, conditionSpan) {
        return $("<span></span>")
          .addClass(this.multiSelectClass)
          .append(
            $("<a></a>")
              .attr("href", "#")
              .text(name)
              .addClass("delete_link")
          )
          .append(
            $("<input type='hidden' />")
              .attr("name", "f[" + index + "][v][]")
              .val(value)
          )
          .appendTo(conditionSpan);
      }
    };
    $.fn.customForm.dataSelects = {};
  })();
  
  (function() {
    /**
     * 表示項目設定用コンポーネント初期化(カスタム項目用)
     * @param Array opts
     * @returns jQuery
     */
    $.fn.viewItemForm = function(opts) {
      opts = $.extend(
        {},
        $.fn.viewItemForm.defaults,
        this.data("view_item_form_options"),
        opts);
      opts.form = this;
      this
        .find("#" + opts.resetButtonId)
          .click(opts, opts.resetEvent)
          .end()
        .find("#" + opts.cancelButtonId)
          .click(opts, opts.cancelEvent)
          .end()
        .find("#" + opts.saveButtonId)
          .click(opts, opts.submitEvent)
          .end();
      $("#" + opts.openLinkId).click(opts, opts.openLinkEvent);
      if (this.css("display") !== undefined && this.css("display") != "none") {
        opts._initSelect(opts.CURRENT_ITEMS);
        opts._initSelect(opts.SELECTABLE_ITEMS);
      }
      return this;
    };
    /**
     * 表示項目設定用コンポーネント終了処理(カスタム項目用)
     * @param Array opts
     * @returns jQuery
     */
    $.fn.unbindViewItemForm = function(opts) {
      opts = $.extend(
        {},
        $.fn.viewItemForm.defaults,
        this.data("view_item_form_options"),
        opts);
      opts.form = this;
      this
        .find("#" + opts.resetButtonId)
          .off("click", opts.resetEvent)
          .end()
        .find("#" + opts.cancelButtonId)
          .off("click", opts.cancelEvent)
          .end()
        .find("#" + opts.saveButtonId)
          .off("click", opts.submitEvent)
          .end();
      $("#" + opts.openLinkId).off("click", opts.openLinkEvent);
      $("#" + opts.currentItemsId + ", #" + opts.selectableItemsId)
        .sortable("destroy").removeTouchEvent()
        .find("li")
          .off("mouseover", opts.mouseOverEvent)
          .off("mouseout", opts.mouseOutEvent);
      return this;
    };
    $.fn.viewItemForm.defaults = {
      SELECTABLE_ITEMS: "SELECTABLE_ITEMS",
      SELECTABLE_URL: "/common/getSelectableCustomItems",
      CURRENT_ITEMS: "CURRENT_ITEMS",
      CURRENT_URL: "/common/getCustomItems",
      openLinkId: "view_item_edit_link",
      openLinkImgId: "view_item_edit_btn", 
      filterLinkId: "filter_form_link",
      openLinkEvent: function(event) {
        event.preventDefault();
        var $this = $(this);
        var data = event.data;
        if ($("#" + event.data.filterLinkId).data("form_open")) {
          $("#" + event.data.filterLinkId).click();
        }
        event.data.form.toggle("fast", function() {
          var btn = $("#" + data.openLinkImgId);
          var src = btn.attr("src");
          if ($(this).css("display") == 'none') {
            btn.attr("src", src.replace("minus\.png", "plus\.png"));
            $this.data("form_open", false);
          } else {
            btn.attr("src", src.replace("plus\.png", "minus\.png"));
            $this.data("form_open", true);
            data._initSelect(data.CURRENT_ITEMS);
            data._initSelect(data.SELECTABLE_ITEMS);
          }
        });
      },
      mouseOverEvent: function() {
        $(this).css({"color": "#FFFFFF", "backgroundColor": "#316AC5", "cursor": "pointer"});
      },
      mouseOutEvent: function() {
        $(this).css({"color": "#000000", "backgroundColor": "transparent", "cursor": "auto"});
      },
      submitEvent: function(event) {
        event.preventDefault();
        var data = {
          module_id: event.data.moduleId,
          view_type: event.data.viewType
        };
        data["items"] = [];
        $("#" + event.data.currentItemsId + " li").each(function() {
          var $this = $(this);
          var item = $this.data("item_def");
          data["items"].push({
            "cmn_m_view_items_def_id": item['CMN_M_VIEW_ITEMS_DEF_ID'],
            "def_type": $this.data("def_type")
          });
          
        })
        $.ajax({
          type: "POST",
          url: "/common/updateViewItem",
          data: data, 
          success: function(data) {
            if (event.data.controller != undefined && event.data.controller instanceof Gridy.ViewController) {
              event.data.controller.updateView({message: "一覧設定を更新しました。"});
              event.data._initSelect(event.data.CURRENT_ITEMS, true);
              event.data._initSelect(event.data.SELECTABLE_ITEMS, true);
            } else {
              location.reload(true);
            }
          },
          error: Gridy.errorHandler
        });
      },
      resetEvent: function(event) {
        event.preventDefault();
        if (!confirm("初期状態に戻ります。\nリセットしてよろしいですか?")) {
          return;
        }
        $.ajax({
          type: "POST",
          url: "/common/resetViewItem",
          data: {
            "module_id": event.data.moduleId,
            "view_type": event.data.viewType
          },
          success: function(data) {
            if (event.data.controller != undefined && event.data.controller instanceof Gridy.ViewController) {
              event.data.controller.updateView({message: "一覧設定をリセットしました。"});
              event.data._initSelect(event.data.CURRENT_ITEMS, true);
              event.data._initSelect(event.data.SELECTABLE_ITEMS, true);
            } else {
              location.reload(true);
            }
          },
          error: Gridy.errorHandler
        });
      },
      cancelEvent: function(event) {
        event.preventDefault();
        event.data._initSelect(event.data.CURRENT_ITEMS);
        event.data._initSelect(event.data.SELECTABLE_ITEMS);
      },
      _initSelect: function(ulType, dataReload) {
        
        if (dataReload || this.form.data(ulType) == undefined) {
          this._getSelectList(
            this.moduleId,
            this.viewType,
            ulType,
            $.proxy(this._createSelect, this));
        } else {
          this._createSelect(ulType);
        }
      },
      _createSelect: function(ulType) {
        var data= this.form.data(ulType);
        if (data == undefined || data["items"] == undefined) {
          return;
        }
        var ul = null;
        if (ulType == this.CURRENT_ITEMS) {
          ul = $("#" + this.currentItemsId);
        } else if (ulType == this.SELECTABLE_ITEMS) {
          ul = $("#" + this.selectableItemsId);
        }
        if (ul == null) {
          return;
        }
        ul.empty();
        for (var i = 0; i < data["items"].length; i++) {
          var item = data["items"][i];
          var _text = item["VIEW_DISP_NAME"];
          if (_text == null || _text == "") {
            _text = item["ITEM_DISP_NAME"];
          }
          $("<li></li>")
            .attr("id", this.form.attr("id") + "_item_" + item['COLUMN_NAME'])
            .text(_text)
            .data("item_def", item)
            .data("def_type", data["def"]["DEF_TYPE"])
            .appendTo(ul);
        }
        this.form.data(ulType, data);
        ul
          .sortable("destroy")
          .sortable({
            connectWith: "#" + this.currentItemsId + ", #" + this.selectableItemsId,
            forcePlaceholderSize: true,
            deactivate: function(event, ui) {
              $("li", this).css({"color": "#000000", "backgroundColor": "transparent"});
            },
            receive: $.proxy(function(event, ui) {
              if (ulType == this.CURRENT_ITEMS && $("#" + this.currentItemsId + " li").size() > this.MAX_CURRENT_ITEM_SIZE) {
                $(ui.sender).sortable("cancel");
              }
            }, this)
          })
          .addTouchEvent()
            .find("li")
              .mouseover(this.mouseOverEvent)
              .mouseout(this.mouseOutEvent);

      },
      _getSelectList: function(moduleId, viewType, ulType, afterSuccess) {
        var url = null;
        if (ulType == this.SELECTABLE_ITEMS) {
          url = this.SELECTABLE_URL;
        } else if (ulType == this.CURRENT_ITEMS) {
          url = this.CURRENT_URL;
        }
        $.ajax({
          url: url,
          data: {
            'module_id': moduleId,
            'view_type': viewType
          },
          dataType: "json",
          success: $.proxy(function(data) {
            this.form.data(ulType, data);
            if (afterSuccess) {
              afterSuccess(ulType);
            }
          }, this)
        });
      }
    };
  })();
  (function() {
    /**
     * 検索条件保存ダイアログ表示
     * @param Array opts
     * @returns jQuery
     */
    $.fn.saveSearchCondition = function(opts) {
      opts = $.extend(
        {},
        $.fn.saveSearchCondition.defaults,
        opts);
      var $this = this;
      var isWindow = $.isWindow(this.get(0));
      var nameId = opts.formId + "_search_condition_name";
      var updateUrlId = opts.formId + "_update_url";
      var editNavigationId = opts.formId + "_edit_navigation";
      var buttons = {
        "登録": function() {
          var name = $("#" + nameId).val();
          var editNavigation = $("#" + editNavigationId);
          if ((editNavigation.size() == 0 || !editNavigation.prop("checked")) && name  == "") {
            alert("名前を入力してください");
            return;
          }
          var target = null;
          if (isWindow || $("#" + updateUrlId).prop("checked")) {
            target = location;
            url_update = true;
          } else {
            target = $this.get(0);
            url_update = false;
          }
          var url = target.href.substring((target.protocol + "//" + target.host).length);
          if (opts.useBodyUpdate) {
            if (url.indexOf(opts.bodyUpdateParam + "=1") == -1) {
              if (url.indexOf("?") == -1) {
                url += "?";
              } else {
                url += "&";
              }
              url += (opts.bodyUpdateParam + "=1");
            }
          }
          var data = {
              categoryId: $("#search_category :selected").val(),
              name: name,
              searchUrl: url,
              updateUrl: url_update
          };
          if (!isWindow) {
            data.id = $this.data("cmn_t_search_condition_id");
          }
          if (opts.navigationId != undefined && $("#" + editNavigationId).prop("checked")) {
            data.navigationId = opts.navigationId;
          }
          $.ajax({
            url: opts.saveSearchConditionUrl,
            type: "POST",
            dataType: "json",
            data: data,
            success: $.proxy(opts.updateSearchConditionsList, opts),
            error: Gridy.errorHandler
          });
          $(this).dialog("close");
        }
      };
      var name = "";
      if (!isWindow) {
        buttons["削除"] = function() {
          if (!confirm("この検索条件を削除しますか?")) {
            return;
          }
          $.ajax({
            url: opts.deleteSearchConditionUrl,
            type: "POST",
            dataType: "json",
            data: {
              id:  $this.data("cmn_t_search_condition_id")
            },
            success: $.proxy(opts.updateSearchConditionsList, opts),
            error: Gridy.errorHandler
          });
          $(this).dialog("close");
        };
        name = this.text();
      }
      var categoryRow = 
        $("<tr></tr>")
          .append(
            $("<th></th>")
              .append(
                $("<label></label>")
                  .text("カテゴリ")
              )
          )
          .append(
            $("<td></td>")
              .append(
                $("<select></select>")
                  .attr("id", "search_category")
                  .css("width", "310px")
                  .css("margin-right", "5px")
                  .append(
                    $("<option></option>")
                      .attr("value", "")
                      .text("カテゴリ無し")
                  )
              )
              .append(
                $("<a></a>")
                  .addClass("btn_blue_f")
                  .attr("href", "#")
                  .text("編集")
                  .click(function(e) {
                    e.preventDefault();
                    $.fn.editSearchCategory(opts);
                  })
              )
          );
      var formTable = 
        $("<table></table>")
        .addClass("form_table")
        .append(categoryRow)
        .append(
          $("<tr></tr>")
            .append(
              $("<th></th>")
                .append(
                  $("<label></label>")
                    .attr("for", nameId)
                    .text("名前")
                )
            )
            .append(
              $("<td></td>")
                .append(
                  $("<input type='text' />")
                    .attr("name", "name")
                    .addClass("input_long")
                    .attr("id", nameId)
                    .val(name)
                )
            )
        );
      if (!isWindow) {
        formTable
          .append(
            $("<tr></tr>")
              .append(
                $("<th></th>")
                .append(
                    $("<label></label>")
                      .attr("for", updateUrlId)
                      .text("URL")
                  )
              )
              .append(
                $("<td></td>")
                  .append(
                    $("<input type='checkbox' />")
                      .attr("name", "updateUrl")
                      .attr("id", updateUrlId)
                      .prop("checked", false)
                  )
                  .append(
                      $("<label></label>")
                        .attr("for", updateUrlId)
                        .text("現在表示中の検索条件に変更")
                  )
              )
          );
      } else if (opts.navigationId != undefined) {
        formTable
        .append(
          $("<tr></tr>")
            .append(
              $("<th></th>")
              .append(
                  $("<label></label>")
                    .attr("for", editNavigationId)
                )
            )
            .append(
              $("<td></td>")
                .append(
                  $("<input type='checkbox' />")
                    .attr("name", "editNavigation")
                    .attr("id", editNavigationId)
                    .prop("checked", false)
                    .click(function(e) {
                      $("#" + nameId).prop("disabled", e.target.checked);
                    })
                )
                .append(
                    $("<label></label>")
                      .attr("for", editNavigationId)
                      .text("この検索条件を初期表示にする")
                )
            )
        );
      }
      var saveSearchConditionDialog = $("<div></div>")
        .addClass("dialog_popup")
        .append(formTable)
        .dialog({
          title: opts.title,
          buttons: buttons,
          width: opts.width,
          height: opts.height,
          modal: true,
          close: function(){
            $(this).remove();
          },
          autoOpen: false
        });
      $.fn.updateSearchCategoryOptions($this.data("category_id"));
      saveSearchConditionDialog.dialog("open");
      return this;
    };
    $.fn.saveSearchCondition.defaults = {
      width: 500,
      height: 230,
      title: "検索条件登録",
      useBodyUpdate: true,
      bodyUpdateParam: "bodyUpdate",
      saveSearchConditionUrl: "/common/saveSearchCondition",
      searchConditionsListId: "search_conditions_side_menu",
      getSearchConditionUrl: "/common/searchConditions",
      deleteSearchConditionUrl: "/common/deleteSearchCondition",
      updateSearchConditionsList: function(data, status, xhr) {
        var opts = this;
        if (data.reload) {
          location.reload();
          return;
        }
        $("#" + opts.searchConditionsListId).load(
          opts.getSearchConditionUrl,
          {
            category: data.category ? data.category : '',
            open: true
          },
          function() {
            if (data.message != undefined && opts.messageId != undefined) {
              var message = $("#" + opts.messageId).text(data.message);
              setTimeout(function() {
                message.fadeOut("slow", function() {
                  message.text("");
                  message.show();
                });
              }, 5000);
            }
          }
        );
      }
    };
    $.fn.updateSearchCategoryOptions = function(selected) {
      $.ajax({
        url: "/common/getSearchCategories",
        type: "POST",
        dataType: "json",
        data: {},
        async: false,
        success: function(data) {
          $('#search_category').html('');
          $('#search_category').append($("<option></option>").attr("value", "").text("カテゴリ無し"));
          $.each(data, function() {
            $('#search_category').append($("<option></option>").attr("value", this.id).text(this.name));
          });
          $('#search_category').val(selected);
          return true;
        },
        error: Gridy.errorHandler
      });
    };
    $.fn.editSearchCategory = function(opts) {
      var selectedCategory = $("#search_category :selected");
      var id = selectedCategory.val();
      var defaultName = id ? selectedCategory.text() : '';
      var formTable = 
        $("<table></table>")
        .addClass("form_table")
        .append(
          $("<tr></tr>")
            .append(
              $("<th></th>")
                .append(
                  $("<label></label>")
                    .text("カテゴリ名")
                )
            )
            .append(
              $("<td></td>")
                .append(
                  $("<input type='text' />")
                    .attr("id", "search_category_name")
                    .addClass("input_long")
                    .val(defaultName)
                )
            )
        );
      var updateSearchCategoryList = function(selected) {
        var category = selected ? selected : '';
        $("#" + opts.searchConditionsListId).load(
            opts.getSearchConditionUrl, {category: category, open: true});
      };
      var buttons = {
        "登録": function() {
          var name = $("#search_category_name").val();
          if( (name == null) || (name == false)) {
            alert("カテゴリ名を入力してください");
          } else {
            $.ajax({
              url: "/common/saveSearchCategory",
              type: "POST",
              dataType: "json",
              data: {
                'id' : id,
                'name' : name
              },
              async: false,
              success: function(data, status, xhr) {
                $.fn.updateSearchCategoryOptions(data.id);
                updateSearchCategoryList(data.id);
              },
              error: Gridy.errorHandler
            });
            $(this).dialog("close");
          }
        }
      };
      if(id) {
        buttons["削除"] = function() {
          if(!confirm("このカテゴリを削除しますか?")) {
            return;
          }
          $.ajax({
            url: "/common/deleteSearchCategory",
            type: "POST",
            dataType: "json",
            data: {
              'id' : id
            },
            async: false,
            success: function(data, status, xhr) {
              $.fn.updateSearchCategoryOptions();
              updateSearchCategoryList();
            },
            error: Gridy.errorHandler
          });
          $(this).dialog("close");
        }
      }
      $("<div></div>")
        .addClass("dialog_popup")
        .append(formTable)
        .dialog({
          title: "カテゴリ登録",
          buttons: buttons,
          width: 500,
          height: 150,
          modal: true,
          close: function(){
            $(this).remove();
          }
        });
    };
  })();


  (function() {
    /**
     * 共有絞り込み条件保存ダイアログ表示
     * @param Array opts
     * @returns jQuery
     */
    $.fn.saveCompanySearchCondition = function(opts) {
      opts = $.extend(
        {},
        $.fn.saveCompanySearchCondition.defaults,
        opts);
      var $this = this;
      var isWindow = $.isWindow(this.get(0));
      var nameId = opts.formId + "_search_condition_name";
      var updateUrlId = opts.formId + "_update_url";
      var editNavigationId = opts.formId + "_edit_navigation";

      var saveCompanySearchConditionFunction = function() {
        var name = $("#" + nameId).val();
        var editNavigation = $("#" + editNavigationId);
        if ((editNavigation.size() == 0 || !editNavigation.prop("checked")) && name.trim() == "") {
          alert("名前を入力してください");
          return;
        }
        var target = null;
        if (isWindow || $("#" + updateUrlId).prop("checked")) {
          target = location;
          url_update = true;
        } else {
          target = $this.get(0);
          url_update = false;
        }
        var url = target.href.substring((target.protocol + "//" + target.host).length);
        if (opts.useBodyUpdate) {
          if (url.indexOf(opts.bodyUpdateParam + "=1") == -1) {
            if (url.indexOf("?") == -1) {
              url += "?";
            } else {
              url += "&";
            }
            url += (opts.bodyUpdateParam + "=1");
          }
        }
        var data = {
            categoryId: $("#company_search_category :selected").val(),
            name: name.trim(),
            searchUrl: url,
            updateUrl: url_update
        };
        if (!isWindow) {
          data.id = $this.data("cmn_t_search_condition_id");
        }
        if (opts.navigationId != undefined && $("#" + editNavigationId).prop("checked")) {
          data.navigationId = opts.navigationId;
        }
        $.ajax({
          url: opts.saveCompanySearchConditionUrl,
          type: "POST",
          dataType: "json",
          data: data,
          success: $.proxy(opts.updateCompanySearchConditionsList, opts),
          error: function(data, status, xhr) {
            Gridy.errorHandler(data, status, xhr);
            $.proxy(opts.updateCompanySearchConditionsList, opts, data, status, xhr)();
          }
        });
        $(this).dialog("close");
      }
      if(isWindow) {
        condition_dialog_title  = '共有絞り込み条件登録';
        condition_dialog_height = 200;
        var buttons = { "新規登録": saveCompanySearchConditionFunction };
      } else {
        condition_dialog_title = '共有絞り込み条件編集';
        condition_dialog_height = 235;
        var buttons = { "更新": saveCompanySearchConditionFunction };
      }

      var name = "";
      if (!isWindow) {
        buttons["削除"] = function() {
          if (!confirm("この共有絞り込み条件を削除しますか?")) {
            return;
          }
          $.ajax({
            url: opts.deleteCompanySearchConditionUrl,
            type: "POST",
            dataType: "json",
            data: {
              id:  $this.data("cmn_t_search_condition_id")
            },
            success: $.proxy(opts.updateCompanySearchConditionsList, opts),
            error: function(data, status, xhr) {
              Gridy.errorHandler(data, status, xhr);
              $.proxy(opts.updateCompanySearchConditionsList, opts, data, status, xhr)();
            }
          });
          $(this).dialog("close");
        };
        name = this.text();
      }
      var categoryRow = 
        $("<tr></tr>")
          .append(
            $("<th></th>")
              .append(
                $("<label></label>")
                  .text("カテゴリ")
              )
          )
          .append(
            $("<td></td>")
              .append(
                $("<select></select>")
                  .attr("id", "company_search_category")
                  .css("width", "310px")
                  .css("margin-right", "5px")
                  .append(
                    $("<option></option>")
                      .attr("value", "0")
                      .text("全社共有")
                  )
              )
              .append(
                $("<a></a>")
                  .addClass("btn_blue_f")
                  .attr("href", "#")
                  .text("編集")
                  .click(function(e) {
                    e.preventDefault();
                    $.fn.editCompanySearchCategory(opts);
                  })
              )
          );
      var formTable = 
        $("<table></table>")
        .addClass("form_table")
        .append(categoryRow)
        .append(
          $("<tr></tr>")
            .append(
              $("<th></th>")
                .append(
                  $("<label></label>")
                    .attr("for", nameId)
                    .text("名前")
                )
            )
            .append(
              $("<td></td>")
                .append(
                  $("<input type='text' />")
                    .attr("name", "name")
                    .addClass("input_long")
                    .attr("id", nameId)
                    .val(name)
                )
            )
        );
      if (!isWindow) {
        formTable
          .append(
            $("<tr></tr>")
              .append(
                $("<th></th>")
                .append(
                    $("<label></label>")
                      .attr("for", updateUrlId)
                      .text("URL")
                  )
              )
              .append(
                $("<td></td>")
                  .append(
                    $("<input type='checkbox' />")
                      .attr("name", "updateUrl")
                      .attr("id", updateUrlId)
                      .prop("checked", false)
                  )
                  .append(
                      $("<label></label>")
                        .attr("for", updateUrlId)
                        .text("現在表示中の絞り込み条件に変更")
                  )
              )
          );
      }
      var saveCompanySearchConditionDialog = $("<div></div>")
        .addClass("dialog_popup")
        .append(formTable)
        .dialog({
          title: condition_dialog_title,
          buttons: buttons,
          width: opts.width,
          height: condition_dialog_height,
          modal: true,
          close: function(){
            $(this).remove();
          },
          autoOpen: false
        });
      $.fn.updateCompanySearchCategoryOptions($this.data("category_id"));
      saveCompanySearchConditionDialog.dialog("open");
      return this;
    };
    $.fn.saveCompanySearchCondition.defaults = {
      width: 500,
      useBodyUpdate: true,
      bodyUpdateParam: "bodyUpdate",
      saveCompanySearchConditionUrl: "/common/saveCompanySearchCondition",
      companySearchConditionsListId: "company_search_conditions_side_menu",
      getCompanySearchConditionUrl: "/common/companySearchConditions",
      deleteCompanySearchConditionUrl: "/common/deleteCompanySearchCondition",
      updateCompanySearchConditionsList: function(data, status, xhr) {
        var opts = this;
        if (data.reload) {
          location.reload();
          return;
        }
        $("#" + opts.companySearchConditionsListId).load(
          opts.getCompanySearchConditionUrl,
          {
            category: data.category !== null ? data.category : '',
            open: true
          },
          function() {
            if (data.message != undefined && opts.messageId != undefined) {
              var message = $("#" + opts.messageId).text(data.message);
              setTimeout(function() {
                message.fadeOut("slow", function() {
                  message.text("");
                  message.show();
                });
              }, 5000);
            }
          }
        );
      }
    };
    $.fn.updateCompanySearchCategoryOptions = function(selected) {
      $.ajax({
        url: "/common/getCompanySearchCategories",
        type: "POST",
        dataType: "json",
        data: {},
        async: false,
        success: function(data) {
          $('#company_search_category').html('');
          $('#company_search_category').append($("<option></option>").attr("value", "0").text("全社共有"));
          $.each(data, function() {
            $('#company_search_category').append($("<option></option>").attr("value", this.id).text(this.name));
          });
          $('#company_search_category').val(selected);
          return true;
        },
        error: Gridy.errorHandler
      });
    };
    $.fn.getDivisionSelectForAjax = function() {
      $.ajax({
        type: 'post',
        url: '/ks_common2/getDivisionSelectForAjax',
        data: {
          'componentKey': 'category_project',
          'multiSelect': 1,
          'multiSelectMaxLength': 100,
          'popupModuleValue': 'popupModuleValue'
        },
        dataType: 'html',
        async: true,
        success: function(html) {
          $('#projectSelectArea_category_project').append(html);

          // 共有カテゴリの閲覧可能部署を取得
          $.ajax({
            type: 'post',
            url: '/ks_common2/getDivisionSelectCategoryForAjax',
            data: {
              'category_id' : $("#company_search_category :selected").val(),
              'componentKey': 'category_project'
            },
            dataType: 'html',
            async: true,
            success: function(html) {
              if (html !== '') {
                $('#projectSelectArea_category_project').find('.groupName').append(html);
              }
              if ($('#category_company_loading_div') !== undefined) {
                $('#category_company_loading_div').remove();
              }
            }
          });
        }
      });
    };
    $.fn.editCompanySearchCategory = function(opts) {
      var selectedCategory = $("#company_search_category :selected");
      var id = selectedCategory.val();
      var defaultName = (id !== '0') ? selectedCategory.text() : '';
      var formTable = 
        $("<table></table>")
        .addClass("form_table")
        .append(
          $("<tr></tr>")
            .append(
              $("<th></th>")
                .append(
                  $("<label></label>")
                    .text("カテゴリ名")
                )
            )
            .append(
              $("<td></td>")
                .append(
                  $("<input type='text' />")
                    .attr("id", "company_search_category_name")
                    .addClass("input_long")
                    .val(defaultName)
                )
            )
        )
        .append(
          $("<tr></tr>")
            .append(
              $("<th></th>")
                .append(
                  $("<label></label>")
                    .text("閲覧可能部署")
                )
            )
            .append(
              $("<td></td>")
                .append(
                  $("<span></span>")
                    .attr("id", "projectSelectArea_category_project")
                    .text($.fn.getDivisionSelectForAjax())
                )
            )
        );

      var updateCompanySearchCategoryList = function(selected) {
        var category = selected ? selected : '';
        $("#" + opts.companySearchConditionsListId).load(
            opts.getCompanySearchConditionUrl, {category: category, open: true});
      };
      var saveCompanySearchCategory = function() {
        var name = $("#company_search_category_name").val();
        if( (name == null) || (name == false) || name.trim() === '') {
          alert("カテゴリ名を入力してください");
        } else if (name.trim() === '全社共有') {
          alert("カテゴリ名に「全社共有」は使用できません。別のカテゴリ名を指定してください。");
        } else {
          project_ids = new Array();
          $('[name="divisions_category_project\[\]"]').each(function () {
            project_ids.push($(this).val());
          });
          $.ajax({
            url: "/common/saveCompanySearchCategory",
            type: "POST",
            dataType: "json",
            data: {
              'id' : id,
              'name' : name.trim(),
              'relational_project_ids' : project_ids
            },
            async: false,
            success: function(data, status, xhr) {
              $.fn.updateCompanySearchCategoryOptions(data.id);
              updateCompanySearchCategoryList(data.id);
            },
            error: function(data, status, xhr) {
              Gridy.errorHandler(data, status, xhr);
              $.fn.updateCompanySearchCategoryOptions(0);
              updateCompanySearchCategoryList(0);
            }
          });
          $(this).dialog("close");
        }
      };

      if(id !== '0') {
        dialog_title = '共有カテゴリ編集';
        var buttons = { "更新": saveCompanySearchCategory };
      } else {
        dialog_title = '共有カテゴリ新規登録';
        var buttons = { "新規登録": saveCompanySearchCategory };
      }

      if(id !== '0') {
        buttons["削除"] = function() {
          if(!confirm("このカテゴリを削除すると、このカテゴリに所属する全ての共有絞り込み条件も削除します。\nカテゴリを削除しますか?")) {
            return;
          }
          $.ajax({
            url: "/common/deleteCompanySearchCategory",
            type: "POST",
            dataType: "json",
            data: {
              'id' : id
            },
            async: false,
            success: function(data, status, xhr) {
              $.fn.updateCompanySearchCategoryOptions(0);
              updateCompanySearchCategoryList(0);
            },
            error: function(data, status, xhr) {
              Gridy.errorHandler(data, status, xhr);
              $.fn.updateCompanySearchCategoryOptions(0);
              updateCompanySearchCategoryList(0);
            }
          });
          $(this).dialog("close");
        }
      }
      // カテゴリ登録編集ダイアログ表示
      $("<div></div>")
        .addClass("dialog_popup")
        .append(formTable)
        .append(
          $("<div></div>")
            .attr("id","category_company_loading_div")
            .addClass("ajax_data_loading")
            )
        .dialog({
          title: dialog_title,
          buttons: buttons,
          width: 500,
          height: 200,
          modal: true,
          close: function(){
            $(this).remove();
          }
        })
        // 追加された部署のaタグを、クリックすると消去するイベントを追加
        .on('click', 'span .delete_link', function (e) {
          $.fn.customForm.defaults.deleteItemLinkEvent.call(this, e);
        });
    };
  })();


  // ハッシュによる画面遷移
  (function() {
    var hashChangeHandler = null;
    var hashChangeSetIntervalId = null;
    /**
     * window.onhashchangeを使用して、URLのhashが変更されたタイミングで
     * イベントハンドラを実行します
     * hashchange非対応のブラウザでは、setInterval処理を行います。
     * @param handler Function hashが変更されるタイミングで実行される関数
     * @param eventData: Object jQueryのeventDataとしてハンドラに渡される連想配列
     *                         handlerの引数eventのdataプロパティで取得可能
     * 
     */
    Gridy.hashChangeBindHandler = function(handler, eventData) {
      hashChangeHandler = handler;
      if (Gridy.isHashChangeEnable()) {
        if (eventData) {
          $(window).on("hashchange", eventData, handler);
        } else {
          $(window).on("hashchange", handler);
        }
      } else {
        var url = $.url();
        var currentHash = url.attr("fragment");
        hashChangeSetIntervalId = setInterval(function() {
          var url = $.url();
          if (url.attr("fragment") != currentHash) {
            currentHash = url.attr("fragment");
            if (eventData) {
              handler({data: eventData});
            } else {
              handler();
            }
          }
        }, 100);
      }
    };
    /**
     * window.onhashchangeイベントのunbind処理を行う関数
     */
    Gridy.hashChangeUnbindHandler = function() {
      if (Gridy.isHashChangeEnable()) {
        $(window).off("hashchange", hashChangeHandler);
      } else {
        clearInterval(hashChangeSetIntervalId);
      }
    };
  })();

  // 画面項目設定コンポーネント
  (function() {
    var viewItemEditLinkHandler = function(event) {
      event.preventDefault();
      if ($("#filter_form").css("display") != "none") {
        $("#filter_form_link").click();
      }
      updateList(
        $("#viewItem_cmnMNavigationId").val(),
        $("#viewItem_viewType").val(),
        $("#viewItem_groupId").val(),
        "",
        false,
        function() {
        $("#view_item_form").toggle("fast", function() {
          var btn = $("#view_item_edit_btn");
          var src = btn.attr("src");
          if ($(this).css("display") == 'none') {
            btn.attr("src", src.replace("minus\.png", "plus\.png"));
            if (Gridy.isTouchPanel()) {
              $("div.list_table_div").show();
            }
          } else {
            btn.attr("src", src.replace("plus\.png", "minus\.png"));
            if (Gridy.isTouchPanel()) {
              $("div.list_table_div").hide();
            }
          }
        });
      });
    };
    
    var mouseOverHandler = function() {
      $(this).css({"color": "#FFFFFF", "backgroundColor": "#316AC5", "cursor": "pointer"});
    };
    
    var mouseOutHandler = function() {
      $(this).css({"color": "#000000", "backgroundColor": "transparent", "cursor": "auto"});
    };
    
    var updateList = function(navigationId, viewType, groupId, message, forceLoad, afterFunc) {
      if (message == undefined) {
        message = "";
      }
      if (!forceLoad && updateList.loaded) {
        $("#viewItem_message").text(message);
        if (afterFunc != undefined) {
          afterFunc();
        }
        return;
      }
      $.getJSON(
        "/view_setting/viewItem",
        {'cmn_m_navigation_id': navigationId,
          'view_type': viewType,
          'group_id': groupId},
        function(data) {
          if (data != null && data !== '') {
            var maxLength = data['selectableItems'].length + data['currentItems'].length;
            var selectableItems = 
              $("#viewItem_selectableItems")
                .empty()
                .css("minHeight", maxLength + "em");
            for (var i = 0; i < data['selectableItems'].length; i++) {
              $("<li></li>").attr("id", "item_" + data["selectableItems"][i]["id"])
                .text(data["selectableItems"][i]["itemName"])
                .appendTo(selectableItems);
            }
            var currentItems = 
              $("#viewItem_currentItems")
                .empty()
                .css("minHeight", maxLength + "em");
            for (var i = 0; i < data['currentItems'].length; i++) {
              var li = $("<li></li>")
                .attr("id", "item_" + data["currentItems"][i]["id"])
                .text(data["currentItems"][i]["itemName"]);
              if (data['currentItems'][i]["essentialFlag"] == "1") {
                li.addClass("essential")
                  .append("<span class='message'>*</span>");
              }
              li.appendTo(currentItems);
              
            }
            $("#viewItem_currentItems, #viewItem_selectableItems").find("li")
              .mouseover(mouseOverHandler)
              .mouseout(mouseOutHandler);
            $("#viewItem_message").text(message);
            if (afterFunc != undefined) {
              afterFunc();
            }
            updateList.loaded = true;
          }
        }
      );
    };
    updateList.loaded = false;
    
    /**
     *  画面項目設定FORM用イベントハンドラを定義します
     * @param viewUpdateFunc Function 画面項目設定後、一覧画面更新処理を行う関数
     *                       Gridy.ViewControllerオブジェクトが渡された場合
     *                       Gridy.ViewController.updateViewメソッドを実行
     */
    Gridy.viewSettingBindHandler = function(viewUpdateFunc) {
      
      $("#view_item_edit_link").click(viewItemEditLinkHandler);
      $("#viewItem_currentItems, #viewItem_selectableItems")
        .sortable({
          connectWith: "#viewItem_currentItems, #viewItem_selectableItems",
          forcePlaceholderSize: true,
          deactivate: function(event, ui) {
            $("li", this).css({"color": "#000000", "backgroundColor": "transparent"});
          },
          receive: function(event, ui) {
            var className = $(ui.item).attr("class");
            if (className && className.indexOf("essential") != -1) {
              $(ui.sender).sortable("cancel");
            }
          }
        }).addTouchEvent()
        .find("li")
          .mouseover(mouseOverHandler)
          .mouseout(mouseOutHandler);
      $("#viewItem_doReset").click(function(event) {
        event.preventDefault();
        if (!confirm("初期状態に戻ります。\nリセットしてよろしいですか?")) {
          return;
        }
        var navigationId = $("#viewItem_cmnMNavigationId").val();
        var viewType = $("#viewItem_viewType").val();
        var groupId = $("#viewItem_groupId").val();
        $.ajax({
          type: "POST",
          url: "/view_setting/reset",
          data: {
            "cmn_m_navigation_id": navigationId,
            "view_type": viewType,
            "group_id": groupId
          },
          success: function(data) {
            if (viewUpdateFunc != undefined) {
              updateList.loaded = false;
              updateList(navigationId, viewType, groupId, "", true);
              if (viewUpdateFunc instanceof Gridy.ViewController) {
                viewUpdateFunc.updateView({message: "一覧設定をリセットしました。"});
              } else if ($.isFunction(viewUpdateFunc)){
                viewUpdateFunc({message: "一覧設定をリセットしました。"});
              }
            } else {
              updateList(navigationId, viewType, groupId, "一覧設定をリセットしました。", true);
            }
          },
          error: function() {
            updateList(navigationId, viewType, groupId, "一覧設定リセット処理時にエラーが発生しました。", true);
          }
        });
      });
      $("#viewItem_doCancel").click(function(event) {
        event.preventDefault();
        updateList(
          $("#viewItem_cmnMNavigationId").val(),
          $("#viewItem_viewType").val(),
          $("#viewItem_groupId").val(),
          "", true);
      });
      $("#viewItem_doSave").click(function(event) {
        event.preventDefault();
        var navigationId = $("#viewItem_cmnMNavigationId").val();
        var viewType = $("#viewItem_viewType").val();
        var groupId = $("#viewItem_groupId").val();
        var ids = "[";
        $("#viewItem_currentItems li").each(function() {
          if (this.id.substring(0, 5) == "item_") {
            if (ids != "[") {
              ids = ids + ",";
            }
            ids = ids + this.id.substring(5);
          }
        })
        ids = ids + "]";
        $.ajax({
          type: "POST",
          url: "/view_setting/regist",
          data: {
            "item_ids": ids,
            "cmn_m_navigation_id": navigationId,
            "view_type": viewType,
            "group_id": groupId}, 
          success: function(data) {
            if (viewUpdateFunc != undefined) {
              if (viewUpdateFunc instanceof Gridy.ViewController) {
                viewUpdateFunc.updateView({message: "一覧設定を更新しました。"});
              } else if ($.isFunction(viewUpdateFunc)){
                viewUpdateFunc({message: "一覧設定を更新しました。"});
              }
            } else {
              updateList(navigationId, viewType, groupId, "一覧設定を更新しました。", true);
            }
          },
          error: function() {
            updateList(navigationId, viewType, groupId, "一覧設定更新時にエラーが発生しました。", true);
          }
        });
      });
      
    };
    /**
     * 画面項目設定FORM用イベントハンドラのunbind処理を行います。
     */
    Gridy.viewSettingUnbindHandler = function() {
      $("#view_item_edit_link").off("click", viewItemEditLinkHandler);
      $("#viewItem_currentItems, #viewItem_selectableItems")
        .sortable("destroy").removeTouchEvent()
        .find("li")
          .off("mouseover", mouseOverHandler)
          .off("mouseout", mouseOutHandler);
      updateList.loaded = false;
    };
  })();

  /**
   * hashchangeイベントを使用して画面遷移を行うクラス
   * @param opts Object
   *   body: 更新対象となるElementを示すSelector default #main_contents
   *   referrer: 初期画面表示時のreferrer
   *   hashchangeOpts: window.hashchangeイベントハンドラに渡すオプション
   *   actions: 画面表示処理を行うActionオブジェクトを保持する配列
   *   hashLinkOpts: 画面更新後に実行されるjQueryのhashLinkプラグイン実行時に渡すオプション
   *   defaultActionClass getNewActionメソッドでオブジェクト作成するときのデフォルトコンストラクタ default Gridy.Action
   *   title: default 初期表示時の画面タイトル
   *   beforeTriggers: hashchangeイベント実行前に呼び出すカスタムイベント情報
   */
  Gridy.ViewController = function(opts) {
    this.actions = [];
    this.hashchangeOpts = {
      useBodyUpdateParam: true
    };
    $.extend(this, Gridy.ViewController.defaults, opts);
    this.previousAction = null;
    this.previousUrl = null;
    this.previousMainAction = null;
    this.beforeTriggers = [];
  };
  Gridy.ViewController.defaults = {
    body: "#main_contents",
    referrer: document.referrer
  };
  /**
   * 引数のURLに対応するActionオブジェクトを取得します。
   * @param url string
   * @param bodyUpdate bodyで定義したElement全体を更新する場合true
   * @returns Gridy.Action
   */
  Gridy.ViewController.prototype.getAction = function(url, bodyUpdate) {
    var link = $.url(url);
    for (var i = 0; i < this.actions.length; i++) {
      var action = this.actions[i];
      action = action.getAction(link, bodyUpdate);
      if (action) {
        return action;
      }
    }
    return null;
  };
  /**
   * 画面の更新処理を行います
   * @params opts Object
   *   message: 更新完了時に表示するメッセージ
   *   bodyUpdate: trueの場合、bodyで指定したElement全体を更新する default false
   *   useBodyUpdateParam: trueの場合、GETパラメータによってbodyで指定したElement全体を更新する default false
   *   bodyUpdateParam: useBodyUpdateParamで使用するパラメータ名 default bodyUpdate
   * @returns Gridy.ViewController
   */
  Gridy.ViewController.prototype.updateView = function(opts) {
    opts = $.extend({},
      Gridy.ViewController.prototype.updateView.defaults, opts);
    var url = location.href;
    if (url && url.indexOf("#") != -1) {
      url = url.replace(/#/, "");
    }
    if (opts.useBodyUpdateParam) {
      var params = $.url(url);
      if (params.param(opts.bodyUpdateParam)) {
        opts.bodyUpdate = true;
        url = url.replace("?" + opts.bodyUpdateParam + "=1&", "?");
        url = url.replace("?" + opts.bodyUpdateParam + "=1", "");
        url = url.replace("&" + opts.bodyUpdateParam + "=1", "");
      }
    }
    
    var action = this.getAction(url, opts.bodyUpdate);
    if (!action) {
      return this;
    }
    action.ajax(url, opts);
    return this;
  };
  Gridy.ViewController.prototype.updateView.defaults = {
    message: "",
    bodyUpdate: false,
    useBodyUpdateParam: false,
    bodyUpdateParam: "bodyUpdate"
  };
  
  Gridy.ViewController.prototype._addAction = function(action) {
    action.parentAction = null;
    action.controller = this;
    return this;
  };
  /**
   * 画面表示を行うActionを追加します
   * @param action Gridy.Action Actionオブジェクト
   * @returns Gridy.ViewController
   */
  Gridy.ViewController.prototype.addAction = function(action) {
    this.actions.push(action);
    return this._addAction(action);
  };
  /**
   * 画面表示を行うActionを追加します
   * actionsの先頭に追加します
   * @param Gridy.Action action Actionオブジェクト
   * @returns Gridy.ViewController
   */
  Gridy.ViewController.prototype.unshiftAction = function(action) {
    this.actions.unshift(action);
    return this._addAction(action);
  }
  
  /**
   * 引数で指定したActionをcontrollerに追加して返します
   * 初期化用オプションを引数に指定した場合、Gridy.Actionクラスのオブジェクトを作成して返します。
   * @param action Gridy.Action Actionオブジェクト
   * @returns Gridy.Action
   */
  Gridy.ViewController.prototype.getNewAction = function(action) {
    if (!(action instanceof Gridy.Action)) {
      action = new this.defaultActionClass(action);
    }
    this.addAction(action);
    return action;
  };
  /**
   * 引数で渡したActionをcontrollerから削除します
   * @param action Gridy.Action Actionオブジェクト
   * @returns Gridy.ViewController
   */
  Gridy.ViewController.prototype.removeAction = function(action) {
    for (var i = 0; i < this.actions.length; i++) {
      if (action == this.actions[i]) {
        this.actions.splice(i, 1);
        i--;
      }
    }
    action.controller = null;
    return this;
  };
  
  /**
   * 初期処理を行います。
   * @returns Gridy.ViewController
   */
  Gridy.ViewController.prototype.init = function() {
    if (this.title === undefined) {
      this.title = document.title;
    }
    Gridy.hashChangeBindHandler($.proxy(function(event) {
      for (var i = 0; i < this.beforeTriggers.length; i++) {
        var trigger = this.beforeTriggers[i];
        $(trigger.selector).trigger(trigger.eventType, trigger.args);
        if (trigger.ontime) {
          this.beforeTriggers.splice(i, 1);
          i--;
        }
      }
      this.hashchangeOpts.event = event;
      this.updateView(this.hashchangeOpts);
      this.hashchangeOpts.event = null;
      this.hashchangeOpts.message = "";
    }, this));
    this.updateView(this.hashchangeOpts);
    return this;
  };
  
  /**
   * Gridy.ViewControllerにセットして、URLに対応する画面表示処理を行うActionクラス
   * @params opts Object
   *   path: RegExp このActionが呼ばれるURLのpathを示す正規表現
   *   dataType: サーバから取得するデータのタイプ default "html"
   *   handlers: 画面更新後にイベントハンドラ設定処理を行うGridy.Handlerオブジェクトの配列
   *   childActions: このActionが描写した画面の一部のみを更新する子Actionオブジェクトの配列
   *   (例)一覧表示を行うActionに、ページングのテーブルのみ更新するActionをchildActionとして設定する
   *   parentAction: このActionが子Actionの場合、親Actionを設定する
   *   hashLinkOpts: 画面更新後に実行されるjQueryのhashLinkプラグイン実行時に渡すオプション
   *   messageSelector: メッセージを表示するElementを示すSelector default #message
   *   defaultChildActionClass getNewChildActionメソッドでオブジェクト作成するときのデフォルトコンストラクタ default Gridy.PageAction
   *   title: 画面更新後に表示するtitle文字列 設定しない場合変更しない
   *   scrollTop: 画面更新時に画面をトップにスクロールする default false
   */
  Gridy.Action = function(opts) {
    this.handlers = [];
    this.childActions = [];
    this.parentAction = null;
    this.hashLinkOpts = null;
    $.extend(this, this.getDefaults(), opts);
  };
  Gridy.Action.defaults = {
    dataType: "html",
    messageSelector: "#message",
    scrollTop: false
  };
  Gridy.ViewController.defaults.defaultActionClass = Gridy.Action;
  
  /**
   * 初期化時設定のデフォルト値を保持するオブジェクトを返します。
   * @returns Object
   */
  Gridy.Action.prototype.getDefaults = function() {
    return Gridy.Action.defaults;
  };
  /**
   * このActionが画面更新を行った後にイベントハンドラの設定を行うGridy.Handlerオブジェクトを追加します
   * @params Gridy.Handler
   *   Functionオブジェクトが第一引数の場合、
   *   Function handler, Function unbindHandler, Object args, Object unbindArgs, Object context
   *   の引数を順に取得し、Gridy.StaticHandlerオブジェクトを自動作成して登録します
   * @returns Gridy.Action
   */
  Gridy.Action.prototype.addHandler = function(handler) {
    if (handler instanceof Gridy.Handler) {
      this.handlers.push(handler);
    } else if ($.isFunction(handler)) {
      var unbindHandler = null;
      var args = null;
      var unbindArgs = null;
      var context = null;
      if (arguments.length >= 2) {
        unbindHandler = arguments[1];
        if (arguments.length >= 3) {
          args = arguments[2];
          if (arguments.length >= 4) {
            unbindArgs = arguments[3];
            if (arguments.length >= 5) {
              context = arguments[4];
            }
          }
        }
      }
      this.handlers.push(new Gridy.StaticHandler(
        handler, unbindHandler, args, unbindArgs, context
      ));
    }
    return this;
  };
  
  /**
   * このアクションから引数で渡したHandlerオブジェクトを削除します。
   * @param handler Gridy.Handler
   * @return Gridy.Action
   */
  Gridy.Action.prototype.removeHandler = function(handler) {
    for (var i = 0; i < this.handlers.length; i++) {
      if (handler == this.handlers[i]) {
        this.handlers.splice(i, 1);
        i--;
      }
    }
    return this;
  };
  
  Gridy.Action.prototype._addAction = function(action) {
    action.parentAction = this;
    action.controller = this.controller;
    action.messageSelector = this.messageSelector;
    action.hashLinkOpts = this.hashLinkOpts;
    action.title = this.title;
    return this;
  }
  /**
   * このActionが描写した画面の一部のみを更新するActionオブジェクトを追加します
   * @param Gridy.Action action
   * @returns Gridy.Action
   */
  Gridy.Action.prototype.addChildAction = function(action) {
    this.childActions.push(action);
    return this._addAction(action);
  };
  
  /**
   * このActionが描写した画面の一部のみを更新するActionオブジェクトを追加します
   * childActionsの先頭にactionが追加されます
   * @param Gridy.Action action
   * @returns Gridy.Action
   */
  Gridy.Action.prototype.unshiftChildAction = function(action) {
    this.childActions.unshift(action);
    return this._addAction(action);
  };
  
  /**
   * 引数に指定したActionを子アクションとして追加して返します。
   * 初期化用オプションを引数に指定した場合、Gridy.PageActionクラスのオブジェクトを作成して返します。
   * @param Gridy.Action
   * @returns Gridy.Action
   */
  Gridy.Action.prototype.getNewChildAction = function(action) {
    if (!(action instanceof Gridy.Action)) {
      action = new this.defaultChildActionClass(action);
    }
    this.addChildAction(action);
    return action;
  };
  
  /**
   * このActionから引数に渡した子Actionオブジェクトを削除します。
   * @param action Gridy.Action
   * @returns Gridy.Action
   */
  Gridy.Action.prototype.removeChildAction = function(action) {
    for (var i = 0; i < this.childActions.length; i++) {
      if (action == this.childActions[i]) {
        this.childActions.splice(i, 1);
        action.parentAction = null;
        action.controller = null;
        i--;
      }
    }
    return this;
  };
  
  /**
   * 引数のURLに対応するActionオブジェクトを返します。自身または子Actionが対象外の場合nullを返します。
   * @param link Object jQuery URL Parserで作成された更新先URLを示すObject
   * @param bodyUpdate boolean trueの場合、Gridy.ViewController.bodyで指定したElement全体を更新します。
   *   default false
   * @returns Gridy.Action 自身または子Actionが対象外の場合null
   */
  Gridy.Action.prototype.getAction = function(link, bodyUpdate) {
    if (link.attr("path").match(this.path)) {
      if (bodyUpdate) {
        return this;
      }
      for (var i = 0; i < this.childActions.length; i++) {
        var action = this.childActions[i];
        action = action.getChildAction(link);
        if (action) {
          return action;
        }
      }
      return this;
    }
    return null;
  };
  
  /**
   * 引数のURL情報に紐づく子Actionオブジェクトを返します。
   * 対象となるActionオブジェクトが存在しない場合null
   * @param link Object jQuery URL Parserで作成された更新対象URL情報を保持するObject
   * @returns Gridy.Action 対象となるActionオブジェクトが存在しない場合null
   */
  Gridy.Action.prototype.getChildAction = function(link) {
    return null;
  };
  /**
   * Ajax通信処理を実行するメソッド
   * @param url string controllerから渡された対象URL
   * @param opts Object Gridy.ViewController.updateViewの引数に渡されたオプション
   * @returns Gridy.Action
   */
  Gridy.Action.prototype.ajax = function(url, opts) {
    $.ajax(this.getAjaxOpts(url, opts));
    return this;
  };
  /**
   * Ajax通信を行う際のオプション定義を返します
   * @param url string controllerから渡された対象URL
   * @param opts Object Gridy.ViewController.updateViewの引数に渡されたオプション
   * @returns Object
   */
  Gridy.Action.prototype.getAjaxOpts = function(url, opts) {
    return {
      url: url,
      dataType: this.dataType,
//      context: this, jQuery1.4系でバグ有
      success: $.proxy(function(data, textStatus, jqXHR) {
        this.success(data, textStatus, jqXHR);
        if (opts.message) {
          $(this.messageSelector).text(opts.message);
        }
        this.controller.previousAction = this;
        this.controller.previousMainAction = this.getMainAction();
        this.controller.previousUrl = url;
      }, this),
      error: $.proxy(this.error, this)
    };
  };
  /**
   * サーバ通信が成功したときの処理を行います。
   * @params data Object
   * @params textStatus string
   * @params jqXHR XmlHttpRequest
   * @returns Gridy.Action
   */
  Gridy.Action.prototype.success = function(data, textStatus, jqXHR) {
    this.beforeUpdate(data, textStatus, jqXHR);
    this.update(data, textStatus, jqXHR);
    this.afterUpdate(data, textStatus, jqXHR);
    return this;
  };
  /**
   * サーバ通信失敗時の処理を行います
   * @see Gridy.errorHandler
   */
  Gridy.Action.prototype.error = Gridy.errorHandler;
  /**
   * 画面更新前処理を行います。
   * 今回の更新前に画面全体を更新したActionが自分または自分の親Actionで無い場合
   * 前回のActionに紐づくイベントハンドラをunbindします。
   * 前回更新したActionが自分または親Actionの場合
   * 自分に紐づくイベントハンドラをunbindします。
   * @params data Object
   * @params textStatus string
   * @params jqXHR XmlHttpRequest
   * @returns Gridy.Action
   */
  Gridy.Action.prototype.beforeUpdate = function(data, textStatus, jqXHR) {
    var previousAction = this.controller.previousMainAction;
    if (previousAction == null) {
      return this;
    }
    var thisAction = this.getMainAction();
    var action = null;
    if (previousAction != thisAction) {
      action = previousAction;
    } else {
      action = this;
    }
    action.handlersEach(function(handler) {
        handler.unbind();
    });
    return this;
  };
  /**
   * 画面更新処理を行います。
   * Gridy.ViewController.bodyで指定したElementを、取得したデータで更新します。
   * @params data Object
   * @params textStatus string
   * @params jqXHR XmlHttpRequest
   * @returns Gridy.Action
   * 
   */
  Gridy.Action.prototype.update = function(data, textStatus, jqXHR) {
    $(this.controller.body).html(data).hashLink(this.hashLinkOpts);
    var title = this.getTitle();
    if (title) {
      document.title = title;
    }
    if (this.scrollTop) {
      $("html, body").animate({scrollTop:0}, 1);
    }
    return this;
  };
  /**
   * 画面更新後処理を行います。
   * 自身に紐づくイベントハンドラのbind処理を行います。
   * @params data Object
   * @params textStatus string
   * @params jqXHR XmlHttpRequest
   * @returns Gridy.Action
   */
  Gridy.Action.prototype.afterUpdate = function(data, textStatus, jqXHR) {
    this.handlersEach(function(handler) {
      handler.bind();
    });
    return this;
  };
  /**
   * 自身に紐づくhandlerを引数にして実行する関数を実行します。
   * @param func Function
   * @returns Gridy.Action
   */
  Gridy.Action.prototype.handlersEach = function(func) {
    for (var i = 0; i < this.handlers.length; i++) {
      func.call(this, this.handlers[i]);
    }
    for (var i = 0; i < this.childActions.length; i++) {
      var action = this.childActions[i];
      for (var j = 0; j < action.handlers.length; j++) {
        func.call(action, action.handlers[j]);
      }
    }
    return this;
  };
  /**
   * 自身と紐づく最上位の親Actionオブジェクトを返します。存在しない場合自分自身を返します。
   * @returns Gridy.Action
   */
  Gridy.Action.prototype.getMainAction = function() {
    var action = this;
    while (action.parentAction != null) {
      action = action.parentAction;
    }
    return action;
  };
  /**
   * タイトルを返します
   * @returns string
   */
  Gridy.Action.prototype.getTitle = function() {
    return this.title ? this.title : this.controller.title;
  };

  /**
   * 子Actionとして設定して、画面のページング関連のみを更新する用途のAction
   * @see Gridy.Action
   * @param opts Object
   *   dataType: default "json"
   *   listSelector: 更新対象となるテーブルを指定するSelector default #list_table
   *   listJsonName: サーバ通信時にテーブル画面データを示すjsonオブジェクトのkey default list
   *   pagerSelector: 更新対象となるページング関連Elementwo指定するSelector default #pager
   *   pagerJsonName: サーバ通信時にページング関連データを示すjsonオブジェクトのkey default pager
   *   useEmptyTable: 画面一覧数が少ない場合、レイアウト調整用の空テーブルを更新する場合true default true
   *   updatePager: pagerを更新する場合true default true
   *   emptyTableSelector: 更新対象となる空テーブルを指定するSelector default #empty_table
   *   emptyTableJsonName: サーバ通信時に空テーブルデータを示すjsonオブジェクトのkey default emptyTable
   *   messageJsonName: サーバからメッセージを受け取る場合のjsonオブジェクトのkey default message
   */
  Gridy.PageAction = function(opts) {
    Gridy.Action.call(this, opts);
  };
  Gridy.extend(Gridy.Action, Gridy.PageAction);
  Gridy.PageAction.defaults = {
    dataType: "json",
    listSelector: "#list_table",
    listJsonName: "list",
    pagerSelector: "#pager",
    pagerJsonName: "pager",
    useEmptyTable: true,
    updatePager: true,
    emptyTableSelector: "#empty_table",
    emptyTableJsonName: "emptyTable",
    messageJsonName: "message"
  };
  Gridy.Action.defaults.defaultChildActionClass = Gridy.PageAction;

  /**
   * @see Gridy.Action.getDefaults
   */
  Gridy.PageAction.prototype.getDefaults = function() {
    return $.extend({}, Gridy.Action.prototype.getDefaults.call(this), Gridy.PageAction.defaults);
  };
  /**
   * @see Gridy.Action.getChildAction
   * 前回画面更新した親Actionが自身の親Actionだった場合、自身を返す
   */
  Gridy.PageAction.prototype.getChildAction = function(link) {
    if (this.controller.previousMainAction == this.getMainAction()) {
      return this;
    }
    return null;
  };
  /**
   * @see Gridy.Action.update
   * テーブル一覧・ページング・空テーブル(指定した場合)、メッセージ(送信された場合)を更新します
   */
  Gridy.PageAction.prototype.update = function(data, textStatus, jqXHR) {
    $(this.listSelector).html(data[this.listJsonName]).hashLink(this.hashLinkOpts);
    if (this.updatePager) {
      $(this.pagerSelector).html(data[this.pagerJsonName]).hashLink(this.hashLinkOpts);
    }
    if (this.useEmptyTable) {
      $(this.emptyTableSelector).html(data[this.emptyTableJsonName]);
    }
    var message = "";
    if (data[this.messageJsonName] != undefined) {
      message = data[this.messageJsonName];
    }
    $(this.messageSelector).text(message);
    return this;
  };
  /**
   * @see Gridy.Action.getTitle
   */
  Gridy.PageAction.prototype.getTitle = function() {
    return null;
  };
  
  /**
   * 一覧画面から遷移する詳細画面表示を行うAction
   * @see Gridy.Action
   * @param opts Object
   *   usePrevNext: 前へ・次へボタンの操作を行う場合true default true
   *   pageName: 一覧画面のページング数を示すgetパラメータ名 default page
   *   listSelector: 一覧画面のテーブルを示すSelector default #list_table
   *   getIdFromTrSelector: 一覧画面の各行に存在するIDを取得するセレクター default input[name='id[]']
   *   idName: 詳細画面遷移時にキーとなるID名 default id
   *   hashPagerLastPageSelector: 一覧画面のページング内の、最後のページのリンクを示すSelector default #hash_pager_last_page1
   *   getIdsUrl: 一覧画面と同一条件でid一覧のみを取得するサーバへのURL default /{モジュールルートパス}/getIds
   *   prevBtnSelector: 前へボタンを示すSelector default #prev_btn, #prev_btn2
   *   nextBtnSelector: 次へボタンを示すSelector default #next_btn, #next_btn2
   */
  Gridy.DetailAction = function(opts) {
    this.listAction = null;
    Gridy.Action.call(this, opts);
    
    // 前へ・次へボタン設定用変数
    this.index = null;
    this.page = null;
    this.queryParams = null;
    this.listIds = [];
    this.listLastPage = null;
  };
  Gridy.extend(Gridy.Action, Gridy.DetailAction);
  Gridy.DetailAction.defaults = {
    usePrevNext: true,
    pageName: "page",
    listSelector: "#list_table",
    getIdFromTrSelector: "input[name='id[]']",
    idName: "id",
    hashPagerLastPageSelector: "#hash_pager_last_page1",
    getIdsUrl: Gridy.getModulePath() + "/getIds",
    prevBtnSelector: "#prev_btn, #prev_btn2",
    nextBtnSelector: "#next_btn, #next_btn2",
    scrollTop: true
  };
  /**
   * @see Gridy.Action.getDefaults
   */
  Gridy.DetailAction.prototype.getDefaults = function() {
    return $.extend({}, Gridy.Action.prototype.getDefaults.call(this), Gridy.DetailAction.defaults);
  };
  /**
   * 引数の連想配列からpageNameで指定したページング数を取得する関数
   * @param params Object
   * @returns number
   */
  Gridy.DetailAction.prototype.getPageNumber = function(params) {
    var page = params ? params[this.pageName] : null;
    if (page == null || page == '') {
      return 1;
    } else {
      return parseInt(page);
    }
  };
  /**
   * getIdsUrlで指定したサーバURLに、ID一覧取得処理を実行する関数
   * @param params Object 現在のURL#hashに定義された検索条件
   * @param success データ取得成功時に実行する関数。自身のメソッドとして実行する
   * @returns Gridy.DetailAction
   */
  Gridy.DetailAction.prototype.getIds = function(params, success) {
    $.ajax({
      url: this.getIdsUrl,
      dataType: "json",
      data: params,
      context: this,
      success: success
    });
    return this;
  };

  /**
   * 前へ・次へボタン設定処理を実行する関数
   * @param param Object 現在のURL#hashに定義された検索条件
   * @param number index 一覧画面テーブル内に、現在の詳細画面IDが存在するindex
   * @returns Gridy.DetailAction
   */
  Gridy.DetailAction.prototype.setPrevNextBtn = function(params, index) {
    var _setPrev = function(params, prev) {
      var page = this.getPageNumber(params);
      if (prev != null) {
        prev = this.listIds[page][prev];
      }
      var prevBtn = $(this.prevBtnSelector);
      if (prev != null) {
        var url = prevBtn.url();
        var data = Gridy.getQueryParams(url.attr("query"));
        data[this.idName] = prev;
        if (params[this.pageName] != null && params[this.pageName] != '') {
          data[this.pageName] = page;
        }
        prevBtn.attr("href", url.attr("path")+ "?" + $.param(data)).hashLink($.extend({}, this.hashLinkOpts, {selector: false})).show();
      } else {
        prevBtn.hide();
      }
      
    };
    var _setNext = function(params, next) {
      var page = this.getPageNumber(params);
      if (next != null) {
        next = this.listIds[page][next];
      }
      var nextBtn = $(this.nextBtnSelector);
      if (next != null) {
        var url = nextBtn.url();
        var data = Gridy.getQueryParams(url.attr("query"));
        data[this.idName] = next;
        if (params[this.pageName] != null && params[this.pageName] != '') {
          data[this.pageName] = page;
        }
        nextBtn.attr("href", url.attr("path") + "?" + $.param(data)).hashLink($.extend({}, this.hashLinkOpts, {selector: false})).show();
      } else {
        nextBtn.hide();
      }
    };
    var prev = null;
    var next = null;
    var page = this.getPageNumber(params);
    if (index == null && this.listIds[page]) {
      for (var i = 0; i < this.listIds[page].length; i++) {
        if (this.listIds[page][i] == params[this.idName]) {
          index = i;
          break;
        }
      }
    }
    if (index != null) {
      if (index >= 1) {
        prev = index - 1;
        _setPrev.call(this, params, prev);
      } else if (index == 0 && page > 1) {
        var prevParamsDefault = {};
        prevParamsDefault[this.pageName] = page - 1;
        var prevParams = $.extend({}, params, prevParamsDefault);
        if (this.listIds[prevParams[this.pageName]] != null) {
          _setPrev.call(this, prevParams, this.listIds[prevParams[this.pageName]].length - 1);
        } else {
          this.getIds(
            prevParams,
            function(data, textStatus, jqXHR) {
              if (data.ids && data.ids.length != undefined && data.ids.length >= 1) {
                this.listIds[prevParams[this.pageName]] = data.ids;
                this.listLastPage = data.lastPage;
                _setPrev.call(this, prevParams, data.ids.length - 1);
              }
            }
          );
        }
      }
      next = index + 1;
      if (this.listIds[page] && next <= this.listIds[page].length - 1) {
        _setNext.call(this, params, next);
      } else if(page < this.listLastPage) {
        var nextParamsDefault = {};
        nextParamsDefault[this.pageName] = page + 1;
        var nextParams = $.extend({}, params, nextParamsDefault);
        if (this.listIds[nextParams[this.pageName]] != null) {
          _setNext.call(this, nextParams, 0);
        } else {
          this.getIds(
            nextParams,
            function(data, textStatus, jqXHR) {
              if (data.ids && data.ids.length != undefined && data.ids.length >= 1) {
                this.listIds[nextParams[this.pageName]] = data.ids;
                this.listLastPage = data.lastPage;
                _setNext.call(this, nextParams, 0);
              }
            }
          );
        }
      }
    }
    return this;
  };
  /**
   * listSelectorで指定したテーブルの行から詳細画面遷移用のIDを取得する関数
   * @param tr jQueryオブジェクト trのjQueryオブジェクト
   * @returns 詳細画面遷移用のID
   */
  Gridy.DetailAction.prototype.getIdFromTr = function(tr) {
    return tr.find(this.getIdFromTrSelector).val();;
  };
  /**
   * @see Gridy.Action.beforeUpdate
   */
  Gridy.DetailAction.prototype.beforeUpdate = function(data, textStatus, jqXHR) {
    Gridy.Action.prototype.beforeUpdate.call(this, data, textStatus, jqXHR);
    this.index = null;
    this.queryParams = Gridy.getQueryParams();
    this.page = this.getPageNumber(this.queryParams);
    if (this.usePrevNext && this.controller.previousMainAction == this.listAction) {
      var action = this;
      this.listIds = [];
      this.listIds[this.page] = [];
      $(this.listSelector + " tbody tr").each(function() {
        var ths = $(this);
        var k = ths.index();
        var v = action.getIdFromTr(ths);
        action.listIds[action.page][k] = v;
        if (action.queryParams[action.idName] == v) {
          action.index = k;
        }
      });
      if (this.index == null) {
        this.listIds = null;
        this.listLastPage = null;
      } else {
        this.listLastPage = $.url($(this.hashPagerLastPageSelector).attr("href").replace("#", "/")).param(this.pageName);
      }
    }
    return this;
  };
  /**
   * @see Gridy.Action.afterUpdate
   */
  Gridy.DetailAction.prototype.afterUpdate = function(data, textStatus, jqXHR) {
    Gridy.Action.prototype.afterUpdate.call(this, data, textStatus, jqXHR);
    if (this.usePrevNext) {
      if (this.controller.previousAction == null || this.listIds == null) {
        this.getIds(this.queryParams,
          function(data, textStatus, jqXHR) {
            this.listIds = [];
            this.listIds[this.page] = data.ids;
            this.listLastPage = data.lastPage;
            this.setPrevNextBtn(this.queryParams, this.index);
          }
        );
      } else {
        this.setPrevNextBtn(this.queryParams, this.index);
      }
    }
    return this;
  };
  
  /**
   * jQueryのカスタムイベントにより実行、停止処理を行うActionクラス
   * @param opts Object 
   *   trigger: Gridy.EventAction.EVENT_START の場合、実行処理を行う
   *            Gridy.EventAction.EVENT_STOP の場合、停止処理を行う
   *            false 処理を行わない
   *   startName: 実行処理を行うカスタムイベント名
   *   stopName: 停止処理を行うカスタムイベント名
   *   target: カスタムイベントをbindする対象Element default body
   *   initBind: オブジェクト初期化時にカスタムイベントのbind処理を行う場合true default true
   *   updateWhenStart: 実行処理時にAjax通信を行う場合true default false
   *   updateWhenStop: 停止処理時にAjax通信を行う場合true default false
   * 
   */
  Gridy.EventAction = function(opts) {
    this.startEventData = null;
    this.stopEventData = null;
    Gridy.Action.call(this, opts);
    if (this.initBind) {
      this.bindEvent();
    }
  };
  Gridy.extend(Gridy.Action, Gridy.EventAction);
  /**
   * 実行処理を表す定数
   */
  Gridy.EventAction.EVENT_START = "EVENT_START";
  /**
   * 停止処理を表す定数
   */
  Gridy.EventAction.EVENT_STOP = "EVENT_STOP";
  Gridy.EventAction.defaults = {
      trigger: false,
      startName: null,
      stopName: null,
      target: "body",
      initBind: true,
      updateWhenStart: false,
      updateWhenStop: false
  };
  /**
   * @see Gridy.Action.getDefaults
   */
  Gridy.EventAction.prototype.getDefaults = function() {
    return $.extend({}, Gridy.Action.prototype.getDefaults.call(this), Gridy.EventAction.defaults);
  };
  /**
   * カスタムイベントのbind処理を行うメソッド
   * @returns Gridy.EventAction
   */
  Gridy.EventAction.prototype.bindEvent = function() {
    $(this.target).on(this.startName, this.startEventData, $.proxy(this.startFunc, this));
    $(this.target).on(this.stopName, this.stopEventData, $.proxy(this.stopFunc, this));
    return this;
  };
  /**
   * カスタムイベントのunbind処理を行うメソッド
   * @returns Gridy.EventAction
   */
  Gridy.EventAction.prototype.unbindEvent = function() {
    $(this.target).off(this.startName, this.startFunc);
    $(this.target).off(this.stopName, this.stopFunc);
    return this;
  };
  /**
   * @see Gridy.Action.getChildAction
   */
  Gridy.EventAction.prototype.getChildAction = function(link) {
    return this.trigger ? this : null;
  };
  /**
   * @see Gridy.Action.handlersEach
   */
  Gridy.EventAction.prototype.handlersEach = function(func) {
    if (this.trigger) {
      Gridy.Action.prototype.handlersEach.call(this, func);
    }
    return this;
  };
  /**
   * @see Gridy.Action.ajax
   */
  Gridy.EventAction.prototype.ajax = function(url, opts) {
    if ((this.trigger == Gridy.EventAction.EVENT_START && this.updateWhenStart) ||
      (this.trigger == Gridy.EventAction.EVENT_STOP && this.updateWhenStop)) {
      return Gridy.Action.prototype.ajax.call(this, url, opts);
    } else {
      this.getAjaxOpts(url, opts).success.call(this);
      return this;
    }
  };
  /**
   * 実行処理カスタムイベントに設定するイベントハンドラ
   * @param event Event
   * @param controller ViewController
   */
  Gridy.EventAction.prototype.startFunc = function(event, controller) {
    this.trigger = Gridy.EventAction.EVENT_START;
    controller.previousMainAction.unshiftChildAction(this);
    this.controller.beforeTriggers.push({
      selector: this.target,
      eventType: this.stopName,
      args: [false],
      ontime: true
    });
    this.controller.updateView();
  };
  /**
   * 実行処理時に画面更新を行うメソッド
   * @param data
   * @param textStatus
   * @param jqXHR
   * @returns Gridy.EventAction
   */
  Gridy.EventAction.prototype.startUpdate = function(data, textStatus, jqXHR) {
    return this;
  };
  /**
   * 実行処理を開始します
   * @param controller Gridy.ViewController
   * @returns Gridy.EventAction
   */
  Gridy.EventAction.prototype.start = function(controller) {
    $(this.target).trigger(this.startName, [controller]);
    return this;
  };
  /**
   * 停止処理カスタムイベントに設定するイベントハンドラ
   * @param event Event
   * @param updateView boolean 画面更新処理を行う場合true default true
   * @param unbind boolean 登録したハンドラのunbind処理を行う場合true default true
   */
  Gridy.EventAction.prototype.stopFunc = function(event, updateView, unbind) {
    if (this.trigger == false) {
      return;
    }
    updateView = updateView !== undefined ? updateView : true;
    unbind = unbind !== undefined ? unbind : true;
    this.trigger = Gridy.EventAction.EVENT_STOP;
    if (updateView) {
      this.controller.updateView();
    } else if (unbind) {
      this.handlersEach(function(handler) {
        handler.unbind();
      });
    }
    this.parentAction.removeChildAction(this);
    this.trigger = false;
  };
  /**
   * 停止処理時に画面更新を行うメソッド
   * @param data
   * @param textStatus
   * @param jqXHR
   * @returns Gridy.EventAction
   */
  Gridy.EventAction.prototype.stopUpdate = function(data, textStatus, jqXHR) {
    return this;
  };
  /**
   * 停止処理を行います。
   * @param updateView boolean 画面更新処理を行う場合true
   * @param unbind boolean イベントハンドラのunbindを行う場合true
   * @returns Gridy.EventAction
   */
  Gridy.EventAction.prototype.stop = function(updateView, unbind) {
    $(this.target).trigger(this.stopName, [updateView, unbind]);
    return this;
  };
  /**
   * @see Gridy.Action.update
   */
  Gridy.EventAction.prototype.update = function(data, textStatus, jqXHR) {
    switch (this.trigger) {
    case Gridy.EventAction.EVENT_START:
      this.startUpdate(data, textStatus, jqXHR);
      break;
    case Gridy.EventAction.EVENT_STOP:
      this.stopUpdate(data, textStatus, jqXHR);
      break;
    default:
      break;
    }
    return this;
  };
  /**
   * @see Gridy.Action.error
   */
  Gridy.EventAction.prototype.error = function(jqXHR, textStatus, errorThrown) {
    this.stop(false, false);
    return Gridy.Action.prototype.error.apply(this, arguments);
  };
  
  /**
   * Gridy.Actionの画面更新前にunbind、更新後にbind処理を行う為のイベントハンドラ情報を保持するクラス
   * @param selector ハンドラ適用対象Elementを示すSelector
   *    Functionを渡した場合、関数の実行結果をSelectorとして定義する
   * @param eventType String  jQueryオブジェクトのbindメソッドに指定するイベントタイプ
   * @param handler Function 設定するイベントハンドラ
   * @param eventData Object イベントハンドラ実行時に引数のevent.dataに設定されるデータ
   * @param unbindSelector unbind実行時に、bind時とは違うSelectorを定義したい場合に利用
   *   Functionを渡した場合、関数の実行結果をSelectorとして定義する
   * 
   */
  Gridy.Handler = function(selector, eventType, handler, eventData, unbindSelector) {
    this.selector = selector;
    this.eventType = eventType;
    this.handler = handler;
    this.eventData = eventData;
    this.unbindSelector = unbindSelector;
  };
  /**
   * コンストラクタに渡されたselector、unbindSelectorからイベントハンドラ定義用のSelectorを取得する
   * @unbind unbind用のSelectorを取得する場合true default false
   * @returns string or jQueryオブジェクト
   */
  Gridy.Handler.prototype.getSelector = function(unbind) {
    var _getSelector = function(selector) {
      if ($.isFunction(selector)) {
        selector = selector(this);
      } else {
        selector = $(selector);
      }
      return selector;
    };
    var selector = null;
    if (unbind && this.unbindSelector) {
      selector = this.unbindSelector;
    } else {
      selector = this.selector;
    }
    return _getSelector.call(this, selector);
  };
  
  /**
   * コンストラクタに渡された内容でbind処理を実行する
   * @returns Gridy.Handler
   */
  Gridy.Handler.prototype.bind = function() {
    var selector = this.getSelector();
    selector.on(this.eventType, this.eventData, this.handler);
    return this;
  };
  
  /**
   * コンストラクタに渡された内容でunbind処理を実行する
   * @returns Gridy.Handler
   */
  Gridy.Handler.prototype.unbind = function() {
    var selector = this.getSelector(true);
    selector.off(this.eventType, this.handler);
    return this;
  };
  
  /**
   * @see Gridy.Handler
   * jQueryのbind処理に定義できないグローバルな関数を、画面更新のタイミングで実行する為のHandler
   * @param handler Function bind時に実行される関数
   * @param unbindHandler Function unbind時に実行される関数
   * @param args Array handlerを実行するときの引数を保持する配列
   * @param unbindArgs Array unbindHandlerを実行するときの引数を保持する配列
   * @param context Object handler、unbindHandlerを実行するときの実行元オブジェクト default null
   */
  Gridy.StaticHandler = function(handler, unbindHandler, args, unbindArgs, context) {
    Gridy.Handler.call(this, null, null, handler, null, null);
    this.unbindHandler = unbindHandler;
    this.args = args || [];
    if (this.args && !(this.args instanceof Array)) {
      this.args = [this.args];
    }
    this.unbindArgs = unbindArgs || [];
    if (this.unbindArgs && !(this.unbindArgs instanceof Array)) {
      this.unbindArgs = [this.unbindArgs];
    }
    this.context = context || null;
  };
  Gridy.extend(Gridy.Handler, Gridy.StaticHandler);
  /**
   * @see Gridy.Handler.bind
   */
  Gridy.StaticHandler.prototype.bind = function() {
    this.handler.apply(this.context, this.args);
    return this;
  };
  /**
   * @see Gridy.Handler.unbind
   */
  Gridy.StaticHandler.prototype.unbind = function() {
    if (this.unbindHandler) {
      this.unbindHandler.apply(this.context, this.unbindArgs);
    }
    return this;
  };

  /**
   * カスタム項目対応
   * ・カレンダー表示(設立年月日)
   * ・カレンダー表示(設立年月日以外)
   * ・かな自動入力
   */
  Gridy.CustomItemsScript = function() {
    var now = new Date();
    // 日付カレンダー入力(設立年月日)
    $("[class=founded_date_picker]").datepicker({changeYear: true, changeMonth: true, yearSuffix: '', yearRange: '1800:' + now.getFullYear()});

    // 日付カレンダー入力(設立年月日以外)
    $("[class=common_date_picker]").datepicker({changeYear: true, changeMonth: true, yearSuffix: ''});

    // かな自動入力
    var sfaRubyType;
    if ($("#sfa_ruby_type_meta_tag").attr("content") == undefined || $("#sfa_ruby_type_meta_tag").attr("content") == null) {
      sfaRubyType = false;
    } else {
      sfaRubyType = $("#sfa_ruby_type_meta_tag").attr("content") == "katakana" ? true : false;
    }
    $(".custom_input_text_kana").each(function(){
        $(this).autoKana('#'+$(this).attr('id')+"_kana",{katakana:sfaRubyType});
    });
    
  };

  /**
   * カスタム項目対応
   * ・  
  */
  Gridy.CustomCheckListScript = function() {
  /**
   * accuracy value calculation
   */
    $('input:checkbox[id^="accuracy_checklist_id"]').click(function(e) {
      var accuracyValue = 0;
      $('input:checkbox:checked[id^="accuracy_checklist_id"]').each(function() {
        accuracyValue += parseInt($(this).val());
      });
      $('span#accuracy_value').html(accuracyValue);
    });
  };

  /**
   * 二重登録防止処理
   */
  Gridy.checkDblClick = function(event) {
    var name = Gridy.checkDblClick.defaults.dataName;
    var selector = $(this);
    if (event.data && event.data.dataName) {
      name = event.data.dataName;
    }

    if (selector.data(name) == 1) {
      return false;
    } else {
      selector.data(name, 1);
    }
  };
  Gridy.clearDblClick = function(target) {
    if (target == null) {
      return;
    }
    target = $(target);
    $(target).data(Gridy.checkDblClick.defaults.dataName, null);
  };
  Gridy.checkDblClick.defaults = {
    dataName: "btn_update_clicked",
    btnSelector: ".btn_update"
  };

  /**
   * jQuery UI selectableにシフトキー操作処理を拡張
   */
  $.fn.selectableAddShift = function(opts) {
    if (opts == "destroy") {
      this.selectable("destroy");
      opts = this.data("selectableAddShift_options");
      if (opts == undefined) {
        return this;
      }
      $(document.body)
        .off("keydown." + opts.selectableAddShift_keyEventId, opts.selectableAddShift_KeyCheckEvent)
        .off("keyup." + opts.selectableAddShift_keyEventId, opts.selectableAddShift_KeyCheckEvent);
      this.data("selectableAddShift_options", null);
      this.data("selectableAddShift_last_selected_tr", null);
      this.data("selectableAddShift_before_shift_key_press_last_selected_tr", null);
      return this;
    }
    var _originalOpts = opts;
    opts = $.extend({}, $.fn.selectableAddShift.defaults, opts);
    opts.selected = function(event, ui) {
      if (_originalOpts != undefined && _originalOpts.selected != undefined) {
        var ret = _originalOpts.selected.apply(this, arguments);
      }
      $.fn.selectableAddShift.defaults.selected.apply(this, arguments);
    };
    opts.selectableAddShift_keyEventId = new Date().getTime();
    this.data("selectableAddShift_options", opts);
    $(document.body)
      .on("keydown." + opts.selectableAddShift_keyEventId, opts, opts.selectableAddShift_KeyCheckEvent)
      .on("keyup." + opts.selectableAddShift_keyEventId, opts, opts.selectableAddShift_KeyCheckEvent);
    this.selectable(opts);
    return this;
  };
  $.fn.selectableAddShift.defaults = {
    filter: "tr",
    selectableAddShift_KeyCheckEvent: function(event) {
      event.data.selectableAddShift_shiftKeyPress = event.shiftKey;
    },
    selected: function(event, ui) {
      var $this = $(this);
      var opts = $this.data("selectableAddShift_options");
      if ($(ui.selected).filter(opts.filter).size() == 0) {
        return;
      }
      $this.data("selectableAddShift_last_selected_tr", ui.selected);
      if (!opts.selectableAddShift_shiftKeyPress) {
        $this.data("selectableAddShift_before_shift_key_press_last_selected_tr", ui.selected);
      } else {
        var selectedIndex = ui.selected.sectionRowIndex;
        var beforeIndex = null;
        var _beforeTr = $this.data("selectableAddShift_before_shift_key_press_last_selected_tr");
        if (_beforeTr != undefined) {
          beforeIndex = _beforeTr.sectionRowIndex;
          var _firstIndex = null;
          var _secondIndex = null;
          if (selectedIndex > beforeIndex) {
            _firstIndex = beforeIndex;
            _secondIndex = selectedIndex;
          } else {
            _firstIndex = selectedIndex;
            _secondIndex = beforeIndex;
          }
          $this.find("tr").each(function(i) {
            if (i >= _firstIndex && i <= _secondIndex) {
              $(this).addClass("ui-selected");
            }
          });
        }
      }
    }
  };
  
  /**
   * 検索ボックスにフォーカスが当たったときに自動的に検索処理を行うプラグイン
   *   option
   *   search: Function 検索処理実行関数 または実行trigger名
   *   context: search実行時のthisオブジェクト
   *   usePlaceholder: placeholder非対応ブラウザでplaceholderを有効にする場合true
   *     default true
   *   placeholderColor: placeholder非対応Webブラウザでplaceholderを表示する
   *   
   */
  $.fn.focusSearch = function(opts) {
    this.each(function() {
      var $this = $(this);
      var _opts = null;
      if (opts == "destroy") {
        _opts = $this.data("options");
        if (_opts == undefined) {
          return;
        }
        $this
          .blur()
          .off("focus", _opts.focusEvent)
          .off("blur", _opts.blurEvent)
          .removeData("options");
        var placeholder = _opts._checkPlaceholder($this);
        if (placeholder !== false) {
          if ($this.val() == placeholder) {
            $this.val("");
          }
          if ($this.data("defaultColor") != undefined) {
            $this.css("color", $this.data("defaultColor"));
            $this.removeData("defaultColor");
          }
        }
        return;
      } else if (opts == "init") {
        _opts = $this.data("options");
        if (_opts == undefined) {
          return;
        }
        $this
          .val("")
          .blur()
          .data(_opts.previousSearchDataKey, "");
        return;
      }
      _opts = $.extend({}, $.fn.focusSearch.defaults, opts);
      $this
        .data("options", _opts)
        .focus(_opts, _opts.focusEvent)
        .blur(_opts, _opts.blurEvent)
        .blur();
    });
    return this;
  };
  $.fn.focusSearch.defaults = {
    usePlaceholder: true,
    placeholderColor: "#CCC",
    previousSearchDataKey: "previousSearchData",
    focusEvent: function(event) {
      var $this = $(this);
      event.data.blurEvent.apply(this, arguments);
      var placeholder = event.data._checkPlaceholder($this);
      if (placeholder !== false) {
        if ($this.val() == placeholder) {
          $this.val("");
        }
        $this.css("color",  $this.data("defaultColor") != undefined ? $this.data("defaultColor") : $this.css("color"));
      }
      event.data.searchEventIntervalId = setInterval(event.data._intervalEventClosure($this), 500);
    },
    _intervalEventClosure: function(target) {
      var opts = this;
      return function() {
        var _previousSearchData = target.data(opts.previousSearchDataKey);
        if (_previousSearchData == undefined) {
          _previousSearchData = "";
        }
        if (target.val() == _previousSearchData) {
          return;
        }
        var _search = opts.search;
        if (typeof _search == "string") {
          target.trigger(_search);
        } else if (typeof _search == "function") {
          if (opts.context != undefined) {
            _search = $.proxy(opts.search, opts.context);
          }
          _search();
        }
        target.data(opts.previousSearchDataKey, target.val());
      };
    },
    blurEvent: function(event) {
      var $this = $(this);
      if (event.data.searchEventIntervalId != undefined) {
        clearInterval(event.data.searchEventIntervalId);
        delete event.data.searchEventIntervalId;
      }
      var placeholder = event.data._checkPlaceholder($this);
      if (placeholder !== false) {
        if ($this.data("defaultColor") == undefined) {
          $this.data("defaultColor", $this.css("color"));
        }
        if ($this.val() == "" || $this.val() == placeholder) {
          $this
            .val(placeholder)
            .css("color", event.data.placeholderColor);
        }
      }
    },
    _checkPlaceholder: function(target) {
      if (this.usePlaceholder) {
        var placeholder = target.attr("placeholder");
        if (placeholder != undefined && !("placeholder" in document.createElement("input"))) {
          return placeholder;
        }
      }
      return false;
    }
  };
  
  /**
   * リンクをPOSTで送信します
   */
  Gridy.sendPostLink = function(link, hash) {
    if (link == undefined) {
      link = location;
    }
    var href = link.href
    var hrefs = href.split("?");
    var params = {};
    if (hrefs.length == 2) {
      href = hrefs[0];
      params = Gridy.getQueryParams(hrefs[1], hash);
    }
    var form = $("<form></form>")
      .attr("action", href)
      .attr("method", "post");
    for (var key in params) {
      var _val = params[key];
      if (!$.isArray(_val)) {
        _val = [_val];
      }
      for (var i = 0; i < _val.length; i++) {
        $("<input />")
          .attr("type", "hidden")
          .attr("name", key)
          .val(_val[i])
          .appendTo(form);
      }
    }
    form.appendTo("body")
      .submit()
      .remove();
  }
})(jQuery, Gridy);

jQuery(function($) {
  var ajaxLoading = null;
  /**
   * Ajax通信がエラーになった場合、status=401(認証失敗)時にログイン画面にURL遷移するエラーハンドラ
   */
  $("body")
    .ajaxError(function(event, xhr, settings) {
      if (xhr.status == 401) {
        window.location = "/login";
      }
    })
    // Ajax送信中のイメージ表示処理
    .ajaxStart(function() {
      if (ajaxLoading == null) {
        ajaxLoading = 
          $("<div></div>")
          .addClass("ajax_data_loading")
          .appendTo(this);
      }
    })
    .ajaxStop(function() {
      if (ajaxLoading != null) {
        ajaxLoading.remove();
        ajaxLoading = null;
      }
    })
    
    // CSRFトークン
    .ajaxSend(function(event, jqXHR, options) {
      jqXHR.setRequestHeader("X-CSRF-Token", $("#x_csrf_token_meta_tag").attr("content"));
    })
    .on("submit", "form", function(event) {
      if(this.method.replace(/(^\s+)|(\s+$)/g, "").toUpperCase() == 'POST'){
        if ($(":input[name='x_csrf_token']", this).length == 0) {
          $("<input />")
            .attr("type", "hidden")
            .attr("name", "x_csrf_token")
            .val($("#x_csrf_token_meta_tag").attr("content"))
            .appendTo(this);
        }
      }

    });
  /**
   * 二重登録防止処理
   */
  $(document).on('click', Gridy.checkDblClick.defaults.btnSelector, Gridy.checkDblClick);

});