/*
JSCmd.elm
==========

port module JSCmd exposing (..)


port focus : String -> Cmd msg


port confirm : ( String, ReturnInfo ) -> Cmd msg


port highlight : String -> Cmd msg


port setValue : ( String, String ) -> Cmd msg


type alias ReturnInfo =
    { incomingPort : String
    , id : Int
    }

*/
(function ($) {
  'use strict'

  /**
   * Handlers for the JSCmd module's outgoing ports. Should
   * work for most apps.
   *
   * Usage:
   *
   *     var flags = {}
   *     var app = Elm.MyApp.embed(container, flags);
   *     ElmHandlers.setup(app, container, ElmHandlers.ALL);
   *
   *     // Optional hacks that are difficult to do in Elm:
   *     ElmHandlers.preventTextBoxSubmit(container, 'input:text');
   *     ElmHandlers.numericOnlyTextBoxes(container, 'input.numeric-only');
   *
   *     // Or specify your own character blacklist for certain inputs:
   *     ElmHandlers.customTextBoxKeyBlacklist(container, 'input.custom', [123]);
   */
  window.ElmHandlers = {
    ALL: ['confirm', 'setValue', 'focus', 'highlight'],

    setup: function (app, container, ports) {
      var handlers = this;
      if (!container) container = document;
      if (!app.ports) return;
      if (!ports) {
        ports = handlers.ALL;
      }
      var context = {
        app: app,
        container: container
      };
      $.each(ports, function (index) {
        var portName = this;
        var port = app.ports[portName];
        if (port) {
          port.subscribe(function () {
            handlers[portName].apply(context, arguments)
          });
        }
      })
    },

    /**
     * Prevent text boxes from triggering a submit event when the enter key
     * is pressed. If you embed an Elm app in Rails form, this can be a big
     * problem. It is also currently difficult in Elm to stop event propagation
     * for specific key presses.
     */
    preventTextBoxSubmit: function (appContainer, selector) {
      $(appContainer).on('keydown', selector, function (event) {
        if (event.which === 13) {
          event.preventDefault();
          event.stopPropagation();
        }
      })
    },

    /**
     * Block non-numeric key presses for inputs matching a CSS selector.
     * Helpful in creating inputs for currency and numbers, and a pain to do
     * in Elm.
     */
    numericOnlyTextBoxes: function (appContainer, selector) {
      var whitelist = [
        8, // backspace
        45, // minus
        46, // delete
        190 // period
      ];
      $(appContainer).on('keypress', selector, function (event) {
        var code = event.keyCode || event.which;
        var i;
        for (i = 0; i < whitelist.length; i++) {
          if (whitelist[i] === code) {
            return;
          }
        }
        if (isNaN(String.fromCharCode(code))) {
          event.preventDefault();
        }
      });
    },

    /**
     * Prevent a custom list of keys in certain text boxes (a pain to do with Elm,
     * requiring multiple event handlers).
     */
    customTextBoxKeyBlacklist: function (appContainer, selector, blacklist) {
      if (!blacklist || blacklist.length === 0) {
        throw 'invalid blacklist';
      }
      $(appContainer).on('keypress', selector, function (event) {
        var code = event.keyCode || event.which;
        var i;
        for (i = 0; i < blacklist.length; i++) {
          if (blacklist[i] === code) {
            event.preventDefault();
            return;
          }
        }
      });
    },

    /**
     * Show a confirm box, and send a message back on a specified port
     * containing an Int if "Okay" was clicked.
     *
     * For example, you could have the incoming port:
     *
     *     deleteRecord : (Int -> msg) -> Sub msg`
     *
     * And then in your `update` function call:
     *
     *     JSCmd.confirm
     *         ( "Are you sure you want to delete this item?"
     *         , { incomingPort = "deleteRecord", id = recordId }
     *         )
     *
     * And in your subscriptions:
     *
     *     subscriptions model =
     *         deleteRecord (\id -> DeleteRecord id)
     */
    confirm: function (tuple) {
      var msg = tuple[0];
      var returnInfo = tuple[1];

      if (window.confirm(msg)) {
        var port = this.app.ports[ returnInfo.incomingPort ];
        port.send(returnInfo.id);
      }
    },

    /**
     * Primarily useful for when select boxes don't select the right item.
     * This bug in Elm may have been fixed.
     */
    setValue: function (args) {
      var selector = args[0];
      var value = args[1];

      setTimeout(function () {
        $(selector, this.container).val(value);
      }, 50)
    },

    /**
     * Focus an input via CSS selector. The most used feature of this script by far.
     * This needs to be done all the time for a good UX, and there is no way to do it
     * in Elm.
     */
    focus: function (selector) {
      setTimeout(function () {
        var elt = $(selector, this.container).first();
        if (elt.prop('tagName') === 'SELECT') {
          elt.focus();
        } else {
          elt.select();
        }
      }, 50)
    },

    /**
     * Call the jQuery UI highlight effect. This could just as easily be implemented
     * with a CSS 3 transition entirely in Elm now.
     */
    highlight: function (selector) {
      setTimeout(function () {
        $(selector, this.container).effect('highlight');
      }, 50);
    }

  }
})(jQuery);