You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
296 lines
7.6 KiB
CoffeeScript
296 lines
7.6 KiB
CoffeeScript
###
|
|
jQuery Gridly
|
|
Copyright 2015 Kevin Sylvestre
|
|
1.2.9
|
|
###
|
|
|
|
"use strict"
|
|
|
|
$ = jQuery
|
|
|
|
class Animation
|
|
@transitions:
|
|
"webkitTransition": "webkitTransitionEnd"
|
|
"mozTransition": "mozTransitionEnd"
|
|
"oTransition": "oTransitionEnd"
|
|
"transition": "transitionend"
|
|
|
|
@transition: ($el) ->
|
|
el = $el[0]
|
|
return result for type, result of @transitions when el.style[type]?
|
|
|
|
@execute: ($el, callback) ->
|
|
transition = @transition($el)
|
|
if transition? then $el.one(transition, callback) else callback()
|
|
|
|
class Draggable
|
|
|
|
constructor: ($container, selector, callbacks) ->
|
|
@$container = $container
|
|
@selector = selector
|
|
@callbacks = callbacks
|
|
@toggle()
|
|
|
|
bind: (method = 'on') =>
|
|
$(document)[method] 'mousemove touchmove', @moved
|
|
$(document)[method] 'mouseup touchcancel', @ended
|
|
|
|
toggle: (method = 'on') =>
|
|
@$container[method] 'mousedown touchstart', @selector, @began
|
|
@$container[method] 'touchend', @selector, @touchend
|
|
@$container[method] 'click', @selector, @click
|
|
|
|
on: =>
|
|
@toggle('on')
|
|
|
|
off: =>
|
|
@toggle('off')
|
|
|
|
coordinate: (event) =>
|
|
switch event.type
|
|
when 'touchstart','touchmove','touchend','touchcancel' then event.originalEvent.touches[0]
|
|
else event
|
|
|
|
began: (event) =>
|
|
return if @$target
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
@bind('on')
|
|
|
|
@$target = $(event.target).closest(@$container.find(@selector))
|
|
@$target.addClass('dragging')
|
|
|
|
@origin =
|
|
x: @coordinate(event).pageX - @$target.position().left
|
|
y: @coordinate(event).pageY - @$target.position().top
|
|
|
|
@callbacks?.began?(event)
|
|
|
|
ended: (event) =>
|
|
return unless @$target?
|
|
if event.type != 'touchend'
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
@bind('off')
|
|
|
|
@$target.removeClass('dragging')
|
|
|
|
delete @$target
|
|
delete @origin
|
|
|
|
@callbacks?.ended?(event)
|
|
|
|
moved: (event) =>
|
|
return unless @$target?
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
|
|
@$target.css
|
|
left: @coordinate(event).pageX - @origin.x
|
|
top: @coordinate(event).pageY - @origin.y
|
|
|
|
@dragged = @$target
|
|
@callbacks?.moved?(event)
|
|
|
|
click: (event) =>
|
|
return unless @dragged
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
delete @dragged
|
|
|
|
touchend: (event) =>
|
|
@ended(event)
|
|
@click(event)
|
|
|
|
class Gridly
|
|
|
|
@settings:
|
|
base: 60
|
|
gutter: 20
|
|
columns: 12
|
|
draggable:
|
|
zIndex: 800
|
|
selector : '> *'
|
|
|
|
@gridly: ($el, options = {}) ->
|
|
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 = {}) ->
|
|
@$el = $el
|
|
@settings = $.extend {}, Gridly.settings, settings
|
|
@ordinalize(@$('> *'))
|
|
@draggable() unless @settings.draggable is false
|
|
return @
|
|
|
|
ordinalize: ($elements) =>
|
|
for i in [0 .. $elements.length]
|
|
$element = $($elements[i])
|
|
$element.data('position', i)
|
|
|
|
reordinalize: ($element, position) =>
|
|
$element.data('position', position)
|
|
|
|
$: (selector) =>
|
|
@$el.find(selector)
|
|
|
|
compare: (d, s) =>
|
|
return +1 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 0
|
|
|
|
draggable: (method) =>
|
|
@_draggable ?= new Draggable @$el, @settings.draggable.selector,
|
|
began: @draggingBegan
|
|
ended: @draggingEnded
|
|
moved: @draggingMoved
|
|
@_draggable[method]() if method?
|
|
|
|
$sorted: ($elements) =>
|
|
($elements || @$('> *')).sort (a,b) ->
|
|
$a = $(a)
|
|
$b = $(b)
|
|
aPosition = $a.data('position')
|
|
bPosition = $b.data('position')
|
|
aPositionInt = parseInt(aPosition)
|
|
bPositionInt = parseInt(bPosition)
|
|
return -1 if aPosition? and not bPosition?
|
|
return +1 if bPosition? and not aPosition?
|
|
return -1 if not aPosition and not bPosition and $a.index() < $b.index()
|
|
return +1 if not bPosition and not aPosition and $b.index() < $a.index()
|
|
return -1 if aPositionInt < bPositionInt
|
|
return +1 if bPositionInt < aPositionInt
|
|
return 0
|
|
|
|
draggingBegan: (event) =>
|
|
$elements = @$sorted()
|
|
@ordinalize($elements)
|
|
setTimeout @layout, 0
|
|
@settings?.callbacks?.reordering?($elements)
|
|
|
|
draggingEnded: (event) =>
|
|
$elements = @$sorted()
|
|
@ordinalize($elements)
|
|
setTimeout @layout, 0
|
|
@settings?.callbacks?.reordered?($elements, @_draggable.dragged)
|
|
|
|
draggingMoved: (event) =>
|
|
$dragging = $(event.target).closest(@$(@settings.draggable.selector))
|
|
$elements = @$sorted(@$(@settings.draggable.selector))
|
|
positions = @structure($elements).positions
|
|
original = index = $dragging.data('position')
|
|
|
|
for element in positions.filter((position) -> position.$element.is($dragging))
|
|
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 @compare
|
|
|
|
$elements = positions.map (position) -> position.$element
|
|
$elements = (@settings.callbacks?.optimize || @optimize)($elements)
|
|
|
|
for i in [0...$elements.length]
|
|
@reordinalize($($elements[i]), i)
|
|
|
|
@layout()
|
|
|
|
size: ($element) =>
|
|
(($element.data('width') || $element.outerWidth()) + @settings.gutter) / (@settings.base + @settings.gutter)
|
|
|
|
position: ($element, columns) =>
|
|
size = @size($element)
|
|
|
|
height = Infinity
|
|
column = 0
|
|
|
|
for i in [0 ... (columns.length - size)]
|
|
max = Math.max columns[i ... (i + size)]...
|
|
if max < height
|
|
height = max
|
|
column = i
|
|
|
|
for i in [column ... column + size]
|
|
columns[i] = height + ($element.data('height') || $element.outerHeight()) + @settings.gutter
|
|
|
|
x: (column * (@settings.base + @settings.gutter))
|
|
y: height
|
|
|
|
structure: ($elements = @$sorted()) =>
|
|
positions = []
|
|
columns = (0 for i in [0 .. @settings.columns])
|
|
|
|
for index in [0 ... $elements.length]
|
|
$element = $($elements[index])
|
|
|
|
position = @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
|
|
|
|
height: Math.max columns...
|
|
positions: positions
|
|
|
|
layout: =>
|
|
$elements = (@settings.callbacks?.optimize || @optimize)(@$sorted())
|
|
|
|
structure = @structure($elements)
|
|
|
|
for index in [0 ... $elements.length]
|
|
$element = $($elements[index])
|
|
position = structure.positions[index]
|
|
|
|
continue if $element.is('.dragging')
|
|
|
|
$element.css
|
|
position: 'absolute'
|
|
left: position.x
|
|
top: position.y
|
|
|
|
@$el.css
|
|
height: structure.height
|
|
|
|
optimize: (originals) =>
|
|
results = []
|
|
|
|
columns = 0
|
|
while originals.length > 0
|
|
columns = 0 if columns is @settings.columns
|
|
|
|
index = 0
|
|
for index in [0...originals.length]
|
|
break unless columns + @size($(originals[index])) > @settings.columns
|
|
|
|
if index is originals.length
|
|
index = 0
|
|
columns = 0
|
|
|
|
columns += @size($(originals[index]))
|
|
|
|
# Move from originals into results
|
|
results.push(originals.splice(index,1)[0])
|
|
|
|
return results
|
|
|
|
$.fn.extend
|
|
gridly: (option = {}, parameters...) ->
|
|
@each ->
|
|
$this = $(@)
|
|
|
|
options = $.extend {}, $.fn.gridly.defaults, typeof option is "object" and option
|
|
action = if typeof option is "string" then option else option.action
|
|
action ?= "layout"
|
|
|
|
Gridly.gridly($this, options)[action](parameters)
|