Browse files

Merge branch 'icholy/master'

  • Loading branch information...
2 parents 7cef89b + 6e12445 commit 6219ab4c8f5db8185ad70d5a738c0cd677cff0b4 @kristianmandrup committed Aug 13, 2012
Showing with 575 additions and 130 deletions.
  1. +51 −129 index.html
  2. +122 −1 jquery.masonry.js
  3. +402 −0 js/jquery.event.drag-2.2.js
View
180 index.html
@@ -1,132 +1,54 @@
----
-title: jQuery Masonry
-layout: default
-category: homepage
-has_modernizr: true
-mini_bricks:
- - w1 h1
- - w1 h1
- - w1 h2
- - w1 h1
- - w2 h1
- - w1 h2
- - w1 h1
- - w2 h2
- - w2 h1
- - w1 h1
- - w2 h2
- - w2 h1
----
+<!doctype html>
+<html>
+<head>
+ <title>Masonry Drag and Drop</title>
+ <style type="text/css">
+ .box-item {
+ width: 100px;
+ height: 100px;
+ float: left;
+ background-color: grey;
+ margin: 10px;
+ }
+ .handle {
+ background-color: black;
+ height: 20px;
+ cursor: move;
+ }
-<div id="container" class="transitions-enabled clearfix">
-
- <div class="item big-text col2">
- A dynamic layout plugin for jQuery<br />
- The flip-side of CSS floats<br />
- <br />
- &larr; View docs and demos
- </div>
-
- <div class="item big-text">
- Before: CSS Floats
- <div class="mini">
- {% for brick in page.mini_bricks %}
- <div class="{{ brick }}">{{ forloop.index }}</div>
- {% endfor %}
- </div>
- </div>
-
- <div class="item big-text">
- After: Masonry
- <div id="mini-container" class="mini">
- {% for brick in page.mini_bricks %}
- <div class="{{ brick }}">{{ forloop.index }}</div>
- {% endfor %}
- </div>
- </div>
-
- <div class="item link">
- <a href="jquery.masonry.min.js">Download the script jquery.&#8203;masonry.&#8203;min.js</a>
- </div>
-
- <div class="item link">
- <a href="http://meta.metafizzy.co/files/masonry-site.zip">Download this project</a>
- </div>
-
- <div class="item link">
- <a href="https://github.com/desandro/masonry">Masonry on GitHub</a>
- </div>
-
- <div class="item big-text loading">
- <img src="http://i.imgur.com/6RMhx.gif" />
- Loading Examples
- </div>
-
- <div class="item big-text col2">
- Also try <a href="http://vanilla-masonry.desandro.com">Vanilla Masonry</a>.<br />
- <span style="font-size: 0.6em; font-weight: 400;">Just like jQuery Masonry, but without the jQuery. <em>It&rsquo;s delicious!</em></span>
- </div>
-
-</div> <!-- #container -->
-
-<script src="{{ site.jquery_js }}"></script>
-<script src="{{ site.masonry_js }}"></script>
-<script>
- $(function(){
-
- var $container = $('#container');
-
- $('#mini-container').masonry({
- columnWidth: 50
- });
-
- $container.masonry({
- itemSelector: '.item',
- columnWidth: 240,
- isAnimated: !Modernizr.csstransitions
+ .color1 { background-color: blue; z-index: 9999;}
+ .color2 { background-color: green;}
+ .bigger { height: 150px; }
+ .smaller { height: 75px; width: 200px; }
+ .drag-ghost { background-color: orange; }
+ </style>
+ <script src="js/jquery-1.7.1.min.js"></script>
+ <script src="js/jquery.event.drag-2.2.js"></script>
+ <script src="jquery.masonry.js"></script>
+</head>
+<body>
+ <div id="container">
+ <div class="box-item"><div class="handle"></div>A</div>
+ <div class="box-item"><div class="handle"></div>B</div>
+ <div class="box-item bigger"><div class="handle"></div>C</div>
+ <div class="box-item"><div class="handle"></div>D</div>
+ <div class="box-item smaller"><div class="handle"></div>E</div>
+ <div class="box-item"><div class="handle"></div>F</div>
+ <div class="box-item"><div class="handle"></div>G</div>
+ <div class="box-item smaller"><div class="handle">H</div></div>
+ <div class="box-item"><div class="handle"></div>I</div>
+ <div class="box-item smaller"><div class="handle"></div>J</div>
+ <div class="box-item"><div class="handle"></div>K</div>
+ <div class="box-item bigger"><div class="handle"></div>L</div>
+ </div>​
+ <script type="text/javascript">
+ $("#container").masonry({
+ isAnimated : true,
+ isDraggable : true,
+ dragHandleSelector: '.handle',
+ dragClass: 'color1'
});
-
- // Sites using Masonry markup
- var $sites = $('#sites'),
- $loadingItem = $container.find('.loading');
-
- var ajaxError = function(){
- $loadingItem.text('Could not load examples :(');
- };
-
- // dynamically load sites using Masonry from Zootool
- $.getJSON('http://zootool.com/api/users/items/?username=desandro' +
- '&apikey=8b604e5d4841c2cd976241dd90d319d7' +
- '&tag=bestofmasonry&callback=?')
- .error( ajaxError )
- .success(function( data ){
-
- // proceed only if we have data
- if ( !data || !data.length ) {
- ajaxError();
- return;
- }
- var items = [],
- item, datum;
-
- for ( var i=0, len = data.length; i < len; i++ ) {
- datum = data[i];
- item = '<div class="item example"><a href="' + datum.url + '">'
- + '<img src="' + datum.image.replace('/l.', '/m.') + '" />'
- + '<b>' + datum.title + '</b>'
- + '</a></div>';
- items.push( item );
- }
-
- var $items = $( items.join('') );
- $items.imagesLoaded(function(){
- $container
- .masonry( 'remove', $loadingItem ).masonry()
- .append( $items ).masonry( 'appended', $items, true );
- });
-
- });
-
- });
-</script>
+ </script>
+</body>
+</html>
View
123 jquery.masonry.js 100755 → 100644
@@ -69,6 +69,9 @@
$.Mason.settings = {
isResizable: true,
+ isDraggable: false,
+ dragHandleSelector: null,
+ dragClass: null,
isAnimated: false,
animationOptions: {
queue: false,
@@ -147,8 +150,14 @@
// need to get bricks
this.reloadItems();
+ // set up dragging
+ if (this.options.isDraggable ){
+ this._initDrag(this.$bricks);
+ }
+
},
-
+
+
// _init fires when instance is first created
// and when instance is triggered again -> $el.masonry();
_init : function( callback ) {
@@ -452,6 +461,118 @@
$(window).unbind('.masonry');
+ },
+
+ _getDistanceBetween : function(point1, point2){
+ var dx = point1.x - point2.x,
+ dy = point1.y - point2.y;
+ return Math.sqrt(dx*dx + dy*dy);
+ },
+
+ _getBrickPoint : function($brick){
+ var offset = $brick.offset();
+ return {
+ $brick: $brick,
+ x : offset.left + ($brick.outerWidth()/2),
+ y : offset.top + ($brick.outerHeight()/2)
+ };
+ },
+
+ _getClosestBrick : function(brick){
+
+ var $brick = $(brick),
+ _this = this,
+ dPoint = this._getBrickPoint($brick),
+ closest = null,
+ point, dist, $b, last, midpoint;
+
+ // find the index of the closest brick
+ this.$bricks.each(function(i, b){
+
+ $b = $(b);
+ point = _this._getBrickPoint($b);
+
+ // first check the distance from the brick center
+ dist = _this._getDistanceBetween(dPoint, point);
+
+ if(closest === null || dist < closest.dist){
+ closest = {
+ dist: dist,
+ $brick: $b,
+ index: (point.x > dPoint.x) ? i : i+1
+ };
+ }
+
+ // get the distance between the center of the line
+ // connecting the current and last block
+ if(typeof last !== 'undefined'){
+ midpoint = {
+ x : (last.x + point.x) / 2,
+ y : (last.y + point.y) / 2
+ };
+ dist = _this._getDistanceBetween(dPoint, midpoint);
+ if(dist < closest.dist){
+ closest = {
+ dist: dist,
+ $brick: $b,
+ index: (point.x > dPoint.x) ? i : i+1
+ };
+ }
+ }
+
+ last = point;
+
+ });
+
+ return closest;
+ },
+
+ _initDrag : function($bricks){
+
+ var _this = this,
+ dragged = null,
+ pos, closest;
+
+ $bricks.bind('dragstart', function(e) {
+
+ // make sure we're dragging by the right thing
+ if(_this.options.dragHandleSelector !== null && !$(e.target).is(_this.options.dragHandleSelector)){
+ return false;
+ }
+
+ pos = $(this).position();
+
+ // add the dragClass
+ if(_this.options.dragClass !== null){
+ $(this).addClass(_this.options.dragClass);
+ }
+
+ // remove the brick being dragged from the array
+ dragged = this;
+ _this.$bricks = _this.$bricks.not(this);
+ _this._reLayout();
+
+ }).bind('drag', function(e, dd) {
+
+ $(this).css({
+ top : pos.top + dd.deltaY,
+ left : pos.left + dd.deltaX
+ });
+
+ }).bind('dragend', function(e) {
+
+ // remove the dragClass
+ if(_this.options.dragClass !== null){
+ $(this).removeClass(_this.options.dragClass);
+ }
+
+ // insert the brick back into the array
+ closest = _this._getClosestBrick(dragged);
+ _this.$bricks.splice(closest.index , 0, dragged);
+ dragged = null;
+
+ _this._reLayout();
+ });
}
};
View
402 js/jquery.event.drag-2.2.js
@@ -0,0 +1,402 @@
+/*!
+ * jquery.event.drag - v 2.2
+ * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com
+ * Open Source MIT License - http://threedubmedia.com/code/license
+ */
+// Created: 2008-06-04
+// Updated: 2012-05-21
+// REQUIRES: jquery 1.7.x
+
+;(function( $ ){
+
+// add the jquery instance method
+$.fn.drag = function( str, arg, opts ){
+ // figure out the event type
+ var type = typeof str == "string" ? str : "",
+ // figure out the event handler...
+ fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null;
+ // fix the event type
+ if ( type.indexOf("drag") !== 0 )
+ type = "drag"+ type;
+ // were options passed
+ opts = ( str == fn ? arg : opts ) || {};
+ // trigger or bind event handler
+ return fn ? this.bind( type, opts, fn ) : this.trigger( type );
+};
+
+// local refs (increase compression)
+var $event = $.event,
+$special = $event.special,
+// configure the drag special event
+drag = $special.drag = {
+
+ // these are the default settings
+ defaults: {
+ which: 1, // mouse button pressed to start drag sequence
+ distance: 0, // distance dragged before dragstart
+ not: ':input', // selector to suppress dragging on target elements
+ handle: null, // selector to match handle target elements
+ relative: false, // true to use "position", false to use "offset"
+ drop: true, // false to suppress drop events, true or selector to allow
+ click: false // false to suppress click events after dragend (no proxy)
+ },
+
+ // the key name for stored drag data
+ datakey: "dragdata",
+
+ // prevent bubbling for better performance
+ noBubble: true,
+
+ // count bound related events
+ add: function( obj ){
+ // read the interaction data
+ var data = $.data( this, drag.datakey ),
+ // read any passed options
+ opts = obj.data || {};
+ // count another realted event
+ data.related += 1;
+ // extend data options bound with this event
+ // don't iterate "opts" in case it is a node
+ $.each( drag.defaults, function( key, def ){
+ if ( opts[ key ] !== undefined )
+ data[ key ] = opts[ key ];
+ });
+ },
+
+ // forget unbound related events
+ remove: function(){
+ $.data( this, drag.datakey ).related -= 1;
+ },
+
+ // configure interaction, capture settings
+ setup: function(){
+ // check for related events
+ if ( $.data( this, drag.datakey ) )
+ return;
+ // initialize the drag data with copied defaults
+ var data = $.extend({ related:0 }, drag.defaults );
+ // store the interaction data
+ $.data( this, drag.datakey, data );
+ // bind the mousedown event, which starts drag interactions
+ $event.add( this, "touchstart mousedown", drag.init, data );
+ // prevent image dragging in IE...
+ if ( this.attachEvent )
+ this.attachEvent("ondragstart", drag.dontstart );
+ },
+
+ // destroy configured interaction
+ teardown: function(){
+ var data = $.data( this, drag.datakey ) || {};
+ // check for related events
+ if ( data.related )
+ return;
+ // remove the stored data
+ $.removeData( this, drag.datakey );
+ // remove the mousedown event
+ $event.remove( this, "touchstart mousedown", drag.init );
+ // enable text selection
+ drag.textselect( true );
+ // un-prevent image dragging in IE...
+ if ( this.detachEvent )
+ this.detachEvent("ondragstart", drag.dontstart );
+ },
+
+ // initialize the interaction
+ init: function( event ){
+ // sorry, only one touch at a time
+ if ( drag.touched )
+ return;
+ // the drag/drop interaction data
+ var dd = event.data, results;
+ // check the which directive
+ if ( event.which != 0 && dd.which > 0 && event.which != dd.which )
+ return;
+ // check for suppressed selector
+ if ( $( event.target ).is( dd.not ) )
+ return;
+ // check for handle selector
+ if ( dd.handle && !$( event.target ).closest( dd.handle, event.currentTarget ).length )
+ return;
+
+ drag.touched = event.type == 'touchstart' ? this : null;
+ dd.propagates = 1;
+ dd.mousedown = this;
+ dd.interactions = [ drag.interaction( this, dd ) ];
+ dd.target = event.target;
+ dd.pageX = event.pageX;
+ dd.pageY = event.pageY;
+ dd.dragging = null;
+ // handle draginit event...
+ results = drag.hijack( event, "draginit", dd );
+ // early cancel
+ if ( !dd.propagates )
+ return;
+ // flatten the result set
+ results = drag.flatten( results );
+ // insert new interaction elements
+ if ( results && results.length ){
+ dd.interactions = [];
+ $.each( results, function(){
+ dd.interactions.push( drag.interaction( this, dd ) );
+ });
+ }
+ // remember how many interactions are propagating
+ dd.propagates = dd.interactions.length;
+ // locate and init the drop targets
+ if ( dd.drop !== false && $special.drop )
+ $special.drop.handler( event, dd );
+ // disable text selection
+ drag.textselect( false );
+ // bind additional events...
+ if ( drag.touched )
+ $event.add( drag.touched, "touchmove touchend", drag.handler, dd );
+ else
+ $event.add( document, "mousemove mouseup", drag.handler, dd );
+ // helps prevent text selection or scrolling
+ if ( !drag.touched || dd.live )
+ return false;
+ },
+
+ // returns an interaction object
+ interaction: function( elem, dd ){
+ var offset = $( elem )[ dd.relative ? "position" : "offset" ]() || { top:0, left:0 };
+ return {
+ drag: elem,
+ callback: new drag.callback(),
+ droppable: [],
+ offset: offset
+ };
+ },
+
+ // handle drag-releatd DOM events
+ handler: function( event ){
+ // read the data before hijacking anything
+ var dd = event.data;
+ // handle various events
+ switch ( event.type ){
+ // mousemove, check distance, start dragging
+ case !dd.dragging && 'touchmove':
+ event.preventDefault();
+ case !dd.dragging && 'mousemove':
+ // drag tolerance, x² + y² = distance²
+ if ( Math.pow( event.pageX-dd.pageX, 2 ) + Math.pow( event.pageY-dd.pageY, 2 ) < Math.pow( dd.distance, 2 ) )
+ break; // distance tolerance not reached
+ event.target = dd.target; // force target from "mousedown" event (fix distance issue)
+ drag.hijack( event, "dragstart", dd ); // trigger "dragstart"
+ if ( dd.propagates ) // "dragstart" not rejected
+ dd.dragging = true; // activate interaction
+ // mousemove, dragging
+ case 'touchmove':
+ event.preventDefault();
+ case 'mousemove':
+ if ( dd.dragging ){
+ // trigger "drag"
+ drag.hijack( event, "drag", dd );
+ if ( dd.propagates ){
+ // manage drop events
+ if ( dd.drop !== false && $special.drop )
+ $special.drop.handler( event, dd ); // "dropstart", "dropend"
+ break; // "drag" not rejected, stop
+ }
+ event.type = "mouseup"; // helps "drop" handler behave
+ }
+ // mouseup, stop dragging
+ case 'touchend':
+ case 'mouseup':
+ default:
+ if ( drag.touched )
+ $event.remove( drag.touched, "touchmove touchend", drag.handler ); // remove touch events
+ else
+ $event.remove( document, "mousemove mouseup", drag.handler ); // remove page events
+ if ( dd.dragging ){
+ if ( dd.drop !== false && $special.drop )
+ $special.drop.handler( event, dd ); // "drop"
+ drag.hijack( event, "dragend", dd ); // trigger "dragend"
+ }
+ drag.textselect( true ); // enable text selection
+ // if suppressing click events...
+ if ( dd.click === false && dd.dragging )
+ $.data( dd.mousedown, "suppress.click", new Date().getTime() + 5 );
+ dd.dragging = drag.touched = false; // deactivate element
+ break;
+ }
+ },
+
+ // re-use event object for custom events
+ hijack: function( event, type, dd, x, elem ){
+ // not configured
+ if ( !dd )
+ return;
+ // remember the original event and type
+ var orig = { event:event.originalEvent, type:event.type },
+ // is the event drag related or drog related?
+ mode = type.indexOf("drop") ? "drag" : "drop",
+ // iteration vars
+ result, i = x || 0, ia, $elems, callback,
+ len = !isNaN( x ) ? x : dd.interactions.length;
+ // modify the event type
+ event.type = type;
+ // remove the original event
+ event.originalEvent = null;
+ // initialize the results
+ dd.results = [];
+ // handle each interacted element
+ do if ( ia = dd.interactions[ i ] ){
+ // validate the interaction
+ if ( type !== "dragend" && ia.cancelled )
+ continue;
+ // set the dragdrop properties on the event object
+ callback = drag.properties( event, dd, ia );
+ // prepare for more results
+ ia.results = [];
+ // handle each element
+ $( elem || ia[ mode ] || dd.droppable ).each(function( p, subject ){
+ // identify drag or drop targets individually
+ callback.target = subject;
+ // force propagtion of the custom event
+ event.isPropagationStopped = function(){ return false; };
+ // handle the event
+ result = subject ? $event.dispatch.call( subject, event, callback ) : null;
+ // stop the drag interaction for this element
+ if ( result === false ){
+ if ( mode == "drag" ){
+ ia.cancelled = true;
+ dd.propagates -= 1;
+ }
+ if ( type == "drop" ){
+ ia[ mode ][p] = null;
+ }
+ }
+ // assign any dropinit elements
+ else if ( type == "dropinit" )
+ ia.droppable.push( drag.element( result ) || subject );
+ // accept a returned proxy element
+ if ( type == "dragstart" )
+ ia.proxy = $( drag.element( result ) || ia.drag )[0];
+ // remember this result
+ ia.results.push( result );
+ // forget the event result, for recycling
+ delete event.result;
+ // break on cancelled handler
+ if ( type !== "dropinit" )
+ return result;
+ });
+ // flatten the results
+ dd.results[ i ] = drag.flatten( ia.results );
+ // accept a set of valid drop targets
+ if ( type == "dropinit" )
+ ia.droppable = drag.flatten( ia.droppable );
+ // locate drop targets
+ if ( type == "dragstart" && !ia.cancelled )
+ callback.update();
+ }
+ while ( ++i < len )
+ // restore the original event & type
+ event.type = orig.type;
+ event.originalEvent = orig.event;
+ // return all handler results
+ return drag.flatten( dd.results );
+ },
+
+ // extend the callback object with drag/drop properties...
+ properties: function( event, dd, ia ){
+ var obj = ia.callback;
+ // elements
+ obj.drag = ia.drag;
+ obj.proxy = ia.proxy || ia.drag;
+ // starting mouse position
+ obj.startX = dd.pageX;
+ obj.startY = dd.pageY;
+ // current distance dragged
+ obj.deltaX = event.pageX - dd.pageX;
+ obj.deltaY = event.pageY - dd.pageY;
+ // original element position
+ obj.originalX = ia.offset.left;
+ obj.originalY = ia.offset.top;
+ // adjusted element position
+ obj.offsetX = obj.originalX + obj.deltaX;
+ obj.offsetY = obj.originalY + obj.deltaY;
+ // assign the drop targets information
+ obj.drop = drag.flatten( ( ia.drop || [] ).slice() );
+ obj.available = drag.flatten( ( ia.droppable || [] ).slice() );
+ return obj;
+ },
+
+ // determine is the argument is an element or jquery instance
+ element: function( arg ){
+ if ( arg && ( arg.jquery || arg.nodeType == 1 ) )
+ return arg;
+ },
+
+ // flatten nested jquery objects and arrays into a single dimension array
+ flatten: function( arr ){
+ return $.map( arr, function( member ){
+ return member && member.jquery ? $.makeArray( member ) :
+ member && member.length ? drag.flatten( member ) : member;
+ });
+ },
+
+ // toggles text selection attributes ON (true) or OFF (false)
+ textselect: function( bool ){
+ $( document )[ bool ? "unbind" : "bind" ]("selectstart", drag.dontstart )
+ .css("MozUserSelect", bool ? "" : "none" );
+ // .attr("unselectable", bool ? "off" : "on" )
+ document.unselectable = bool ? "off" : "on";
+ },
+
+ // suppress "selectstart" and "ondragstart" events
+ dontstart: function(){
+ return false;
+ },
+
+ // a callback instance contructor
+ callback: function(){}
+
+};
+
+// callback methods
+drag.callback.prototype = {
+ update: function(){
+ if ( $special.drop && this.available.length )
+ $.each( this.available, function( i ){
+ $special.drop.locate( this, i );
+ });
+ }
+};
+
+// patch $.event.$dispatch to allow suppressing clicks
+var $dispatch = $event.dispatch;
+$event.dispatch = function( event ){
+ if ( $.data( this, "suppress."+ event.type ) - new Date().getTime() > 0 ){
+ $.removeData( this, "suppress."+ event.type );
+ return;
+ }
+ return $dispatch.apply( this, arguments );
+};
+
+// event fix hooks for touch events...
+var touchHooks =
+$event.fixHooks.touchstart =
+$event.fixHooks.touchmove =
+$event.fixHooks.touchend =
+$event.fixHooks.touchcancel = {
+ props: "clientX clientY pageX pageY screenX screenY".split( " " ),
+ filter: function( event, orig ) {
+ if ( orig ){
+ var touched = ( orig.touches && orig.touches[0] )
+ || ( orig.changedTouches && orig.changedTouches[0] )
+ || null;
+ // iOS webkit: touchstart, touchmove, touchend
+ if ( touched )
+ $.each( touchHooks.props, function( i, prop ){
+ event[ prop ] = touched[ prop ];
+ });
+ }
+ return event;
+ }
+};
+
+// share the same special event configuration with related events...
+$special.draginit = $special.dragstart = $special.dragend = drag;
+
+})( jQuery );

0 comments on commit 6219ab4

Please sign in to comment.