├── LICENSE ├── core ├── vendor │ └── jquery.touch-punch.min.js ├── jquery.shapeshift.min.js ├── jquery.shapeshift.coffee └── jquery.shapeshift.js ├── Shapeshift.jquery.json ├── demos ├── basic.html ├── mosaic.html ├── multiwidth.html ├── clone.html ├── shuffle.html └── trash.html └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2013 Scott Walter Elwood 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 5 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 6 | and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions 9 | of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 12 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 13 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 14 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 15 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /core/vendor/jquery.touch-punch.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI Touch Punch 0.2.2 3 | * 4 | * Copyright 2011, Dave Furfero 5 | * Dual licensed under the MIT or GPL Version 2 licenses. 6 | * 7 | * Depends: 8 | * jquery.ui.widget.js 9 | * jquery.ui.mouse.js 10 | */ 11 | (function(b){b.support.touch="ontouchend" in document;if(!b.support.touch){return;}var c=b.ui.mouse.prototype,e=c._mouseInit,a;function d(g,h){if(g.originalEvent.touches.length>1){return;}g.preventDefault();var i=g.originalEvent.changedTouches[0],f=document.createEvent("MouseEvents");f.initMouseEvent(h,true,true,window,1,i.screenX,i.screenY,i.clientX,i.clientY,false,false,false,false,0,null);g.target.dispatchEvent(f);}c._touchStart=function(g){var f=this;if(a||!f._mouseCapture(g.originalEvent.changedTouches[0])){return;}a=true;f._touchMoved=false;d(g,"mouseover");d(g,"mousemove");d(g,"mousedown");};c._touchMove=function(f){if(!a){return;}this._touchMoved=true;d(f,"mousemove");};c._touchEnd=function(f){if(!a){return;}d(f,"mouseup");d(f,"mouseout");if(!this._touchMoved){d(f,"click");}a=false;};c._mouseInit=function(){var f=this;f.element.bind("touchstart",b.proxy(f,"_touchStart")).bind("touchmove",b.proxy(f,"_touchMove")).bind("touchend",b.proxy(f,"_touchEnd"));e.call(f);};})(jQuery); -------------------------------------------------------------------------------- /Shapeshift.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Shapeshift", 3 | "title": "jQuery Shapeshift", 4 | "description": "jQuery plugin which can arrange a collection of elements into a grid while also providing drag and drop functionality.", 5 | "keywords": [ 6 | "grid", 7 | "drag", 8 | "drop", 9 | "arrange", 10 | "organize", 11 | "animation" 12 | ], 13 | "version": "2.0.0", 14 | "author": { 15 | "name": "Scott Elwood", 16 | "url": "http://scottelwood.com" 17 | }, 18 | "maintainers": [ 19 | { 20 | "name": "We the Media, inc.", 21 | "email": "dev@wtmworldwide.com", 22 | "url": "http://wtmworldwide.com" 23 | }, 24 | { 25 | "name": "Scott Elwood", 26 | "email": "scott@wtmworldwide.com", 27 | "url": "http://scottelwood.com" 28 | } 29 | ], 30 | "licenses": [ 31 | { 32 | "type": "MIT", 33 | "url": "https://github.com/McPants/jquery.shapeshift/blob/master/LICENSE" 34 | } 35 | ], 36 | "bugs": "https://github.com/McPants/jquery.shapeshift/issues", 37 | "homepage": "https://github.com/McPants/jquery.shapeshift", 38 | "docs": "https://github.com/McPants/jquery.shapeshift", 39 | "download": "https://github.com/McPants/jquery.shapeshift", 40 | "dependencies": { 41 | "jquery": ">=1.9.0", 42 | "jquery-ui": ">=1.10.0" 43 | } 44 | } -------------------------------------------------------------------------------- /demos/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Basic Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 36 | 37 | 38 | 43 | 44 | 45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | 56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | 67 | -------------------------------------------------------------------------------- /demos/mosaic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Basic Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 37 | 38 | 39 | 49 | 50 | 51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | 62 | -------------------------------------------------------------------------------- /demos/multiwidth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Multiwidth Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 39 | 40 | 41 | 48 | 49 | 50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | 59 | -------------------------------------------------------------------------------- /demos/clone.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Clone Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 37 | 38 | 39 | 50 | 51 | 52 |
53 |
A
54 |
B
55 |
C
56 |
D
57 |
E
58 |
59 | 60 |
61 |
62 |
63 |
64 | 65 | -------------------------------------------------------------------------------- /demos/shuffle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Basic Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 42 | 43 | 44 | 54 | 55 | 56 |
57 |
1
58 |
2
59 |
3
60 |
4
61 |
5
62 |
6
63 |
7
64 |
8
65 |
66 | 67 | -------------------------------------------------------------------------------- /demos/trash.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Basic Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 44 | 45 | 46 | 56 | 57 | 58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | 69 |
70 | Drag items in here to destroy them. 71 |
72 | 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Shapeshift v2.0 2 | =============== 3 | 4 | [Check out a demo here.](http://mcpants.github.com/jquery.shapeshift/) 5 | 6 | **April 16th, 2013: Version 2.0 released.** 7 | **There may be bugs and we are still browser testing. Please report any bugs you find through issues.** 8 | 9 | 10 | Column Grid System + Drag and Drop 11 | ---------------------------------- 12 | 13 | Inspired heavily by the [jQuery Masonry plugin](http://masonry.desandro.com/), Shapeshift is a plugin which will dynamically arrange a collection of elements into a column grid system similar to [Pinterest](http://www.pinterest.com). What sets it apart is the ability to drag and drop items within the grid while still maintaining a logical index position for each item. This allows for the grid to be rendered exactly the same every time Shapeshift is used, as long as the child elements are in the correct order. 14 | 15 | 16 | Features 17 | -------- 18 | 19 | * **Drag and Drop** 20 | Rearrange items within a container or even drag items between multiple Shapeshift enabled containers. Dragging elements around will physically change their index position within their parent container. When a page reloads, as long as the child elements are placed in the correct order then the grid will look exactly the same. 21 | 22 | * **Works on Touch Devices** 23 | Shapeshift uses jQuery UI Draggable/Droppable for help with the drag and drop system. Luckily there is already a plugin called [jQuery Touch Punch](http://touchpunch.furf.com/) which provides touch support for jQuery UI D/D. It can be found in the vendor folder. 24 | 25 | * **Multiwidth Elements** 26 | A new feature in 2.0 is the ability to add elements that can span across multiple columns as long as their width is correctly set through CSS. 27 | 28 | * **Responsive Grid** 29 | Enabled by default, Shapeshift will listen for window resize events and arrange the elements within it according to the space provided by their parent container. 30 | 31 | 32 | Documentation 33 | ------------- 34 | Check out our [Wiki](https://github.com/McPants/jquery.shapeshift/wiki) for [full documentation](https://github.com/McPants/jquery.shapeshift/wiki/2.0-api-documentation). 35 | 36 | 37 | Credits 38 | ------- 39 | 40 | A big thanks to all of our [contributors](https://github.com/McPants/jquery.shapeshift/graphs/contributors)! 41 | 42 | ![we the media](http://wtmworldwide.com/wtm.png) 43 | 44 | Shapeshift is maintained by [We The Media, inc.](http://wtmworldwide.com/) 45 | 46 | 47 | Sites Using Shapeshift 48 | ---------------------- 49 | 50 | Got a project that you are using shapeshift on? Let us know and we will happily throw a link to your page here! 51 | 52 | 53 | For Contributors 54 | ---------------- 55 | 56 | Feel like you've got an idea on how to optimize the code and want to share it? We are totally open to new changes, however this is one of the first publically available plugins that I am offering and therefore do not have an exact process on pull requests. Feel free to fork the project all you want, but be aware any pull requests that are made may take a while to get implemented (if at all). 57 | -------------------------------------------------------------------------------- /core/jquery.shapeshift.min.js: -------------------------------------------------------------------------------- 1 | (function(){(function(e,c,a){var b,f,d;d="shapeshift";f={selector:"*",enableDrag:true,enableCrossDrop:true,enableResize:true,enableTrash:false,align:"center",colWidth:null,columns:null,minColumns:1,autoHeight:true,maxHeight:null,minHeight:100,gutterX:10,gutterY:10,paddingX:10,paddingY:10,animated:true,animateOnInit:false,animationSpeed:225,animationThreshold:100,dragClone:false,deleteClone:true,dragRate:100,dragWhitelist:"*",crossDropWhitelist:"*",cutoffStart:null,cutoffEnd:null,handle:false,cloneClass:"ss-cloned-child",activeClass:"ss-active-child",draggedClass:"ss-dragged-child",placeholderClass:"ss-placeholder-child",originalContainerClass:"ss-original-container",currentContainerClass:"ss-current-container",previousContainerClass:"ss-previous-container"};b=(function(){function g(i,h){this.element=i;this.options=e.extend({},f,h);this.globals={};this.$container=e(i);if(this.errorCheck()){this.init()}}g.prototype.errorCheck=function(){var h,j,k,i;i=this.options;k=false;j="Shapeshift ERROR:";if(i.colWidth===null){h=this.$container.children(i.selector);if(h.length===0){k=true;console.error(""+j+" option colWidth must be specified if Shapeshift is initialized with no active children.")}}return !k};g.prototype.init=function(){this.createEvents();this.setGlobals();this.setIdentifier();this.setActiveChildren();this.enableFeatures();this.gridInit();this.render();return this.afterInit()};g.prototype.createEvents=function(){var i,h,j=this;h=this.options;i=this.$container;i.off("ss-arrange").on("ss-arrange",function(l,k){if(k==null){k=false}return j.render(false,k)});i.off("ss-rearrange").on("ss-rearrange",function(){return j.render(true)});i.off("ss-setTargetPosition").on("ss-setTargetPosition",function(){return j.setTargetPosition()});i.off("ss-destroy").on("ss-destroy",function(){return j.destroy()});return i.off("ss-shuffle").on("ss-shuffle",function(){return j.shuffle()})};g.prototype.setGlobals=function(){this.globals.animated=this.options.animateOnInit;return this.globals.dragging=false};g.prototype.afterInit=function(){return this.globals.animated=this.options.animated};g.prototype.setIdentifier=function(){this.identifier="shapeshifted_container_"+Math.random().toString(36).substring(7);return this.$container.addClass(this.identifier)};g.prototype.enableFeatures=function(){if(this.options.enableResize){this.enableResize()}if(this.options.enableDrag||this.options.enableCrossDrop){return this.enableDragNDrop()}};g.prototype.setActiveChildren=function(){var s,p,h,j,o,q,t,r,m,l,n,k;t=this.options;s=this.$container.children(t.selector);p=t.activeClass;r=s.length;for(o=m=0;0<=r?mr;o=0<=r?++m:--m){e(s[o]).addClass(p)}this.setParsedChildren();j=t.columns;k=[];for(o=l=0,n=this.parsedChildren.length;0<=n?ln;o=0<=n?++l:--l){h=this.parsedChildren[o].colspan;q=t.minColumns;if(h>j&&h>q){t.minColumns=h;k.push(console.error("Shapeshift ERROR: There are child elements that have a larger colspan than the minimum columns set through options.\noptions.minColumns has been set to "+h))}else{k.push(void 0)}}return k};g.prototype.setParsedChildren=function(){var m,h,o,j,n,k,l;h=this.$container.find("."+this.options.activeClass).filter(":visible");k=h.length;n=[];for(j=l=0;0<=k?lk;j=0<=k?++l:--l){m=e(h[j]);o={i:j,el:m,colspan:parseInt(m.attr("data-ss-colspan"))||1,height:m.outerHeight()};n.push(o)}return this.parsedChildren=n};g.prototype.gridInit=function(){var i,h,k,j,l;j=this.options.gutterX;if(!(this.options.colWidth>=1)){k=this.parsedChildren[0];h=k.el.outerWidth();i=k.colspan;l=(h-((i-1)*j))/i;return this.globals.col_width=l+j}else{return this.globals.col_width=this.options.colWidth+j}};g.prototype.render=function(i,h){if(i==null){i=false}this.setGridColumns();return this.arrange(i,h)};g.prototype.setGridColumns=function(){var s,v,u,j,l,q,k,t,p,h,r,w,m,n,o;q=this.globals;w=this.options;u=q.col_width;t=w.gutterX;m=w.paddingX;h=this.$container.innerWidth()-(m*2);r=w.minColumns;l=w.columns||Math.floor((h+t)/u);if(r&&r>l){l=r}q.columns=l;v=this.parsedChildren.length;if(l>v){s=0;for(p=n=0,o=this.parsedChildren.length;0<=o?no;p=0<=o?++n:--n){j=this.parsedChildren[p].colspan;if(j+s<=l){s+=j}}l=s}q.child_offset=m;switch(w.align){case"center":k=(l*u)-t;return q.child_offset+=(h-k)/2;case"right":k=(l*u)-t;return q.child_offset+=h-k}};g.prototype.arrange=function(m,y){var z,v,x,h,r,o,s,q,n,w,j,u,B,p,A,t,l,k;if(m){this.setParsedChildren()}n=this.globals;p=this.options;v=this.$container;o=this.getPositions();A=this.parsedChildren;l=A.length;x=n.animated&&l<=p.animationThreshold;h=p.animationSpeed;q=p.draggedClass;for(w=k=0;0<=l?kl;w=0<=l?++k:--k){z=A[w].el;r=o[w];j=z.hasClass(q);if(j){t=p.placeholderClass;z=z.siblings("."+t)}if(x&&!j){z.stop(true,false).animate(r,h,function(){})}else{z.css(r)}}if(y){if(x){setTimeout((function(){return v.trigger("ss-drop-complete")}),h)}else{v.trigger("ss-drop-complete")}}v.trigger("ss-arranged");if(p.autoHeight){s=n.container_height;u=p.maxHeight;B=p.minHeight;if(B&&su){s=u}}return v.height(s)}};g.prototype.getPositions=function(v){var n,y,q,o,l,z,h,w,m,t,B,p,A,x,s,k,j,r,u=this;if(v==null){v=true}l=this.globals;m=this.options;h=m.gutterY;t=m.paddingY;o=m.draggedClass;B=this.parsedChildren;k=B.length;n=[];for(w=j=0,r=l.columns;0<=r?jr;w=0<=r?++j:--j){n.push(t)}x=function(I){var D,H,C,G,E,F,i;D=I.col;H=I.colspan;G=(I.col*l.col_width)+l.child_offset;E=n[D];p[I.i]={left:G,top:E};n[D]+=I.height+h;if(H>=1){i=[];for(C=F=1;1<=H?FH;C=1<=H?++F:--F){i.push(n[D+C]=n[D])}return i}};y=function(i){var N,E,C,M,I,D,H,L,J,K,G,F;J=n.length-i.colspan+1;L=n.slice(0).splice(0,J);N=void 0;for(H=G=0;0<=J?GJ;H=0<=J?++G:--G){E=u.lowestCol(L,H);C=i.colspan;M=n[E];I=true;for(K=F=1;1<=C?FC;K=1<=C?++F:--F){D=n[E+K];if(MH;I=0<=H?++G:--G){E=s[I];E.col=y(E);if(E.col>=0){x(E);K.push(I)}}F=[];for(i=D=C=K.length-1;D>=0;i=D+=-1){J=K[i];F.push(s.splice(J,1))}return F};p=[];(q=function(){var D,C,i;i=[];for(w=C=0;0<=k?Ck;w=0<=k?++C:--C){D=B[w];if(!(!v&&D.el.hasClass(o))){if(D.colspan>1){D.col=y(D)}else{D.col=u.lowestCol(n)}if(D.col===void 0){s.push(D)}else{x(D)}i.push(A())}else{i.push(void 0)}}return i})();if(m.autoHeight){z=n[this.highestCol(n)]-h;l.container_height=z+t}return p};g.prototype.enableDragNDrop=function(){var p,v,n,y,l,t,o,w,q,z,s,m,j,x,r,k,i,h,u=this;j=this.options;v=this.$container;l=j.activeClass;m=j.draggedClass;r=j.placeholderClass;x=j.originalContainerClass;o=j.currentContainerClass;k=j.previousContainerClass;w=j.deleteClone;z=j.dragRate;q=j.dragClone;t=j.cloneClass;y=n=p=h=i=null;s=false;if(j.enableDrag){v.children("."+l).filter(j.dragWhitelist).draggable({addClasses:false,containment:"document",handle:j.handle,zIndex:9999,start:function(C,B){var A;u.globals.dragging=true;y=e(C.target);if(q){p=y.clone(false,false).insertBefore(y).addClass(t)}y.addClass(m);A=y.prop("tagName");n=e("<"+A+" class='"+r+"' style='height: "+(y.height())+"px; width: "+(y.width())+"px'>");y.parent().addClass(x).addClass(o);h=y.outerHeight()/2;return i=y.outerWidth()/2},drag:function(B,A){if(!s&&!(q&&w&&e("."+o)[0]===e("."+x)[0])){n.remove().appendTo("."+o);e("."+o).trigger("ss-setTargetPosition");s=true;c.setTimeout((function(){return s=false}),z)}A.position.left=B.pageX-y.parent().offset().left-i;return A.position.top=B.pageY-y.parent().offset().top-h},stop:function(){var A,C,B;u.globals.dragging=false;C=e("."+x);A=e("."+o);B=e("."+k);y.removeClass(m);e("."+r).remove();if(q){if(w&&e("."+o)[0]===e("."+x)[0]){p.remove();e("."+o).trigger("ss-rearrange")}else{p.removeClass(t)}}if(C[0]===A[0]){A.trigger("ss-rearranged",y)}else{C.trigger("ss-removed",y);A.trigger("ss-added",y)}C.trigger("ss-arrange").removeClass(x);A.trigger("ss-arrange",true).removeClass(o);B.trigger("ss-arrange").removeClass(k);return y=n=null}})}if(j.enableCrossDrop){return v.droppable({accept:j.crossDropWhitelist,tolerance:"intersect",over:function(A){e("."+k).removeClass(k);e("."+o).removeClass(o).addClass(k);return e(A.target).addClass(o)},drop:function(E,B){var A,D,C;if(u.options.enableTrash){D=e("."+x);A=e("."+o);C=e("."+k);y=e(B.helper);A.trigger("ss-trashed",y);y.remove();D.trigger("ss-rearrange").removeClass(x);A.trigger("ss-rearrange").removeClass(o);return C.trigger("ss-arrange").removeClass(k)}}})}};g.prototype.setTargetPosition=function(){var C,A,k,t,j,y,u,n,p,m,B,w,z,o,s,q,r,v,x,h,l,i;m=this.options;if(!m.enableTrash){p=m.draggedClass;C=e("."+p);A=C.parent();B=this.parsedChildren;j=this.getPositions(false);x=j.length;s=C.offset().left-A.offset().left+(this.globals.col_width/2);q=C.offset().top-A.offset().top+(C.height()/2);r=9999999;v=0;if(x>1){u=m.cutoffStart+1||0;y=m.cutoffEnd||x;for(z=i=u;u<=y?iy;z=u<=y?++i:--i){t=j[z];if(t){l=s-t.left;h=q-t.top;if(l>0&&h>0){n=Math.sqrt((h*h)+(l*l));if(nB[z].height/2){v++}}}}}}if(v===B.length){k=B[v-1].el;C.insertAfter(k)}else{k=B[v].el;C.insertBefore(k)}}else{if(x===1){t=j[0];if(t.leftk;j=0<=k?++l:--l){h.push([n[j],j])}h.sort(function(o,i){var p;p=o[0]-i[0];if(p===0){p=o[1]-i[1]}return p});return h[m][1]};g.prototype.highestCol=function(h){return e.inArray(Math.max.apply(c,h),h)};g.prototype.destroy=function(){var h,j,i;j=this.$container;j.off("ss-arrange");j.off("ss-rearrange");j.off("ss-setTargetPosition");j.off("ss-destroy");i=this.options.activeClass;h=j.find("."+i);if(this.options.enableDrag){h.draggable("destroy")}if(this.options.enableCrossDrop){j.droppable("destroy")}h.removeClass(i);return j.removeClass(this.identifier)};return g})();return e.fn[d]=function(g){return this.each(function(){var k,i,j,h;i=(j=e(this).attr("class"))!=null?(h=j.match(/shapeshifted_container_\w+/))!=null?h[0]:void 0:void 0;if(i){k="resize."+i;e(c).off(k);e(this).removeClass(i)}return e.data(this,"plugin_"+d,new b(this,g))})}})(jQuery,window,document)}).call(this); -------------------------------------------------------------------------------- /core/jquery.shapeshift.coffee: -------------------------------------------------------------------------------- 1 | # Project: jQuery.Shapeshift 2 | # Description: Align elements to grid with drag and drop. 3 | # Author: Scott Elwood 4 | # Maintained By: We the Media, inc. 5 | # License: MIT 6 | 7 | (($, window, document) -> 8 | pluginName = "shapeshift" 9 | defaults = 10 | # The Basics 11 | selector: "*" 12 | 13 | # Features 14 | enableDrag: true 15 | enableCrossDrop: true 16 | enableResize: true 17 | enableTrash: false 18 | 19 | # Grid Properties 20 | align: "center" 21 | colWidth: null 22 | columns: null 23 | minColumns: 1 24 | autoHeight: true 25 | maxHeight: null 26 | minHeight: 100 27 | gutterX: 10 28 | gutterY: 10 29 | paddingX: 10 30 | paddingY: 10 31 | 32 | # Animation 33 | animated: true 34 | animateOnInit: false 35 | animationSpeed: 225 36 | animationThreshold: 100 37 | 38 | # Drag/Drop Options 39 | dragClone: false 40 | deleteClone: true 41 | dragRate: 100 42 | dragWhitelist: "*" 43 | crossDropWhitelist: "*" 44 | cutoffStart: null 45 | cutoffEnd: null 46 | handle: false 47 | 48 | # Customize CSS 49 | cloneClass: "ss-cloned-child" 50 | activeClass: "ss-active-child" 51 | draggedClass: "ss-dragged-child" 52 | placeholderClass: "ss-placeholder-child" 53 | originalContainerClass: "ss-original-container" 54 | currentContainerClass: "ss-current-container" 55 | previousContainerClass: "ss-previous-container" 56 | 57 | class Plugin 58 | constructor: (@element, options) -> 59 | @options = $.extend {}, defaults, options 60 | @globals = {} 61 | @$container = $(element) 62 | 63 | if @errorCheck() 64 | @init() 65 | 66 | 67 | # ---------------------------- 68 | # errorCheck: 69 | # Determine if there are any conflicting options 70 | # ---------------------------- 71 | errorCheck: -> 72 | options = @options 73 | errors = false 74 | error_msg = "Shapeshift ERROR:" 75 | 76 | # If there are no available children, a colWidth must be set 77 | if options.colWidth is null 78 | $children = @$container.children(options.selector) 79 | 80 | if $children.length is 0 81 | errors = true 82 | console.error "#{error_msg} option colWidth must be specified if Shapeshift is initialized with no active children." 83 | 84 | return !errors 85 | 86 | # ---------------------------- 87 | # Init: 88 | # Only enable features on initialization, 89 | # then call a full render of the elements 90 | # ---------------------------- 91 | init: -> 92 | @createEvents() 93 | @setGlobals() 94 | @setIdentifier() 95 | @setActiveChildren() 96 | @enableFeatures() 97 | @gridInit() 98 | @render() 99 | @afterInit() 100 | 101 | # ---------------------------- 102 | # createEvents: 103 | # Triggerable events on the container 104 | # which run certain functions 105 | # ---------------------------- 106 | createEvents: -> 107 | options = @options 108 | $container = @$container 109 | 110 | $container.off("ss-arrange").on "ss-arrange", (e, trigger_drop_finished = false) => @render(false, trigger_drop_finished) 111 | $container.off("ss-rearrange").on "ss-rearrange", => @render(true) 112 | $container.off("ss-setTargetPosition").on "ss-setTargetPosition", => @setTargetPosition() 113 | $container.off("ss-destroy").on "ss-destroy", => @destroy() 114 | $container.off("ss-shuffle").on "ss-shuffle", => @shuffle() 115 | 116 | # ---------------------------- 117 | # setGlobals: 118 | # Globals that only need to be set on initialization 119 | # ---------------------------- 120 | setGlobals: -> 121 | # Prevent initial animation if applicable 122 | @globals.animated = @options.animateOnInit 123 | @globals.dragging = false 124 | 125 | # ---------------------------- 126 | # afterInit: 127 | # Take care of some dirty business 128 | # ---------------------------- 129 | afterInit: -> 130 | # Return animation to normal 131 | @globals.animated = @options.animated 132 | 133 | # ---------------------------- 134 | # setIdentifier 135 | # Create a random identifier to tie to this container so that 136 | # it is easy to unbind the specific resize event from the browser 137 | # ---------------------------- 138 | setIdentifier: -> 139 | @identifier = "shapeshifted_container_" + Math.random().toString(36).substring(7) 140 | @$container.addClass(@identifier) 141 | 142 | # ---------------------------- 143 | # enableFeatures: 144 | # Enables options features 145 | # ---------------------------- 146 | enableFeatures: -> 147 | @enableResize() if @options.enableResize 148 | @enableDragNDrop() if @options.enableDrag or @options.enableCrossDrop 149 | 150 | # ---------------------------- 151 | # setActiveChildren: 152 | # Make sure that only the children set by the 153 | # selector option can be affected by Shapeshifting 154 | # ---------------------------- 155 | setActiveChildren: -> 156 | options = @options 157 | 158 | # Add active child class to each available child element 159 | $children = @$container.children(options.selector) 160 | active_child_class = options.activeClass 161 | total = $children.length 162 | 163 | for i in [0...total] 164 | $($children[i]).addClass(active_child_class) 165 | 166 | @setParsedChildren() 167 | 168 | # Detect if there are any colspans wider than 169 | # the column options that were set 170 | columns = options.columns 171 | for i in [0...@parsedChildren.length] 172 | colspan = @parsedChildren[i].colspan 173 | 174 | min_columns = options.minColumns 175 | if colspan > columns and colspan > min_columns 176 | options.minColumns = colspan 177 | console.error "Shapeshift ERROR: There are child elements that have a larger colspan than the minimum columns set through options.\noptions.minColumns has been set to #{colspan}" 178 | 179 | # ---------------------------- 180 | # setParsedChildren: 181 | # Calculates and returns commonly used 182 | # attributes for all the active children 183 | # ---------------------------- 184 | setParsedChildren: -> 185 | $children = @$container.find("." + @options.activeClass).filter(":visible") 186 | total = $children.length 187 | 188 | parsedChildren = [] 189 | for i in [0...total] 190 | $child = $($children[i]) 191 | child = 192 | i: i 193 | el: $child 194 | colspan: parseInt($child.attr("data-ss-colspan")) || 1 195 | height: $child.outerHeight() 196 | parsedChildren.push child 197 | @parsedChildren = parsedChildren 198 | 199 | # ---------------------------- 200 | # setGrid: 201 | # Calculates the dimensions of each column 202 | # and determines to total number of columns 203 | # ---------------------------- 204 | gridInit: -> 205 | gutter_x = @options.gutterX 206 | 207 | unless @options.colWidth >= 1 208 | # Determine single item / col width 209 | first_child = @parsedChildren[0] 210 | fc_width = first_child.el.outerWidth() 211 | fc_colspan = first_child.colspan 212 | single_width = (fc_width - ((fc_colspan - 1) * gutter_x)) / fc_colspan 213 | @globals.col_width = single_width + gutter_x 214 | else 215 | @globals.col_width = @options.colWidth + gutter_x 216 | 217 | # ---------------------------- 218 | # render: 219 | # Determine the active children and 220 | # arrange them to the calculated grid 221 | # ---------------------------- 222 | render: (reparse = false, trigger_drop_finished) -> 223 | @setActiveChildren() if reparse 224 | @setGridColumns() 225 | @arrange(false, trigger_drop_finished) 226 | 227 | # ---------------------------- 228 | # setGrid: 229 | # Calculates the dimensions of each column 230 | # and determines to total number of columns 231 | # ---------------------------- 232 | setGridColumns: -> 233 | # Common 234 | globals = @globals 235 | options = @options 236 | col_width = globals.col_width 237 | gutter_x = options.gutterX 238 | padding_x = options.paddingX 239 | inner_width = @$container.innerWidth() - (padding_x * 2) 240 | 241 | # Determine how many columns there currently can be 242 | minColumns = options.minColumns 243 | columns = options.columns || Math.floor (inner_width + gutter_x) / col_width 244 | if minColumns and minColumns > columns 245 | columns = minColumns 246 | globals.columns = columns 247 | 248 | # Columns cannot exceed children span 249 | children_count = @parsedChildren.length 250 | if columns > children_count 251 | actual_columns = 0 252 | for i in [0...@parsedChildren.length] 253 | colspan = @parsedChildren[i].colspan 254 | 255 | if colspan + actual_columns <= columns 256 | actual_columns += colspan 257 | 258 | columns = actual_columns 259 | 260 | # Calculate the child offset from the left 261 | globals.child_offset = padding_x 262 | switch options.align 263 | when "center" 264 | grid_width = (columns * col_width) - gutter_x 265 | globals.child_offset += (inner_width - grid_width) / 2 266 | 267 | when "right" 268 | grid_width = (columns * col_width) - gutter_x 269 | globals.child_offset += (inner_width - grid_width) 270 | 271 | # ---------------------------- 272 | # arrange: 273 | # Animates the elements into their calcluated positions 274 | # ---------------------------- 275 | arrange: (reparse, trigger_drop_finished) -> 276 | @setParsedChildren() if reparse 277 | 278 | globals = @globals 279 | options = @options 280 | 281 | # Common 282 | $container = @$container 283 | child_positions = @getPositions() 284 | 285 | parsed_children = @parsedChildren 286 | total_children = parsed_children.length 287 | 288 | animated = globals.animated and total_children <= options.animationThreshold 289 | animation_speed = options.animationSpeed 290 | dragged_class = options.draggedClass 291 | 292 | # Arrange each child element 293 | for i in [0...total_children] 294 | $child = parsed_children[i].el 295 | attributes = child_positions[i] 296 | is_dragged_child = $child.hasClass(dragged_class) 297 | 298 | if is_dragged_child 299 | placeholder_class = options.placeholderClass 300 | $child = $child.siblings("." + placeholder_class) 301 | 302 | if animated and !is_dragged_child 303 | $child.stop(true, false).animate attributes, animation_speed, -> 304 | else 305 | $child.css attributes 306 | 307 | 308 | if trigger_drop_finished 309 | if animated 310 | setTimeout (-> 311 | $container.trigger("ss-drop-complete") 312 | ), animation_speed 313 | else 314 | $container.trigger("ss-drop-complete") 315 | $container.trigger("ss-arranged") 316 | 317 | # Set the container height 318 | if options.autoHeight 319 | container_height = globals.container_height 320 | max_height = options.maxHeight 321 | min_height = options.minHeight 322 | 323 | if min_height and container_height < min_height 324 | container_height = min_height 325 | else if max_height and container_height > max_height 326 | container_height = max_height 327 | 328 | $container.height container_height 329 | 330 | # ---------------------------- 331 | # getPositions: 332 | # Go over each child and determine which column they 333 | # fit into and return an array of their x/y dimensions 334 | # ---------------------------- 335 | getPositions: (include_dragged = true) -> 336 | globals = @globals 337 | options = @options 338 | gutter_y = options.gutterY 339 | padding_y = options.paddingY 340 | dragged_class = options.draggedClass 341 | 342 | parsed_children = @parsedChildren 343 | total_children = parsed_children.length 344 | 345 | # Store the height for each column 346 | col_heights = [] 347 | for i in [0...globals.columns] 348 | col_heights.push padding_y 349 | 350 | # ---------------------------- 351 | # savePosition 352 | # Takes a child which has been correctly placed in a 353 | # column and saves it to that final x/y position. 354 | # ---------------------------- 355 | savePosition = (child) => 356 | col = child.col 357 | colspan = child.colspan 358 | offset_x = (child.col * globals.col_width) + globals.child_offset 359 | offset_y = col_heights[col] 360 | 361 | positions[child.i] = left: offset_x, top: offset_y 362 | col_heights[col] += child.height + gutter_y 363 | 364 | if colspan >= 1 365 | for j in [1...colspan] 366 | col_heights[col + j] = col_heights[col] 367 | 368 | # ---------------------------- 369 | # determineMultiposition 370 | # Children with multiple column spans will need special 371 | # rules to determine if they are currently able to be 372 | # placed in the grid. 373 | # ---------------------------- 374 | determineMultiposition = (child) => 375 | # Only use the columns that this child can fit into 376 | possible_cols = col_heights.length - child.colspan + 1 377 | possible_col_heights = col_heights.slice(0).splice(0, possible_cols) 378 | 379 | chosen_col = undefined 380 | for offset in [0...possible_cols] 381 | col = @lowestCol(possible_col_heights, offset) 382 | colspan = child.colspan 383 | height = col_heights[col] 384 | 385 | kosher = true 386 | 387 | # Determine if it is able to be placed at this col 388 | for span in [1...colspan] 389 | next_height = col_heights[col + span] 390 | 391 | # The next height must not be higher 392 | if height < next_height 393 | kosher = false 394 | break 395 | 396 | if kosher 397 | chosen_col = col 398 | break 399 | 400 | return chosen_col 401 | 402 | # ---------------------------- 403 | # recalculateSavedChildren 404 | # Sometimes child elements cannot save the first time around, 405 | # iterate over those children and determine if its ok to place now. 406 | # ---------------------------- 407 | saved_children = [] 408 | recalculateSavedChildren = => 409 | to_pop = [] 410 | for saved_i in [0...saved_children.length] 411 | saved_child = saved_children[saved_i] 412 | saved_child.col = determineMultiposition(saved_child) 413 | 414 | if saved_child.col >= 0 415 | savePosition(saved_child) 416 | to_pop.push(saved_i) 417 | 418 | # Popeye. Lol. 419 | for pop_i in [to_pop.length - 1..0] by -1 420 | index = to_pop[pop_i] 421 | saved_children.splice(index,1) 422 | 423 | # ---------------------------- 424 | # determinePositions 425 | # Iterate over all the parsed children and determine 426 | # the calculations needed to get its x/y value. 427 | # ---------------------------- 428 | positions = [] 429 | do determinePositions = => 430 | for i in [0...total_children] 431 | child = parsed_children[i] 432 | 433 | unless !include_dragged and child.el.hasClass(dragged_class) 434 | if child.colspan > 1 435 | child.col = determineMultiposition(child) 436 | else 437 | child.col = @lowestCol(col_heights) 438 | 439 | if child.col is undefined 440 | saved_children.push child 441 | else 442 | savePosition(child) 443 | 444 | recalculateSavedChildren() 445 | 446 | # Store the container height since we already have the data 447 | if options.autoHeight 448 | grid_height = col_heights[@highestCol(col_heights)] - gutter_y 449 | globals.container_height = grid_height + padding_y 450 | 451 | return positions 452 | 453 | # ---------------------------- 454 | # enableDrag: 455 | # Optional feature. 456 | # Initialize dragging. 457 | # ---------------------------- 458 | enableDragNDrop: -> 459 | options = @options 460 | 461 | $container = @$container 462 | active_class = options.activeClass 463 | dragged_class = options.draggedClass 464 | placeholder_class = options.placeholderClass 465 | original_container_class = options.originalContainerClass 466 | current_container_class = options.currentContainerClass 467 | previous_container_class = options.previousContainerClass 468 | delete_clone = options.deleteClone 469 | drag_rate = options.dragRate 470 | drag_clone = options.dragClone 471 | clone_class = options.cloneClass 472 | 473 | $selected = $placeholder = $clone = selected_offset_y = selected_offset_x = null 474 | drag_timeout = false 475 | 476 | if options.enableDrag 477 | $container.children("." + active_class).filter(options.dragWhitelist).draggable 478 | addClasses: false 479 | containment: 'document' 480 | handle: options.handle 481 | zIndex: 9999 482 | 483 | start: (e, ui) => 484 | @globals.dragging = true 485 | 486 | # Set $selected globals 487 | $selected = $(e.target) 488 | 489 | if drag_clone 490 | $clone = $selected.clone(false, false).insertBefore($selected).addClass(clone_class) 491 | 492 | $selected.addClass(dragged_class) 493 | 494 | # Create Placeholder 495 | selected_tag = $selected.prop("tagName") 496 | $placeholder = $("<#{selected_tag} class='#{placeholder_class}' style='height: #{$selected.height()}px; width: #{$selected.width()}px'>") 497 | 498 | # Set current container 499 | $selected.parent().addClass(original_container_class).addClass(current_container_class) 500 | 501 | # For manually centering the element with respect to mouse position 502 | selected_offset_y = $selected.outerHeight() / 2 503 | selected_offset_x = $selected.outerWidth() / 2 504 | 505 | drag: (e, ui) => 506 | if !drag_timeout and !(drag_clone and delete_clone and $("." + current_container_class)[0] is $("." + original_container_class)[0]) 507 | # Append placeholder to container 508 | $placeholder.remove().appendTo("." + current_container_class) 509 | 510 | # Set drag target and rearrange everything 511 | $("." + current_container_class).trigger("ss-setTargetPosition") 512 | 513 | # Disallow dragging from occurring too much 514 | drag_timeout = true 515 | window.setTimeout ( -> 516 | drag_timeout = false 517 | ), drag_rate 518 | 519 | # Manually center the element with respect to mouse position 520 | ui.position.left = e.pageX - $selected.parent().offset().left - selected_offset_x; 521 | ui.position.top = e.pageY - $selected.parent().offset().top - selected_offset_y; 522 | 523 | stop: => 524 | @globals.dragging = false 525 | 526 | $original_container = $("." + original_container_class) 527 | $current_container = $("." + current_container_class) 528 | $previous_container = $("." + previous_container_class) 529 | 530 | # Clear globals 531 | $selected.removeClass(dragged_class) 532 | $("." + placeholder_class).remove() 533 | 534 | if drag_clone 535 | if delete_clone and $("." + current_container_class)[0] is $("." + original_container_class)[0] 536 | $clone.remove() 537 | $("." + current_container_class).trigger("ss-rearrange") 538 | else 539 | $clone.removeClass(clone_class) 540 | 541 | # Trigger Events 542 | if $original_container[0] is $current_container[0] 543 | $current_container.trigger("ss-rearranged", $selected) 544 | else 545 | $original_container.trigger("ss-removed", $selected) 546 | $current_container.trigger("ss-added", $selected) 547 | 548 | # Arrange dragged item into place and clear container classes 549 | $original_container.trigger("ss-arrange").removeClass(original_container_class) 550 | $current_container.trigger("ss-arrange", true).removeClass(current_container_class) 551 | $previous_container.trigger("ss-arrange").removeClass(previous_container_class) 552 | 553 | $selected = $placeholder = null 554 | 555 | 556 | if options.enableCrossDrop 557 | $container.droppable 558 | accept: options.crossDropWhitelist 559 | tolerance: 'intersect' 560 | over: (e) => 561 | $("." + previous_container_class).removeClass(previous_container_class) 562 | $("." + current_container_class).removeClass(current_container_class).addClass(previous_container_class) 563 | $(e.target).addClass(current_container_class) 564 | 565 | drop: (e, selected) => 566 | if @options.enableTrash 567 | $original_container = $("." + original_container_class) 568 | $current_container = $("." + current_container_class) 569 | $previous_container = $("." + previous_container_class) 570 | $selected = $(selected.helper) 571 | 572 | $current_container.trigger("ss-trashed", $selected) 573 | $selected.remove() 574 | 575 | $original_container.trigger("ss-rearrange").removeClass(original_container_class) 576 | $current_container.trigger("ss-rearrange").removeClass(current_container_class) 577 | $previous_container.trigger("ss-arrange").removeClass(previous_container_class) 578 | 579 | # ---------------------------- 580 | # getTargetPosition: 581 | # Determine the target position for the selected 582 | # element and arrange it into place 583 | # ---------------------------- 584 | setTargetPosition: -> 585 | options = @options 586 | 587 | unless options.enableTrash 588 | dragged_class = options.draggedClass 589 | 590 | $selected = $("." + dragged_class) 591 | $start_container = $selected.parent() 592 | parsed_children = @parsedChildren 593 | child_positions = @getPositions(false) 594 | total_positions = child_positions.length 595 | 596 | selected_x = $selected.offset().left - $start_container.offset().left + (@globals.col_width / 2) 597 | selected_y = $selected.offset().top - $start_container.offset().top + ($selected.height() / 2) 598 | 599 | shortest_distance = 9999999 600 | target_position = 0 601 | 602 | if total_positions > 1 603 | cutoff_start = options.cutoffStart + 1 || 0 604 | cutoff_end = options.cutoffEnd || total_positions 605 | 606 | for position_i in [cutoff_start...cutoff_end] 607 | attributes = child_positions[position_i] 608 | 609 | if attributes 610 | y_dist = selected_x - attributes.left 611 | x_dist = selected_y - attributes.top 612 | 613 | if y_dist > 0 and x_dist > 0 614 | distance = Math.sqrt((x_dist * x_dist) + (y_dist * y_dist)) 615 | 616 | if distance < shortest_distance 617 | shortest_distance = distance 618 | target_position = position_i 619 | 620 | if position_i is total_positions - 1 621 | if y_dist > parsed_children[position_i].height / 2 622 | target_position++ 623 | 624 | 625 | 626 | if target_position is parsed_children.length 627 | $target = parsed_children[target_position - 1].el 628 | $selected.insertAfter($target) 629 | else 630 | $target = parsed_children[target_position].el 631 | $selected.insertBefore($target) 632 | else 633 | if total_positions is 1 634 | attributes = child_positions[0] 635 | 636 | if attributes.left < selected_x 637 | @$container.append $selected 638 | else 639 | @$container.prepend $selected 640 | else 641 | @$container.append $selected 642 | 643 | @arrange(true) 644 | 645 | if $start_container[0] isnt $selected.parent()[0] 646 | previous_container_class = options.previousContainerClass 647 | $("." + previous_container_class).trigger "ss-rearrange" 648 | else 649 | placeholder_class = @options.placeholderClass 650 | $("." + placeholder_class).remove() 651 | 652 | # ---------------------------- 653 | # resize: 654 | # Optional feature. 655 | # Runs a full render of the elements when 656 | # the browser window is resized. 657 | # ---------------------------- 658 | enableResize: -> 659 | animation_speed = @options.animationSpeed 660 | 661 | resizing = false 662 | binding = "resize." + @identifier 663 | $(window).on binding, => 664 | unless resizing 665 | resizing = true 666 | 667 | # Some funkyness to prevent too many renderings 668 | setTimeout (=> @render()), animation_speed / 3 669 | setTimeout (=> @render()), animation_speed / 3 670 | 671 | setTimeout => 672 | resizing = false 673 | @render() 674 | , animation_speed / 3 675 | 676 | # ---------------------------- 677 | # shuffle: 678 | # Randomly sort the child elements 679 | # ---------------------------- 680 | shuffle: -> 681 | calculateShuffled = (container, activeClass) -> 682 | shuffle = (arr) -> 683 | j = undefined 684 | x = undefined 685 | i = arr.length 686 | 687 | while i 688 | j = parseInt(Math.random() * i) 689 | x = arr[--i] 690 | arr[i] = arr[j] 691 | arr[j] = x 692 | arr 693 | return container.each(-> 694 | items = container.find("." + activeClass).filter(":visible") 695 | (if (items.length) then container.html(shuffle(items)) else this) 696 | ) 697 | 698 | unless @globals.dragging 699 | calculateShuffled @$container, @options.activeClass 700 | @enableFeatures() 701 | @$container.trigger "ss-rearrange" 702 | 703 | # ---------------------------- 704 | # lowestCol: 705 | # Helper 706 | # Returns the index position of the 707 | # array column with the lowest number 708 | # ---------------------------- 709 | lowestCol: (array, offset = 0) -> 710 | length = array.length 711 | augmented_array = [] 712 | 713 | for i in [0...length] 714 | augmented_array.push [array[i], i] 715 | 716 | augmented_array.sort (a, b) -> 717 | ret = a[0] - b[0] 718 | ret = a[1] - b[1] if ret is 0 719 | ret 720 | augmented_array[offset][1] 721 | 722 | # ---------------------------- 723 | # highestCol: 724 | # Helper 725 | # Returns the index position of the 726 | # array column with the highest number 727 | # ---------------------------- 728 | highestCol: (array) -> 729 | $.inArray Math.max.apply(window,array), array 730 | 731 | # ---------------------------- 732 | # destroy: 733 | # ---------------------------- 734 | destroy: -> 735 | $container = @$container 736 | $container.off("ss-arrange") 737 | $container.off("ss-rearrange") 738 | $container.off("ss-setTargetPosition") 739 | $container.off("ss-destroy") 740 | 741 | active_class = @options.activeClass 742 | $active_children = $container.find("." + active_class) 743 | 744 | if @options.enableDrag 745 | $active_children.draggable('destroy') 746 | if @options.enableCrossDrop 747 | $container.droppable('destroy') 748 | 749 | $active_children.removeClass(active_class) 750 | $container.removeClass(@identifier) 751 | 752 | 753 | $.fn[pluginName] = (options) -> 754 | @each -> 755 | # Destroy any old resize events 756 | old_class = $(@).attr("class")?.match(/shapeshifted_container_\w+/)?[0] 757 | if old_class 758 | bound_indentifier = "resize." + old_class 759 | $(window).off(bound_indentifier) 760 | $(@).removeClass(old_class) 761 | 762 | # Create the new plugin instance 763 | $.data(@, "plugin_#{pluginName}", new Plugin(@, options)) 764 | 765 | )(jQuery, window, document) 766 | -------------------------------------------------------------------------------- /core/jquery.shapeshift.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.4.0 2 | (function() { 3 | 4 | (function($, window, document) { 5 | var Plugin, defaults, pluginName; 6 | pluginName = "shapeshift"; 7 | defaults = { 8 | selector: "*", 9 | enableDrag: true, 10 | enableCrossDrop: true, 11 | enableResize: true, 12 | enableTrash: false, 13 | align: "center", 14 | colWidth: null, 15 | columns: null, 16 | minColumns: 1, 17 | autoHeight: true, 18 | maxHeight: null, 19 | minHeight: 100, 20 | gutterX: 10, 21 | gutterY: 10, 22 | paddingX: 10, 23 | paddingY: 10, 24 | animated: true, 25 | animateOnInit: false, 26 | animationSpeed: 225, 27 | animationThreshold: 100, 28 | dragClone: false, 29 | deleteClone: true, 30 | dragRate: 100, 31 | dragWhitelist: "*", 32 | crossDropWhitelist: "*", 33 | cutoffStart: null, 34 | cutoffEnd: null, 35 | handle: false, 36 | cloneClass: "ss-cloned-child", 37 | activeClass: "ss-active-child", 38 | draggedClass: "ss-dragged-child", 39 | placeholderClass: "ss-placeholder-child", 40 | originalContainerClass: "ss-original-container", 41 | currentContainerClass: "ss-current-container", 42 | previousContainerClass: "ss-previous-container" 43 | }; 44 | Plugin = (function() { 45 | 46 | function Plugin(element, options) { 47 | this.element = element; 48 | this.options = $.extend({}, defaults, options); 49 | this.globals = {}; 50 | this.$container = $(element); 51 | if (this.errorCheck()) { 52 | this.init(); 53 | } 54 | } 55 | 56 | Plugin.prototype.errorCheck = function() { 57 | var $children, error_msg, errors, options; 58 | options = this.options; 59 | errors = false; 60 | error_msg = "Shapeshift ERROR:"; 61 | if (options.colWidth === null) { 62 | $children = this.$container.children(options.selector); 63 | if ($children.length === 0) { 64 | errors = true; 65 | console.error("" + error_msg + " option colWidth must be specified if Shapeshift is initialized with no active children."); 66 | } 67 | } 68 | return !errors; 69 | }; 70 | 71 | Plugin.prototype.init = function() { 72 | this.createEvents(); 73 | this.setGlobals(); 74 | this.setIdentifier(); 75 | this.setActiveChildren(); 76 | this.enableFeatures(); 77 | this.gridInit(); 78 | this.render(); 79 | return this.afterInit(); 80 | }; 81 | 82 | Plugin.prototype.createEvents = function() { 83 | var $container, options, 84 | _this = this; 85 | options = this.options; 86 | $container = this.$container; 87 | $container.off("ss-arrange").on("ss-arrange", function(e, trigger_drop_finished) { 88 | if (trigger_drop_finished == null) { 89 | trigger_drop_finished = false; 90 | } 91 | return _this.render(false, trigger_drop_finished); 92 | }); 93 | $container.off("ss-rearrange").on("ss-rearrange", function() { 94 | return _this.render(true); 95 | }); 96 | $container.off("ss-setTargetPosition").on("ss-setTargetPosition", function() { 97 | return _this.setTargetPosition(); 98 | }); 99 | $container.off("ss-destroy").on("ss-destroy", function() { 100 | return _this.destroy(); 101 | }); 102 | return $container.off("ss-shuffle").on("ss-shuffle", function() { 103 | return _this.shuffle(); 104 | }); 105 | }; 106 | 107 | Plugin.prototype.setGlobals = function() { 108 | this.globals.animated = this.options.animateOnInit; 109 | return this.globals.dragging = false; 110 | }; 111 | 112 | Plugin.prototype.afterInit = function() { 113 | return this.globals.animated = this.options.animated; 114 | }; 115 | 116 | Plugin.prototype.setIdentifier = function() { 117 | this.identifier = "shapeshifted_container_" + Math.random().toString(36).substring(7); 118 | return this.$container.addClass(this.identifier); 119 | }; 120 | 121 | Plugin.prototype.enableFeatures = function() { 122 | if (this.options.enableResize) { 123 | this.enableResize(); 124 | } 125 | if (this.options.enableDrag || this.options.enableCrossDrop) { 126 | return this.enableDragNDrop(); 127 | } 128 | }; 129 | 130 | Plugin.prototype.setActiveChildren = function() { 131 | var $children, active_child_class, colspan, columns, i, min_columns, options, total, _i, _j, _ref, _results; 132 | options = this.options; 133 | $children = this.$container.children(options.selector); 134 | active_child_class = options.activeClass; 135 | total = $children.length; 136 | for (i = _i = 0; 0 <= total ? _i < total : _i > total; i = 0 <= total ? ++_i : --_i) { 137 | $($children[i]).addClass(active_child_class); 138 | } 139 | this.setParsedChildren(); 140 | columns = options.columns; 141 | _results = []; 142 | for (i = _j = 0, _ref = this.parsedChildren.length; 0 <= _ref ? _j < _ref : _j > _ref; i = 0 <= _ref ? ++_j : --_j) { 143 | colspan = this.parsedChildren[i].colspan; 144 | min_columns = options.minColumns; 145 | if (colspan > columns && colspan > min_columns) { 146 | options.minColumns = colspan; 147 | _results.push(console.error("Shapeshift ERROR: There are child elements that have a larger colspan than the minimum columns set through options.\noptions.minColumns has been set to " + colspan)); 148 | } else { 149 | _results.push(void 0); 150 | } 151 | } 152 | return _results; 153 | }; 154 | 155 | Plugin.prototype.setParsedChildren = function() { 156 | var $child, $children, child, i, parsedChildren, total, _i; 157 | $children = this.$container.find("." + this.options.activeClass).filter(":visible"); 158 | total = $children.length; 159 | parsedChildren = []; 160 | for (i = _i = 0; 0 <= total ? _i < total : _i > total; i = 0 <= total ? ++_i : --_i) { 161 | $child = $($children[i]); 162 | child = { 163 | i: i, 164 | el: $child, 165 | colspan: parseInt($child.attr("data-ss-colspan")) || 1, 166 | height: $child.outerHeight() 167 | }; 168 | parsedChildren.push(child); 169 | } 170 | return this.parsedChildren = parsedChildren; 171 | }; 172 | 173 | Plugin.prototype.gridInit = function() { 174 | var fc_colspan, fc_width, first_child, gutter_x, single_width; 175 | gutter_x = this.options.gutterX; 176 | if (!(this.options.colWidth >= 1)) { 177 | first_child = this.parsedChildren[0]; 178 | fc_width = first_child.el.outerWidth(); 179 | fc_colspan = first_child.colspan; 180 | single_width = (fc_width - ((fc_colspan - 1) * gutter_x)) / fc_colspan; 181 | return this.globals.col_width = single_width + gutter_x; 182 | } else { 183 | return this.globals.col_width = this.options.colWidth + gutter_x; 184 | } 185 | }; 186 | 187 | Plugin.prototype.render = function(reparse, trigger_drop_finished) { 188 | if (reparse == null) { 189 | reparse = false; 190 | } 191 | if (reparse) { 192 | this.setActiveChildren(); 193 | } 194 | this.setGridColumns(); 195 | return this.arrange(false, trigger_drop_finished); 196 | }; 197 | 198 | Plugin.prototype.setGridColumns = function() { 199 | var actual_columns, children_count, col_width, colspan, columns, globals, grid_width, gutter_x, i, inner_width, minColumns, options, padding_x, _i, _ref; 200 | globals = this.globals; 201 | options = this.options; 202 | col_width = globals.col_width; 203 | gutter_x = options.gutterX; 204 | padding_x = options.paddingX; 205 | inner_width = this.$container.innerWidth() - (padding_x * 2); 206 | minColumns = options.minColumns; 207 | columns = options.columns || Math.floor((inner_width + gutter_x) / col_width); 208 | if (minColumns && minColumns > columns) { 209 | columns = minColumns; 210 | } 211 | globals.columns = columns; 212 | children_count = this.parsedChildren.length; 213 | if (columns > children_count) { 214 | actual_columns = 0; 215 | for (i = _i = 0, _ref = this.parsedChildren.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { 216 | colspan = this.parsedChildren[i].colspan; 217 | if (colspan + actual_columns <= columns) { 218 | actual_columns += colspan; 219 | } 220 | } 221 | columns = actual_columns; 222 | } 223 | globals.child_offset = padding_x; 224 | switch (options.align) { 225 | case "center": 226 | grid_width = (columns * col_width) - gutter_x; 227 | return globals.child_offset += (inner_width - grid_width) / 2; 228 | case "right": 229 | grid_width = (columns * col_width) - gutter_x; 230 | return globals.child_offset += inner_width - grid_width; 231 | } 232 | }; 233 | 234 | Plugin.prototype.arrange = function(reparse, trigger_drop_finished) { 235 | var $child, $container, animated, animation_speed, attributes, child_positions, container_height, dragged_class, globals, i, is_dragged_child, max_height, min_height, options, parsed_children, placeholder_class, total_children, _i; 236 | if (reparse) { 237 | this.setParsedChildren(); 238 | } 239 | globals = this.globals; 240 | options = this.options; 241 | $container = this.$container; 242 | child_positions = this.getPositions(); 243 | parsed_children = this.parsedChildren; 244 | total_children = parsed_children.length; 245 | animated = globals.animated && total_children <= options.animationThreshold; 246 | animation_speed = options.animationSpeed; 247 | dragged_class = options.draggedClass; 248 | for (i = _i = 0; 0 <= total_children ? _i < total_children : _i > total_children; i = 0 <= total_children ? ++_i : --_i) { 249 | $child = parsed_children[i].el; 250 | attributes = child_positions[i]; 251 | is_dragged_child = $child.hasClass(dragged_class); 252 | if (is_dragged_child) { 253 | placeholder_class = options.placeholderClass; 254 | $child = $child.siblings("." + placeholder_class); 255 | } 256 | if (animated && !is_dragged_child) { 257 | $child.stop(true, false).animate(attributes, animation_speed, function() {}); 258 | } else { 259 | $child.css(attributes); 260 | } 261 | } 262 | if (trigger_drop_finished) { 263 | if (animated) { 264 | setTimeout((function() { 265 | return $container.trigger("ss-drop-complete"); 266 | }), animation_speed); 267 | } else { 268 | $container.trigger("ss-drop-complete"); 269 | } 270 | } 271 | $container.trigger("ss-arranged"); 272 | if (options.autoHeight) { 273 | container_height = globals.container_height; 274 | max_height = options.maxHeight; 275 | min_height = options.minHeight; 276 | if (min_height && container_height < min_height) { 277 | container_height = min_height; 278 | } else if (max_height && container_height > max_height) { 279 | container_height = max_height; 280 | } 281 | return $container.height(container_height); 282 | } 283 | }; 284 | 285 | Plugin.prototype.getPositions = function(include_dragged) { 286 | var col_heights, determineMultiposition, determinePositions, dragged_class, globals, grid_height, gutter_y, i, options, padding_y, parsed_children, positions, recalculateSavedChildren, savePosition, saved_children, total_children, _i, _ref, 287 | _this = this; 288 | if (include_dragged == null) { 289 | include_dragged = true; 290 | } 291 | globals = this.globals; 292 | options = this.options; 293 | gutter_y = options.gutterY; 294 | padding_y = options.paddingY; 295 | dragged_class = options.draggedClass; 296 | parsed_children = this.parsedChildren; 297 | total_children = parsed_children.length; 298 | col_heights = []; 299 | for (i = _i = 0, _ref = globals.columns; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { 300 | col_heights.push(padding_y); 301 | } 302 | savePosition = function(child) { 303 | var col, colspan, j, offset_x, offset_y, _j, _results; 304 | col = child.col; 305 | colspan = child.colspan; 306 | offset_x = (child.col * globals.col_width) + globals.child_offset; 307 | offset_y = col_heights[col]; 308 | positions[child.i] = { 309 | left: offset_x, 310 | top: offset_y 311 | }; 312 | col_heights[col] += child.height + gutter_y; 313 | if (colspan >= 1) { 314 | _results = []; 315 | for (j = _j = 1; 1 <= colspan ? _j < colspan : _j > colspan; j = 1 <= colspan ? ++_j : --_j) { 316 | _results.push(col_heights[col + j] = col_heights[col]); 317 | } 318 | return _results; 319 | } 320 | }; 321 | determineMultiposition = function(child) { 322 | var chosen_col, col, colspan, height, kosher, next_height, offset, possible_col_heights, possible_cols, span, _j, _k; 323 | possible_cols = col_heights.length - child.colspan + 1; 324 | possible_col_heights = col_heights.slice(0).splice(0, possible_cols); 325 | chosen_col = void 0; 326 | for (offset = _j = 0; 0 <= possible_cols ? _j < possible_cols : _j > possible_cols; offset = 0 <= possible_cols ? ++_j : --_j) { 327 | col = _this.lowestCol(possible_col_heights, offset); 328 | colspan = child.colspan; 329 | height = col_heights[col]; 330 | kosher = true; 331 | for (span = _k = 1; 1 <= colspan ? _k < colspan : _k > colspan; span = 1 <= colspan ? ++_k : --_k) { 332 | next_height = col_heights[col + span]; 333 | if (height < next_height) { 334 | kosher = false; 335 | break; 336 | } 337 | } 338 | if (kosher) { 339 | chosen_col = col; 340 | break; 341 | } 342 | } 343 | return chosen_col; 344 | }; 345 | saved_children = []; 346 | recalculateSavedChildren = function() { 347 | var index, pop_i, saved_child, saved_i, to_pop, _j, _k, _ref1, _ref2, _results; 348 | to_pop = []; 349 | for (saved_i = _j = 0, _ref1 = saved_children.length; 0 <= _ref1 ? _j < _ref1 : _j > _ref1; saved_i = 0 <= _ref1 ? ++_j : --_j) { 350 | saved_child = saved_children[saved_i]; 351 | saved_child.col = determineMultiposition(saved_child); 352 | if (saved_child.col >= 0) { 353 | savePosition(saved_child); 354 | to_pop.push(saved_i); 355 | } 356 | } 357 | _results = []; 358 | for (pop_i = _k = _ref2 = to_pop.length - 1; _k >= 0; pop_i = _k += -1) { 359 | index = to_pop[pop_i]; 360 | _results.push(saved_children.splice(index, 1)); 361 | } 362 | return _results; 363 | }; 364 | positions = []; 365 | (determinePositions = function() { 366 | var child, _j, _results; 367 | _results = []; 368 | for (i = _j = 0; 0 <= total_children ? _j < total_children : _j > total_children; i = 0 <= total_children ? ++_j : --_j) { 369 | child = parsed_children[i]; 370 | if (!(!include_dragged && child.el.hasClass(dragged_class))) { 371 | if (child.colspan > 1) { 372 | child.col = determineMultiposition(child); 373 | } else { 374 | child.col = _this.lowestCol(col_heights); 375 | } 376 | if (child.col === void 0) { 377 | saved_children.push(child); 378 | } else { 379 | savePosition(child); 380 | } 381 | _results.push(recalculateSavedChildren()); 382 | } else { 383 | _results.push(void 0); 384 | } 385 | } 386 | return _results; 387 | })(); 388 | if (options.autoHeight) { 389 | grid_height = col_heights[this.highestCol(col_heights)] - gutter_y; 390 | globals.container_height = grid_height + padding_y; 391 | } 392 | return positions; 393 | }; 394 | 395 | Plugin.prototype.enableDragNDrop = function() { 396 | var $clone, $container, $placeholder, $selected, active_class, clone_class, current_container_class, delete_clone, drag_clone, drag_rate, drag_timeout, dragged_class, options, original_container_class, placeholder_class, previous_container_class, selected_offset_x, selected_offset_y, 397 | _this = this; 398 | options = this.options; 399 | $container = this.$container; 400 | active_class = options.activeClass; 401 | dragged_class = options.draggedClass; 402 | placeholder_class = options.placeholderClass; 403 | original_container_class = options.originalContainerClass; 404 | current_container_class = options.currentContainerClass; 405 | previous_container_class = options.previousContainerClass; 406 | delete_clone = options.deleteClone; 407 | drag_rate = options.dragRate; 408 | drag_clone = options.dragClone; 409 | clone_class = options.cloneClass; 410 | $selected = $placeholder = $clone = selected_offset_y = selected_offset_x = null; 411 | drag_timeout = false; 412 | if (options.enableDrag) { 413 | $container.children("." + active_class).filter(options.dragWhitelist).draggable({ 414 | addClasses: false, 415 | containment: 'document', 416 | handle: options.handle, 417 | zIndex: 9999, 418 | start: function(e, ui) { 419 | var selected_tag; 420 | _this.globals.dragging = true; 421 | $selected = $(e.target); 422 | if (drag_clone) { 423 | $clone = $selected.clone(false, false).insertBefore($selected).addClass(clone_class); 424 | } 425 | $selected.addClass(dragged_class); 426 | selected_tag = $selected.prop("tagName"); 427 | $placeholder = $("<" + selected_tag + " class='" + placeholder_class + "' style='height: " + ($selected.height()) + "px; width: " + ($selected.width()) + "px'>"); 428 | $selected.parent().addClass(original_container_class).addClass(current_container_class); 429 | selected_offset_y = $selected.outerHeight() / 2; 430 | return selected_offset_x = $selected.outerWidth() / 2; 431 | }, 432 | drag: function(e, ui) { 433 | if (!drag_timeout && !(drag_clone && delete_clone && $("." + current_container_class)[0] === $("." + original_container_class)[0])) { 434 | $placeholder.remove().appendTo("." + current_container_class); 435 | $("." + current_container_class).trigger("ss-setTargetPosition"); 436 | drag_timeout = true; 437 | window.setTimeout((function() { 438 | return drag_timeout = false; 439 | }), drag_rate); 440 | } 441 | ui.position.left = e.pageX - $selected.parent().offset().left - selected_offset_x; 442 | return ui.position.top = e.pageY - $selected.parent().offset().top - selected_offset_y; 443 | }, 444 | stop: function() { 445 | var $current_container, $original_container, $previous_container; 446 | _this.globals.dragging = false; 447 | $original_container = $("." + original_container_class); 448 | $current_container = $("." + current_container_class); 449 | $previous_container = $("." + previous_container_class); 450 | $selected.removeClass(dragged_class); 451 | $("." + placeholder_class).remove(); 452 | if (drag_clone) { 453 | if (delete_clone && $("." + current_container_class)[0] === $("." + original_container_class)[0]) { 454 | $clone.remove(); 455 | $("." + current_container_class).trigger("ss-rearrange"); 456 | } else { 457 | $clone.removeClass(clone_class); 458 | $original_container.shapeshift($original_container.data("plugin_shapeshift").options); 459 | $current_container.shapeshift($current_container.data("plugin_shapeshift").options); 460 | } 461 | } 462 | if ($original_container[0] === $current_container[0]) { 463 | $current_container.trigger("ss-rearranged", $selected); 464 | } else { 465 | $original_container.trigger("ss-removed", $selected); 466 | $current_container.trigger("ss-added", $selected); 467 | } 468 | $original_container.trigger("ss-arrange").removeClass(original_container_class); 469 | $current_container.trigger("ss-arrange", true).removeClass(current_container_class); 470 | $previous_container.trigger("ss-arrange").removeClass(previous_container_class); 471 | return $selected = $placeholder = null; 472 | } 473 | }); 474 | } 475 | if (options.enableCrossDrop) { 476 | return $container.droppable({ 477 | accept: options.crossDropWhitelist, 478 | tolerance: 'intersect', 479 | over: function(e) { 480 | $("." + previous_container_class).removeClass(previous_container_class); 481 | $("." + current_container_class).removeClass(current_container_class).addClass(previous_container_class); 482 | return $(e.target).addClass(current_container_class); 483 | }, 484 | drop: function(e, selected) { 485 | var $current_container, $original_container, $previous_container; 486 | if (_this.options.enableTrash) { 487 | $original_container = $("." + original_container_class); 488 | $current_container = $("." + current_container_class); 489 | $previous_container = $("." + previous_container_class); 490 | $selected = $(selected.helper); 491 | $current_container.trigger("ss-trashed", $selected); 492 | $selected.remove(); 493 | $original_container.trigger("ss-rearrange").removeClass(original_container_class); 494 | $current_container.trigger("ss-rearrange").removeClass(current_container_class); 495 | return $previous_container.trigger("ss-arrange").removeClass(previous_container_class); 496 | } 497 | } 498 | }); 499 | } 500 | }; 501 | 502 | Plugin.prototype.setTargetPosition = function() { 503 | var $selected, $start_container, $target, attributes, child_positions, cutoff_end, cutoff_start, distance, dragged_class, options, parsed_children, placeholder_class, position_i, previous_container_class, selected_x, selected_y, shortest_distance, target_position, total_positions, x_dist, y_dist, _i; 504 | options = this.options; 505 | if (!options.enableTrash) { 506 | dragged_class = options.draggedClass; 507 | $selected = $("." + dragged_class); 508 | $start_container = $selected.parent(); 509 | parsed_children = this.parsedChildren; 510 | child_positions = this.getPositions(false); 511 | total_positions = child_positions.length; 512 | selected_x = $selected.offset().left - $start_container.offset().left + (this.globals.col_width / 2); 513 | selected_y = $selected.offset().top - $start_container.offset().top + ($selected.height() / 2); 514 | shortest_distance = 9999999; 515 | target_position = 0; 516 | if (total_positions > 1) { 517 | cutoff_start = options.cutoffStart + 1 || 0; 518 | cutoff_end = options.cutoffEnd || total_positions; 519 | for (position_i = _i = cutoff_start; cutoff_start <= cutoff_end ? _i < cutoff_end : _i > cutoff_end; position_i = cutoff_start <= cutoff_end ? ++_i : --_i) { 520 | attributes = child_positions[position_i]; 521 | if (attributes) { 522 | y_dist = selected_x - attributes.left; 523 | x_dist = selected_y - attributes.top; 524 | if (y_dist > 0 && x_dist > 0) { 525 | distance = Math.sqrt((x_dist * x_dist) + (y_dist * y_dist)); 526 | if (distance < shortest_distance) { 527 | shortest_distance = distance; 528 | target_position = position_i; 529 | if (position_i === total_positions - 1) { 530 | if (y_dist > parsed_children[position_i].height / 2) { 531 | target_position++; 532 | } 533 | } 534 | } 535 | } 536 | } 537 | } 538 | if (target_position === parsed_children.length) { 539 | $target = parsed_children[target_position - 1].el; 540 | $selected.insertAfter($target); 541 | } else { 542 | $target = parsed_children[target_position].el; 543 | $selected.insertBefore($target); 544 | } 545 | } else { 546 | if (total_positions === 1) { 547 | attributes = child_positions[0]; 548 | if (attributes.left < selected_x) { 549 | this.$container.append($selected); 550 | } else { 551 | this.$container.prepend($selected); 552 | } 553 | } else { 554 | this.$container.append($selected); 555 | } 556 | } 557 | this.arrange(true); 558 | if ($start_container[0] !== $selected.parent()[0]) { 559 | previous_container_class = options.previousContainerClass; 560 | if ($("." + previous_container_class).data("plugin_shapeshift").options.enableCrossDrop == true) { 561 | return $("." + previous_container_class).trigger("ss-rearrange"); 562 | } else { 563 | return $("." + previous_container_class); 564 | } 565 | } 566 | } else { 567 | placeholder_class = this.options.placeholderClass; 568 | return $("." + placeholder_class).remove(); 569 | } 570 | }; 571 | 572 | Plugin.prototype.enableResize = function() { 573 | var animation_speed, binding, resizing, 574 | _this = this; 575 | animation_speed = this.options.animationSpeed; 576 | resizing = false; 577 | binding = "resize." + this.identifier; 578 | return $(window).on(binding, function() { 579 | if (!resizing) { 580 | resizing = true; 581 | setTimeout((function() { 582 | return _this.render(); 583 | }), animation_speed / 3); 584 | setTimeout((function() { 585 | return _this.render(); 586 | }), animation_speed / 3); 587 | return setTimeout(function() { 588 | resizing = false; 589 | return _this.render(); 590 | }, animation_speed / 3); 591 | } 592 | }); 593 | }; 594 | 595 | Plugin.prototype.shuffle = function() { 596 | var calculateShuffled; 597 | calculateShuffled = function(container, activeClass) { 598 | var shuffle; 599 | shuffle = function(arr) { 600 | var i, j, x; 601 | j = void 0; 602 | x = void 0; 603 | i = arr.length; 604 | while (i) { 605 | j = parseInt(Math.random() * i); 606 | x = arr[--i]; 607 | arr[i] = arr[j]; 608 | arr[j] = x; 609 | } 610 | return arr; 611 | }; 612 | return container.each(function() { 613 | var items; 614 | items = container.find("." + activeClass).filter(":visible"); 615 | if (items.length) { 616 | return container.html(shuffle(items)); 617 | } else { 618 | return this; 619 | } 620 | }); 621 | }; 622 | if (!this.globals.dragging) { 623 | calculateShuffled(this.$container, this.options.activeClass); 624 | this.enableFeatures(); 625 | return this.$container.trigger("ss-rearrange"); 626 | } 627 | }; 628 | 629 | Plugin.prototype.lowestCol = function(array, offset) { 630 | var augmented_array, i, length, _i; 631 | if (offset == null) { 632 | offset = 0; 633 | } 634 | length = array.length; 635 | augmented_array = []; 636 | for (i = _i = 0; 0 <= length ? _i < length : _i > length; i = 0 <= length ? ++_i : --_i) { 637 | augmented_array.push([array[i], i]); 638 | } 639 | augmented_array.sort(function(a, b) { 640 | var ret; 641 | ret = a[0] - b[0]; 642 | if (ret === 0) { 643 | ret = a[1] - b[1]; 644 | } 645 | return ret; 646 | }); 647 | return augmented_array[offset][1]; 648 | }; 649 | 650 | Plugin.prototype.highestCol = function(array) { 651 | return $.inArray(Math.max.apply(window, array), array); 652 | }; 653 | 654 | Plugin.prototype.destroy = function() { 655 | var $active_children, $container, active_class; 656 | $container = this.$container; 657 | $container.off("ss-arrange"); 658 | $container.off("ss-rearrange"); 659 | $container.off("ss-setTargetPosition"); 660 | $container.off("ss-destroy"); 661 | active_class = this.options.activeClass; 662 | $active_children = $container.find("." + active_class); 663 | if (this.options.enableDrag) { 664 | $active_children.draggable('destroy'); 665 | } 666 | if (this.options.enableCrossDrop) { 667 | $container.droppable('destroy'); 668 | } 669 | $active_children.removeClass(active_class); 670 | return $container.removeClass(this.identifier); 671 | }; 672 | 673 | return Plugin; 674 | 675 | })(); 676 | return $.fn[pluginName] = function(options) { 677 | return this.each(function() { 678 | var bound_indentifier, old_class, _ref, _ref1; 679 | old_class = (_ref = $(this).attr("class")) != null ? (_ref1 = _ref.match(/shapeshifted_container_\w+/)) != null ? _ref1[0] : void 0 : void 0; 680 | if (old_class) { 681 | bound_indentifier = "resize." + old_class; 682 | $(window).off(bound_indentifier); 683 | $(this).removeClass(old_class); 684 | } 685 | return $.data(this, "plugin_" + pluginName, new Plugin(this, options)); 686 | }); 687 | }; 688 | })(jQuery, window, document); 689 | 690 | }).call(this); 691 | --------------------------------------------------------------------------------