wbotelhos/inplace

View on GitHub
lib/inplace.js

Summary

Maintainability
A
0 mins
Test Coverage
/*!
 * Inplace - An inplace editor
 *
 * The MIT License
 *
 * @author:  Washington Botelho
 * @doc:     wbotelhos.com/inplace
 * @version: 0.1.0
 *
 */

;
(function($) {
  'use strict';

  $.inplace = {
    buttonOrder:   ['cancel', 'save'],
    cancel:        true,
    cancelClass:   'inplace__cancel',
    cancelValue:   'Cancel',
    checkable:     ['checkbox', 'radio'],
    fieldClass:    'inplace__field',
    fieldName:     undefined,
    fieldTemplate: '{name}',
    fieldType:     'text',
    method:        'PATCH',
    save:          true,
    saveClass:     'inplace__save',
    saveValue:     'Save',
    selectable:    ['select-one', 'select-multiple'],

    typeful: [
      'color',
      'date',
      'datetime',
      'datetime-local',
      'email',
      'hidden',
      'month',
      'number',
      'password',
      'range',
      'search',
      'tel',
      'text',
      'textarea',
      'time',
      'url',
      'week'
    ]
  }

  $.fn.inplace = function(options) {
    return this.each(function() {
      return (new $.inplace.Inplace(this, options))._create();
    });
  }

  $.inplace.Inplace = (function() {
    var Inplace = function(element, options) {
      this.el      = $(element);
      this.element = element;

      this.options = $.extend({}, $.inplace, options, this.el.data());
    }

    Inplace.prototype = {
      _activate: function() {
        var field = this._field();

        this
          .el
          .off('click.inplace.el')
          .addClass('inplace--active')
          .html(field)
          .trigger('inplace:activate', this.element);

        for (var i = 0; i < this.options.buttonOrder.length; i++) {
          var value = this.options.buttonOrder[i];

          if (this.options[value]) {
            this._build(value, 'button').appendTo(this.el);
          }
        }

        var actived = $('.inplace--active').not(this.el);

        for (var i = 0; i < actived.length; i++) {
          actived.data('inplace')._deactivate();
        }

        field.trigger('focus');
      },

      _bindCancel: function() {
        this.el.on('click.inplace.cancel', '[data-inplace-cancel]', this._deactivate.bind(this));
      },

      _bindClick: function() {
        this.el.on('click.inplace.el', this._activate.bind(this));
      },

      _bindField: function() {
        var typeful = [];

        for (var i = 0; i < $.inplace.typeful.length; i++) {
          typeful.push('[type="' + $.inplace.typeful[i] + '"]');
        }

        var fields = typeful.join(',');

        this._bindKey('keypress.inplace.field', 13, fields, this._request.bind(this));
        this._bindKey('keyup.inplace.field', 27, fields, this._deactivate.bind(this));
      },

      _bindKey: function(event, keycode, fields, callback) {
        this.el.on(event, fields, function(evt) {
          if ((evt.which || evt.keyCode) === keycode) {
            callback();
          }
        });
      },

      _bindSave: function() {
        this.el.on('click.inplace.save', '[data-inplace-save]', this._request.bind(this));
      },

      _build: function(kind, type) {
        var options = {
          'class': this.options[kind + 'Class'],
          type:    type,
          value:   this.options[kind + 'Value']
        };

        if (kind === 'field') {
          var attributes = this.el.data('attributes');

          if (attributes) {
            options = $.extend({}, options, attributes);
          }
        }

        options['data-inplace-' + kind] = '';

        return $('<input />', options);
      },

      _create: function() {
        this._bindClick();
        this._bindField();
        this._bindSave();
        this._bindCancel();

        this.el.data('inplace', this);

        return this;
      },

      _deactivate: function() {
        this._bindClick();

        this
          .el
          .removeClass('inplace--active')
          .html(this.element.getAttribute('data-field-value'))
          .trigger('inplace:deactivate', this.element);
      },

      _done: function(json) {
        this._deactivate();

        this.options.fieldValue = json[this.options.fieldName];

        this.element.setAttribute('data-field-value', this.options.fieldValue);
        this.el.trigger('inplace:done', json);
      },

      _field: function() {
        return this._build('field', this.options.fieldType);
      },

      _request: function() {
        $.ajax(this._requestOptions()).done(this._done.bind(this));
      },

      _requestOptions: function() {
        var data = {};
        var name = this.options.fieldTemplate.replace('{name}', this.options.fieldName);

        data[name] = this.el.find('[data-inplace-field]').val();

        return { data: data, method: this.options.method, url: this.options.url };
      }
    };

    return Inplace;
  })();
})(jQuery);