失われたコードを思い出して書く。フォームのコントロールを設定したり取得したり

それにしても、一度書いたものはそこそこ覚えているものなのに、最近忘れっぽい。
思い出すのも一苦労。ふぅ〜

<!DOCTYPE html>
<title></title>
<meta charset="UTF-8">
<style>
</style>

<form>
  <p>
    <input type="text" name="@hoge" value="0">
    <input type="text" name="@hoge" value="0">
    <input type="text" name="@hoge" value="0">

  <p>
    <select name="@fuga" multiple>
      <option selected>aaa
      <option value="bbb">BBB
      <option value="ccc">CCC
      <option value="ddd">DDD
    </select>
    <select name="@fuga">
      <option selected>aaa
      <option value="bbb">BBB
      <option value="ccc">CCC
      <option value="ddd">DDD
    </select>
    <select name="@fuga">
      <option selected>aaa
      <option value="bbb">BBB
      <option value="ccc">CCC
      <option value="ddd">DDD
    </select>
    <select name="@fuga">
      <option selected>aaa
      <option value="bbb">BBB
      <option value="ccc">CCC
      <option value="ddd">DDD
    </select>

  <p>
    <input type="checkbox" name="@piyo" value="aaa" checked>AAA
    <input type="checkbox" name="@piyo" value="bbb">BBB
    <input type="checkbox" name="@piyo" value="ccc">CCC

  <p>
    <input type="radio" name="@babu" value="aaa" checked>AAA
    <input type="radio" name="@babu" value="bbb">BBB
    <input type="radio" name="@babu" value="ccc">CCC

</form>

<script>

(function () {
  var A = Array.prototype;
  
  //前後の空白を取り除く
  function trim (str) { return str.replace (/^\s+|\s+$/g, ''); } 
  
  //name を単一化
  function normalizeByName (a, b) { return (-1 < a.indexOf (b.name)) ? a: a.concat (b.name); }
  
  // フォームのコントロールか?
  function isControls (e) {
    switch (e.tagName) {
    case 'INPUT' : switch (e.type) { case 'submit' : case 'reset' : case 'button' : case 'image': return false; }
    case 'TEXTAREA' : case 'SELECT' : return !!(e.name /*|| e.id*/); //@name の有無
    } return false;
  }
  
  //使われている name をすべて取得
  function getAllControlsName () {
    return A.filter.call (this.form.elements, isControls)
            .reduce (normalizeByName, []);
  }

  //_______________________
  
  function Controller (form) {
    this.form = form;
  }

  //_______________________

  function getControl (name) {
    return getControlsByName.call (this, name).reduce (function (result, e) {
      switch (e.type) {
      
      case 'radio' : case 'checkbox' :
        return (e.checked) ? result.concat (e.value): result;
      
      case 'select-one' :
      case 'select-multiple' :
        return result.concat (A.reduce.call (e.options, function (result, opt) {
          return (opt.selected)
          ? result.concat ((opt.hasAttribute ('value')) ? opt.value: trim (opt.text))
          : result;
        }, []));

      default :
        return result.concat (e.value);
      }
    }, []);
  }
  //_______________________

  // フォームの要素に、値を取得
  function getControlsValue (/*Array or Object or String, Array or Object or String, ..*/) {
    if (1 > arguments.length) // すべての名前を取得
      return getAllControlsName.call (this).map (
        function (name) { return [name, getControl.call (this, name)]; }, this);
    
    return A.map.call (arguments, function (arg) {
      switch (true) {

      // 配列ならば
      case (arg instanceof Array) :
        return arg.map (function (name) { return [name, getControl.call (this, name)]; }, this);
      
      // オブジェクトならば
      case ('object' === typeof arg) :
        for (var i in arg)
          if (arg.hasOwnProperty (i))
            arg[i] = getControl.call (this, i); // result[i] = getControl.call (this, i); return result;
        return arg;
      
      //文字列ならば
      case ('string' === typeof arg) :
        return getControl.call (this, arg);
    
      default:
        throw new Error ('引数が不正');
      }
    }, this)
  }


  //_______________________
  
  function setAryOptions (opt) {
    var v = opt.hasAttribute ('value') ? opt.value: trim (opt.text);
    var r = (-1 < this.value.indexOf (v));
    opt.selected = r;
  }

  function setOptions (opt) {
    var v = opt.hasAttribute ('value') ? opt.value: trim (opt.text);
    var r = (this.value === v);
    if (r) opt.selected = r;
    return r;
  }
  
  // 要素に、値をセット
  function setControls (obj) {
    if (obj.value instanceof Array) //配列ならば、文字列化
      obj.value.forEach (function (v, i) { this.value[i] = String (v); }, obj);
    else
      obj.value = String (obj.value);
    
    this.getControlsByName (obj.name).forEach (function (e, i) {
      var val = (this.value instanceof Array) ? this.value[i] || '': this.value;

      switch (e.type) {
      case 'select-one' :
          A.some.call (e.options, setOptions, {'value': val }); //途中で中止なので some
          break;

      case 'select-multiple' :
          A.forEach.call (e.options, setAryOptions, this);
          break;
      
      case 'radio' :
          //e.checked = (e.value === val); break;

      case 'checkbox' :
          e.checked = (this.value instanceof Array)
                      ? (-1 < this.value.indexOf (e.value))
                      : (e.value === this.value); break;
      default :
          e.value = val;
      }
    }, obj);
  }


  // フォームの要素に、値をセットする。
  function setControlsValue (parm/*[name, value], [],..*/) {
    if (1 > arguments.length)
      throw new Error ('引数がない');
    
    A.slice.call (arguments, 0).map (function (arg) {
      var i, buf = [];
      switch (true) {
      // name が配列なら
      case (arg instanceof Array) :
        return {'name': arg[0], 'value': arg[1] };
      
      // name がオブジェクトなら
      case ('object' === typeof arg) :
        for (i in arg)
          if (arg.hasOwnProperty (i))
            buf.push ({'name' : i, 'value': name[i]});
        return buf;
      }
    }).forEach (setControls, this)
  }

  //_______________________

  // 指定した名前のコントロールのを返す(1個でも配列で返す)
  function getControlsByName (name) {
    var es = this.form.elements[name];
    return ('tagName' in es) ? [es]: A.slice.call (es, 0);
  }
  

  // フォームのリセット
  function reset () {
    this.form.reset ();
    return this;
  }

  //_______________________
  
  function create (form) {
    if (form)
      if ('FORM' === form.nodeName)
        return new Controller (form);
    else
      return null;
  }
  
  //_______________________
  Controller.prototype.setControlsValue = setControlsValue; //
  Controller.prototype.getControlsValue = getControlsValue; // コントロールの値を返す
  Controller.prototype.getControlsByName = getControlsByName; // コントロールを配列で返す
  Controller.prototype.reset = reset;

  this.FormController = create;

  /*
  フォームのコントロールを操作する
  var xxx = FormController (document.querySelector ('form'));
  
  値の設定
  xxx.setControlsValue ([name1, value1], [name2, value2], ...);
  xxx.setControlsValue ([name1, [value1, value2, value3], ...);
  xxx.setControlsValue ({ 'name1': value1, 'name2': [value2_1, value2_2], ... });

  値の取得
  xxx.getControlsValue (); // ==> [[name1, value1],[name2, value2],..]
  xxx.getControlsValue (name1, nam2, ..); // ==> [[name1, value1], [name2, value2], ..]
  xxx.getControlsValue (name1, arrName, object, ..); [[value1], [[aryName1, aryValue1],..], object,..]
  
  var obj = { 'name1': null, 'name2': null, ... };
  xxx.getControlsValue (obj); // == > { 'name1': value1, 'name2': value2, ... }
  
  */

})();


var f = FormController (document.querySelector ('form'));


f.setControlsValue (['@hoge', 123], ['@fuga', 'bbb'], ['@piyo', 'ccc'], ['@babu', 'aaa']);
alert ('設定値を変えた');


f.setControlsValue (['@hoge', [123, 456, 789]], ['@fuga', ['aaa', 'bbb', 'ccc', 'ddd']],
                    ['@piyo', ['aaa', 'bbb', 'ccc']], ['@babu', ['aaa', 'bbb']]);
alert ('設定値を変えた');


alert('値の取得 引数無し\n' + f.getControlsValue ().join("\n"));
alert('値の取得 複数の引数\n' + f.getControlsValue ('@hoge', '@fuga', '@piyo', '@babu').join("\n"));
alert('値の取得 配列で\n' + f.getControlsValue (['@hoge', '@fuga', '@piyo', '@babu']).join("\n"));

var obj = { '@hoge': null, '@fuga': null, '@piyo': null, '@babu': null };
f.getControlsValue (obj);
alert('値の取得 オブジェクトで\n' + [ obj['@hoge'], obj['@fuga'], obj['@piyo'], obj['@babu']].join ("\n"));


</script>