// Generated by CoffeeScript 2.5.1
(function() {
  /*
  jQuery Gridly
  Copyright 2020 Kevin Sylvestre
  1.3.0
  */
  "use strict";
  var $, Animation, Draggable, Gridly;

  $ = jQuery;

  Animation = (function() {
    class Animation {
      static transition($el) {
        var el, ref, result, type;
        el = $el[0];
        ref = this.transitions;
        for (type in ref) {
          result = ref[type];
          if (el.style[type] != null) {
            return result;
          }
        }
      }

      static execute($el, callback) {
        var transition;
        transition = this.transition($el);
        if (transition != null) {
          return $el.one(transition, callback);
        } else {
          return callback();
        }
      }

    };

    Animation.transitions = {
      "webkitTransition": "webkitTransitionEnd",
      "mozTransition": "mozTransitionEnd",
      "oTransition": "oTransitionEnd",
      "transition": "transitionend"
    };

    return Animation;

  }).call(this);

  Draggable = class Draggable {
    constructor($container, selector, callbacks) {
      this.bind = this.bind.bind(this);
      this.toggle = this.toggle.bind(this);
      this.on = this.on.bind(this);
      this.off = this.off.bind(this);
      this.coordinate = this.coordinate.bind(this);
      this.began = this.began.bind(this);
      this.ended = this.ended.bind(this);
      this.moved = this.moved.bind(this);
      this.click = this.click.bind(this);
      this.touchend = this.touchend.bind(this);
      this.$container = $container;
      this.selector = selector;
      this.callbacks = callbacks;
      this.toggle();
    }

    bind(method = 'on') {
      $(document)[method]('mousemove touchmove', this.moved);
      return $(document)[method]('mouseup touchcancel', this.ended);
    }

    toggle(method = 'on') {
      this.$container[method]('mousedown touchstart', this.selector, this.began);
      this.$container[method]('touchend', this.selector, this.touchend);
      return this.$container[method]('click', this.selector, this.click);
    }

    on() {
      return this.toggle('on');
    }

    off() {
      return this.toggle('off');
    }

    coordinate(event) {
      switch (event.type) {
        case 'touchstart':
        case 'touchmove':
        case 'touchend':
        case 'touchcancel':
          return event.originalEvent.touches[0];
        default:
          return event;
      }
    }

    began(event) {
      var ref;
      if (this.$target) {
        return;
      }
      event.preventDefault();
      event.stopPropagation();
      this.bind('on');
      this.$target = $(event.target).closest(this.$container.find(this.selector));
      this.$target.addClass('dragging');
      this.origin = {
        x: this.coordinate(event).pageX - this.$target.position().left,
        y: this.coordinate(event).pageY - this.$target.position().top
      };
      return (ref = this.callbacks) != null ? typeof ref.began === "function" ? ref.began(event) : void 0 : void 0;
    }

    ended(event) {
      var ref;
      if (this.$target == null) {
        return;
      }
      if (event.type !== 'touchend') {
        event.preventDefault();
        event.stopPropagation();
      }
      this.bind('off');
      this.$target.removeClass('dragging');
      delete this.$target;
      delete this.origin;
      return (ref = this.callbacks) != null ? typeof ref.ended === "function" ? ref.ended(event) : void 0 : void 0;
    }

    moved(event) {
      var ref;
      if (this.$target == null) {
        return;
      }
      event.preventDefault();
      event.stopPropagation();
      this.$target.css({
        left: this.coordinate(event).pageX - this.origin.x,
        top: this.coordinate(event).pageY - this.origin.y
      });
      this.dragged = this.$target;
      return (ref = this.callbacks) != null ? typeof ref.moved === "function" ? ref.moved(event) : void 0 : void 0;
    }

    click(event) {
      if (!this.dragged) {
        return;
      }
      event.preventDefault();
      event.stopPropagation();
      return delete this.dragged;
    }

    touchend(event) {
      this.ended(event);
      return this.click(event);
    }

  };

  Gridly = (function() {
    class Gridly {
      static gridly($el, options = {}) {
        var data;
        data = $el.data('_gridly');
        if (data) {
          $.extend(data.settings, options);
        } else {
          data = new Gridly($el, options);
          $el.data('_gridly', data);
        }
        return data;
      }

      constructor($el, settings = {}) {
        this.ordinalize = this.ordinalize.bind(this);
        this.reordinalize = this.reordinalize.bind(this);
        this.$ = this.$.bind(this);
        this.compare = this.compare.bind(this);
        this.draggable = this.draggable.bind(this);
        this.$sorted = this.$sorted.bind(this);
        this.draggingBegan = this.draggingBegan.bind(this);
        this.draggingEnded = this.draggingEnded.bind(this);
        this.draggingMoved = this.draggingMoved.bind(this);
        this.size = this.size.bind(this);
        this.position = this.position.bind(this);
        this.structure = this.structure.bind(this);
        this.layout = this.layout.bind(this);
        this.optimize = this.optimize.bind(this);
        this.$el = $el;
        this.settings = $.extend({}, Gridly.settings, settings);
        this.ordinalize(this.$('> *'));
        if (this.settings.draggable !== false) {
          this.draggable();
        }
        return this;
      }

      ordinalize($elements) {
        var $element, i, j, ref, results1;
        results1 = [];
        for (i = j = 0, ref = $elements.length; (0 <= ref ? j <= ref : j >= ref); i = 0 <= ref ? ++j : --j) {
          $element = $($elements[i]);
          results1.push($element.data('position', i));
        }
        return results1;
      }

      reordinalize($element, position) {
        return $element.data('position', position);
      }

      $(selector) {
        return this.$el.find(selector);
      }

      compare(d, s) {
        if (d.y > s.y + s.h) {
          return +1;
        }
        if (s.y > d.y + d.h) {
          return -1;
        }
        if ((d.x + (d.w / 2)) > (s.x + (s.w / 2))) {
          return +1;
        }
        if ((s.x + (s.w / 2)) > (d.x + (d.w / 2))) {
          return -1;
        }
        return 0;
      }

      draggable(method) {
        if (this._draggable == null) {
          this._draggable = new Draggable(this.$el, this.settings.draggable.selector, {
            began: this.draggingBegan,
            ended: this.draggingEnded,
            moved: this.draggingMoved
          });
        }
        if (method != null) {
          return this._draggable[method]();
        }
      }

      $sorted($elements) {
        return ($elements || this.$('> *')).sort(function(a, b) {
          var $a, $b, aPosition, aPositionInt, bPosition, bPositionInt;
          $a = $(a);
          $b = $(b);
          aPosition = $a.data('position');
          bPosition = $b.data('position');
          aPositionInt = parseInt(aPosition);
          bPositionInt = parseInt(bPosition);
          if ((aPosition != null) && (bPosition == null)) {
            return -1;
          }
          if ((bPosition != null) && (aPosition == null)) {
            return +1;
          }
          if (!aPosition && !bPosition && $a.index() < $b.index()) {
            return -1;
          }
          if (!bPosition && !aPosition && $b.index() < $a.index()) {
            return +1;
          }
          if (aPositionInt < bPositionInt) {
            return -1;
          }
          if (bPositionInt < aPositionInt) {
            return +1;
          }
          return 0;
        });
      }

      draggingBegan(event) {
        var $elements, ref, ref1;
        $elements = this.$sorted();
        this.ordinalize($elements);
        setTimeout(this.layout, 0);
        return (ref = this.settings) != null ? (ref1 = ref.callbacks) != null ? typeof ref1.reordering === "function" ? ref1.reordering($elements) : void 0 : void 0 : void 0;
      }

      draggingEnded(event) {
        var $elements, ref, ref1;
        $elements = this.$sorted();
        this.ordinalize($elements);
        setTimeout(this.layout, 0);
        return (ref = this.settings) != null ? (ref1 = ref.callbacks) != null ? typeof ref1.reordered === "function" ? ref1.reordered($elements, this._draggable.dragged) : void 0 : void 0 : void 0;
      }

      draggingMoved(event) {
        var $dragging, $elements, element, i, index, j, k, len, original, positions, ref, ref1, ref2;
        $dragging = $(event.target).closest(this.$(this.settings.draggable.selector));
        $elements = this.$sorted(this.$(this.settings.draggable.selector));
        positions = this.structure($elements).positions;
        original = index = $dragging.data('position');
        ref = positions.filter(function(position) {
          return position.$element.is($dragging);
        });
        for (j = 0, len = ref.length; j < len; j++) {
          element = ref[j];
          element.x = $dragging.position().left;
          element.y = $dragging.position().top;
          element.w = $dragging.data('width') || $dragging.outerWidth();
          element.h = $dragging.data('height') || $dragging.outerHeight();
        }
        positions.sort(this.compare);
        $elements = positions.map(function(position) {
          return position.$element;
        });
        $elements = (((ref1 = this.settings.callbacks) != null ? ref1.optimize : void 0) || this.optimize)($elements);
        for (i = k = 0, ref2 = $elements.length; (0 <= ref2 ? k < ref2 : k > ref2); i = 0 <= ref2 ? ++k : --k) {
          this.reordinalize($($elements[i]), i);
        }
        return this.layout();
      }

      size($element) {
        return (($element.data('width') || $element.outerWidth()) + this.settings.gutter) / (this.settings.base + this.settings.gutter);
      }

      position($element, columns) {
        var column, height, i, j, k, max, ref, ref1, ref2, size;
        size = this.size($element);
        height = 2e308;
        column = 0;
        for (i = j = 0, ref = columns.length - size; (0 <= ref ? j < ref : j > ref); i = 0 <= ref ? ++j : --j) {
          max = Math.max(...columns.slice(i, (i + size)));
          if (max < height) {
            height = max;
            column = i;
          }
        }
        for (i = k = ref1 = column, ref2 = column + size; (ref1 <= ref2 ? k < ref2 : k > ref2); i = ref1 <= ref2 ? ++k : --k) {
          columns[i] = height + ($element.data('height') || $element.outerHeight()) + this.settings.gutter;
        }
        return {
          x: column * (this.settings.base + this.settings.gutter),
          y: height
        };
      }

      structure($elements = this.$sorted()) {
        var $element, columns, i, index, j, position, positions, ref;
        positions = [];
        columns = (function() {
          var j, ref, results1;
          results1 = [];
          for (i = j = 0, ref = this.settings.columns; (0 <= ref ? j <= ref : j >= ref); i = 0 <= ref ? ++j : --j) {
            results1.push(0);
          }
          return results1;
        }).call(this);
        for (index = j = 0, ref = $elements.length; (0 <= ref ? j < ref : j > ref); index = 0 <= ref ? ++j : --j) {
          $element = $($elements[index]);
          position = this.position($element, columns);
          positions.push({
            x: position.x,
            y: position.y,
            w: $element.data('width') || $element.outerWidth(),
            h: $element.data('height') || $element.outerHeight(),
            $element: $element
          });
        }
        return {
          height: Math.max(...columns),
          positions: positions
        };
      }

      layout() {
        var $element, $elements, index, j, position, ref, ref1, structure;
        $elements = (((ref = this.settings.callbacks) != null ? ref.optimize : void 0) || this.optimize)(this.$sorted());
        structure = this.structure($elements);
        for (index = j = 0, ref1 = $elements.length; (0 <= ref1 ? j < ref1 : j > ref1); index = 0 <= ref1 ? ++j : --j) {
          $element = $($elements[index]);
          position = structure.positions[index];
          if ($element.is('.dragging')) {
            continue;
          }
          $element.css({
            position: 'absolute',
            left: position.x,
            top: position.y
          });
        }
        return this.$el.css({
          height: structure.height
        });
      }

      optimize(originals) {
        var columns, index, j, ref, results;
        results = [];
        columns = 0;
        while (originals.length > 0) {
          if (columns === this.settings.columns) {
            columns = 0;
          }
          index = 0;
          for (index = j = 0, ref = originals.length; (0 <= ref ? j < ref : j > ref); index = 0 <= ref ? ++j : --j) {
            if (!(columns + this.size($(originals[index])) > this.settings.columns)) {
              break;
            }
          }
          if (index === originals.length) {
            index = 0;
            columns = 0;
          }
          columns += this.size($(originals[index]));
          // Move from originals into results
          results.push(originals.splice(index, 1)[0]);
        }
        return results;
      }

    };

    Gridly.settings = {
      base: 60,
      gutter: 20,
      columns: 12,
      draggable: {
        zIndex: 800,
        selector: '> *'
      }
    };

    return Gridly;

  }).call(this);

  $.fn.extend({
    gridly: function(option = {}, ...parameters) {
      return this.each(function() {
        var $this, action, options;
        $this = $(this);
        options = $.extend({}, $.fn.gridly.defaults, typeof option === "object" && option);
        action = typeof option === "string" ? option : option.action;
        if (action == null) {
          action = "layout";
        }
        return Gridly.gridly($this, options)[action](parameters);
      });
    }
  });

}).call(this);
