(function ($) {

  /**
   * Based on /misc/machine-name.js
   * Replaces the core Drupal.behaviors.machineName
   * adds transliteration of non-Roman characters to Roman characters.
   */

  /**
   * Attach the machine-readable name form element behavior.
   */
  Drupal.behaviors.machineName = {
    /**
     * Attaches the behavior.
     *
     * @param settings.machineName
     *   A list of elements to process, keyed by the HTML ID of the form element
     *   containing the human-readable value. Each element is an object defining
     *   the following properties:
     *   - target: The HTML ID of the machine name form element.
     *   - suffix: The HTML ID of a container to show the machine name preview in
     *     (usually a field suffix after the human-readable name form element).
     *   - label: The label to show for the machine name preview.
     *   - replace_pattern: A regular expression (without modifiers) matching
     *     disallowed characters in the machine name; e.g., '[^a-z0-9]+'.
     *   - replace: A character to replace disallowed characters with; e.g., '_'
     *     or '-'.
     *   - standalone: Whether the preview should stay in its own element rather
     *     than the suffix of the source element.
     *   - field_prefix: The #field_prefix of the form element.
     *   - field_suffix: The #field_suffix of the form element.
     */
    attach: function (context, settings) {
      var self = this;

      if (!settings.machineName) {
        return;
      }

      $.each(settings.machineName, function (source_id, options) {
        var $source = $(source_id, context).addClass('machine-name-source');
        var $target = $(options.target, context).addClass('machine-name-target');
        var $suffix = $(options.suffix, context);
        var $wrapper = $target.closest('.form-item');

        var $div = $('<div />').appendTo('body');
        $div.attr('id', 'safeword-data');

        // All elements have to exist.
        if (!$source.length || !$target.length || !$suffix.length || !$wrapper.length) {
          return;
        }
        // Skip processing upon a form validation error on the machine name.
        if ($target.hasClass('error')) {
          return;
        }
        // Figure out the maximum length for the machine name.
        options.maxlength = $target.attr('maxlength');
        // Hide the form item container of the machine name form element.
        $wrapper.hide();
        // Determine the initial machine name value. Unless the machine name form
        // element is disabled or not empty, the initial default value is based on
        // the human-readable form element value.
        if ($target.is(':disabled') || $target.val() != '') {
          var machine = $target.val();
        }
        else {
          var machine = self.transliterate($source.val(), options);
        }
        // Append the machine name preview to the source field.
        var $preview = $('<span class="machine-name-value">' + options.field_prefix + Drupal.checkPlain(machine) + options.field_suffix + '</span>');
        $suffix.empty();
        if (options.label) {
          $suffix.append(' ').append('<span class="machine-name-label">' + options.label + ':</span>');
        }
        $suffix.append(' ').append($preview);

        // If the machine name cannot be edited, stop further processing.
        if ($target.is(':disabled')) {
          return;
        }

        // If it is editable, append an edit link.
        var $link = $('<span class="admin-link"><a href="#">' + Drupal.t('Edit') + '</a></span>')
          .click(function () {
            $wrapper.show();
            $target.focus();
            $suffix.hide();
            $source.unbind('.machineName');
            return false;
          });
        $suffix.append(' ').append($link);

        // Preview the machine name in realtime when the human-readable name
        // changes, but only if there is no machine name yet; i.e., only upon
        // initial creation, not when editing.
        if ($target.val() == '') {
          // Helper function to wait until a specified time after last trigger event.
          // If called again before timeout, resets timer to wait for full period uninterrupted.
          var typingPause = (function(){
            var timer = 0;
            return function(ms, callback) {
              clearTimeout(timer);
              timer = setTimeout(callback, ms);
            }
          })();


          $source.bind('keyup.machineName change.machineName input.machineName', function () {
            // Transliterate non-Roman characters to equivalent Roman characters.

            // Wait for this number of milliseconds to check typing has stopped.
            typingPause(300, function() {
              // Trigger if the source field has a value.
              if ($source.val() !== '') {
                var request = $.ajax({
                  cache: false,
                  url: Drupal.settings.basePath + "safeword/ajax/transliterate",
                  type: "POST",
                  dataType: "json",
                  data: {source: $source.val()},
                  success: function (data, textStatus, jqXHR) {
                    machine = self.transliterate(data, options);
                    // Set the machine name to the transliterated value.
                    if (machine != '') {
                      if (machine != options.replace) {
                        $target.val(machine);
                        $preview.html(options.field_prefix + Drupal.checkPlain(machine) + options.field_suffix);
                      }
                      $suffix.show();
                    }
                    else {
                      $suffix.hide();
                      $target.val(machine);
                      $preview.empty();
                    }
                  },
                  error: function (jqXHR, textStatus, errorThrown) {
                    alert(errorThrown);
                  }
                });
              }
              else {
                $suffix.hide();
                $target.val(machine);
                $preview.empty();
              }
            });
          });

          // Initialize machine name preview.
          $source.keyup();
        }
      });
    },

    /**
     * Transliterate a human-readable name to a machine name.
     *
     * @param source
     *   A string to transliterate.
     * @param settings
     *   The machine name settings for the corresponding field, containing:
     *   - replace_pattern: A regular expression (without modifiers) matching
     *     disallowed characters in the machine name; e.g., '[^a-z0-9]+'.
     *   - replace: A character to replace disallowed characters with; e.g., '_'
     *     or '-'.
     *   - maxlength: The maximum length of the machine name.
     *
     * @return
     *   The transliterated source string.
     */
    transliterate: function (source, settings) {
      var rx = new RegExp(settings.replace_pattern, 'g');
      return source.toLowerCase().replace(rx, settings.replace).substr(0, settings.maxlength);
    }

  };

})(jQuery);
