├── 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 | 
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'>"+A+">");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'>#{selected_tag}>")
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'>" + selected_tag + ">");
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 |
--------------------------------------------------------------------------------