function inspect(object) {
  var a = []
  for (var p in object) {
    a.push(p+':'+object[p])
  }
  return a.join('\n')
}

//オブジェクトをマージする
//  obj: マージされるオブジェクト
//  add: マージするオブジェクト
function mergeObject(obj, add) {
  if (typeof add=='function') return //functionはマージしない
  for (var pi in add) {
    if (!obj[pi]) obj[pi] = add[pi]
      else mergeObject(obj[pi], add[pi])
  }
}

/*
　ハッシュをQueryStringに変換する
*/
function hashToQueryString(hash) {
  var result = []
  for(var key in hash) {
    result.push(key + '=' + encodeURIComponent(hash[key]))
  }
  return result.join('&')
}

/*
  例外を発生させる
  superClass: 継承元クラス
*/
function abort(msg) {
  throw msg
}

function dateToString(date) {
  return date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date.getDate()
}

function timeToString(time) {
  return ('0'+time.getHours()).slice(-2) + ":" + ('0'+time.getMinutes()).slice(-2)
}

/* 値を３桁コンマ形式文字列に変換する */
function commaEach3Digits(value) {
  if (value==undefined || value==null) return ''
  value = String(value)
  value.match(/([^\d]*)(\d*)(.*)/)
  var s = RegExp.$1
  var i = RegExp.$2
  var d = RegExp.$3
  var r = []
  while (i.match(/^(.*?)(\d{1,3})$/)) {
    r.unshift(RegExp.$2)
    i = RegExp.$1
  }
  if (d.match(/^(.*\.\d*?)0+$/)) d = RegExp.$1
  if (d=='.') d = ''
  return s+r.join(',')+d
}


/*
  例外をトラップするコンテキストを提供
  func: 例外をトラップした状態で実行するブロック
*/
function TrapExceptionBlock(func) {
  try {
    return func()
  } catch(e) {
    if (e>'') alert(e)
    return false
  }
}

/*
  オブジェクトを継承する
  superClass: 継承元クラス
*/
Function.prototype.inherit = function(superClass) {
  var Temp = new Function()
  Temp.prototype = superClass.prototype
  this.prototype = new Temp()
  this.prototype.constructor = this
  this.prototype.superclass = superClass.prototype
}

/*
  Arrayにeachメソッドを定義
*/
Array.prototype.each = function(func) {
  for(var i=0; i<this.length; i++) {
    func(this[i])
  }
}

/*
  多次元配列をばらして、一次元配列にする
  関数の引数に配列を渡すことと複数の引数を渡すことのどちらも許可したい場合に用いる
  戻り値: 作成された一次元配列
*/
Array.prototype.flatten = function() {
  result = []
  for(var i=0; i<this.length; i++) {
    if (this[i] instanceof Array) {
      result = result.concat(this[i].flatten())
    } else {
      result.push(this[i])
    }
  }
  return result
}

/*
  document.getElementByIdの代替ルーチン
*/
function $() {
  var elements = []
  for (var i=0; i<arguments.length; i++) {
    var item = arguments[i]
    var element = this.document.getElementById(item)
    if (arguments.length == 1) return element
    elements.push(element)
  }
  return elements
}

/*
  thisを含め、子ノードを再帰的に巡回する
  node: 巡回するノード(nullの場合は巡回しない)
  block: ノード毎に実行するブロック function(node) {}
*/
function eachNode(node, block) {
  if (node) {
    block(node)
    with (node) {
      if (node.innerHTML) {
        /* Geckoはロード中にcolgroupにアクセスするとテーブルの表示が動的に更新されなくなる */
        var skip = -1
        if (node.innerHTML.match(/^<colgroup>/)) skip = 0
          else if (node.innerHTML.match(/^[^<]+<colgroup>/)) skip = 1
        for (var i=0; i<childNodes.length; i++) {
          if (i!=skip) eachNode(childNodes[i], block)
        }
      }
    }
  }
}

var expire = new Date()
expire.setTime(expire.getTime()+6*30*24*60*60*1000) //６ヶ月後

//Cookieの値を取得する
function getCookie(name){
  var tmp = (document.cookie+";").match(name+"=([^;]*);")
  if(tmp!=null) return unescape(tmp[1]); else return ""
}

/*
　Cookieの値を設定する
　name: Cookieの名前
　value: 値
　expire: 有効期限、省略時はブラウザを閉じるまで
*/
function setCookie(name, value, expire) {
  var tmp = name+"="+escape(value)+";"
  if (expire>'') tmp += "expires="+expire.toGMTString()
  document.cookie = tmp
}

/*
　既定の有効期限でCookieの値を設定する
　name: Cookieの名前
　value: 値
*/
function setCookieWithDefExpire(name, value) {
  setCookie(name, value, expire)
}

/*
　Cookieを削除する
　name: Cookieの名前
　一年前の有効期限を指定して削除
*/
function deleteCookie(name) {
  var expire = new Date()
  expire.setFullYear(expire.getFullYear() - 1)
  setCookie(name, '', expire)
}

//戻るボタンの動作
//　最初のページなら閉じる。
//　２ページ目以降なら１ページ戻る
function backOrClose() {
  if (history.length) history.back()
    else close()
}

/*
  必須項目が入力されているかどうかチェック
  入力されていなければ例外処理
*/
function CheckRequired(element, name) {
  var n = name
  with (element) {
    if (type!='select-one' && value=='' ||
        type=='select-one' && options[selectedIndex].text=='') {
      focus()
      throw n+'は必ず入力してください'
    }
  }
}

/*
  hidden属性INPUTタグを作成する
  name: 名前
  value: 値
*/
function CreateHidden(name, value) {
  elm = document.createElement('input')
  elm.type = 'hidden'
  elm.name = name
  elm.value = value
  return elm
}

/*
  FORMタグを作成する
  action: アクション
  method: メソッド(省略時はget)
*/
function CreateForm(action, method) {
  var form = document.createElement('form')
  form.action = action || ''
  form.method = method || 'get'
  return form
}

/*
  Hiddenタグを作成して、thisの子要素として追加する
  このルーチンをFormのメソッドに登録して使用する
  this: フォーム
  name: 名前
  value: 値
*/
function AppendHidden(name, value) {
  this.appendChild(CreateHidden(name, value))
}

/*
  イベントハンドラを追加する
  object: イベントを追加する対象
  eventName: イベントの名前
  handler: イベントハンドラ function(source)
    source: イベントを起こしたオブジェクト、ハンドラ内ではthisはハンドラ自身となる
*/
function AddEventHandler(object, eventName, handler)
{
  var result
  if (object) {
    if (!object.GetEventHandlers) {
      object.GetEventHandlers = function(eventName) {
        var handlers = object['_handler_'+eventName]
        if (!handlers) {
          handlers = object['_handler_'+eventName] = []
          object[eventName] = function(event) {
            if (event) window.event = event
            return TrapExceptionBlock(function() {
              for (var i=0; i<handlers.length; i++) {
                result = handlers[i](object) //実行する
                if (result==false) return false
              }
            })
          }
        }
        return handlers
      }
    }
    object.GetEventHandlers(eventName).push(handler)
  }
}

function cancelInput(element, message) {
  alert(message)
  element.value = ''
  abort()
}  

/*
  数値入力チェックイベントを設定する
  arguments: 入力要素の配列
*/
function SetNumberCheck() {
  var args = Array.prototype.flatten.apply(arguments) //argumentsはIEではArrayではない。
  for (var i=0; i<args.length; i++) {
    AddEventHandler(args[i], 'onchange', function(source) {
      with (source) {
        if (value>'' && isNaN(value)) cancelInput(source, '数値を入力してください.')
      }
    })
  }
}

/*
  整数入力チェックイベントを設定する
  source: 入力要素
*/
function SetIntegerCheck(source, digits) {
  AddEventHandler(source, 'onchange', function() {
    with (source) {
      if (value>'') {
        value = value.replace(/,/g, '')
        if (!value.match('^[\\+\\-]?(\\d+)$'))
          cancelInput(source, '整数を入力してください.')
        if (RegExp.$1.length>digits)
          cancelInput(source, '桁数が多すぎます.')
      }
    }
  })
}

/*
  実数入力チェックイベントを設定する
  source: 入力要素
*/
function SetFloatCheck(source, digits, decimalPlace) {
  AddEventHandler(source, 'onchange', function() {
    with (source) {
      if (value>'') {
        value = value.replace(/,/g, '')
        if (isNaN(value)) cancelInput(source, '数値を入力してください.')
        integerDigits = digits-decimalPlace
        if (!value.match('^[\\+\\-]?\\d{1,'+integerDigits+'}($|\\.\\d{0,'+decimalPlace+'}$)'))
          cancelInput(source, '桁数が多すぎます.')
      }
    }
  })
}

/*
  日付入力要素に、日付正規化を施す
  source: 入力要素
*/
function SetDateNormalize(source) {
  AddEventHandler(source, 'onchange', function() {
    var value
    if (source.value) {
      if (source.value.match(/^\d{4,}\/\d+$/)) { //日付省略は１日
        value = new Date(source.value+'/1') 
      } else if (source.value.match(/^\d+\/\d+$/)) {//年省略は今年
        var year = (new Date()).getFullYear()
        value = new Date(year+'/'+source.value)
      } else {
        value = new Date(source.value)
      }
      with (value) {
        if (isNaN(getFullYear()))
          cancelInput(source, '日付を入力してください.')
        source.value = getFullYear()+'/'+(getMonth()+1)+'/'+getDate()
      }
    }
  })
}

/*
  時刻入力要素に、時刻正規化を施す
  source: 入力要素
*/
function SetTimeNormalize(source) {
  AddEventHandler(source, 'onchange', function() {
    var value
    if (source.value) {
      source.value = source.value.replace('.', ':')
      if (source.value.match(/^\d+$/)) source.value = source.value+':0'
      if (!source.value.match(/^(\d{1,2}):(\d{1,2})$/))
        cancelInput(source, '時刻を入力してください.')
      value = new Date()
      value.setHours(RegExp.$1)
      value.setMinutes(RegExp.$2)
      source.value = timeToString(value)
    }
  })
}


/*
  数値入力チェックイベントを設定する
  source: 入力要素
*/
function SetRangeCheck(source, minValue, maxValue) {
  AddEventHandler(source, 'onchange', function() {
    with (source) {
      if (value>'') {
        if (minValue!=null && value<minValue) cancelInput(source, String(minValue)+' 以上を入力してください.')
        if (maxValue!=null && value>maxValue) cancelInput(source, String(maxValue)+' 以下を入力してください.')
      }
    }
  })
}

/*
  一方を入力すると、もう一方にも同じデータがコピーされるようにする
  source: コピー元入力要素
  dest: コピー先入力要素
*/
function SetPair(source, dest) {
  AddEventHandler(source, 'onchange', function() {
    dest.value = source.value
    dest.onchange
  })
}

/*
  innerTextに代入する、クロスブラウザ
  dest: 代入先要素
  text: 代入するテキスト
*/
function SetInnerText(dest, text) {
  if (dest.firstChild) dest.replaceChild(document.createTextNode(text), dest.firstChild)
    else dest.appendChild(document.createTextNode(text))
}

/*
  HTMLをtextに変換する。実体参照・<BR>を変換する
  html: 変換元
  戻り値: 変換後のテキスト
*/
function htmlToText(html) {
  var element = document.createElement('DIV')
  element.innerHTML = html
  var result = element.innerText
  if (result==undefined) result = element.textContent
  delete element
  return result
}

/* 指定した要素の次に、新しい要素を追加する
  newElement: 新しい要素
  referenceElement: 追加位置の基準となる要素
*/
function insertAfter(newElement, referenceElement) {
  referenceElement.parentNode.insertBefore(newElement, referenceElement.nextSibling)
}

/* 要素に値を設定する
  element: 値を設定する要素
  value: 設定する値
*/
function setValue(element, value) {
  if (!element) return //要素が空の場合は何もしない
  if (value==undefined || value==null) value = ''
  if (element.tagName=='INPUT') {
    if (element.type=='image') {
      element.src = value
    } else if (element.type=='checkbox') {
      element.checked = value
    } else {
      element.value = value
    }
  } else {
    element.value = value
  }
  if (element.onchange) element.onchange()
}

/* 要素の値を取得する
  element: 値を設定する要素
*/
function getValue(element) {
  if (!element) return undefined
  if (element.tagName=='INPUT') {
    if (element.type=='image') {
      return element.src
    } else if (element.type=='checkbox') {
      return element.checked
    } else {
      return element.value
    }
  } else {
    return element.value
  }
}

//要素の有効・無効を設定する
//　element: 要素
//　value: false or true
function setElementEnabled(element, value) {
  if (!element) return
  element.disabled = !value
}

//要素の表示・非表示を設定する
//　element: 要素
//　value: false or true
function setElementVisible(element, value) {
  if (!element) return
  element.style.display = value ? '' : 'none'
}

//フォームに入力された値を連想配列に追加する
//　id・nameのどちらかが指定されている要素が処理対象となる
//　form: 追加元フォーム
//　param: 追加先連想配列
function AddFormToParams(form, params) {
  for(var i=0; i<form.elements.length; i++) {
    var element = form.elements[i]
    if (!element.type.match(/button|image/)) {
      var key = element.id ? element.id : element.name
      var value = getValue(element)
      if (key) params[key] = value
    }
  }
}

//連想配列をフォームに追加する
//　form: 追加先フォーム
//　param: 追加元連想配列
function AddParamsToForm(form, params) {
  form.appendHidden = AppendHidden; //AppendHiddenメソッドを追加する
  for (var key in params) {
    form.appendHidden(key, params[key])
  }
}

//暗黙のパラメータ
implicitParams = {}

//Submitする。
//  form: Submitするフォーム
//  requestUri: 遷移先のページ
//  params: 付加するパラメータ
function SubmitForm(form, requestUri, params) {
  AddParamsToForm(form, implicitParams)
  AddParamsToForm(form, params)
  if (requestUri) form.action = requestUri
  form.method = 'POST'
  form.submit()
}

// パラメータを設定して指定したページに遷移する。
// POSTメソッドでリクエストする
function SendPageRequest(requestUri, params) {
  var form = CreateForm(); //サブミット専用フォームを作成
  document.body.appendChild(form)
  try {
    AddParamsToForm(form, params)
    form.action = requestUri
    form.method = 'POST'
    form.submit()
  } catch(e) {
    if (e>'') alert(e)
  } finally {
    document.body.removeChild(form)
  }
}

