(function($) {

  /**
   * Apply "placeholder" attribute polyfill to one or more forms.
   * @return {jQuery} jQuery object.
   */
  $.fn.placeholder = function() {

    // Browser natively supports placeholders? Bail.
      if (typeof (document.createElement('input')).placeholder != 'undefined')
        return $(this);

    // No elements?
      if (this.length == 0)
        return $this;

    // Multiple elements?
      if (this.length > 1) {

        for (var i=0; i < this.length; i++)
          $(this[i]).placeholder();

        return $this;

      }

    // Vars.
      var $this = $(this);

    // Text, TextArea.
      $this.find('input[type=text],textarea')
        .each(function() {

          var i = $(this);

          if (i.val() == ''
          ||  i.val() == i.attr('placeholder'))
            i
              .addClass('polyfill-placeholder')
              .val(i.attr('placeholder'));

        })
        .on('blur', function() {

          var i = $(this);

          if (i.attr('name').match(/-polyfill-field$/))
            return;

          if (i.val() == '')
            i
              .addClass('polyfill-placeholder')
              .val(i.attr('placeholder'));

        })
        .on('focus', function() {

          var i = $(this);

          if (i.attr('name').match(/-polyfill-field$/))
            return;

          if (i.val() == i.attr('placeholder'))
            i
              .removeClass('polyfill-placeholder')
              .val('');

        });

    // Password.
      $this.find('input[type=password]')
        .each(function() {

          var i = $(this);
          var x = $(
                $('<div>')
                  .append(i.clone())
                  .remove()
                  .html()
                  .replace(/type="password"/i, 'type="text"')
                  .replace(/type=password/i, 'type=text')
          );

          if (i.attr('id') != '')
            x.attr('id', i.attr('id') + '-polyfill-field');

          if (i.attr('name') != '')
            x.attr('name', i.attr('name') + '-polyfill-field');

          x.addClass('polyfill-placeholder')
            .val(x.attr('placeholder')).insertAfter(i);

          if (i.val() == '')
            i.hide();
          else
            x.hide();

          i
            .on('blur', function(event) {

              event.preventDefault();

              var x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]');

              if (i.val() == '') {

                i.hide();
                x.show();

              }

            });

          x
            .on('focus', function(event) {

              event.preventDefault();

              var i = x.parent().find('input[name=' + x.attr('name').replace('-polyfill-field', '') + ']');

              x.hide();

              i
                .show()
                .focus();

            })
            .on('keypress', function(event) {

              event.preventDefault();
              x.val('');

            });

        });

    // Events.
      $this
        .on('submit', function() {

          $this.find('input[type=text],input[type=password],textarea')
            .each(function(event) {

              var i = $(this);

              if (i.attr('name').match(/-polyfill-field$/))
                i.attr('name', '');

              if (i.val() == i.attr('placeholder')) {

                i.removeClass('polyfill-placeholder');
                i.val('');

              }

            });

        })
        .on('reset', function(event) {

          event.preventDefault();

          $this.find('select')
            .val($('option:first').val());

          $this.find('input,textarea')
            .each(function() {

              var i = $(this),
                x;

              i.removeClass('polyfill-placeholder');

              switch (this.type) {

                case 'submit':
                case 'reset':
                  break;

                case 'password':
                  i.val(i.attr('defaultValue'));

                  x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]');

                  if (i.val() == '') {
                    i.hide();
                    x.show();
                  }
                  else {
                    i.show();
                    x.hide();
                  }

                  break;

                case 'checkbox':
                case 'radio':
                  i.attr('checked', i.attr('defaultValue'));
                  break;

                case 'text':
                case 'textarea':
                  i.val(i.attr('defaultValue'));

                  if (i.val() == '') {
                    i.addClass('polyfill-placeholder');
                    i.val(i.attr('placeholder'));
                  }

                  break;

                default:
                  i.val(i.attr('defaultValue'));
                  break;

              }
            });

        });

    return $this;

  };

  /**
   * Moves elements to/from the first positions of their respective parents.
   * @param {jQuery} $elements Elements (or selector) to move.
   * @param {bool} condition If true, moves elements to the top. Otherwise, moves elements back to their original locations.
   */
  $.prioritize = function($elements, condition) {

    var key = '__prioritize';

    // Expand $elements if it's not already a jQuery object.
      if (typeof $elements != 'jQuery')
        $elements = $($elements);

    // Step through elements.
      $elements.each(function() {

        var $e = $(this), $p,
          $parent = $e.parent();

        // No parent? Bail.
          if ($parent.length == 0)
            return;

        // Not moved? Move it.
          if (!$e.data(key)) {

            // Condition is false? Bail.
              if (!condition)
                return;

            // Get placeholder (which will serve as our point of reference for when this element needs to move back).
              $p = $e.prev();

              // Couldn't find anything? Means this element's already at the top, so bail.
                if ($p.length == 0)
                  return;

            // Move element to top of parent.
              $e.prependTo($parent);

            // Mark element as moved.
              $e.data(key, $p);

          }

        // Moved already?
          else {

            // Condition is true? Bail.
              if (condition)
                return;

            $p = $e.data(key);

            // Move element back to its original location (using our placeholder).
              $e.insertAfter($p);

            // Unmark element as moved.
              $e.removeData(key);

          }

      });

  };

})(jQuery);