画像ファイルをドラッグアンドドロップして文字列化(base64)する。その2?

<!DOCTYPE hrml>
<head>
  <meta charset="utf-8">
  <title></title>

<body>
<p>

<form>
  <p>
    <img id="img" src="" alt="写真" width="200" height="100"><br>
    この図形領域にデスクトップから画像ファイルをドラッグ&ドロップすると画像を入力できます
  </p>
  <input type="hidden" name="hide" value=""><br>
  <textarea name="text" cols="180" rows="35"></textarea>

</form>


<script>
//_____________________________________________________________________

{
  /*#####################################################
     input[type="hidden"]の要素の value値に変更があった場合
     change イベントを発火させる
     cbFuncを指定すると、changeイベントが発火する前に呼ばれる
  #####################################################*/

  const

    option = {
      attributes      : true,
      attributeFilter : ['value']
    }
    ,

    handler =
      function ([mutation]) {
        let
          target = mutation.target, // => input[type="hidden"]
          doc    = target.ownerDocument,
          event  = doc.createEvent ('MouseEvents');

        event.initEvent ('change', true, true);

        if (this.cbFunc)
          this.cbFunc.call (this.cbObj, event);

        target.dispatchEvent (event);
      }
  ;

  //_____________________________


  class AddChangeEvent {

    constructor (element = null, cbFunc = null, cbObj = null) {
      if (null === element)
        throw new Error ('要素がありません');

      let observer = new MutationObserver (handler.bind (this));
      observer.observe (element, option);

      this.element  = element;
      this.cbFunc   = cbFunc;
      this.cbObj    = cbObj;
      this.observer = observer;
    }

  }

  //_____________________________

  this.AddChangeEvent = AddChangeEvent;
}

//_____________________________________________________________________


/*#####################################################
  ファイルを読み終わったら、コールバック関数を実行する
#####################################################*/

FileLoader: {

  const
    FILE_MAX_SIZE = 65536,
    IMAGE_FILE_TYPE = 'image/',

    handler =
      function (event) {
        this.cbFunc.call (this.cbObj, event.target);
      }
  ;


  class FileLoader {

    constructor (file, cbFunc = null, cbObj = null) {
      this.cbFunc = cbFunc;
      this.cbObj = cbObj;

      let reader = new FileReader ();
      reader.addEventListener ('load', this, false);
      reader.readAsDataURL (file);
    }


    // file の load 後に発火 event.target = FileReader
    handleEvent (event) {
      handler.call (this, event);
    }
  }

  //__________________

  class ImageLoader extends FileLoader {

    constructor (target, file, cbFunc, cbObj) {
      if (2 > arguments.length)
        throw new Error ('引数が足りません');

      let { size, type } = file;
      if (FILE_MAX_SIZE < size)
        throw new Error ('ファイルサイズが制限を超えました');
      if (type.indexOf (IMAGE_FILE_TYPE))
        throw new Error ('イメージファイルではありません');

      super (file, cbFunc, cbObj);
      this.target = target;
    }


    handleEvent (event) {
      this.target.src = event.target.result;
      handler.call (this, event);
    }

  }

  //__________________

  this.FileLoader = FileLoader;
  this.ImageLoader = ImageLoader;
}


//_____________________________________________________________________

{
  /*#####################################################
    指定された要素にドラッグアンドドロップイベントが発生した時に
    コールバック関数を実行する
  #####################################################*/


  class AddDnDEventFromDeskTop {

    constructor (target, cbFunc, cbObj) {
      if (1 > arguments.length)
        throw new Error ('引数が不足しています');

      this.target = target;
      this.cbFunc = cbFunc;
      this.cbObj  = cbObj;

      target.addEventListener ('dragover', this, false);
      target.addEventListener ('drop', this, false);
      target.dropzone = 'copy';
    }



    handleEvent (event) {
      event.stopPropagation ();
      event.preventDefault ();

      switch (event.type) {
      case 'drop' :     this.cbFunc.call (this.cbObj, event); break;
      case 'dragover' : event.dataTransfer.dropEffect = 'copy'; break;
      }
    }

  }

  //___________________

  this. AddDnDEventFromDeskTop = AddDnDEventFromDeskTop;
}

//_____________________________________________________________________

let
  [form, img, hide] = document.querySelectorAll ('form, img, input[type="hidden"]');


form.addEventListener ('change', function (event) {
  let
    target = event.target,
    {name, type, value} = target,
    form = target.form,
    text = form.elements['text'],
    line = [],
    reg = /(^data:image\/.+;base64,|.{180}|.+$)/g,
    tmp;

  while (tmp = reg.exec (value)) {
    line.push (tmp[0]);
  }


  text.value = line.join ('\n');

}, false);


new AddChangeEvent (hide);
new AddDnDEventFromDeskTop (img, DnDHandler);

//_____________________________________________________________________

function DnDHandler (event) {
  let
    {dataTransfer, target} = event;

  if (dataTransfer)
  if (dataTransfer.files)
    new ImageLoader (target, dataTransfer.files[0], step2);

}


function step2 (fileReader) {
  let
    hide = document.querySelector ('input[type="hidden"]');
    hide.value = fileReader.result;
}


</script>

img要素のsrcの値が変化するのを監視して、イベントを発火させてるとスマートな気もするが、なんだかな。