kai-jacobsen/kontentblocks

View on GitHub
js/src/wpMediaExt/KB.media.Crop.js

Summary

Maintainability
C
7 hrs
Test Coverage
(function (wp, $) {

  if (!wp || !wp.media) {
    return;
  }
  media = wp.media;
  l10n = media.view.l10n = typeof _wpMediaViewsL10n === 'undefined' ? {} : _wpMediaViewsL10n;

  //wp.media.controller.KBImageDetails = wp.media.controller.State.extend({
  //  defaults: _.defaults({
  //    id:       'kb-image-details',
  //    title:    'Hab nix',
  //    content:  'image-details',
  //    menu:     false,
  //    router:   false,
  //    toolbar:  'image-details',
  //    editing:  false,
  //    priority: 60
  //  }, wp.media.controller.Library.prototype.defaults ),
  //
  //  /**
  //   * @since 3.9.0
  //   *
  //   * @param options Attributes
  //   */
  //  initialize: function( options ) {
  //    this.image = options.image;
  //    wp.media.controller.State.prototype.initialize.apply( this, arguments );
  //  },
  //
  //  /**
  //   * @since 3.9.0
  //   */
  //  activate: function() {
  //    this.frame.modal.$el.addClass('kb-image-details');
  //  }
  //});


  // supersede the default MediaFrame.Post view
  var oldMediaFrame = wp.media.view.MediaFrame.ImageDetails;
  wp.media.view.MediaFrame.KBImageDetails = oldMediaFrame.extend({
    initialize: function () {
      oldMediaFrame.prototype.initialize.apply(this, arguments);
    }
    //createStates: function() {
    //  this.states.add([
    //    new wp.media.controller.ImageDetails({
    //      image: this.image,
    //      editable: true
    //    }),
    //    new wp.media.controller.ImageDetails({
    //      image: this.image,
    //      editable: true
    //    }),
    //    new wp.media.controller.ReplaceImage({
    //      id: 'replace-image',
    //      library:   wp.media.query( { type: 'image' } ),
    //      image: this.image,
    //      multiple:  false,
    //      title:     'Bla',
    //      toolbar: 'replace',
    //      priority:  80,
    //      displaySettings: true
    //    }),
    //    new wp.media.controller.EditImage( {
    //      image: this.image,
    //      selection: this.options.selection
    //    } )
    //  ]);
    //}
  });


  /**
   * wp.media.view.Toolbar.Select
   *
   * @class
   * @augments wp.media.view.Toolbar.Select
   * @augments wp.media.view.Toolbar
   * @augments wp.media.View
   * @augments wp.Backbone.View
   * @augments Backbone.View
   */
  media.view.Toolbar.KBSelect = media.view.Toolbar.Select.extend({
    initialize: function () {
      var options = this.options;
      _.bindAll(this, 'clickSelect');

      _.defaults(options, {
        event: 'select',
        state: 'kb-cropper',
        reset: true,
        close: false,
        text: l10n.select,

        // Does the button rely on the selection?
        requires: {
          selection: true
        }
      });

      options.items = _.defaults(options.items || {}, {
        select: {
          style: 'primary',
          text: options.text,
          priority: 80,
          click: this.clickSelect,
          requires: options.requires
        }
      });
      // Call 'initialize' directly on the parent class.
      media.view.Toolbar.prototype.initialize.apply(this, arguments);
    }
  });


  /**
   * wp.media.view.Cropper
   *
   * Uses the imgAreaSelect plugin to allow a user to crop an image.
   *
   * Takes imgAreaSelect options from
   * wp.customize.HeaderControl.calculateImageSelectOptions via
   * wp.customize.HeaderControl.openMM.
   *
   * @class
   * @augments wp.media.View
   * @augments wp.Backbone.View
   * @augments Backbone.View
   */
  media.view.KBCropper = media.View.extend({
    className: 'crop-content',
    template: media.template('crop-content'),
    initialize: function () {
      _.bindAll(this, 'onImageLoad');
    },
    ready: function () {
      this.controller.frame.on('content:error:crop', this.onError, this);
      this.$image = this.$el.find('.crop-image');
      this.$image.on('load', this.onImageLoad);
      jQuery(window).on('resize.cropper', _.debounce(this.onImageLoad, 250));
    },
    prepare: function () {
      return {
        title: l10n.cropYourImage,
        url: this.options.attachment.get('url')
      };
    },
    onImageLoad: function () {
      var imgOptions = this.controller.get('imgSelectOptions');
      if (typeof imgOptions === 'function') {
        imgOptions = imgOptions(this.options.attachment, this.controller);
      }

      imgOptions = _.extend(imgOptions, {parent: this.$el});
      this.trigger('image-loaded');
      this.controller.imgSelect = this.$image.imgAreaSelect(imgOptions);
    },
    onError: function () {
      var filename = this.options.attachment.get('filename');

      this.views.add('.upload-errors', new media.view.UploaderStatusError({
        filename: media.view.UploaderStatus.prototype.filename(filename),
        message: _wpMediaViewsL10n.cropError
      }), {at: 0});
    }
  });


  /**
   * wp.media.controller.Cropper
   *
   * A state for cropping an image.
   *
   * @class
   * @augments wp.media.controller.State
   * @augments Backbone.Model
   */
  media.controller.KBCropper = media.controller.State.extend({
    defaults: {
      id: 'kb-cropper',
      title: l10n.cropImage,
      // Region mode defaults.
      toolbar: 'crop',
      content: 'crop',
      router: false,
      canSkipCrop: false
    },
    activate: function () {
      this.frame.on('content:create:crop', this.createCropContent, this);
      this.frame.on('close', this.removeCropper, this);
      this.set('selection', new Backbone.Collection(this.frame._selection.single));
    },

    deactivate: function () {
      this.frame.toolbar.mode('browse');
    },

    createCropContent: function () {
      this.cropperView = new wp.media.view.KBCropper({
        controller: this,
        attachment: this.get('selection').first()
      });
      this.cropperView.on('image-loaded', this.createCropToolbar, this);
      this.frame.content.set(this.cropperView);
    },
    removeCropper: function () {
      this.imgSelect.cancelSelection();
      this.imgSelect.setOptions({remove: true});
      this.imgSelect.update();
      this.cropperView.remove();
    },
    createCropToolbar: function () {
      var canSkipCrop, toolbarOptions;
      canSkipCrop = this.get('canSkipCrop') || false;
      toolbarOptions = {
        controller: this.frame,
        items: {
          insert: {
            style: 'primary',
            text: l10n.cropImage,
            priority: 80,
            requires: {library: false, selection: false},

            click: function () {
              var self = this,
                selection = this.controller.state().get('selection').first();
              selection.set({
                cropDetails: this.controller.state().imgSelect.getSelection(),
                cropOptions: this.controller.state().frame.options.cropOptions
              });
              this.$el.text(l10n.cropping);
              this.$el.attr('disabled', true);
              this.controller.state().doCrop(selection).done(function (croppedImage) {
                self.controller.trigger('cropped', croppedImage);
                self.controller.handleCroppedImage(croppedImage);
                //self.controller.close();
                self.controller.setState('library');
                self.controller.toolbar.mode('select');
                self.controller.createSelection();
                self.controller.dispose();
                self.controller.close();
              }).fail(function () {
                self.controller.trigger('content:error:crop');
              });
            }
          }
        }
      };

      if (canSkipCrop) {
        _.extend(toolbarOptions.items, {
          skip: {
            style: 'secondary',
            text: l10n.skipCropping,
            priority: 70,
            requires: {library: false, selection: false},
            click: function () {
              var selection = this.controller.state().get('selection').first();
              this.controller.state().cropperView.remove();
              this.controller.trigger('skippedcrop', selection);
              this.controller.close();
            }
          }
        });
      }
      this.frame.toolbar.set(new wp.media.view.Toolbar(toolbarOptions));
    },

    doCrop: function (attachment) {

      return wp.ajax.post('cropImage', {
        nonce: attachment.get('nonces').edit,
        _ajax_nonce: KB.appData.config.nonces.create,
        id: attachment.get('id'),
        cropDetails: attachment.get('cropDetails'),
        cropOptions: attachment.get('cropOptions')
      });
    }
  });

  media.view.KBCropperFrame = media.view.MediaFrame.Select.extend({
    initialize: function () {
      if (!this.options.cropOptions) {
        this.options.cropOptions = {};
      }
      this.options.cropOptions = _.defaults(this.options.cropOptions, {
        maxWidth: 500,
        maxHeight: 500
      });

      _.defaults(this.options, {
        selection: [],
        library: {
          type: 'image'
        },
        cache: false,
        multiple: false,
        state: 'library',
        content: 'library'
      });

      if (!this.options.croppedCallback) {
        this.options.croppedCallback = jQuery.noop
      }

      media.view.MediaFrame.prototype.initialize.apply(this, arguments);
      this.createSelection();
      this.createStates();
      this.bindHandlers();

      this.states.get('library').get('selection').on('add', function (model) {
        var that = this;
        model.on('change:uploading', function () {
          var width, height;
          width = model.get('width');
          height = model.get('height');

          if (width < that.options.cropOptions.maxWidth || height < that.options.cropOptions.maxHeight) {
            that.options.cropOptions.maxWidth = that.options.cropOptions.maxWidth / 2;
            that.options.cropOptions.maxHeight = that.options.cropOptions.maxHeight / 2;
          }
        });
      }, this);

    },
    reset: function () {
      this.states.invoke('trigger', 'reset');
      this.createSelection();
      return this;
    },
    createStates: function () {
      var options = this.options;
      this.states.add([
        new media.controller.KBCropper({
          imgSelectOptions: this.calculateImageSelectOptions
        })
      ]);

      this.states.add([
        // Main states.
        new media.controller.Library({
          library: media.query(options.library),
          multiple: options.multiple,
          title: options.title,
          menu: false,
          priority: 20
        })
      ]);
    },
    /**
     * Toolbars
     *
     * @param {Object} toolbar
     * @param {Object} [options={}]
     * @this wp.media.controller.Region
     */
    createSelectToolbar: function (toolbar, options) {
      options = options || this.options.button || {};
      options.controller = this;
      toolbar.view = new media.view.Toolbar.KBSelect(options);
    },
    calculateImageSelectOptions: function (attachment, controller) {
      var xInit = parseInt(controller.frame.options.cropOptions.maxWidth, 10),
        yInit = parseInt(controller.frame.options.cropOptions.maxHeight, 10),
        ratio, xImg, yImg, realHeight, realWidth,
        imgSelectOptions;


      realWidth = attachment.get('width');
      realHeight = attachment.get('height');

      ratio = xInit / yInit;
      xImg = realWidth;
      yImg = realHeight;


      if (xImg / yImg > ratio) {
        yInit = yImg;
        xInit = yInit * ratio;
      } else {
        xInit = xImg;
        yInit = xInit / ratio;
      }

      imgSelectOptions = {
        handles: 'corners',
        aspectRatio: xInit + ':' + yInit,
        keys: true,
        instance: true,
        persistent: true,
        imageWidth: realWidth,
        imageHeight: realHeight,
        x1: 0,
        y1: 0,
        x2: xInit,
        y2: yInit,
        minWidth: controller.frame.options.cropOptions.maxWidth,
        minHeight: controller.frame.options.cropOptions.maxHeight,
        fadeSpeed: 1000
      };


      // @TODO max values to options

      return imgSelectOptions;
    },
    handleCroppedImage: function (image) {
      var model = new wp.media.model.Attachment(image);
      this.options.croppedCallback.call(this.options.parentController, model);
    }
  });


//
//  /*
//  Demo code
//   */
//  function croppedCallback(attachment){
//    jQuery('.kb-cropped-image').html('<img src="' + attachment.get('sizes').full.url +'">');
//  }
//
//  sesame = new media.view.KBCropperFrame({
//    cropOptions: {
//      maxWidth: 500, //target width
//      maxHeight: 360 // target height
//    },
//    croppedCallback: croppedCallback //defaults to jquery.noop()
//  });
//
//  jQuery('button.js-kb-crop-image').on('click', function(){
//    sesame.open();
//  });
})(window.wp, jQuery);