diff --git a/demo/common/script.js b/demo/common/script.js
new file mode 100644
index 00000000..287be662
--- /dev/null
+++ b/demo/common/script.js
@@ -0,0 +1,24 @@
+'use strict';
+angular.module('app', ['gridster', 'ui.bootstrap', 'ngRoute'])
+ .config(['$routeProvider',
+ function($routeProvider) {
+ $routeProvider
+ .when('/main', {
+ templateUrl: 'demo/main/view.html',
+ controller: 'MainCtrl'
+ })
+ .when('/dashboard', {
+ templateUrl: 'demo/dashboard/view.html',
+ controller: 'DashboardCtrl'
+ })
+ .otherwise({
+ redirectTo: '/main'
+ });
+ }
+ ])
+ .controller('RootCtrl', function($scope) {
+ $scope.$on('$locationChangeStart', function(e, next, current) {
+ $scope.page = next.split('/').splice(-1);
+ $scope.styleUrl = 'demo/' + $scope.page + '/style.css'
+ });
+ });
diff --git a/demo/dashboard/script.js b/demo/dashboard/script.js
index 21b0cf9c..49dcde56 100644
--- a/demo/dashboard/script.js
+++ b/demo/dashboard/script.js
@@ -1,152 +1,154 @@
angular.module('app')
-.controller('DashboardCtrl', ['$scope', '$timeout',
- function($scope, $timeout) {
- $scope.gridsterOptions = {
- margins: [20, 20],
- columns: 4,
- draggable: {
- handle: 'h3'
- }
- };
-
- $scope.dashboards = {
- '1': {
- id: '1',
- name: 'Home',
- widgets: [{
- col: 0,
- row: 0,
- sizeY: 1,
- sizeX: 1,
- name: "Widget 1"
- }, {
- col: 2,
- row: 1,
- sizeY: 1,
- sizeX: 1,
- name: "Widget 2"
- }]
- },
- '2': {
- id: '2',
- name: 'Other',
- widgets: [{
- col: 1,
- row: 1,
- sizeY: 1,
- sizeX: 2,
- name: "Other Widget 1"
- }, {
- col: 1,
- row: 3,
- sizeY: 1,
- sizeX: 1,
- name: "Other Widget 2"
- }]
- }
- };
+ .controller('DashboardCtrl', ['$scope', '$timeout',
+ function($scope, $timeout) {
+ $scope.gridsterOptions = {
+ margins: [20, 20],
+ columns: 4,
+ draggable: {
+ handle: 'h3'
+ }
+ };
+
+ $scope.visible = false;
+
+ $scope.dashboards = {
+ '1': {
+ id: '1',
+ name: 'Home',
+ widgets: [{
+ col: 0,
+ row: 0,
+ sizeY: 1,
+ sizeX: 1,
+ name: "Widget 1"
+ }, {
+ col: 2,
+ row: 1,
+ sizeY: 1,
+ sizeX: 1,
+ name: "Widget 2"
+ }]
+ },
+ '2': {
+ id: '2',
+ name: 'Other',
+ widgets: [{
+ col: 1,
+ row: 1,
+ sizeY: 1,
+ sizeX: 2,
+ name: "Other Widget 1"
+ }, {
+ col: 1,
+ row: 3,
+ sizeY: 1,
+ sizeX: 1,
+ name: "Other Widget 2"
+ }]
+ }
+ };
- $scope.clear = function() {
- $scope.dashboard.widgets = [];
- };
+ $scope.clear = function() {
+ $scope.dashboard.widgets = [];
+ };
- $scope.addWidget = function() {
- $scope.dashboard.widgets.push({
- name: "New Widget",
- sizeX: 1,
- sizeY: 1
+ $scope.addWidget = function() {
+ $scope.dashboard.widgets.push({
+ name: "New Widget",
+ sizeX: 1,
+ sizeY: 1
+ });
+ };
+
+ $scope.$watch('selectedDashboardId', function(newVal, oldVal) {
+ if (newVal !== oldVal) {
+ $scope.dashboard = $scope.dashboards[newVal];
+ } else {
+ $scope.dashboard = $scope.dashboards[1];
+ }
});
- };
- $scope.$watch('selectedDashboardId', function(newVal, oldVal) {
- if (newVal !== oldVal) {
- $scope.dashboard = $scope.dashboards[newVal];
- } else {
- $scope.dashboard = $scope.dashboards[1];
- }
- });
+ // init dashboard
+ $scope.selectedDashboardId = '1';
- // init dashboard
- $scope.selectedDashboardId = '1';
+ }
+ ])
+
+ .controller('CustomWidgetCtrl', ['$scope', '$uibModal',
+ function($scope, $modal) {
+
+ $scope.remove = function(widget) {
+ $scope.dashboard.widgets.splice($scope.dashboard.widgets.indexOf(widget), 1);
+ };
+
+ $scope.openSettings = function(widget) {
+ $modal.open({
+ scope: $scope,
+ templateUrl: 'demo/dashboard/widget_settings.html',
+ controller: 'WidgetSettingsCtrl',
+ resolve: {
+ widget: function() {
+ return widget;
+ }
+ }
+ });
+ };
- }
-])
+ }
+ ])
-.controller('CustomWidgetCtrl', ['$scope', '$modal',
- function($scope, $modal) {
+ .controller('WidgetSettingsCtrl', ['$scope', '$timeout', '$rootScope', '$uibModalInstance', 'widget',
+ function($scope, $timeout, $rootScope, $modalInstance, widget) {
+ $scope.widget = widget;
- $scope.remove = function(widget) {
- $scope.dashboard.widgets.splice($scope.dashboard.widgets.indexOf(widget), 1);
- };
+ $scope.form = {
+ name: widget.name,
+ sizeX: widget.sizeX,
+ sizeY: widget.sizeY,
+ col: widget.col,
+ row: widget.row
+ };
- $scope.openSettings = function(widget) {
- $modal.open({
- scope: $scope,
- templateUrl: 'demo/dashboard/widget_settings.html',
- controller: 'WidgetSettingsCtrl',
- resolve: {
- widget: function() {
- return widget;
- }
- }
- });
- };
-
- }
-])
-
-.controller('WidgetSettingsCtrl', ['$scope', '$timeout', '$rootScope', '$modalInstance', 'widget',
- function($scope, $timeout, $rootScope, $modalInstance, widget) {
- $scope.widget = widget;
-
- $scope.form = {
- name: widget.name,
- sizeX: widget.sizeX,
- sizeY: widget.sizeY,
- col: widget.col,
- row: widget.row
- };
-
- $scope.sizeOptions = [{
- id: '1',
- name: '1'
- }, {
- id: '2',
- name: '2'
- }, {
- id: '3',
- name: '3'
- }, {
- id: '4',
- name: '4'
- }];
-
- $scope.dismiss = function() {
- $modalInstance.dismiss();
- };
-
- $scope.remove = function() {
- $scope.dashboard.widgets.splice($scope.dashboard.widgets.indexOf(widget), 1);
- $modalInstance.close();
- };
-
- $scope.submit = function() {
- angular.extend(widget, $scope.form);
-
- $modalInstance.close(widget);
- };
-
- }
-])
-
-// helper code
-.filter('object2Array', function() {
- return function(input) {
- var out = [];
- for (i in input) {
- out.push(input[i]);
+ $scope.sizeOptions = [{
+ id: '1',
+ name: '1'
+ }, {
+ id: '2',
+ name: '2'
+ }, {
+ id: '3',
+ name: '3'
+ }, {
+ id: '4',
+ name: '4'
+ }];
+
+ $scope.dismiss = function() {
+ $modalInstance.dismiss();
+ };
+
+ $scope.remove = function() {
+ $scope.dashboard.widgets.splice($scope.dashboard.widgets.indexOf(widget), 1);
+ $modalInstance.close();
+ };
+
+ $scope.submit = function() {
+ angular.extend(widget, $scope.form);
+
+ $modalInstance.close(widget);
+ };
+
+ }
+ ])
+
+ // helper code
+ .filter('object2Array', function() {
+ return function(input) {
+ var out = [];
+ for (i in input) {
+ out.push(input[i]);
+ }
+ return out;
}
- return out;
- }
-});
+ });
diff --git a/demo/dashboard/view.html b/demo/dashboard/view.html
index fa61b8db..0fe14ad8 100644
--- a/demo/dashboard/view.html
+++ b/demo/dashboard/view.html
@@ -4,9 +4,8 @@
Dashboard
-
-
+
\ No newline at end of file
+
diff --git a/demo/main/script.js b/demo/main/script.js
index e97ff89f..476ea2c4 100644
--- a/demo/main/script.js
+++ b/demo/main/script.js
@@ -1,178 +1,178 @@
angular.module('app')
-.directive('integer', function() {
- return {
- require: 'ngModel',
- link: function(scope, ele, attr, ctrl) {
- ctrl.$parsers.unshift(function(viewValue) {
- if (viewValue === '' || viewValue === null || typeof viewValue === 'undefined') {
- return null;
- }
- return parseInt(viewValue, 10);
- });
- }
- };
-})
+ .directive('integer', function() {
+ return {
+ require: 'ngModel',
+ link: function(scope, ele, attr, ctrl) {
+ ctrl.$parsers.unshift(function(viewValue) {
+ if (viewValue === '' || viewValue === null || typeof viewValue === 'undefined') {
+ return null;
+ }
+ return parseInt(viewValue, 10);
+ });
+ }
+ };
+ })
-.controller('MainCtrl', function($scope) {
+ .controller('MainCtrl', function($scope) {
- $scope.gridsterOpts = {
- margins: [20, 20],
- outerMargin: false,
- pushing: true,
- floating: true,
- draggable: {
- enabled: false
- },
- resizable: {
- enabled: false,
- handles: ['n', 'e', 's', 'w', 'se', 'sw']
- }
- };
+ $scope.gridsterOpts = {
+ margins: [20, 20],
+ outerMargin: false,
+ pushing: true,
+ floating: true,
+ draggable: {
+ enabled: false
+ },
+ resizable: {
+ enabled: false,
+ handles: ['n', 'e', 's', 'w', 'se', 'sw']
+ }
+ };
- // these map directly to gridsterItem options
- $scope.standardItems = [{
- sizeX: 2,
- sizeY: 1,
- row: 0,
- col: 0
- }, {
- sizeX: 2,
- sizeY: 2,
- row: 0,
- col: 2
- }, {
- sizeX: 2,
- sizeY: 1,
- row: 2,
- col: 1
- }, {
- sizeX: 1,
- sizeY: 1,
- row: 2,
- col: 3
- }, {
- sizeX: 1,
- sizeY: 1,
- row: 2,
- col: 4
- }, {
- sizeX: 1,
- sizeY: 1,
- row: 0,
- col: 4
- }, {
- sizeX: 1,
- sizeY: 1,
- row: 0,
- col: 5
- }, {
- sizeX: 2,
- sizeY: 1,
- row: 1,
- col: 0
- }, {
- sizeX: 1,
- sizeY: 1,
- row: 1,
- col: 4
- }, {
- sizeX: 1,
- sizeY: 2,
- row: 1,
- col: 5
- }, {
- sizeX: 1,
- sizeY: 1,
- row: 2,
- col: 0
- }];
+ // these map directly to gridsterItem options
+ $scope.standardItems = [{
+ sizeX: 2,
+ sizeY: 1,
+ row: 0,
+ col: 0
+ }, {
+ sizeX: 2,
+ sizeY: 2,
+ row: 0,
+ col: 2
+ }, {
+ sizeX: 2,
+ sizeY: 1,
+ row: 2,
+ col: 1
+ }, {
+ sizeX: 1,
+ sizeY: 1,
+ row: 2,
+ col: 3
+ }, {
+ sizeX: 1,
+ sizeY: 1,
+ row: 2,
+ col: 4
+ }, {
+ sizeX: 1,
+ sizeY: 1,
+ row: 0,
+ col: 4
+ }, {
+ sizeX: 1,
+ sizeY: 1,
+ row: 0,
+ col: 5
+ }, {
+ sizeX: 2,
+ sizeY: 1,
+ row: 1,
+ col: 0
+ }, {
+ sizeX: 1,
+ sizeY: 1,
+ row: 1,
+ col: 4
+ }, {
+ sizeX: 1,
+ sizeY: 2,
+ row: 1,
+ col: 5
+ }, {
+ sizeX: 1,
+ sizeY: 1,
+ row: 2,
+ col: 0
+ }];
- // these are non-standard, so they require mapping options
- $scope.customItems = [{
- size: {
- x: 2,
- y: 1
- },
- position: [0, 0]
- }, {
- size: {
- x: 2,
- y: 2
- },
- position: [0, 2]
- }, {
- size: {
- x: 1,
- y: 1
- },
- position: [1, 4]
- }, {
- size: {
- x: 1,
- y: 2
- },
- position: [1, 5]
- }, {
- size: {
- x: 1,
- y: 1
- },
- position: [2, 0]
- }, {
- size: {
- x: 2,
- y: 1
- },
- position: [2, 1]
- }, {
- size: {
- x: 1,
- y: 1
- },
- position: [2, 3]
- }, {
- size: {
- x: 1,
- y: 1
- },
- position: [0, 4]
- }, {
- size: {
- x: 1,
- y: 1
- },
- position: [0, 5]
- }, {
- size: {
- x: 2,
- y: 1
- },
- position: [1, 0]
- }, {
- size: {
- x: 1,
- y: 1
- },
- position: [2, 4]
- }];
+ // these are non-standard, so they require mapping options
+ $scope.customItems = [{
+ size: {
+ x: 2,
+ y: 1
+ },
+ position: [0, 0]
+ }, {
+ size: {
+ x: 2,
+ y: 2
+ },
+ position: [0, 2]
+ }, {
+ size: {
+ x: 1,
+ y: 1
+ },
+ position: [1, 4]
+ }, {
+ size: {
+ x: 1,
+ y: 2
+ },
+ position: [1, 5]
+ }, {
+ size: {
+ x: 1,
+ y: 1
+ },
+ position: [2, 0]
+ }, {
+ size: {
+ x: 2,
+ y: 1
+ },
+ position: [2, 1]
+ }, {
+ size: {
+ x: 1,
+ y: 1
+ },
+ position: [2, 3]
+ }, {
+ size: {
+ x: 1,
+ y: 1
+ },
+ position: [0, 4]
+ }, {
+ size: {
+ x: 1,
+ y: 1
+ },
+ position: [0, 5]
+ }, {
+ size: {
+ x: 2,
+ y: 1
+ },
+ position: [1, 0]
+ }, {
+ size: {
+ x: 1,
+ y: 1
+ },
+ position: [2, 4]
+ }];
- $scope.emptyItems = [{
- name: 'Item1'
- }, {
- name: 'Item2'
- }, {
- name: 'Item3'
- }, {
- name: 'Item4'
- }];
+ $scope.emptyItems = [{
+ name: 'Item1'
+ }, {
+ name: 'Item2'
+ }, {
+ name: 'Item3'
+ }, {
+ name: 'Item4'
+ }];
- // map the gridsterItem to the custom item structure
- $scope.customItemMap = {
- sizeX: 'item.size.x',
- sizeY: 'item.size.y',
- row: 'item.position[0]',
- col: 'item.position[1]'
- };
+ // map the gridsterItem to the custom item structure
+ $scope.customItemMap = {
+ sizeX: 'item.size.x',
+ sizeY: 'item.size.y',
+ row: 'item.position[0]',
+ col: 'item.position[1]'
+ };
-});
+ });
diff --git a/dist/angular-gridster.min.js b/dist/angular-gridster.min.js
index 07d7c878..86f61099 100644
--- a/dist/angular-gridster.min.js
+++ b/dist/angular-gridster.min.js
@@ -5,4 +5,4 @@
* @version: 0.13.14
* @license: MIT
*/
-!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";return a.module("gridster",[]).constant("gridsterConfig",{columns:6,pushing:!0,floating:!0,swapping:!1,width:"auto",colWidth:"auto",rowHeight:"match",margins:[10,10],outerMargin:!0,sparse:!1,isMobile:!1,mobileBreakPoint:600,mobileModeEnabled:!0,minColumns:1,minRows:1,maxRows:100,defaultSizeX:2,defaultSizeY:1,minSizeX:1,maxSizeX:null,minSizeY:1,maxSizeY:null,saveGridItemCalculatedHeightInMobile:!1,resizable:{enabled:!0,handles:["s","e","n","w","se","ne","sw","nw"]},draggable:{enabled:!0,scrollSensitivity:20,scrollSpeed:15}}).controller("GridsterCtrl",["gridsterConfig","$timeout",function(b,c){var d=this;a.extend(this,b),this.resizable=a.extend({},b.resizable||{}),this.draggable=a.extend({},b.draggable||{});var e=!1;this.layoutChanged=function(){e||(e=!0,c(function(){e=!1,d.loaded&&d.floatItemsUp(),d.updateHeight(d.movingItem?d.movingItem.sizeY:0)},30))},this.grid=[],this.allItems=[],this.destroy=function(){this.grid&&(this.grid=[]),this.$element=null,this.allItems&&(this.allItems.length=0,this.allItems=null)},this.setOptions=function(b){if(b)if(b=a.extend({},b),b.draggable&&(a.extend(this.draggable,b.draggable),delete b.draggable),b.resizable&&(a.extend(this.resizable,b.resizable),delete b.resizable),a.extend(this,b),this.margins&&2===this.margins.length)for(var c=0,d=this.margins.length;c-1&&c>-1&&a.sizeX+c<=this.columns&&a.sizeY+b<=this.maxRows},this.autoSetItemPosition=function(a){for(var b=0;b=a.col&&d<=a.row+a.sizeY-1&&e>=a.row},this.removeItem=function(a){for(var b,c=0,d=this.grid.length;c-1;){for(var e=1,f=b;f>-1;){var g=this.grid[a];if(g){var h=g[f];if(h&&(!c||c.indexOf(h)===-1)&&h.sizeX>=e&&h.sizeY>=d)return h}++e,--f}--a,++d}return null},this.putItems=function(a){for(var b=0,c=a.length;b=b)){for(;a.row-1;){var h=this.getItems(g,b,d,c,a);if(0!==h.length)break;e=g,f=b,--g}null!==e&&this.putItem(a,e,f)}},this.updateHeight=function(a){var b=this.minRows;a=a||0;for(var c=this.grid.length;c>=0;--c){var d=this.grid[c];if(d)for(var e=0,f=d.length;e0?Math.min(this.maxRows,b):Math.max(this.maxRows,b)},this.pixelsToRows=function(a,b){return this.outerMargin||(a+=this.margins[0]/2),b===!0?Math.ceil(a/this.curRowHeight):b===!1?Math.floor(a/this.curRowHeight):Math.round(a/this.curRowHeight)},this.pixelsToColumns=function(a,b){return this.outerMargin||(a+=this.margins[1]/2),b===!0?Math.ceil(a/this.curColWidth):b===!1?Math.floor(a/this.curColWidth):Math.round(a/this.curColWidth)}}]).directive("gridsterPreview",function(){return{replace:!0,scope:!0,require:"^gridster",template:'',link:function(a,b,c,d){a.previewStyle=function(){return d.movingItem?{display:"block",height:d.movingItem.sizeY*d.curRowHeight-d.margins[0]+"px",width:d.movingItem.sizeX*d.curColWidth-d.margins[1]+"px",top:d.movingItem.row*d.curRowHeight+(d.outerMargin?d.margins[0]:0)+"px",left:d.movingItem.col*d.curColWidth+(d.outerMargin?d.margins[1]:0)+"px"}:{display:"none"}}}}}).directive("gridster",["$timeout","$window","$rootScope","gridsterDebounce",function(b,c,d,e){return{scope:!0,restrict:"EAC",controller:"GridsterCtrl",controllerAs:"gridster",compile:function(f){return f.prepend(''),function(f,g,h,i){function j(){g.css("height",i.gridHeight*i.curRowHeight+(i.outerMargin?i.margins[0]:-i.margins[0])+"px")}function k(a){if(i.setOptions(a),l(g[0])){"auto"===i.width?i.curWidth=g[0].offsetWidth||parseInt(g.css("width"),10):i.curWidth=i.width,"auto"===i.colWidth?i.curColWidth=(i.curWidth+(i.outerMargin?-i.margins[1]:i.margins[1]))/i.columns:i.curColWidth=i.colWidth,i.curRowHeight=i.rowHeight,"string"==typeof i.rowHeight&&("match"===i.rowHeight?i.curRowHeight=Math.round(i.curColWidth):i.rowHeight.indexOf("*")!==-1?i.curRowHeight=Math.round(i.curColWidth*i.rowHeight.replace("*","").replace(" ","")):i.rowHeight.indexOf("/")!==-1&&(i.curRowHeight=Math.round(i.curColWidth/i.rowHeight.replace("/","").replace(" ","")))),i.isMobile=i.mobileModeEnabled&&i.curWidth<=i.mobileBreakPoint;for(var b=0,c=i.grid.length;bb&&(d=b-p-r,z=h-d),q+ic&&(f=c-q-s,A=i-f),p+=d,q+=f,e.css({top:q+"px",left:p+"px"}),k(a),!0}function o(a){return!(!e.hasClass("gridster-item-moving")||e.hasClass("gridster-item-resizing"))&&(z=A=0,l(a),!0)}var p,q,r,s,t,u,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=b[0],E=["select","option","input","textarea","button"],F=null,G=null;this.enable=function(){if(F!==!0){if(F=!0,G)return void G.enable();G=new d(e[0],m,n,o),G.enable()}},this.disable=function(){F!==!1&&(F=!1,G&&G.disable())},this.toggle=function(a){a?this.enable():this.disable()},this.destroy=function(){this.disable()}}return e}]).factory("GridsterResizable",["GridsterTouch",function(b){function c(c,d,e,f,g){function h(h){function i(a){c.addClass("gridster-item-moving"),c.addClass("gridster-item-resizing"),e.movingItem=f,f.setElementSizeX(),f.setElementSizeY(),f.setElementPosition(),e.updateHeight(1),d.$apply(function(){e.resizable&&e.resizable.start&&e.resizable.start(a,c,g,f)})}function j(a){var b=f.row,i=f.col,j=f.sizeX,k=f.sizeY,l=e.resizable&&e.resizable.resize,m=f.col;["w","nw","sw"].indexOf(h)!==-1&&(m=e.pixelsToColumns(o,!1));var n=f.row;["n","ne","nw"].indexOf(h)!==-1&&(n=e.pixelsToRows(p,!1));var s=f.sizeX;["n","s"].indexOf(h)===-1&&(s=e.pixelsToColumns(q,!0));var t=f.sizeY;["e","w"].indexOf(h)===-1&&(t=e.pixelsToRows(r,!0));var u=n>-1&&m>-1&&s+m<=e.columns&&t+n<=e.maxRows;!u||e.pushing===!1&&0!==e.getItems(n,m,s,t,f).length||(f.row=n,f.col=m,f.sizeX=s,f.sizeY=t);var v=f.row!==b||f.col!==i||f.sizeX!==j||f.sizeY!==k;(l||v)&&d.$apply(function(){l&&e.resizable.resize(a,c,g,f)})}function k(a){c.removeClass("gridster-item-moving"),c.removeClass("gridster-item-resizing"),e.movingItem=null,f.setPosition(f.row,f.col),f.setSizeY(f.sizeY),f.setSizeX(f.sizeX),d.$apply(function(){e.resizable&&e.resizable.stop&&e.resizable.stop(a,c,g,f)})}function l(a){switch(a.which){case 1:break;case 2:case 3:return}return u=e.draggable.enabled,u&&(e.draggable.enabled=!1,d.$broadcast("gridster-draggable-changed",e)),z=a.pageX,A=a.pageY,o=parseInt(c.css("left"),10),p=parseInt(c.css("top"),10),q=c[0].offsetWidth,r=c[0].offsetHeight,s=f.sizeX,t=f.sizeY,i(a),!0}function m(a){var b=e.curWidth-1;x=a.pageX,y=a.pageY;var d=x-z+B,f=y-A+C;B=C=0,z=x,A=y;var g=f,h=d;return w.indexOf("n")>=0&&(r-g=0&&(r+gE&&(f=E-p-r,C=g-f),r+=f),w.indexOf("w")>=0&&(q-h=0&&(q+hb&&(d=b-o-q,B=h-d),q+=d),c.css({top:p+"px",left:o+"px",width:q+"px",height:r+"px"}),j(a),!0}function n(a){return e.draggable.enabled!==u&&(e.draggable.enabled=u,d.$broadcast("gridster-draggable-changed",e)),B=C=0,k(a),!0}var o,p,q,r,s,t,u,v,w=h,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=9999,F=0,G=function(){return(f.minSizeY?f.minSizeY:1)*e.curRowHeight-e.margins[0]},H=function(){return(f.minSizeX?f.minSizeX:1)*e.curColWidth-e.margins[1]},I=null;this.enable=function(){I||(I=a.element(''),c.append(I)),v=new b(I[0],l,m,n),v.enable()},this.disable=function(){I&&(I.remove(),I=null),v.disable(),v=void 0},this.destroy=function(){this.disable()}}var i=[],j=e.resizable.handles;"string"==typeof j&&(j=e.resizable.handles.split(","));for(var k=!1,l=0,m=j.length;l-1&&c>-1&&a.sizeX+c<=this.columns&&a.sizeY+b<=this.maxRows},this.autoSetItemPosition=function(a){for(var b=0;b=a.col&&d<=a.row+a.sizeY-1&&e>=a.row},this.removeItem=function(a){for(var b,c=0,d=this.grid.length;c-1;){for(var e=1,f=b;f>-1;){var g=this.grid[a];if(g){var h=g[f];if(h&&(!c||c.indexOf(h)===-1)&&h.sizeX>=e&&h.sizeY>=d)return h}++e,--f}--a,++d}return null},this.putItems=function(a){for(var b=0,c=a.length;b=b)){for(;a.row-1;){var h=this.getItems(g,b,d,c,a);if(0!==h.length)break;e=g,f=b,--g}null!==e&&this.putItem(a,e,f)}},this.updateHeight=function(a){var b=this.minRows;a=a||0;for(var c=this.grid.length;c>=0;--c){var d=this.grid[c];if(d)for(var e=0,f=d.length;e0?Math.min(this.maxRows,b):Math.max(this.maxRows,b)},this.pixelsToRows=function(a,b){return this.outerMargin||(a+=this.margins[0]/2),b===!0?Math.ceil(a/this.curRowHeight):b===!1?Math.floor(a/this.curRowHeight):Math.round(a/this.curRowHeight)},this.pixelsToColumns=function(a,b){return this.outerMargin||(a+=this.margins[1]/2),b===!0?Math.ceil(a/this.curColWidth):b===!1?Math.floor(a/this.curColWidth):Math.round(a/this.curColWidth)}}]).directive("gridsterPreview",function(){return{replace:!0,scope:!0,require:"^gridster",template:'',link:function(a,b,c,d){a.previewStyle=function(){return d.movingItem?{display:"block",height:d.movingItem.sizeY*d.curRowHeight-d.margins[0]+"px",width:d.movingItem.sizeX*d.curColWidth-d.margins[1]+"px",top:d.movingItem.row*d.curRowHeight+(d.outerMargin?d.margins[0]:0)+"px",left:d.movingItem.col*d.curColWidth+(d.outerMargin?d.margins[1]:0)+"px"}:{display:"none"}}}}}).directive("gridster",["$timeout","$window","$rootScope",function(a,c,d){return{scope:!0,restrict:"EAC",controller:"GridsterCtrl",controllerAs:"gridster",compile:function(c){return c.prepend(''),function(c,e,f,g){function h(){e.css("height",g.gridHeight*g.curRowHeight+(g.outerMargin?g.margins[0]:-g.margins[0])+"px")}function i(a){if(g.setOptions(a),j(e[0])){"auto"===g.width?g.curWidth=e[0].offsetWidth||parseInt(e.css("width"),10):g.curWidth=g.width,"auto"===g.colWidth?g.curColWidth=(g.curWidth+(g.outerMargin?-g.margins[1]:g.margins[1]))/g.columns:g.curColWidth=g.colWidth,g.curRowHeight=g.rowHeight,"string"==typeof g.rowHeight&&("match"===g.rowHeight?g.curRowHeight=Math.round(g.curColWidth):g.rowHeight.indexOf("*")!==-1?g.curRowHeight=Math.round(g.curColWidth*g.rowHeight.replace("*","").replace(" ","")):g.rowHeight.indexOf("/")!==-1&&(g.curRowHeight=Math.round(g.curColWidth/g.rowHeight.replace("/","").replace(" ","")))),g.isMobile=g.mobileModeEnabled&&g.curWidth<=g.mobileBreakPoint;for(var b=0,c=g.grid.length;bb&&(d=b-p-r,z=h-d),q+ic&&(f=c-q-s,A=i-f),p+=d,q+=f,e.css({top:q+"px",left:p+"px"}),k(a),!0}function o(a){return!(!e.hasClass("gridster-item-moving")||e.hasClass("gridster-item-resizing"))&&(z=A=0,l(a),!0)}var p,q,r,s,t,u,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=b[0],E=["select","option","input","textarea","button"],F=null,G=null;this.enable=function(){if(F!==!0){if(F=!0,G)return void G.enable();G=new d(e[0],m,n,o),G.enable()}},this.disable=function(){F!==!1&&(F=!1,G&&G.disable())},this.toggle=function(a){a?this.enable():this.disable()},this.destroy=function(){this.disable()}}return e}]).factory("GridsterResizable",["GridsterTouch",function(b){function c(c,d,e,f,g){function h(h){function i(a){c.addClass("gridster-item-moving"),c.addClass("gridster-item-resizing"),e.movingItem=f,f.setElementSizeX(),f.setElementSizeY(),f.setElementPosition(),e.updateHeight(1),d.$apply(function(){e.resizable&&e.resizable.start&&e.resizable.start(a,c,g,f)})}function j(a){var b=f.row,i=f.col,j=f.sizeX,k=f.sizeY,l=e.resizable&&e.resizable.resize,m=f.col;["w","nw","sw"].indexOf(h)!==-1&&(m=e.pixelsToColumns(o,!1));var n=f.row;["n","ne","nw"].indexOf(h)!==-1&&(n=e.pixelsToRows(p,!1));var s=f.sizeX;["n","s"].indexOf(h)===-1&&(s=e.pixelsToColumns(q,!0));var t=f.sizeY;["e","w"].indexOf(h)===-1&&(t=e.pixelsToRows(r,!0));var u=n>-1&&m>-1&&s+m<=e.columns&&t+n<=e.maxRows;!u||e.pushing===!1&&0!==e.getItems(n,m,s,t,f).length||(f.row=n,f.col=m,f.sizeX=s,f.sizeY=t);var v=f.row!==b||f.col!==i||f.sizeX!==j||f.sizeY!==k;(l||v)&&d.$apply(function(){l&&e.resizable.resize(a,c,g,f)})}function k(a){c.removeClass("gridster-item-moving"),c.removeClass("gridster-item-resizing"),e.movingItem=null,f.setPosition(f.row,f.col),f.setSizeY(f.sizeY),f.setSizeX(f.sizeX),d.$apply(function(){e.resizable&&e.resizable.stop&&e.resizable.stop(a,c,g,f)})}function l(a){switch(a.which){case 1:break;case 2:case 3:return}return u=e.draggable.enabled,u&&(e.draggable.enabled=!1,d.$broadcast("gridster-draggable-changed",e)),z=a.pageX,A=a.pageY,o=parseInt(c.css("left"),10),p=parseInt(c.css("top"),10),q=c[0].offsetWidth,r=c[0].offsetHeight,s=f.sizeX,t=f.sizeY,i(a),!0}function m(a){var b=e.curWidth-1;x=a.pageX,y=a.pageY;var d=x-z+B,f=y-A+C;B=C=0,z=x,A=y;var g=f,h=d;return w.indexOf("n")>=0&&(r-g=0&&(r+gE&&(f=E-p-r,C=g-f),r+=f),w.indexOf("w")>=0&&(q-h=0&&(q+hb&&(d=b-o-q,B=h-d),q+=d),c.css({top:p+"px",left:o+"px",width:q+"px",height:r+"px"}),j(a),!0}function n(a){return e.draggable.enabled!==u&&(e.draggable.enabled=u,d.$broadcast("gridster-draggable-changed",e)),B=C=0,k(a),!0}var o,p,q,r,s,t,u,v,w=h,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=9999,F=0,G=function(){return(f.minSizeY?f.minSizeY:1)*e.curRowHeight-e.margins[0]},H=function(){return(f.minSizeX?f.minSizeX:1)*e.curColWidth-e.margins[1]},I=null;this.enable=function(){I||(I=a.element(''),c.append(I)),v=new b(I[0],l,m,n),v.enable()},this.disable=function(){I&&(I.remove(),I=null),v.disable(),v=void 0},this.destroy=function(){this.disable()}}var i=[],j=e.resizable.handles;"string"==typeof j&&(j=e.resizable.handles.split(","));for(var k=!1,l=0,m=j.length;l
-
+
Angular Gridster
-
+
-
+
-
-
-
+
+
+
-
+
@@ -59,9 +33,9 @@
Angular Gridster
- - Main Demo
+
- Main Demo
- - Dashboard Demo
+
- Dashboard Demo
- |
@@ -76,4 +50,4 @@
-
\ No newline at end of file
+
diff --git a/package.json b/package.json
index 570b8d51..b7413e4d 100644
--- a/package.json
+++ b/package.json
@@ -42,5 +42,8 @@
"type": "git",
"url": "git@github.com:ManifestWebDesign/angular-gridster.git"
},
- "main": "dist/angular-gridster.min.js"
+ "main": "dist/angular-gridster.min.js",
+ "dependencies": {
+ "resize-observer-polyfill": "^1.5.0"
+ }
}
diff --git a/src/angular-gridster.js b/src/angular-gridster.js
index c6607e0a..f0f7e184 100755
--- a/src/angular-gridster.js
+++ b/src/angular-gridster.js
@@ -5,1203 +5,1219 @@
if (typeof define === 'function' && define.amd) {
// AMD
- define(['angular'], factory);
+ define(['angular', 'resize-observer-polyfill'], factory);
} else if (typeof exports === 'object') {
// CommonJS
- module.exports = factory(require('angular'));
+ module.exports = factory(require('angular'), require('resize-observer-polyfill'));
} else {
// Browser, nothing "exported". Only registered as a module with angular.
- factory(root.angular);
+ factory(root.angular, window.ResizeObserver);
}
-}(this, function(angular) {
-
+}(this, function(angular, ResizeObserver) {
'use strict';
+ ResizeObserver = ResizeObserver.default || ResizeObserver;
+
+ var _utils = {
+ //region Test via a getter in the options object to see if the passive property is accessed
+ supportsPassive: (function() {
+ var result = false;
+ try {
+ var opts = Object.defineProperty({}, 'passive', {
+ get: function() {
+ result = {
+ passive: false
+ };
+ }
+ });
+ window.addEventListener('test', null, opts);
+ } catch (e) {}
+ return result;
+ })()
+ };
+
// This returned angular module 'gridster' is what is exported.
return angular.module('gridster', [])
- .constant('gridsterConfig', {
- columns: 6, // number of columns in the grid
- pushing: true, // whether to push other items out of the way
- floating: true, // whether to automatically float items up so they stack
- swapping: false, // whether or not to have items switch places instead of push down if they are the same size
- width: 'auto', // width of the grid. "auto" will expand the grid to its parent container
- colWidth: 'auto', // width of grid columns. "auto" will divide the width of the grid evenly among the columns
- rowHeight: 'match', // height of grid rows. 'match' will make it the same as the column width, a numeric value will be interpreted as pixels, '/2' is half the column width, '*5' is five times the column width, etc.
- margins: [10, 10], // margins in between grid items
- outerMargin: true,
- sparse: false, // "true" can increase performance of dragging and resizing for big grid (e.g. 20x50)
- isMobile: false, // toggle mobile view
- mobileBreakPoint: 600, // width threshold to toggle mobile mode
- mobileModeEnabled: true, // whether or not to toggle mobile mode when screen width is less than mobileBreakPoint
- minColumns: 1, // minimum amount of columns the grid can scale down to
- minRows: 1, // minimum amount of rows to show if the grid is empty
- maxRows: 100, // maximum amount of rows in the grid
- defaultSizeX: 2, // default width of an item in columns
- defaultSizeY: 1, // default height of an item in rows
- minSizeX: 1, // minimum column width of an item
- maxSizeX: null, // maximum column width of an item
- minSizeY: 1, // minumum row height of an item
- maxSizeY: null, // maximum row height of an item
- saveGridItemCalculatedHeightInMobile: false, // grid item height in mobile display. true- to use the calculated height by sizeY given
- resizable: { // options to pass to resizable handler
- enabled: true,
- handles: ['s', 'e', 'n', 'w', 'se', 'ne', 'sw', 'nw']
- },
- draggable: { // options to pass to draggable handler
- enabled: true,
- scrollSensitivity: 20, // Distance in pixels from the edge of the viewport after which the viewport should scroll, relative to pointer
- scrollSpeed: 15 // Speed at which the window should scroll once the mouse pointer gets within scrollSensitivity distance
- }
- })
-
- .controller('GridsterCtrl', ['gridsterConfig', '$timeout',
- function(gridsterConfig, $timeout) {
-
- var gridster = this;
+ .constant('gridsterConfig', {
+ columns: 6, // number of columns in the grid
+ pushing: true, // whether to push other items out of the way
+ floating: true, // whether to automatically float items up so they stack
+ swapping: false, // whether or not to have items switch places instead of push down if they are the same size
+ width: 'auto', // width of the grid. "auto" will expand the grid to its parent container
+ colWidth: 'auto', // width of grid columns. "auto" will divide the width of the grid evenly among the columns
+ rowHeight: 'match', // height of grid rows. 'match' will make it the same as the column width, a numeric value will be interpreted as pixels, '/2' is half the column width, '*5' is five times the column width, etc.
+ margins: [10, 10], // margins in between grid items
+ outerMargin: true,
+ sparse: false, // "true" can increase performance of dragging and resizing for big grid (e.g. 20x50)
+ isMobile: false, // toggle mobile view
+ mobileBreakPoint: 600, // width threshold to toggle mobile mode
+ mobileModeEnabled: true, // whether or not to toggle mobile mode when screen width is less than mobileBreakPoint
+ minColumns: 1, // minimum amount of columns the grid can scale down to
+ minRows: 1, // minimum amount of rows to show if the grid is empty
+ maxRows: 100, // maximum amount of rows in the grid
+ defaultSizeX: 2, // default width of an item in columns
+ defaultSizeY: 1, // default height of an item in rows
+ minSizeX: 1, // minimum column width of an item
+ maxSizeX: null, // maximum column width of an item
+ minSizeY: 1, // minumum row height of an item
+ maxSizeY: null, // maximum row height of an item
+ saveGridItemCalculatedHeightInMobile: false, // grid item height in mobile display. true- to use the calculated height by sizeY given
+ resizable: { // options to pass to resizable handler
+ enabled: true,
+ handles: ['s', 'e', 'n', 'w', 'se', 'ne', 'sw', 'nw']
+ },
+ draggable: { // options to pass to draggable handler
+ enabled: true,
+ scrollSensitivity: 20, // Distance in pixels from the edge of the viewport after which the viewport should scroll, relative to pointer
+ scrollSpeed: 15 // Speed at which the window should scroll once the mouse pointer gets within scrollSensitivity distance
+ }
+ })
- /**
- * Create options from gridsterConfig constant
- */
- angular.extend(this, gridsterConfig);
+ .controller('GridsterCtrl', ['gridsterConfig', '$timeout',
+ function(gridsterConfig, $timeout) {
- this.resizable = angular.extend({}, gridsterConfig.resizable || {});
- this.draggable = angular.extend({}, gridsterConfig.draggable || {});
+ var gridster = this;
- var flag = false;
- this.layoutChanged = function() {
- if (flag) {
- return;
- }
- flag = true;
- $timeout(function() {
- flag = false;
- if (gridster.loaded) {
- gridster.floatItemsUp();
- }
- gridster.updateHeight(gridster.movingItem ? gridster.movingItem.sizeY : 0);
- }, 30);
- };
+ /**
+ * Create options from gridsterConfig constant
+ */
+ angular.extend(this, gridsterConfig);
- /**
- * A positional array of the items in the grid
- */
- this.grid = [];
- this.allItems = [];
+ this.resizable = angular.extend({}, gridsterConfig.resizable || {});
+ this.draggable = angular.extend({}, gridsterConfig.draggable || {});
- /**
- * Clean up after yourself
- */
- this.destroy = function() {
- // empty the grid to cut back on the possibility
- // of circular references
- if (this.grid) {
- this.grid = [];
- }
- this.$element = null;
+ var flag = false;
+ this.layoutChanged = function() {
+ if (flag) {
+ return;
+ }
+ flag = true;
+ $timeout(function() {
+ flag = false;
+ if (gridster.loaded) {
+ gridster.floatItemsUp();
+ }
+ gridster.updateHeight(gridster.movingItem ? gridster.movingItem.sizeY : 0);
+ }, 30);
+ };
- if (this.allItems) {
- this.allItems.length = 0;
- this.allItems = null;
- }
- };
+ /**
+ * A positional array of the items in the grid
+ */
+ this.grid = [];
+ this.allItems = [];
- /**
- * Overrides default options
- *
- * @param {Object} options The options to override
- */
- this.setOptions = function(options) {
- if (!options) {
- return;
- }
+ /**
+ * Clean up after yourself
+ */
+ this.destroy = function() {
+ // empty the grid to cut back on the possibility
+ // of circular references
+ if (this.grid) {
+ this.grid = [];
+ }
+ this.$element = null;
- options = angular.extend({}, options);
+ if (this.allItems) {
+ this.allItems.length = 0;
+ this.allItems = null;
+ }
+ };
- // all this to avoid using jQuery...
- if (options.draggable) {
- angular.extend(this.draggable, options.draggable);
- delete(options.draggable);
- }
- if (options.resizable) {
- angular.extend(this.resizable, options.resizable);
- delete(options.resizable);
- }
+ /**
+ * Overrides default options
+ *
+ * @param {Object} options The options to override
+ */
+ this.setOptions = function(options) {
+ if (!options) {
+ return;
+ }
- angular.extend(this, options);
+ options = angular.extend({}, options);
- if (!this.margins || this.margins.length !== 2) {
- this.margins = [0, 0];
- } else {
- for (var x = 0, l = this.margins.length; x < l; ++x) {
- this.margins[x] = parseInt(this.margins[x], 10);
- if (isNaN(this.margins[x])) {
- this.margins[x] = 0;
+ // all this to avoid using jQuery...
+ if (options.draggable) {
+ angular.extend(this.draggable, options.draggable);
+ delete(options.draggable);
+ }
+ if (options.resizable) {
+ angular.extend(this.resizable, options.resizable);
+ delete(options.resizable);
+ }
+
+ angular.extend(this, options);
+
+ if (!this.margins || this.margins.length !== 2) {
+ this.margins = [0, 0];
+ } else {
+ for (var x = 0, l = this.margins.length; x < l; ++x) {
+ this.margins[x] = parseInt(this.margins[x], 10);
+ if (isNaN(this.margins[x])) {
+ this.margins[x] = 0;
+ }
}
}
- }
- };
+ };
- /**
- * Check if item can occupy a specified position in the grid
- *
- * @param {Object} item The item in question
- * @param {Number} row The row index
- * @param {Number} column The column index
- * @returns {Boolean} True if if item fits
- */
- this.canItemOccupy = function(item, row, column) {
- return row > -1 && column > -1 && item.sizeX + column <= this.columns && item.sizeY + row <= this.maxRows;
- };
+ /**
+ * Check if item can occupy a specified position in the grid
+ *
+ * @param {Object} item The item in question
+ * @param {Number} row The row index
+ * @param {Number} column The column index
+ * @returns {Boolean} True if if item fits
+ */
+ this.canItemOccupy = function(item, row, column) {
+ return row > -1 && column > -1 && item.sizeX + column <= this.columns && item.sizeY + row <= this.maxRows;
+ };
- /**
- * Set the item in the first suitable position
- *
- * @param {Object} item The item to insert
- */
- this.autoSetItemPosition = function(item) {
- // walk through each row and column looking for a place it will fit
- for (var rowIndex = 0; rowIndex < this.maxRows; ++rowIndex) {
- for (var colIndex = 0; colIndex < this.columns; ++colIndex) {
- // only insert if position is not already taken and it can fit
- var items = this.getItems(rowIndex, colIndex, item.sizeX, item.sizeY, item);
- if (items.length === 0 && this.canItemOccupy(item, rowIndex, colIndex)) {
- this.putItem(item, rowIndex, colIndex);
- return;
+ /**
+ * Set the item in the first suitable position
+ *
+ * @param {Object} item The item to insert
+ */
+ this.autoSetItemPosition = function(item) {
+ // walk through each row and column looking for a place it will fit
+ for (var rowIndex = 0; rowIndex < this.maxRows; ++rowIndex) {
+ for (var colIndex = 0; colIndex < this.columns; ++colIndex) {
+ // only insert if position is not already taken and it can fit
+ var items = this.getItems(rowIndex, colIndex, item.sizeX, item.sizeY, item);
+ if (items.length === 0 && this.canItemOccupy(item, rowIndex, colIndex)) {
+ this.putItem(item, rowIndex, colIndex);
+ return;
+ }
}
}
- }
- throw new Error('Unable to place item!');
- };
+ throw new Error('Unable to place item!');
+ };
- /**
- * Gets items at a specific coordinate
- *
- * @param {Number} row
- * @param {Number} column
- * @param {Number} sizeX
- * @param {Number} sizeY
- * @param {Array} excludeItems An array of items to exclude from selection
- * @returns {Array} Items that match the criteria
- */
- this.getItems = function(row, column, sizeX, sizeY, excludeItems) {
- var items = [];
- if (!sizeX || !sizeY) {
- sizeX = sizeY = 1;
- }
- if (excludeItems && !(excludeItems instanceof Array)) {
- excludeItems = [excludeItems];
- }
- var item;
- if (this.sparse === false) { // check all cells
- for (var h = 0; h < sizeY; ++h) {
- for (var w = 0; w < sizeX; ++w) {
- item = this.getItem(row + h, column + w, excludeItems);
- if (item && (!excludeItems || excludeItems.indexOf(item) === -1) && items.indexOf(item) === -1) {
+ /**
+ * Gets items at a specific coordinate
+ *
+ * @param {Number} row
+ * @param {Number} column
+ * @param {Number} sizeX
+ * @param {Number} sizeY
+ * @param {Array} excludeItems An array of items to exclude from selection
+ * @returns {Array} Items that match the criteria
+ */
+ this.getItems = function(row, column, sizeX, sizeY, excludeItems) {
+ var items = [];
+ if (!sizeX || !sizeY) {
+ sizeX = sizeY = 1;
+ }
+ if (excludeItems && !(excludeItems instanceof Array)) {
+ excludeItems = [excludeItems];
+ }
+ var item;
+ if (this.sparse === false) { // check all cells
+ for (var h = 0; h < sizeY; ++h) {
+ for (var w = 0; w < sizeX; ++w) {
+ item = this.getItem(row + h, column + w, excludeItems);
+ if (item && (!excludeItems || excludeItems.indexOf(item) === -1) && items.indexOf(item) === -1) {
+ items.push(item);
+ }
+ }
+ }
+ } else { // check intersection with all items
+ var bottom = row + sizeY - 1;
+ var right = column + sizeX - 1;
+ for (var i = 0; i < this.allItems.length; ++i) {
+ item = this.allItems[i];
+ if (item && (!excludeItems || excludeItems.indexOf(item) === -1) && items.indexOf(item) === -1 && this.intersect(item, column, right, row, bottom)) {
items.push(item);
}
}
}
- } else { // check intersection with all items
- var bottom = row + sizeY - 1;
- var right = column + sizeX - 1;
- for (var i = 0; i < this.allItems.length; ++i) {
- item = this.allItems[i];
- if (item && (!excludeItems || excludeItems.indexOf(item) === -1) && items.indexOf(item) === -1 && this.intersect(item, column, right, row, bottom)) {
- items.push(item);
- }
+ return items;
+ };
+
+ /**
+ * @param {Array} items
+ * @returns {Object} An item that represents the bounding box of the items
+ */
+ this.getBoundingBox = function(items) {
+
+ if (items.length === 0) {
+ return null;
+ }
+ if (items.length === 1) {
+ return {
+ row: items[0].row,
+ col: items[0].col,
+ sizeY: items[0].sizeY,
+ sizeX: items[0].sizeX
+ };
}
- }
- return items;
- };
- /**
- * @param {Array} items
- * @returns {Object} An item that represents the bounding box of the items
- */
- this.getBoundingBox = function(items) {
+ var maxRow = 0;
+ var maxCol = 0;
+ var minRow = 9999;
+ var minCol = 9999;
+
+ for (var i = 0, l = items.length; i < l; ++i) {
+ var item = items[i];
+ minRow = Math.min(item.row, minRow);
+ minCol = Math.min(item.col, minCol);
+ maxRow = Math.max(item.row + item.sizeY, maxRow);
+ maxCol = Math.max(item.col + item.sizeX, maxCol);
+ }
- if (items.length === 0) {
- return null;
- }
- if (items.length === 1) {
return {
- row: items[0].row,
- col: items[0].col,
- sizeY: items[0].sizeY,
- sizeX: items[0].sizeX
+ row: minRow,
+ col: minCol,
+ sizeY: maxRow - minRow,
+ sizeX: maxCol - minCol
};
- }
-
- var maxRow = 0;
- var maxCol = 0;
- var minRow = 9999;
- var minCol = 9999;
-
- for (var i = 0, l = items.length; i < l; ++i) {
- var item = items[i];
- minRow = Math.min(item.row, minRow);
- minCol = Math.min(item.col, minCol);
- maxRow = Math.max(item.row + item.sizeY, maxRow);
- maxCol = Math.max(item.col + item.sizeX, maxCol);
- }
-
- return {
- row: minRow,
- col: minCol,
- sizeY: maxRow - minRow,
- sizeX: maxCol - minCol
};
- };
- /**
- * Checks if item intersects specified box
- *
- * @param {object} item
- * @param {number} left
- * @param {number} right
- * @param {number} top
- * @param {number} bottom
- */
+ /**
+ * Checks if item intersects specified box
+ *
+ * @param {object} item
+ * @param {number} left
+ * @param {number} right
+ * @param {number} top
+ * @param {number} bottom
+ */
- this.intersect = function(item, left, right, top, bottom) {
- return (left <= item.col + item.sizeX - 1 &&
- right >= item.col &&
- top <= item.row + item.sizeY - 1 &&
- bottom >= item.row);
- };
+ this.intersect = function(item, left, right, top, bottom) {
+ return (left <= item.col + item.sizeX - 1 &&
+ right >= item.col &&
+ top <= item.row + item.sizeY - 1 &&
+ bottom >= item.row);
+ };
- /**
- * Removes an item from the grid
- *
- * @param {Object} item
- */
- this.removeItem = function(item) {
- var index;
- for (var rowIndex = 0, l = this.grid.length; rowIndex < l; ++rowIndex) {
- var columns = this.grid[rowIndex];
- if (!columns) {
- continue;
- }
- index = columns.indexOf(item);
- if (index !== -1) {
- columns[index] = null;
- break;
+ /**
+ * Removes an item from the grid
+ *
+ * @param {Object} item
+ */
+ this.removeItem = function(item) {
+ var index;
+ for (var rowIndex = 0, l = this.grid.length; rowIndex < l; ++rowIndex) {
+ var columns = this.grid[rowIndex];
+ if (!columns) {
+ continue;
+ }
+ index = columns.indexOf(item);
+ if (index !== -1) {
+ columns[index] = null;
+ break;
+ }
}
- }
- if (this.sparse) {
- index = this.allItems.indexOf(item);
- if (index !== -1) {
- this.allItems.splice(index, 1);
+ if (this.sparse) {
+ index = this.allItems.indexOf(item);
+ if (index !== -1) {
+ this.allItems.splice(index, 1);
+ }
}
- }
- this.layoutChanged();
- };
+ this.layoutChanged();
+ };
- /**
- * Returns the item at a specified coordinate
- *
- * @param {Number} row
- * @param {Number} column
- * @param {Array} excludeItems Items to exclude from selection
- * @returns {Object} The matched item or null
- */
- this.getItem = function(row, column, excludeItems) {
- if (excludeItems && !(excludeItems instanceof Array)) {
- excludeItems = [excludeItems];
- }
- var sizeY = 1;
- while (row > -1) {
- var sizeX = 1,
- col = column;
- while (col > -1) {
- var items = this.grid[row];
- if (items) {
- var item = items[col];
- if (item && (!excludeItems || excludeItems.indexOf(item) === -1) && item.sizeX >= sizeX && item.sizeY >= sizeY) {
- return item;
+ /**
+ * Returns the item at a specified coordinate
+ *
+ * @param {Number} row
+ * @param {Number} column
+ * @param {Array} excludeItems Items to exclude from selection
+ * @returns {Object} The matched item or null
+ */
+ this.getItem = function(row, column, excludeItems) {
+ if (excludeItems && !(excludeItems instanceof Array)) {
+ excludeItems = [excludeItems];
+ }
+ var sizeY = 1;
+ while (row > -1) {
+ var sizeX = 1,
+ col = column;
+ while (col > -1) {
+ var items = this.grid[row];
+ if (items) {
+ var item = items[col];
+ if (item && (!excludeItems || excludeItems.indexOf(item) === -1) && item.sizeX >= sizeX && item.sizeY >= sizeY) {
+ return item;
+ }
}
+ ++sizeX;
+ --col;
}
- ++sizeX;
- --col;
+ --row;
+ ++sizeY;
}
- --row;
- ++sizeY;
- }
- return null;
- };
+ return null;
+ };
- /**
- * Insert an array of items into the grid
- *
- * @param {Array} items An array of items to insert
- */
- this.putItems = function(items) {
- for (var i = 0, l = items.length; i < l; ++i) {
- this.putItem(items[i]);
- }
- };
+ /**
+ * Insert an array of items into the grid
+ *
+ * @param {Array} items An array of items to insert
+ */
+ this.putItems = function(items) {
+ for (var i = 0, l = items.length; i < l; ++i) {
+ this.putItem(items[i]);
+ }
+ };
- /**
- * Insert a single item into the grid
- *
- * @param {Object} item The item to insert
- * @param {Number} row (Optional) Specifies the items row index
- * @param {Number} column (Optional) Specifies the items column index
- * @param {Array} ignoreItems
- */
- this.putItem = function(item, row, column, ignoreItems) {
- // auto place item if no row specified
- if (typeof row === 'undefined' || row === null) {
- row = item.row;
- column = item.col;
+ /**
+ * Insert a single item into the grid
+ *
+ * @param {Object} item The item to insert
+ * @param {Number} row (Optional) Specifies the items row index
+ * @param {Number} column (Optional) Specifies the items column index
+ * @param {Array} ignoreItems
+ */
+ this.putItem = function(item, row, column, ignoreItems) {
+ // auto place item if no row specified
if (typeof row === 'undefined' || row === null) {
- this.autoSetItemPosition(item);
- return;
+ row = item.row;
+ column = item.col;
+ if (typeof row === 'undefined' || row === null) {
+ this.autoSetItemPosition(item);
+ return;
+ }
}
- }
- // keep item within allowed bounds
- if (!this.canItemOccupy(item, row, column)) {
- column = Math.min(this.columns - item.sizeX, Math.max(0, column));
- row = Math.min(this.maxRows - item.sizeY, Math.max(0, row));
- }
+ // keep item within allowed bounds
+ if (!this.canItemOccupy(item, row, column)) {
+ column = Math.min(this.columns - item.sizeX, Math.max(0, column));
+ row = Math.min(this.maxRows - item.sizeY, Math.max(0, row));
+ }
- // check if item is already in grid
- if (item.oldRow !== null && typeof item.oldRow !== 'undefined') {
- var samePosition = item.oldRow === row && item.oldColumn === column;
- var inGrid = this.grid[row] && this.grid[row][column] === item;
- if (samePosition && inGrid) {
- item.row = row;
- item.col = column;
- return;
- } else {
- // remove from old position
- var oldRow = this.grid[item.oldRow];
- if (oldRow && oldRow[item.oldColumn] === item) {
- delete oldRow[item.oldColumn];
+ // check if item is already in grid
+ if (item.oldRow !== null && typeof item.oldRow !== 'undefined') {
+ var samePosition = item.oldRow === row && item.oldColumn === column;
+ var inGrid = this.grid[row] && this.grid[row][column] === item;
+ if (samePosition && inGrid) {
+ item.row = row;
+ item.col = column;
+ return;
+ } else {
+ // remove from old position
+ var oldRow = this.grid[item.oldRow];
+ if (oldRow && oldRow[item.oldColumn] === item) {
+ delete oldRow[item.oldColumn];
+ }
}
}
- }
- item.oldRow = item.row = row;
- item.oldColumn = item.col = column;
+ item.oldRow = item.row = row;
+ item.oldColumn = item.col = column;
- this.moveOverlappingItems(item, ignoreItems);
-
- if (!this.grid[row]) {
- this.grid[row] = [];
- }
- this.grid[row][column] = item;
+ this.moveOverlappingItems(item, ignoreItems);
- if (this.sparse && this.allItems.indexOf(item) === -1) {
- this.allItems.push(item);
- }
+ if (!this.grid[row]) {
+ this.grid[row] = [];
+ }
+ this.grid[row][column] = item;
- if (this.movingItem === item) {
- this.floatItemUp(item);
- }
- this.layoutChanged();
- };
+ if (this.sparse && this.allItems.indexOf(item) === -1) {
+ this.allItems.push(item);
+ }
- /**
- * Trade row and column if item1 with item2
- *
- * @param {Object} item1
- * @param {Object} item2
- */
- this.swapItems = function(item1, item2) {
- this.grid[item1.row][item1.col] = item2;
- this.grid[item2.row][item2.col] = item1;
-
- var item1Row = item1.row;
- var item1Col = item1.col;
- item1.row = item2.row;
- item1.col = item2.col;
- item2.row = item1Row;
- item2.col = item1Col;
- };
+ if (this.movingItem === item) {
+ this.floatItemUp(item);
+ }
+ this.layoutChanged();
+ };
- /**
- * Prevents items from being overlapped
- *
- * @param {Object} item The item that should remain
- * @param {Array} ignoreItems
- */
- this.moveOverlappingItems = function(item, ignoreItems) {
- // don't move item, so ignore it
- if (!ignoreItems) {
- ignoreItems = [item];
- } else if (ignoreItems.indexOf(item) === -1) {
- ignoreItems = ignoreItems.slice(0);
- ignoreItems.push(item);
- }
+ /**
+ * Trade row and column if item1 with item2
+ *
+ * @param {Object} item1
+ * @param {Object} item2
+ */
+ this.swapItems = function(item1, item2) {
+ this.grid[item1.row][item1.col] = item2;
+ this.grid[item2.row][item2.col] = item1;
+
+ var item1Row = item1.row;
+ var item1Col = item1.col;
+ item1.row = item2.row;
+ item1.col = item2.col;
+ item2.row = item1Row;
+ item2.col = item1Col;
+ };
- // get the items in the space occupied by the item's coordinates
- var overlappingItems = this.getItems(
- item.row,
- item.col,
- item.sizeX,
- item.sizeY,
- ignoreItems
- );
- this.moveItemsDown(overlappingItems, item.row + item.sizeY, ignoreItems);
- };
+ /**
+ * Prevents items from being overlapped
+ *
+ * @param {Object} item The item that should remain
+ * @param {Array} ignoreItems
+ */
+ this.moveOverlappingItems = function(item, ignoreItems) {
+ // don't move item, so ignore it
+ if (!ignoreItems) {
+ ignoreItems = [item];
+ } else if (ignoreItems.indexOf(item) === -1) {
+ ignoreItems = ignoreItems.slice(0);
+ ignoreItems.push(item);
+ }
+
+ // get the items in the space occupied by the item's coordinates
+ var overlappingItems = this.getItems(
+ item.row,
+ item.col,
+ item.sizeX,
+ item.sizeY,
+ ignoreItems
+ );
+ this.moveItemsDown(overlappingItems, item.row + item.sizeY, ignoreItems);
+ };
- /**
- * Moves an array of items to a specified row
- *
- * @param {Array} items The items to move
- * @param {Number} newRow The target row
- * @param {Array} ignoreItems
- */
- this.moveItemsDown = function(items, newRow, ignoreItems) {
- if (!items || items.length === 0) {
- return;
- }
- items.sort(function(a, b) {
- return a.row - b.row;
- });
+ /**
+ * Moves an array of items to a specified row
+ *
+ * @param {Array} items The items to move
+ * @param {Number} newRow The target row
+ * @param {Array} ignoreItems
+ */
+ this.moveItemsDown = function(items, newRow, ignoreItems) {
+ if (!items || items.length === 0) {
+ return;
+ }
+ items.sort(function(a, b) {
+ return a.row - b.row;
+ });
- ignoreItems = ignoreItems ? ignoreItems.slice(0) : [];
- var topRows = {},
- item, i, l;
+ ignoreItems = ignoreItems ? ignoreItems.slice(0) : [];
+ var topRows = {},
+ item, i, l;
- // calculate the top rows in each column
- for (i = 0, l = items.length; i < l; ++i) {
- item = items[i];
- var topRow = topRows[item.col];
- if (typeof topRow === 'undefined' || item.row < topRow) {
- topRows[item.col] = item.row;
+ // calculate the top rows in each column
+ for (i = 0, l = items.length; i < l; ++i) {
+ item = items[i];
+ var topRow = topRows[item.col];
+ if (typeof topRow === 'undefined' || item.row < topRow) {
+ topRows[item.col] = item.row;
+ }
}
- }
-
- // move each item down from the top row in its column to the row
- for (i = 0, l = items.length; i < l; ++i) {
- item = items[i];
- var rowsToMove = newRow - topRows[item.col];
- this.moveItemDown(item, item.row + rowsToMove, ignoreItems);
- ignoreItems.push(item);
- }
- };
- /**
- * Moves an item down to a specified row
- *
- * @param {Object} item The item to move
- * @param {Number} newRow The target row
- * @param {Array} ignoreItems
- */
- this.moveItemDown = function(item, newRow, ignoreItems) {
- if (item.row >= newRow) {
- return;
- }
- while (item.row < newRow) {
- ++item.row;
- this.moveOverlappingItems(item, ignoreItems);
- }
- this.putItem(item, item.row, item.col, ignoreItems);
- };
+ // move each item down from the top row in its column to the row
+ for (i = 0, l = items.length; i < l; ++i) {
+ item = items[i];
+ var rowsToMove = newRow - topRows[item.col];
+ this.moveItemDown(item, item.row + rowsToMove, ignoreItems);
+ ignoreItems.push(item);
+ }
+ };
- /**
- * Moves all items up as much as possible
- */
- this.floatItemsUp = function() {
- if (this.floating === false) {
- return;
- }
- for (var rowIndex = 0, l = this.grid.length; rowIndex < l; ++rowIndex) {
- var columns = this.grid[rowIndex];
- if (!columns) {
- continue;
+ /**
+ * Moves an item down to a specified row
+ *
+ * @param {Object} item The item to move
+ * @param {Number} newRow The target row
+ * @param {Array} ignoreItems
+ */
+ this.moveItemDown = function(item, newRow, ignoreItems) {
+ if (item.row >= newRow) {
+ return;
}
- for (var colIndex = 0, len = columns.length; colIndex < len; ++colIndex) {
- var item = columns[colIndex];
- if (item) {
- this.floatItemUp(item);
- }
+ while (item.row < newRow) {
+ ++item.row;
+ this.moveOverlappingItems(item, ignoreItems);
}
- }
- };
-
- /**
- * Float an item up to the most suitable row
- *
- * @param {Object} item The item to move
- */
- this.floatItemUp = function(item) {
- if (this.floating === false) {
- return;
- }
- var colIndex = item.col,
- sizeY = item.sizeY,
- sizeX = item.sizeX,
- bestRow = null,
- bestColumn = null,
- rowIndex = item.row - 1;
-
- while (rowIndex > -1) {
- var items = this.getItems(rowIndex, colIndex, sizeX, sizeY, item);
- if (items.length !== 0) {
- break;
- }
- bestRow = rowIndex;
- bestColumn = colIndex;
- --rowIndex;
- }
- if (bestRow !== null) {
- this.putItem(item, bestRow, bestColumn);
- }
- };
+ this.putItem(item, item.row, item.col, ignoreItems);
+ };
- /**
- * Update gridsters height
- *
- * @param {Number} plus (Optional) Additional height to add
- */
- this.updateHeight = function(plus) {
- var maxHeight = this.minRows;
- plus = plus || 0;
- for (var rowIndex = this.grid.length; rowIndex >= 0; --rowIndex) {
- var columns = this.grid[rowIndex];
- if (!columns) {
- continue;
+ /**
+ * Moves all items up as much as possible
+ */
+ this.floatItemsUp = function() {
+ if (this.floating === false) {
+ return;
}
- for (var colIndex = 0, len = columns.length; colIndex < len; ++colIndex) {
- if (columns[colIndex]) {
- maxHeight = Math.max(maxHeight, rowIndex + plus + columns[colIndex].sizeY);
+ for (var rowIndex = 0, l = this.grid.length; rowIndex < l; ++rowIndex) {
+ var columns = this.grid[rowIndex];
+ if (!columns) {
+ continue;
+ }
+ for (var colIndex = 0, len = columns.length; colIndex < len; ++colIndex) {
+ var item = columns[colIndex];
+ if (item) {
+ this.floatItemUp(item);
+ }
}
}
- }
- this.gridHeight = this.maxRows - maxHeight > 0 ? Math.min(this.maxRows, maxHeight) : Math.max(this.maxRows, maxHeight);
- };
-
- /**
- * Returns the number of rows that will fit in given amount of pixels
- *
- * @param {Number} pixels
- * @param {Boolean} ceilOrFloor (Optional) Determines rounding method
- */
- this.pixelsToRows = function(pixels, ceilOrFloor) {
- if (!this.outerMargin) {
- pixels += this.margins[0] / 2;
- }
+ };
- if (ceilOrFloor === true) {
- return Math.ceil(pixels / this.curRowHeight);
- } else if (ceilOrFloor === false) {
- return Math.floor(pixels / this.curRowHeight);
- }
+ /**
+ * Float an item up to the most suitable row
+ *
+ * @param {Object} item The item to move
+ */
+ this.floatItemUp = function(item) {
+ if (this.floating === false) {
+ return;
+ }
+ var colIndex = item.col,
+ sizeY = item.sizeY,
+ sizeX = item.sizeX,
+ bestRow = null,
+ bestColumn = null,
+ rowIndex = item.row - 1;
- return Math.round(pixels / this.curRowHeight);
- };
+ while (rowIndex > -1) {
+ var items = this.getItems(rowIndex, colIndex, sizeX, sizeY, item);
+ if (items.length !== 0) {
+ break;
+ }
+ bestRow = rowIndex;
+ bestColumn = colIndex;
+ --rowIndex;
+ }
+ if (bestRow !== null) {
+ this.putItem(item, bestRow, bestColumn);
+ }
+ };
- /**
- * Returns the number of columns that will fit in a given amount of pixels
- *
- * @param {Number} pixels
- * @param {Boolean} ceilOrFloor (Optional) Determines rounding method
- * @returns {Number} The number of columns
- */
- this.pixelsToColumns = function(pixels, ceilOrFloor) {
- if (!this.outerMargin) {
- pixels += this.margins[1] / 2;
- }
+ /**
+ * Update gridsters height
+ *
+ * @param {Number} plus (Optional) Additional height to add
+ */
+ this.updateHeight = function(plus) {
+ var maxHeight = this.minRows;
+ plus = plus || 0;
+ for (var rowIndex = this.grid.length; rowIndex >= 0; --rowIndex) {
+ var columns = this.grid[rowIndex];
+ if (!columns) {
+ continue;
+ }
+ for (var colIndex = 0, len = columns.length; colIndex < len; ++colIndex) {
+ if (columns[colIndex]) {
+ maxHeight = Math.max(maxHeight, rowIndex + plus + columns[colIndex].sizeY);
+ }
+ }
+ }
+ this.gridHeight = this.maxRows - maxHeight > 0 ? Math.min(this.maxRows, maxHeight) : Math.max(this.maxRows, maxHeight);
+ };
- if (ceilOrFloor === true) {
- return Math.ceil(pixels / this.curColWidth);
- } else if (ceilOrFloor === false) {
- return Math.floor(pixels / this.curColWidth);
- }
+ /**
+ * Returns the number of rows that will fit in given amount of pixels
+ *
+ * @param {Number} pixels
+ * @param {Boolean} ceilOrFloor (Optional) Determines rounding method
+ */
+ this.pixelsToRows = function(pixels, ceilOrFloor) {
+ if (!this.outerMargin) {
+ pixels += this.margins[0] / 2;
+ }
- return Math.round(pixels / this.curColWidth);
- };
- }
- ])
+ if (ceilOrFloor === true) {
+ return Math.ceil(pixels / this.curRowHeight);
+ } else if (ceilOrFloor === false) {
+ return Math.floor(pixels / this.curRowHeight);
+ }
- .directive('gridsterPreview', function() {
- return {
- replace: true,
- scope: true,
- require: '^gridster',
- template: '',
- link: function(scope, $el, attrs, gridster) {
+ return Math.round(pixels / this.curRowHeight);
+ };
/**
- * @returns {Object} style object for preview element
+ * Returns the number of columns that will fit in a given amount of pixels
+ *
+ * @param {Number} pixels
+ * @param {Boolean} ceilOrFloor (Optional) Determines rounding method
+ * @returns {Number} The number of columns
*/
- scope.previewStyle = function() {
- if (!gridster.movingItem) {
- return {
- display: 'none'
- };
+ this.pixelsToColumns = function(pixels, ceilOrFloor) {
+ if (!this.outerMargin) {
+ pixels += this.margins[1] / 2;
}
- return {
- display: 'block',
- height: (gridster.movingItem.sizeY * gridster.curRowHeight - gridster.margins[0]) + 'px',
- width: (gridster.movingItem.sizeX * gridster.curColWidth - gridster.margins[1]) + 'px',
- top: (gridster.movingItem.row * gridster.curRowHeight + (gridster.outerMargin ? gridster.margins[0] : 0)) + 'px',
- left: (gridster.movingItem.col * gridster.curColWidth + (gridster.outerMargin ? gridster.margins[1] : 0)) + 'px'
- };
+ if (ceilOrFloor === true) {
+ return Math.ceil(pixels / this.curColWidth);
+ } else if (ceilOrFloor === false) {
+ return Math.floor(pixels / this.curColWidth);
+ }
+
+ return Math.round(pixels / this.curColWidth);
};
}
- };
- })
-
- /**
- * The gridster directive
- *
- * @param {Function} $timeout
- * @param {Object} $window
- * @param {Object} $rootScope
- * @param {Function} gridsterDebounce
- */
- .directive('gridster', ['$timeout', '$window', '$rootScope', 'gridsterDebounce',
- function($timeout, $window, $rootScope, gridsterDebounce) {
+ ])
+
+ .directive('gridsterPreview', function() {
return {
+ replace: true,
scope: true,
- restrict: 'EAC',
- controller: 'GridsterCtrl',
- controllerAs: 'gridster',
- compile: function($tplElem) {
-
- $tplElem.prepend('');
-
- return function(scope, $elem, attrs, gridster) {
- gridster.loaded = false;
+ require: '^gridster',
+ template: '',
+ link: function(scope, $el, attrs, gridster) {
+
+ /**
+ * @returns {Object} style object for preview element
+ */
+ scope.previewStyle = function() {
+ if (!gridster.movingItem) {
+ return {
+ display: 'none'
+ };
+ }
- gridster.$element = $elem;
+ return {
+ display: 'block',
+ height: (gridster.movingItem.sizeY * gridster.curRowHeight - gridster.margins[0]) + 'px',
+ width: (gridster.movingItem.sizeX * gridster.curColWidth - gridster.margins[1]) + 'px',
+ top: (gridster.movingItem.row * gridster.curRowHeight + (gridster.outerMargin ? gridster.margins[0] : 0)) + 'px',
+ left: (gridster.movingItem.col * gridster.curColWidth + (gridster.outerMargin ? gridster.margins[1] : 0)) + 'px'
+ };
+ };
+ }
+ };
+ })
- scope.gridster = gridster;
+ /**
+ * The gridster directive
+ *
+ * @param {Function} $timeout
+ * @param {Object} $window
+ * @param {Object} $rootScope
+ */
+ .directive('gridster', ['$timeout', '$window', '$rootScope',
+ function($timeout, $window, $rootScope) {
+ return {
+ scope: true,
+ restrict: 'EAC',
+ controller: 'GridsterCtrl',
+ controllerAs: 'gridster',
+ compile: function($tplElem) {
- $elem.addClass('gridster');
+ $tplElem.prepend('');
- var isVisible = function(ele) {
- return ele.style.visibility !== 'hidden' && ele.style.display !== 'none';
- };
+ return function(scope, $elem, attrs, gridster) {
+ gridster.loaded = false;
- function updateHeight() {
- $elem.css('height', (gridster.gridHeight * gridster.curRowHeight) + (gridster.outerMargin ? gridster.margins[0] : -gridster.margins[0]) + 'px');
- }
+ gridster.$element = $elem;
- scope.$watch(function() {
- return gridster.gridHeight;
- }, updateHeight);
+ scope.gridster = gridster;
- scope.$watch(function() {
- return gridster.movingItem;
- }, function() {
- gridster.updateHeight(gridster.movingItem ? gridster.movingItem.sizeY : 0);
- });
+ $elem.addClass('gridster');
- function refresh(config) {
- gridster.setOptions(config);
+ var isVisible = function(ele) {
+ return ele.style.visibility !== 'hidden' && ele.offsetParent != null;
+ };
- if (!isVisible($elem[0])) {
- return;
+ function updateHeight() {
+ $elem.css('height', (gridster.gridHeight * gridster.curRowHeight) + (gridster.outerMargin ? gridster.margins[0] : -gridster.margins[0]) + 'px');
}
- // resolve "auto" & "match" values
- if (gridster.width === 'auto') {
- gridster.curWidth = $elem[0].offsetWidth || parseInt($elem.css('width'), 10);
- } else {
- gridster.curWidth = gridster.width;
- }
+ scope.$watch(function() {
+ return gridster.gridHeight;
+ }, updateHeight);
- if (gridster.colWidth === 'auto') {
- gridster.curColWidth = (gridster.curWidth + (gridster.outerMargin ? -gridster.margins[1] : gridster.margins[1])) / gridster.columns;
- } else {
- gridster.curColWidth = gridster.colWidth;
- }
+ scope.$watch(function() {
+ return gridster.movingItem;
+ }, function() {
+ gridster.updateHeight(gridster.movingItem ? gridster.movingItem.sizeY : 0);
+ });
- gridster.curRowHeight = gridster.rowHeight;
- if (typeof gridster.rowHeight === 'string') {
- if (gridster.rowHeight === 'match') {
- gridster.curRowHeight = Math.round(gridster.curColWidth);
- } else if (gridster.rowHeight.indexOf('*') !== -1) {
- gridster.curRowHeight = Math.round(gridster.curColWidth * gridster.rowHeight.replace('*', '').replace(' ', ''));
- } else if (gridster.rowHeight.indexOf('/') !== -1) {
- gridster.curRowHeight = Math.round(gridster.curColWidth / gridster.rowHeight.replace('/', '').replace(' ', ''));
+ function refresh(config) {
+ gridster.setOptions(config);
+
+ if (!isVisible($elem[0])) {
+ return;
}
- }
- gridster.isMobile = gridster.mobileModeEnabled && gridster.curWidth <= gridster.mobileBreakPoint;
+ // resolve "auto" & "match" values
+ if (gridster.width === 'auto') {
+ gridster.curWidth = $elem[0].offsetWidth || parseInt($elem.css('width'), 10);
+ } else {
+ gridster.curWidth = gridster.width;
+ }
- // loop through all items and reset their CSS
- for (var rowIndex = 0, l = gridster.grid.length; rowIndex < l; ++rowIndex) {
- var columns = gridster.grid[rowIndex];
- if (!columns) {
- continue;
+ if (gridster.colWidth === 'auto') {
+ gridster.curColWidth = (gridster.curWidth + (gridster.outerMargin ? -gridster.margins[1] : gridster.margins[1])) / gridster.columns;
+ } else {
+ gridster.curColWidth = gridster.colWidth;
}
- for (var colIndex = 0, len = columns.length; colIndex < len; ++colIndex) {
- if (columns[colIndex]) {
- var item = columns[colIndex];
- item.setElementPosition();
- item.setElementSizeY();
- item.setElementSizeX();
+ gridster.curRowHeight = gridster.rowHeight;
+ if (typeof gridster.rowHeight === 'string') {
+ if (gridster.rowHeight === 'match') {
+ gridster.curRowHeight = Math.round(gridster.curColWidth);
+ } else if (gridster.rowHeight.indexOf('*') !== -1) {
+ gridster.curRowHeight = Math.round(gridster.curColWidth * gridster.rowHeight.replace('*', '').replace(' ', ''));
+ } else if (gridster.rowHeight.indexOf('/') !== -1) {
+ gridster.curRowHeight = Math.round(gridster.curColWidth / gridster.rowHeight.replace('/', '').replace(' ', ''));
}
}
- }
- updateHeight();
- }
+ gridster.isMobile = gridster.mobileModeEnabled && gridster.curWidth <= gridster.mobileBreakPoint;
- var optionsKey = attrs.gridster;
- if (optionsKey) {
- scope.$parent.$watch(optionsKey, function(newConfig) {
- refresh(newConfig);
- }, true);
- } else {
- refresh({});
- }
+ // loop through all items and reset their CSS
+ for (var rowIndex = 0, l = gridster.grid.length; rowIndex < l; ++rowIndex) {
+ var columns = gridster.grid[rowIndex];
+ if (!columns) {
+ continue;
+ }
- scope.$watch(function() {
- return gridster.loaded;
- }, function() {
- if (gridster.loaded) {
- $elem.addClass('gridster-loaded');
- $rootScope.$broadcast('gridster-loaded', gridster);
- } else {
- $elem.removeClass('gridster-loaded');
+ for (var colIndex = 0, len = columns.length; colIndex < len; ++colIndex) {
+ if (columns[colIndex]) {
+ var item = columns[colIndex];
+ item.setElementPosition();
+ item.setElementSizeY();
+ item.setElementSizeX();
+ }
+ }
+ }
+
+ updateHeight();
}
- });
- scope.$watch(function() {
- return gridster.isMobile;
- }, function() {
- if (gridster.isMobile) {
- $elem.addClass('gridster-mobile').removeClass('gridster-desktop');
+ var optionsKey = attrs.gridster;
+ if (optionsKey) {
+ scope.$parent.$watch(optionsKey, function(newConfig) {
+ refresh(newConfig);
+ }, true);
} else {
- $elem.removeClass('gridster-mobile').addClass('gridster-desktop');
+ refresh({});
}
- $rootScope.$broadcast('gridster-mobile-changed', gridster);
- });
-
- scope.$watch(function() {
- return gridster.draggable;
- }, function() {
- $rootScope.$broadcast('gridster-draggable-changed', gridster);
- }, true);
-
- scope.$watch(function() {
- return gridster.resizable;
- }, function() {
- $rootScope.$broadcast('gridster-resizable-changed', gridster);
- }, true);
- var prevWidth = $elem[0].offsetWidth || parseInt($elem.css('width'), 10);
+ scope.$watch(function() {
+ return gridster.loaded;
+ }, function() {
+ if (gridster.loaded) {
+ $elem.addClass('gridster-loaded');
+ $rootScope.$broadcast('gridster-loaded', gridster);
+ } else {
+ $elem.removeClass('gridster-loaded');
+ }
+ });
- var resize = function() {
- var width = $elem[0].offsetWidth || parseInt($elem.css('width'), 10);
+ scope.$watch(function() {
+ return gridster.isMobile;
+ }, function() {
+ if (gridster.isMobile) {
+ $elem.addClass('gridster-mobile').removeClass('gridster-desktop');
+ } else {
+ $elem.removeClass('gridster-mobile').addClass('gridster-desktop');
+ }
+ $rootScope.$broadcast('gridster-mobile-changed', gridster);
+ });
- if (!width || width === prevWidth || gridster.movingItem) {
- return;
- }
- prevWidth = width;
+ scope.$watch(function() {
+ return gridster.draggable;
+ }, function() {
+ $rootScope.$broadcast('gridster-draggable-changed', gridster);
+ }, true);
- if (gridster.loaded) {
- $elem.removeClass('gridster-loaded');
- }
+ scope.$watch(function() {
+ return gridster.resizable;
+ }, function() {
+ $rootScope.$broadcast('gridster-resizable-changed', gridster);
+ }, true);
- refresh();
+ var prevWidth = $elem[0].offsetWidth || parseInt($elem.css('width'), 10);
- if (gridster.loaded) {
- $elem.addClass('gridster-loaded');
- }
+ var resize = function() {
+ var width = $elem[0].offsetWidth || parseInt($elem.css('width'), 10);
- $rootScope.$broadcast('gridster-resized', [width, $elem[0].offsetHeight], gridster);
- };
-
- // track element width changes any way we can
- var onResize = gridsterDebounce(function onResize() {
- resize();
- $timeout(function() {
- scope.$apply();
- });
- }, 100);
+ if (width === prevWidth || gridster.movingItem) {
+ return;
+ }
+ prevWidth = width;
- scope.$watch(function() {
- return isVisible($elem[0]);
- }, onResize);
+ if (gridster.loaded) {
+ $elem.removeClass('gridster-loaded');
+ }
- // see https://github.com/sdecima/javascript-detect-element-resize
- if (typeof window.addResizeListener === 'function') {
- window.addResizeListener($elem[0], onResize);
- } else {
- scope.$watch(function() {
- return $elem[0].offsetWidth || parseInt($elem.css('width'), 10);
- }, resize);
- }
- var $win = angular.element($window);
- $win.on('resize', onResize);
-
- // be sure to cleanup
- scope.$on('$destroy', function() {
- gridster.destroy();
- $win.off('resize', onResize);
- if (typeof window.removeResizeListener === 'function') {
- window.removeResizeListener($elem[0], onResize);
- }
- });
+ refresh();
- // allow a little time to place items before floating up
- $timeout(function() {
- scope.$watch('gridster.floating', function() {
- gridster.floatItemsUp();
- });
- gridster.loaded = true;
- }, 100);
- };
- }
- };
- }
- ])
-
- .controller('GridsterItemCtrl', function() {
- this.$element = null;
- this.gridster = null;
- this.row = null;
- this.col = null;
- this.sizeX = null;
- this.sizeY = null;
- this.minSizeX = 0;
- this.minSizeY = 0;
- this.maxSizeX = null;
- this.maxSizeY = null;
-
- this.init = function($element, gridster) {
- this.$element = $element;
- this.gridster = gridster;
- this.sizeX = gridster.defaultSizeX;
- this.sizeY = gridster.defaultSizeY;
- };
-
- this.destroy = function() {
- // set these to null to avoid the possibility of circular references
- this.gridster = null;
- this.$element = null;
- };
+ if (gridster.loaded) {
+ $elem.addClass('gridster-loaded');
+ }
- /**
- * Returns the items most important attributes
- */
- this.toJSON = function() {
- return {
- row: this.row,
- col: this.col,
- sizeY: this.sizeY,
- sizeX: this.sizeX
- };
- };
+ $rootScope.$broadcast('gridster-resized', [width, $elem[0].offsetHeight], gridster);
+ scope.$apply();
+ };
- this.isMoving = function() {
- return this.gridster.movingItem === this;
- };
+ // track element width changes any way we can
+ var resizeObserver = new ResizeObserver(resize);
+ resizeObserver.observe($elem[0]);
- /**
- * Set the items position
- *
- * @param {Number} row
- * @param {Number} column
- */
- this.setPosition = function(row, column) {
- this.gridster.putItem(this, row, column);
+ // be sure to cleanup
+ scope.$on('$destroy', function() {
+ gridster.destroy();
+ resizeObserver.disconnect();
+ });
- if (!this.isMoving()) {
- this.setElementPosition();
- }
- };
+ // allow a little time to place items before floating up
+ $timeout(function() {
+ resize();
- /**
- * Sets a specified size property
- *
- * @param {String} key Can be either "x" or "y"
- * @param {Number} value The size amount
- * @param {Boolean} preventMove
- */
- this.setSize = function(key, value, preventMove) {
- key = key.toUpperCase();
- var camelCase = 'size' + key,
- titleCase = 'Size' + key;
- if (value === '') {
- return;
- }
- value = parseInt(value, 10);
- if (isNaN(value) || value === 0) {
- value = this.gridster['default' + titleCase];
- }
- var max = key === 'X' ? this.gridster.columns : this.gridster.maxRows;
- if (this['max' + titleCase]) {
- max = Math.min(this['max' + titleCase], max);
- }
- if (this.gridster['max' + titleCase]) {
- max = Math.min(this.gridster['max' + titleCase], max);
- }
- if (key === 'X' && this.cols) {
- max -= this.cols;
- } else if (key === 'Y' && this.rows) {
- max -= this.rows;
+ scope.$watch('gridster.floating', function() {
+ gridster.floatItemsUp();
+ });
+ gridster.loaded = true;
+ }, 100);
+ };
+ }
+ };
}
+ ])
- var min = 0;
- if (this['min' + titleCase]) {
- min = Math.max(this['min' + titleCase], min);
- }
- if (this.gridster['min' + titleCase]) {
- min = Math.max(this.gridster['min' + titleCase], min);
- }
+ .controller('GridsterItemCtrl', function() {
+ this.$element = null;
+ this.gridster = null;
+ this.row = null;
+ this.col = null;
+ this.sizeX = null;
+ this.sizeY = null;
+ this.minSizeX = 0;
+ this.minSizeY = 0;
+ this.maxSizeX = null;
+ this.maxSizeY = null;
+
+ this.init = function($element, gridster) {
+ this.$element = $element;
+ this.gridster = gridster;
+ this.sizeX = gridster.defaultSizeX;
+ this.sizeY = gridster.defaultSizeY;
+ };
- value = Math.max(Math.min(value, max), min);
+ this.destroy = function() {
+ // set these to null to avoid the possibility of circular references
+ this.gridster = null;
+ this.$element = null;
+ };
- var changed = (this[camelCase] !== value || (this['old' + titleCase] && this['old' + titleCase] !== value));
- this['old' + titleCase] = this[camelCase] = value;
+ /**
+ * Returns the items most important attributes
+ */
+ this.toJSON = function() {
+ return {
+ row: this.row,
+ col: this.col,
+ sizeY: this.sizeY,
+ sizeX: this.sizeX
+ };
+ };
- if (!this.isMoving()) {
- this['setElement' + titleCase]();
- }
- if (!preventMove && changed) {
- this.gridster.moveOverlappingItems(this);
- this.gridster.layoutChanged();
- }
+ this.isMoving = function() {
+ return this.gridster.movingItem === this;
+ };
- return changed;
- };
+ /**
+ * Set the items position
+ *
+ * @param {Number} row
+ * @param {Number} column
+ */
+ this.setPosition = function(row, column) {
+ this.gridster.putItem(this, row, column);
- /**
- * Sets the items sizeY property
- *
- * @param {Number} rows
- * @param {Boolean} preventMove
- */
- this.setSizeY = function(rows, preventMove) {
- return this.setSize('Y', rows, preventMove);
- };
+ if (!this.isMoving()) {
+ this.setElementPosition();
+ }
+ };
- /**
- * Sets the items sizeX property
- *
- * @param {Number} columns
- * @param {Boolean} preventMove
- */
- this.setSizeX = function(columns, preventMove) {
- return this.setSize('X', columns, preventMove);
- };
+ /**
+ * Sets a specified size property
+ *
+ * @param {String} key Can be either "x" or "y"
+ * @param {Number} value The size amount
+ * @param {Boolean} preventMove
+ */
+ this.setSize = function(key, value, preventMove) {
+ key = key.toUpperCase();
+ var camelCase = 'size' + key,
+ titleCase = 'Size' + key;
+ if (value === '') {
+ return;
+ }
+ value = parseInt(value, 10);
+ if (isNaN(value) || value === 0) {
+ value = this.gridster['default' + titleCase];
+ }
+ var max = key === 'X' ? this.gridster.columns : this.gridster.maxRows;
+ if (this['max' + titleCase]) {
+ max = Math.min(this['max' + titleCase], max);
+ }
+ if (this.gridster['max' + titleCase]) {
+ max = Math.min(this.gridster['max' + titleCase], max);
+ }
+ if (key === 'X' && this.cols) {
+ max -= this.cols;
+ } else if (key === 'Y' && this.rows) {
+ max -= this.rows;
+ }
- /**
- * Sets an elements position on the page
- */
- this.setElementPosition = function() {
- if (this.gridster.isMobile) {
- this.$element.css({
- marginLeft: this.gridster.margins[0] + 'px',
- marginRight: this.gridster.margins[0] + 'px',
- marginTop: this.gridster.margins[1] + 'px',
- marginBottom: this.gridster.margins[1] + 'px',
- top: '',
- left: ''
- });
- } else {
- this.$element.css({
- margin: 0,
- top: (this.row * this.gridster.curRowHeight + (this.gridster.outerMargin ? this.gridster.margins[0] : 0)) + 'px',
- left: (this.col * this.gridster.curColWidth + (this.gridster.outerMargin ? this.gridster.margins[1] : 0)) + 'px'
- });
- }
- };
+ var min = 0;
+ if (this['min' + titleCase]) {
+ min = Math.max(this['min' + titleCase], min);
+ }
+ if (this.gridster['min' + titleCase]) {
+ min = Math.max(this.gridster['min' + titleCase], min);
+ }
- /**
- * Sets an elements height
- */
- this.setElementSizeY = function() {
- if (this.gridster.isMobile && !this.gridster.saveGridItemCalculatedHeightInMobile) {
- this.$element.css('height', '');
- } else {
- this.$element.css('height', (this.sizeY * this.gridster.curRowHeight - this.gridster.margins[0]) + 'px');
- }
- };
+ value = Math.max(Math.min(value, max), min);
- /**
- * Sets an elements width
- */
- this.setElementSizeX = function() {
- if (this.gridster.isMobile) {
- this.$element.css('width', '');
- } else {
- this.$element.css('width', (this.sizeX * this.gridster.curColWidth - this.gridster.margins[1]) + 'px');
- }
- };
+ var changed = (this[camelCase] !== value || (this['old' + titleCase] && this['old' + titleCase] !== value));
+ this['old' + titleCase] = this[camelCase] = value;
- /**
- * Gets an element's width
- */
- this.getElementSizeX = function() {
- return (this.sizeX * this.gridster.curColWidth - this.gridster.margins[1]);
- };
+ if (!this.isMoving()) {
+ this['setElement' + titleCase]();
+ }
+ if (!preventMove && changed) {
+ this.gridster.moveOverlappingItems(this);
+ this.gridster.layoutChanged();
+ }
- /**
- * Gets an element's height
- */
- this.getElementSizeY = function() {
- return (this.sizeY * this.gridster.curRowHeight - this.gridster.margins[0]);
- };
+ return changed;
+ };
- })
+ /**
+ * Sets the items sizeY property
+ *
+ * @param {Number} rows
+ * @param {Boolean} preventMove
+ */
+ this.setSizeY = function(rows, preventMove) {
+ return this.setSize('Y', rows, preventMove);
+ };
- .factory('GridsterTouch', [function() {
- return function GridsterTouch(target, startEvent, moveEvent, endEvent) {
- var lastXYById = {};
+ /**
+ * Sets the items sizeX property
+ *
+ * @param {Number} columns
+ * @param {Boolean} preventMove
+ */
+ this.setSizeX = function(columns, preventMove) {
+ return this.setSize('X', columns, preventMove);
+ };
- // Opera doesn't have Object.keys so we use this wrapper
- var numberOfKeys = function(theObject) {
- if (Object.keys) {
- return Object.keys(theObject).length;
+ /**
+ * Sets an elements position on the page
+ */
+ this.setElementPosition = function() {
+ if (this.gridster.isMobile) {
+ this.$element.css({
+ marginLeft: this.gridster.margins[0] + 'px',
+ marginRight: this.gridster.margins[0] + 'px',
+ marginTop: this.gridster.margins[1] + 'px',
+ marginBottom: this.gridster.margins[1] + 'px',
+ top: '',
+ left: ''
+ });
+ } else {
+ this.$element.css({
+ margin: 0,
+ top: (this.row * this.gridster.curRowHeight + (this.gridster.outerMargin ? this.gridster.margins[0] : 0)) + 'px',
+ left: (this.col * this.gridster.curColWidth + (this.gridster.outerMargin ? this.gridster.margins[1] : 0)) + 'px'
+ });
}
+ };
- var n = 0,
- key;
- for (key in theObject) {
- ++n;
+ /**
+ * Sets an elements height
+ */
+ this.setElementSizeY = function() {
+ if (this.gridster.isMobile && !this.gridster.saveGridItemCalculatedHeightInMobile) {
+ this.$element.css('height', '');
+ } else {
+ this.$element.css('height', (this.sizeY * this.gridster.curRowHeight - this.gridster.margins[0]) + 'px');
}
-
- return n;
};
- // this calculates the delta needed to convert pageX/Y to offsetX/Y because offsetX/Y don't exist in the TouchEvent object or in Firefox's MouseEvent object
- var computeDocumentToElementDelta = function(theElement) {
- var elementLeft = 0;
- var elementTop = 0;
- var oldIEUserAgent = navigator.userAgent.match(/\bMSIE\b/);
-
- for (var offsetElement = theElement; offsetElement != null; offsetElement = offsetElement.offsetParent) {
- // the following is a major hack for versions of IE less than 8 to avoid an apparent problem on the IEBlog with double-counting the offsets
- // this may not be a general solution to IE7's problem with offsetLeft/offsetParent
- if (oldIEUserAgent &&
- (!document.documentMode || document.documentMode < 8) &&
- offsetElement.currentStyle.position === 'relative' && offsetElement.offsetParent && offsetElement.offsetParent.currentStyle.position === 'relative' && offsetElement.offsetLeft === offsetElement.offsetParent.offsetLeft) {
- // add only the top
- elementTop += offsetElement.offsetTop;
- } else {
- elementLeft += offsetElement.offsetLeft;
- elementTop += offsetElement.offsetTop;
- }
+ /**
+ * Sets an elements width
+ */
+ this.setElementSizeX = function() {
+ if (this.gridster.isMobile) {
+ this.$element.css('width', '');
+ } else {
+ this.$element.css('width', (this.sizeX * this.gridster.curColWidth - this.gridster.margins[1]) + 'px');
}
+ };
- return {
- x: elementLeft,
- y: elementTop
- };
+ /**
+ * Gets an element's width
+ */
+ this.getElementSizeX = function() {
+ return (this.sizeX * this.gridster.curColWidth - this.gridster.margins[1]);
};
- // cache the delta from the document to our event target (reinitialized each mousedown/MSPointerDown/touchstart)
- var documentToTargetDelta = computeDocumentToElementDelta(target);
- var useSetReleaseCapture = false;
+ /**
+ * Gets an element's height
+ */
+ this.getElementSizeY = function() {
+ return (this.sizeY * this.gridster.curRowHeight - this.gridster.margins[0]);
+ };
- // common event handler for the mouse/pointer/touch models and their down/start, move, up/end, and cancel events
- var doEvent = function(theEvtObj) {
+ })
- if (theEvtObj.type === 'mousemove' && numberOfKeys(lastXYById) === 0) {
- return;
- }
+ .factory('GridsterTouch', [function() {
+ return function GridsterTouch(target, startEvent, moveEvent, endEvent) {
+ var lastXYById = {};
- var prevent = true;
-
- var pointerList = theEvtObj.changedTouches ? theEvtObj.changedTouches : [theEvtObj];
- for (var i = 0; i < pointerList.length; ++i) {
- var pointerObj = pointerList[i];
- var pointerId = (typeof pointerObj.identifier !== 'undefined') ? pointerObj.identifier : (typeof pointerObj.pointerId !== 'undefined') ? pointerObj.pointerId : 1;
-
- // use the pageX/Y coordinates to compute target-relative coordinates when we have them (in ie < 9, we need to do a little work to put them there)
- if (typeof pointerObj.pageX === 'undefined') {
- // initialize assuming our source element is our target
- pointerObj.pageX = pointerObj.offsetX + documentToTargetDelta.x;
- pointerObj.pageY = pointerObj.offsetY + documentToTargetDelta.y;
-
- if (pointerObj.srcElement.offsetParent === target && document.documentMode && document.documentMode === 8 && pointerObj.type === 'mousedown') {
- // source element is a child piece of VML, we're in IE8, and we've not called setCapture yet - add the origin of the source element
- pointerObj.pageX += pointerObj.srcElement.offsetLeft;
- pointerObj.pageY += pointerObj.srcElement.offsetTop;
- } else if (pointerObj.srcElement !== target && !document.documentMode || document.documentMode < 8) {
- // source element isn't the target (most likely it's a child piece of VML) and we're in a version of IE before IE8 -
- // the offsetX/Y values are unpredictable so use the clientX/Y values and adjust by the scroll offsets of its parents
- // to get the document-relative coordinates (the same as pageX/Y)
- var sx = -2,
- sy = -2; // adjust for old IE's 2-pixel border
- for (var scrollElement = pointerObj.srcElement; scrollElement !== null; scrollElement = scrollElement.parentNode) {
- sx += scrollElement.scrollLeft ? scrollElement.scrollLeft : 0;
- sy += scrollElement.scrollTop ? scrollElement.scrollTop : 0;
- }
+ // Opera doesn't have Object.keys so we use this wrapper
+ var numberOfKeys = function(theObject) {
+ if (Object.keys) {
+ return Object.keys(theObject).length;
+ }
+
+ var n = 0,
+ key;
+ for (key in theObject) {
+ ++n;
+ }
+
+ return n;
+ };
- pointerObj.pageX = pointerObj.clientX + sx;
- pointerObj.pageY = pointerObj.clientY + sy;
+ // this calculates the delta needed to convert pageX/Y to offsetX/Y because offsetX/Y don't exist in the TouchEvent object or in Firefox's MouseEvent object
+ var computeDocumentToElementDelta = function(theElement) {
+ var elementLeft = 0;
+ var elementTop = 0;
+ var oldIEUserAgent = navigator.userAgent.match(/\bMSIE\b/);
+
+ for (var offsetElement = theElement; offsetElement != null; offsetElement = offsetElement.offsetParent) {
+ // the following is a major hack for versions of IE less than 8 to avoid an apparent problem on the IEBlog with double-counting the offsets
+ // this may not be a general solution to IE7's problem with offsetLeft/offsetParent
+ if (oldIEUserAgent &&
+ (!document.documentMode || document.documentMode < 8) &&
+ offsetElement.currentStyle.position === 'relative' && offsetElement.offsetParent && offsetElement.offsetParent.currentStyle.position === 'relative' && offsetElement.offsetLeft === offsetElement.offsetParent.offsetLeft) {
+ // add only the top
+ elementTop += offsetElement.offsetTop;
+ } else {
+ elementLeft += offsetElement.offsetLeft;
+ elementTop += offsetElement.offsetTop;
}
}
+ return {
+ x: elementLeft,
+ y: elementTop
+ };
+ };
- var pageX = pointerObj.pageX;
- var pageY = pointerObj.pageY;
+ // cache the delta from the document to our event target (reinitialized each mousedown/MSPointerDown/touchstart)
+ var documentToTargetDelta = computeDocumentToElementDelta(target);
+ var useSetReleaseCapture = false;
- if (theEvtObj.type.match(/(start|down)$/i)) {
- // clause for processing MSPointerDown, touchstart, and mousedown
+ // common event handler for the mouse/pointer/touch models and their down/start, move, up/end, and cancel events
+ var doEvent = function(theEvtObj) {
- // refresh the document-to-target delta on start in case the target has moved relative to document
- documentToTargetDelta = computeDocumentToElementDelta(target);
+ if (theEvtObj.type === 'mousemove' && numberOfKeys(lastXYById) === 0) {
+ return;
+ }
- // protect against failing to get an up or end on this pointerId
- if (lastXYById[pointerId]) {
- if (endEvent) {
- endEvent({
- target: theEvtObj.target,
- which: theEvtObj.which,
- pointerId: pointerId,
- pageX: pageX,
- pageY: pageY
- });
- }
+ var prevent = true;
+
+ var pointerList = theEvtObj.changedTouches ? theEvtObj.changedTouches : [theEvtObj];
+ for (var i = 0; i < pointerList.length; ++i) {
+ var pointerObj = pointerList[i];
+ var pointerId = (typeof pointerObj.identifier !== 'undefined') ? pointerObj.identifier : (typeof pointerObj.pointerId !== 'undefined') ? pointerObj.pointerId : 1;
+
+ // use the pageX/Y coordinates to compute target-relative coordinates when we have them (in ie < 9, we need to do a little work to put them there)
+ if (typeof pointerObj.pageX === 'undefined') {
+ // initialize assuming our source element is our target
+ pointerObj.pageX = pointerObj.offsetX + documentToTargetDelta.x;
+ pointerObj.pageY = pointerObj.offsetY + documentToTargetDelta.y;
+
+ if (pointerObj.srcElement.offsetParent === target && document.documentMode && document.documentMode === 8 && pointerObj.type === 'mousedown') {
+ // source element is a child piece of VML, we're in IE8, and we've not called setCapture yet - add the origin of the source element
+ pointerObj.pageX += pointerObj.srcElement.offsetLeft;
+ pointerObj.pageY += pointerObj.srcElement.offsetTop;
+ } else if (pointerObj.srcElement !== target && !document.documentMode || document.documentMode < 8) {
+ // source element isn't the target (most likely it's a child piece of VML) and we're in a version of IE before IE8 -
+ // the offsetX/Y values are unpredictable so use the clientX/Y values and adjust by the scroll offsets of its parents
+ // to get the document-relative coordinates (the same as pageX/Y)
+ var sx = -2,
+ sy = -2; // adjust for old IE's 2-pixel border
+ for (var scrollElement = pointerObj.srcElement; scrollElement !== null; scrollElement = scrollElement.parentNode) {
+ sx += scrollElement.scrollLeft ? scrollElement.scrollLeft : 0;
+ sy += scrollElement.scrollTop ? scrollElement.scrollTop : 0;
+ }
- delete lastXYById[pointerId];
+ pointerObj.pageX = pointerObj.clientX + sx;
+ pointerObj.pageY = pointerObj.clientY + sy;
+ }
}
- if (startEvent) {
- if (prevent) {
- prevent = startEvent({
- target: theEvtObj.target,
- which: theEvtObj.which,
- pointerId: pointerId,
- pageX: pageX,
- pageY: pageY
- });
+
+ var pageX = pointerObj.pageX;
+ var pageY = pointerObj.pageY;
+
+ if (theEvtObj.type.match(/(start|down)$/i)) {
+ // clause for processing MSPointerDown, touchstart, and mousedown
+
+ // refresh the document-to-target delta on start in case the target has moved relative to document
+ documentToTargetDelta = computeDocumentToElementDelta(target);
+
+ // protect against failing to get an up or end on this pointerId
+ if (lastXYById[pointerId]) {
+ if (endEvent) {
+ endEvent({
+ target: theEvtObj.target,
+ which: theEvtObj.which,
+ pointerId: pointerId,
+ pageX: pageX,
+ pageY: pageY
+ });
+ }
+
+ delete lastXYById[pointerId];
}
- }
- // init last page positions for this pointer
- lastXYById[pointerId] = {
- x: pageX,
- y: pageY
- };
+ if (startEvent) {
+ if (prevent) {
+ prevent = startEvent({
+ target: theEvtObj.target,
+ which: theEvtObj.which,
+ pointerId: pointerId,
+ pageX: pageX,
+ pageY: pageY
+ });
+ }
+ }
- // IE pointer model
- if (target.msSetPointerCapture && prevent) {
- target.msSetPointerCapture(pointerId);
- } else if (theEvtObj.type === 'mousedown' && numberOfKeys(lastXYById) === 1) {
- if (useSetReleaseCapture) {
- target.setCapture(true);
- } else {
- document.addEventListener('mousemove', doEvent, false);
- document.addEventListener('mouseup', doEvent, false);
- document.addEventListener('mouseleave', doEvent, false);
+ // init last page positions for this pointer
+ lastXYById[pointerId] = {
+ x: pageX,
+ y: pageY
+ };
+
+ // IE pointer model
+ if (target.msSetPointerCapture && prevent) {
+ target.msSetPointerCapture(pointerId);
+ } else if (theEvtObj.type === 'mousedown' && numberOfKeys(lastXYById) === 1) {
+ if (useSetReleaseCapture) {
+ target.setCapture(true);
+ } else {
+ document.addEventListener('mousemove', doEvent, false);
+ document.addEventListener('mouseup', doEvent, false);
+ document.addEventListener('mouseleave', doEvent, false);
+ }
}
- }
- } else if (theEvtObj.type.match(/move$/i)) {
- // clause handles mousemove, MSPointerMove, and touchmove
+ } else if (theEvtObj.type.match(/move$/i)) {
+ // clause handles mousemove, MSPointerMove, and touchmove
+
+ if (lastXYById[pointerId] && !(lastXYById[pointerId].x === pageX && lastXYById[pointerId].y === pageY)) {
+ // only extend if the pointer is down and it's not the same as the last point
+
+ if (moveEvent && prevent) {
+ prevent = moveEvent({
+ target: theEvtObj.target,
+ which: theEvtObj.which,
+ pointerId: pointerId,
+ pageX: pageX,
+ pageY: pageY
+ });
+ }
- if (lastXYById[pointerId] && !(lastXYById[pointerId].x === pageX && lastXYById[pointerId].y === pageY)) {
- // only extend if the pointer is down and it's not the same as the last point
+ // update last page positions for this pointer
+ lastXYById[pointerId].x = pageX;
+ lastXYById[pointerId].y = pageY;
+ }
+ } else if (lastXYById[pointerId] && theEvtObj.type.match(/(up|end|cancel|leave)$/i)) {
+ // clause handles up/end/cancel
- if (moveEvent && prevent) {
- prevent = moveEvent({
+ if (endEvent && prevent) {
+ prevent = endEvent({
target: theEvtObj.target,
which: theEvtObj.which,
pointerId: pointerId,
@@ -1210,1031 +1226,1014 @@
});
}
- // update last page positions for this pointer
- lastXYById[pointerId].x = pageX;
- lastXYById[pointerId].y = pageY;
- }
- } else if (lastXYById[pointerId] && theEvtObj.type.match(/(up|end|cancel|leave)$/i)) {
- // clause handles up/end/cancel
-
- if (endEvent && prevent) {
- prevent = endEvent({
- target: theEvtObj.target,
- which: theEvtObj.which,
- pointerId: pointerId,
- pageX: pageX,
- pageY: pageY
- });
- }
-
- // delete last page positions for this pointer
- delete lastXYById[pointerId];
+ // delete last page positions for this pointer
+ delete lastXYById[pointerId];
- // in the Microsoft pointer model, release the capture for this pointer
- // in the mouse model, release the capture or remove document-level event handlers if there are no down points
- // nothing is required for the iOS touch model because capture is implied on touchstart
- if (target.msReleasePointerCapture) {
- target.msReleasePointerCapture(pointerId);
- } else if (theEvtObj.type === 'mouseup' && numberOfKeys(lastXYById) === 0) {
- if (useSetReleaseCapture) {
- target.releaseCapture();
- } else {
- document.removeEventListener('mousemove', doEvent, false);
- document.removeEventListener('mouseup', doEvent, false);
- document.removeEventListener('mouseleave', doEvent, false);
+ // in the Microsoft pointer model, release the capture for this pointer
+ // in the mouse model, release the capture or remove document-level event handlers if there are no down points
+ // nothing is required for the iOS touch model because capture is implied on touchstart
+ if (target.msReleasePointerCapture) {
+ target.msReleasePointerCapture(pointerId);
+ } else if (theEvtObj.type === 'mouseup' && numberOfKeys(lastXYById) === 0) {
+ if (useSetReleaseCapture) {
+ target.releaseCapture();
+ } else {
+ document.removeEventListener('mousemove', doEvent, false);
+ document.removeEventListener('mouseup', doEvent, false);
+ document.removeEventListener('mouseleave', doEvent, false);
+ }
}
}
}
- }
- if (prevent) {
- if (theEvtObj.preventDefault) {
- theEvtObj.preventDefault();
- }
+ if (prevent) {
+ if (theEvtObj.preventDefault) {
+ theEvtObj.preventDefault();
+ }
- if (theEvtObj.preventManipulation) {
- theEvtObj.preventManipulation();
- }
+ if (theEvtObj.preventManipulation) {
+ theEvtObj.preventManipulation();
+ }
- if (theEvtObj.preventMouseEvent) {
- theEvtObj.preventMouseEvent();
+ if (theEvtObj.preventMouseEvent) {
+ theEvtObj.preventMouseEvent();
+ }
}
- }
- };
-
- // saving the settings for contentZooming and touchaction before activation
- var contentZooming, msTouchAction;
-
- this.enable = function() {
-
- if (window.navigator.msPointerEnabled) {
- // Microsoft pointer model
- target.addEventListener('MSPointerDown', doEvent, false);
- target.addEventListener('MSPointerMove', doEvent, false);
- target.addEventListener('MSPointerUp', doEvent, false);
- target.addEventListener('MSPointerCancel', doEvent, false);
+ };
- // css way to prevent panning in our target area
- if (typeof target.style.msContentZooming !== 'undefined') {
- contentZooming = target.style.msContentZooming;
- target.style.msContentZooming = 'none';
- }
+ // saving the settings for contentZooming and touchaction before activation
+ var contentZooming, msTouchAction;
- // new in Windows Consumer Preview: css way to prevent all built-in touch actions on our target
- // without this, you cannot touch draw on the element because IE will intercept the touch events
- if (typeof target.style.msTouchAction !== 'undefined') {
- msTouchAction = target.style.msTouchAction;
- target.style.msTouchAction = 'none';
- }
- } else if (target.addEventListener) {
- // iOS touch model
- target.addEventListener('touchstart', doEvent, false);
- target.addEventListener('touchmove', doEvent, false);
- target.addEventListener('touchend', doEvent, false);
- target.addEventListener('touchcancel', doEvent, false);
+ this.enable = function() {
- // mouse model
- target.addEventListener('mousedown', doEvent, false);
+ if (window.navigator.msPointerEnabled) {
+ // Microsoft pointer model
+ target.addEventListener('MSPointerDown', doEvent, false);
+ target.addEventListener('MSPointerMove', doEvent, false);
+ target.addEventListener('MSPointerUp', doEvent, false);
+ target.addEventListener('MSPointerCancel', doEvent, false);
+
+ // css way to prevent panning in our target area
+ if (typeof target.style.msContentZooming !== 'undefined') {
+ contentZooming = target.style.msContentZooming;
+ target.style.msContentZooming = 'none';
+ }
- // mouse model with capture
- // rejecting gecko because, unlike ie, firefox does not send events to target when the mouse is outside target
- if (target.setCapture && !window.navigator.userAgent.match(/\bGecko\b/)) {
+ // new in Windows Consumer Preview: css way to prevent all built-in touch actions on our target
+ // without this, you cannot touch draw on the element because IE will intercept the touch events
+ if (typeof target.style.msTouchAction !== 'undefined') {
+ msTouchAction = target.style.msTouchAction;
+ target.style.msTouchAction = 'none';
+ }
+ } else if (target.addEventListener) {
+ // iOS touch model
+ target.addEventListener('touchstart', doEvent, _utils.supportsPassive);
+ target.addEventListener('touchmove', doEvent, _utils.supportsPassive);
+ target.addEventListener('touchend', doEvent, _utils.supportsPassive);
+ target.addEventListener('touchcancel', doEvent, _utils.supportsPassive);
+
+ // mouse model
+ target.addEventListener('mousedown', doEvent, _utils.supportsPassive);
+
+ // mouse model with capture
+ // rejecting gecko because, unlike ie, firefox does not send events to target when the mouse is outside target
+ if (target.setCapture && !window.navigator.userAgent.match(/\bGecko\b/)) {
+ useSetReleaseCapture = true;
+
+ target.addEventListener('mousemove', doEvent, _utils.supportsPassive);
+ target.addEventListener('mouseup', doEvent, _utils.supportsPassive);
+ }
+ } else if (target.attachEvent && target.setCapture) {
+ // legacy IE mode - mouse with capture
useSetReleaseCapture = true;
-
- target.addEventListener('mousemove', doEvent, false);
- target.addEventListener('mouseup', doEvent, false);
+ target.attachEvent('onmousedown', function() {
+ doEvent(window.event);
+ window.event.returnValue = false;
+ return false;
+ });
+ target.attachEvent('onmousemove', function() {
+ doEvent(window.event);
+ window.event.returnValue = false;
+ return false;
+ });
+ target.attachEvent('onmouseup', function() {
+ doEvent(window.event);
+ window.event.returnValue = false;
+ return false;
+ });
}
- } else if (target.attachEvent && target.setCapture) {
- // legacy IE mode - mouse with capture
- useSetReleaseCapture = true;
- target.attachEvent('onmousedown', function() {
- doEvent(window.event);
- window.event.returnValue = false;
- return false;
- });
- target.attachEvent('onmousemove', function() {
- doEvent(window.event);
- window.event.returnValue = false;
- return false;
- });
- target.attachEvent('onmouseup', function() {
- doEvent(window.event);
- window.event.returnValue = false;
- return false;
- });
- }
- };
+ };
- this.disable = function() {
- if (window.navigator.msPointerEnabled) {
- // Microsoft pointer model
- target.removeEventListener('MSPointerDown', doEvent, false);
- target.removeEventListener('MSPointerMove', doEvent, false);
- target.removeEventListener('MSPointerUp', doEvent, false);
- target.removeEventListener('MSPointerCancel', doEvent, false);
+ this.disable = function() {
+ if (window.navigator.msPointerEnabled) {
+ // Microsoft pointer model
+ target.removeEventListener('MSPointerDown', doEvent, false);
+ target.removeEventListener('MSPointerMove', doEvent, false);
+ target.removeEventListener('MSPointerUp', doEvent, false);
+ target.removeEventListener('MSPointerCancel', doEvent, false);
+
+ // reset zooming to saved value
+ if (contentZooming) {
+ target.style.msContentZooming = contentZooming;
+ }
- // reset zooming to saved value
- if (contentZooming) {
- target.style.msContentZooming = contentZooming;
+ // reset touch action setting
+ if (msTouchAction) {
+ target.style.msTouchAction = msTouchAction;
+ }
+ } else if (target.removeEventListener) {
+ // iOS touch model
+ target.removeEventListener('touchstart', doEvent, _utils.supportsPassive);
+ target.removeEventListener('touchmove', doEvent, _utils.supportsPassive);
+ target.removeEventListener('touchend', doEvent, _utils.supportsPassive);
+ target.removeEventListener('touchcancel', doEvent, _utils.supportsPassive);
+
+ // mouse model
+ target.removeEventListener('mousedown', doEvent, _utils.supportsPassive);
+
+ // mouse model with capture
+ // rejecting gecko because, unlike ie, firefox does not send events to target when the mouse is outside target
+ if (target.setCapture && !window.navigator.userAgent.match(/\bGecko\b/)) {
+ useSetReleaseCapture = true;
+
+ target.removeEventListener('mousemove', doEvent, _utils.supportsPassive);
+ target.removeEventListener('mouseup', doEvent, _utils.supportsPassive);
+ }
+ } else if (target.detachEvent && target.setCapture) {
+ // legacy IE mode - mouse with capture
+ useSetReleaseCapture = true;
+ target.detachEvent('onmousedown');
+ target.detachEvent('onmousemove');
+ target.detachEvent('onmouseup');
}
+ };
- // reset touch action setting
- if (msTouchAction) {
- target.style.msTouchAction = msTouchAction;
- }
- } else if (target.removeEventListener) {
- // iOS touch model
- target.removeEventListener('touchstart', doEvent, false);
- target.removeEventListener('touchmove', doEvent, false);
- target.removeEventListener('touchend', doEvent, false);
- target.removeEventListener('touchcancel', doEvent, false);
+ return this;
+ };
+ }])
- // mouse model
- target.removeEventListener('mousedown', doEvent, false);
+ .factory('GridsterDraggable', ['$document', '$window', 'GridsterTouch',
+ function($document, $window, GridsterTouch) {
+ function GridsterDraggable($el, scope, gridster, item, itemOptions) {
- // mouse model with capture
- // rejecting gecko because, unlike ie, firefox does not send events to target when the mouse is outside target
- if (target.setCapture && !window.navigator.userAgent.match(/\bGecko\b/)) {
- useSetReleaseCapture = true;
+ var elmX, elmY, elmW, elmH,
- target.removeEventListener('mousemove', doEvent, false);
- target.removeEventListener('mouseup', doEvent, false);
- }
- } else if (target.detachEvent && target.setCapture) {
- // legacy IE mode - mouse with capture
- useSetReleaseCapture = true;
- target.detachEvent('onmousedown');
- target.detachEvent('onmousemove');
- target.detachEvent('onmouseup');
- }
- };
+ mouseX = 0,
+ mouseY = 0,
+ lastMouseX = 0,
+ lastMouseY = 0,
+ mOffX = 0,
+ mOffY = 0,
- return this;
- };
- }])
+ minTop = 0,
+ minLeft = 0,
+ realdocument = $document[0];
- .factory('GridsterDraggable', ['$document', '$window', 'GridsterTouch',
- function($document, $window, GridsterTouch) {
- function GridsterDraggable($el, scope, gridster, item, itemOptions) {
+ var originalCol, originalRow;
+ var inputTags = ['select', 'option', 'input', 'textarea', 'button'];
- var elmX, elmY, elmW, elmH,
+ function dragStart(event) {
+ $el.addClass('gridster-item-moving');
+ gridster.movingItem = item;
- mouseX = 0,
- mouseY = 0,
- lastMouseX = 0,
- lastMouseY = 0,
- mOffX = 0,
- mOffY = 0,
+ gridster.updateHeight(item.sizeY);
+ scope.$apply(function() {
+ if (gridster.draggable && gridster.draggable.start) {
+ gridster.draggable.start(event, $el, itemOptions, item);
+ }
+ });
+ }
- minTop = 0,
- minLeft = 0,
- realdocument = $document[0];
+ function drag(event) {
+ var oldRow = item.row,
+ oldCol = item.col,
+ hasCallback = gridster.draggable && gridster.draggable.drag,
+ scrollSensitivity = gridster.draggable.scrollSensitivity,
+ scrollSpeed = gridster.draggable.scrollSpeed;
+
+ var row = Math.min(gridster.pixelsToRows(elmY), gridster.maxRows - 1);
+ var col = Math.min(gridster.pixelsToColumns(elmX), gridster.columns - 1);
+
+ var itemsInTheWay = gridster.getItems(row, col, item.sizeX, item.sizeY, item);
+ var hasItemsInTheWay = itemsInTheWay.length !== 0;
+
+ if (gridster.swapping === true && hasItemsInTheWay) {
+ var boundingBoxItem = gridster.getBoundingBox(itemsInTheWay),
+ sameSize = boundingBoxItem.sizeX === item.sizeX && boundingBoxItem.sizeY === item.sizeY,
+ sameRow = boundingBoxItem.row === oldRow,
+ sameCol = boundingBoxItem.col === oldCol,
+ samePosition = boundingBoxItem.row === row && boundingBoxItem.col === col,
+ inline = sameRow || sameCol;
+
+ if (sameSize && itemsInTheWay.length === 1) {
+ if (samePosition) {
+ gridster.swapItems(item, itemsInTheWay[0]);
+ } else if (inline) {
+ return;
+ }
+ } else if (boundingBoxItem.sizeX <= item.sizeX && boundingBoxItem.sizeY <= item.sizeY && inline) {
+ var emptyRow = item.row <= row ? item.row : row + item.sizeY,
+ emptyCol = item.col <= col ? item.col : col + item.sizeX,
+ rowOffset = emptyRow - boundingBoxItem.row,
+ colOffset = emptyCol - boundingBoxItem.col;
+
+ for (var i = 0, l = itemsInTheWay.length; i < l; ++i) {
+ var itemInTheWay = itemsInTheWay[i];
+
+ var itemsInFreeSpace = gridster.getItems(
+ itemInTheWay.row + rowOffset,
+ itemInTheWay.col + colOffset,
+ itemInTheWay.sizeX,
+ itemInTheWay.sizeY,
+ item
+ );
+
+ if (itemsInFreeSpace.length === 0) {
+ gridster.putItem(itemInTheWay, itemInTheWay.row + rowOffset, itemInTheWay.col + colOffset);
+ }
+ }
+ }
+ }
- var originalCol, originalRow;
- var inputTags = ['select', 'option', 'input', 'textarea', 'button'];
+ if (gridster.pushing !== false || !hasItemsInTheWay) {
+ item.row = row;
+ item.col = col;
+ }
- function dragStart(event) {
- $el.addClass('gridster-item-moving');
- gridster.movingItem = item;
+ if (event.pageY - realdocument.body.scrollTop < scrollSensitivity) {
+ realdocument.body.scrollTop = realdocument.body.scrollTop - scrollSpeed;
+ } else if ($window.innerHeight - (event.pageY - realdocument.body.scrollTop) < scrollSensitivity) {
+ realdocument.body.scrollTop = realdocument.body.scrollTop + scrollSpeed;
+ }
- gridster.updateHeight(item.sizeY);
- scope.$apply(function() {
- if (gridster.draggable && gridster.draggable.start) {
- gridster.draggable.start(event, $el, itemOptions, item);
+ if (event.pageX - realdocument.body.scrollLeft < scrollSensitivity) {
+ realdocument.body.scrollLeft = realdocument.body.scrollLeft - scrollSpeed;
+ } else if ($window.innerWidth - (event.pageX - realdocument.body.scrollLeft) < scrollSensitivity) {
+ realdocument.body.scrollLeft = realdocument.body.scrollLeft + scrollSpeed;
}
- });
- }
- function drag(event) {
- var oldRow = item.row,
- oldCol = item.col,
- hasCallback = gridster.draggable && gridster.draggable.drag,
- scrollSensitivity = gridster.draggable.scrollSensitivity,
- scrollSpeed = gridster.draggable.scrollSpeed;
-
- var row = Math.min(gridster.pixelsToRows(elmY), gridster.maxRows - 1);
- var col = Math.min(gridster.pixelsToColumns(elmX), gridster.columns - 1);
-
- var itemsInTheWay = gridster.getItems(row, col, item.sizeX, item.sizeY, item);
- var hasItemsInTheWay = itemsInTheWay.length !== 0;
-
- if (gridster.swapping === true && hasItemsInTheWay) {
- var boundingBoxItem = gridster.getBoundingBox(itemsInTheWay),
- sameSize = boundingBoxItem.sizeX === item.sizeX && boundingBoxItem.sizeY === item.sizeY,
- sameRow = boundingBoxItem.row === oldRow,
- sameCol = boundingBoxItem.col === oldCol,
- samePosition = boundingBoxItem.row === row && boundingBoxItem.col === col,
- inline = sameRow || sameCol;
-
- if (sameSize && itemsInTheWay.length === 1) {
- if (samePosition) {
- gridster.swapItems(item, itemsInTheWay[0]);
- } else if (inline) {
- return;
- }
- } else if (boundingBoxItem.sizeX <= item.sizeX && boundingBoxItem.sizeY <= item.sizeY && inline) {
- var emptyRow = item.row <= row ? item.row : row + item.sizeY,
- emptyCol = item.col <= col ? item.col : col + item.sizeX,
- rowOffset = emptyRow - boundingBoxItem.row,
- colOffset = emptyCol - boundingBoxItem.col;
-
- for (var i = 0, l = itemsInTheWay.length; i < l; ++i) {
- var itemInTheWay = itemsInTheWay[i];
-
- var itemsInFreeSpace = gridster.getItems(
- itemInTheWay.row + rowOffset,
- itemInTheWay.col + colOffset,
- itemInTheWay.sizeX,
- itemInTheWay.sizeY,
- item
- );
-
- if (itemsInFreeSpace.length === 0) {
- gridster.putItem(itemInTheWay, itemInTheWay.row + rowOffset, itemInTheWay.col + colOffset);
+ if (hasCallback || oldRow !== item.row || oldCol !== item.col) {
+ scope.$apply(function() {
+ if (hasCallback) {
+ gridster.draggable.drag(event, $el, itemOptions, item);
}
- }
+ });
}
}
- if (gridster.pushing !== false || !hasItemsInTheWay) {
- item.row = row;
- item.col = col;
- }
-
- if (event.pageY - realdocument.body.scrollTop < scrollSensitivity) {
- realdocument.body.scrollTop = realdocument.body.scrollTop - scrollSpeed;
- } else if ($window.innerHeight - (event.pageY - realdocument.body.scrollTop) < scrollSensitivity) {
- realdocument.body.scrollTop = realdocument.body.scrollTop + scrollSpeed;
- }
-
- if (event.pageX - realdocument.body.scrollLeft < scrollSensitivity) {
- realdocument.body.scrollLeft = realdocument.body.scrollLeft - scrollSpeed;
- } else if ($window.innerWidth - (event.pageX - realdocument.body.scrollLeft) < scrollSensitivity) {
- realdocument.body.scrollLeft = realdocument.body.scrollLeft + scrollSpeed;
- }
+ function dragStop(event) {
+ $el.removeClass('gridster-item-moving');
+ var row = Math.min(gridster.pixelsToRows(elmY), gridster.maxRows - 1);
+ var col = Math.min(gridster.pixelsToColumns(elmX), gridster.columns - 1);
+ if (gridster.pushing !== false || gridster.getItems(row, col, item.sizeX, item.sizeY, item).length === 0) {
+ item.row = row;
+ item.col = col;
+ }
+ gridster.movingItem = null;
+ item.setPosition(item.row, item.col);
- if (hasCallback || oldRow !== item.row || oldCol !== item.col) {
scope.$apply(function() {
- if (hasCallback) {
- gridster.draggable.drag(event, $el, itemOptions, item);
+ if (gridster.draggable && gridster.draggable.stop) {
+ gridster.draggable.stop(event, $el, itemOptions, item);
}
});
}
- }
-
- function dragStop(event) {
- $el.removeClass('gridster-item-moving');
- var row = Math.min(gridster.pixelsToRows(elmY), gridster.maxRows - 1);
- var col = Math.min(gridster.pixelsToColumns(elmX), gridster.columns - 1);
- if (gridster.pushing !== false || gridster.getItems(row, col, item.sizeX, item.sizeY, item).length === 0) {
- item.row = row;
- item.col = col;
- }
- gridster.movingItem = null;
- item.setPosition(item.row, item.col);
- scope.$apply(function() {
- if (gridster.draggable && gridster.draggable.stop) {
- gridster.draggable.stop(event, $el, itemOptions, item);
+ function mouseDown(e) {
+ if (inputTags.indexOf(e.target.nodeName.toLowerCase()) !== -1) {
+ return false;
}
- });
- }
-
- function mouseDown(e) {
- if (inputTags.indexOf(e.target.nodeName.toLowerCase()) !== -1) {
- return false;
- }
- var $target = angular.element(e.target);
+ var $target = angular.element(e.target);
- // exit, if a resize handle was hit
- if ($target.hasClass('gridster-item-resizable-handler')) {
- return false;
- }
+ // exit, if a resize handle was hit
+ if ($target.hasClass('gridster-item-resizable-handler')) {
+ return false;
+ }
- // exit, if the target has it's own click event
- if ($target.attr('onclick') || $target.attr('ng-click')) {
- return false;
- }
+ // exit, if the target has it's own click event
+ if ($target.attr('onclick') || $target.attr('ng-click')) {
+ return false;
+ }
- // only works if you have jQuery
- if ($target.closest && $target.closest('.gridster-no-drag').length) {
- return false;
- }
+ // only works if you have jQuery
+ if ($target.closest && $target.closest('.gridster-no-drag').length) {
+ return false;
+ }
- // apply drag handle filter
- if (gridster.draggable && gridster.draggable.handle) {
- var $dragHandles = angular.element($el[0].querySelectorAll(gridster.draggable.handle));
- var match = false;
- outerloop:
- for (var h = 0, hl = $dragHandles.length; h < hl; ++h) {
- var handle = $dragHandles[h];
- if (handle === e.target) {
- match = true;
- break;
- }
- var target = e.target;
- for (var p = 0; p < 20; ++p) {
- var parent = target.parentNode;
- if (parent === $el[0] || !parent) {
+ // apply drag handle filter
+ if (gridster.draggable && gridster.draggable.handle) {
+ var $dragHandles = angular.element($el[0].querySelectorAll(gridster.draggable.handle));
+ var match = false;
+ outerloop:
+ for (var h = 0, hl = $dragHandles.length; h < hl; ++h) {
+ var handle = $dragHandles[h];
+ if (handle === e.target) {
+ match = true;
break;
}
- if (parent === handle) {
- match = true;
- break outerloop;
+ var target = e.target;
+ for (var p = 0; p < 20; ++p) {
+ var parent = target.parentNode;
+ if (parent === $el[0] || !parent) {
+ break;
+ }
+ if (parent === handle) {
+ match = true;
+ break outerloop;
+ }
+ target = parent;
}
- target = parent;
}
+ if (!match) {
+ return false;
}
- if (!match) {
- return false;
}
- }
-
- switch (e.which) {
- case 1:
- // left mouse button
- break;
- case 2:
- case 3:
- // right or middle mouse button
- return;
- }
- lastMouseX = e.pageX;
- lastMouseY = e.pageY;
+ switch (e.which) {
+ case 1:
+ // left mouse button
+ break;
+ case 2:
+ case 3:
+ // right or middle mouse button
+ return;
+ }
- elmX = parseInt($el.css('left'), 10);
- elmY = parseInt($el.css('top'), 10);
- elmW = $el[0].offsetWidth;
- elmH = $el[0].offsetHeight;
+ lastMouseX = e.pageX;
+ lastMouseY = e.pageY;
- originalCol = item.col;
- originalRow = item.row;
+ elmX = parseInt($el.css('left'), 10);
+ elmY = parseInt($el.css('top'), 10);
+ elmW = $el[0].offsetWidth;
+ elmH = $el[0].offsetHeight;
- dragStart(e);
+ originalCol = item.col;
+ originalRow = item.row;
- return true;
- }
+ dragStart(e);
- function mouseMove(e) {
- if (!$el.hasClass('gridster-item-moving') || $el.hasClass('gridster-item-resizing')) {
- return false;
+ return true;
}
- var maxLeft = gridster.curWidth - 1;
- var maxTop = gridster.curRowHeight * gridster.maxRows - 1;
+ function mouseMove(e) {
+ if (!$el.hasClass('gridster-item-moving') || $el.hasClass('gridster-item-resizing')) {
+ return false;
+ }
- // Get the current mouse position.
- mouseX = e.pageX;
- mouseY = e.pageY;
+ var maxLeft = gridster.curWidth - 1;
+ var maxTop = gridster.curRowHeight * gridster.maxRows - 1;
- // Get the deltas
- var diffX = mouseX - lastMouseX + mOffX;
- var diffY = mouseY - lastMouseY + mOffY;
- mOffX = mOffY = 0;
+ // Get the current mouse position.
+ mouseX = e.pageX;
+ mouseY = e.pageY;
- // Update last processed mouse positions.
- lastMouseX = mouseX;
- lastMouseY = mouseY;
+ // Get the deltas
+ var diffX = mouseX - lastMouseX + mOffX;
+ var diffY = mouseY - lastMouseY + mOffY;
+ mOffX = mOffY = 0;
- var dX = diffX,
- dY = diffY;
- if (elmX + dX < minLeft) {
- diffX = minLeft - elmX;
- mOffX = dX - diffX;
- } else if (elmX + elmW + dX > maxLeft) {
- diffX = maxLeft - elmX - elmW;
- mOffX = dX - diffX;
- }
+ // Update last processed mouse positions.
+ lastMouseX = mouseX;
+ lastMouseY = mouseY;
- if (elmY + dY < minTop) {
- diffY = minTop - elmY;
- mOffY = dY - diffY;
- } else if (elmY + elmH + dY > maxTop) {
- diffY = maxTop - elmY - elmH;
- mOffY = dY - diffY;
- }
- elmX += diffX;
- elmY += diffY;
+ var dX = diffX,
+ dY = diffY;
+ if (elmX + dX < minLeft) {
+ diffX = minLeft - elmX;
+ mOffX = dX - diffX;
+ } else if (elmX + elmW + dX > maxLeft) {
+ diffX = maxLeft - elmX - elmW;
+ mOffX = dX - diffX;
+ }
- // set new position
- $el.css({
- 'top': elmY + 'px',
- 'left': elmX + 'px'
- });
+ if (elmY + dY < minTop) {
+ diffY = minTop - elmY;
+ mOffY = dY - diffY;
+ } else if (elmY + elmH + dY > maxTop) {
+ diffY = maxTop - elmY - elmH;
+ mOffY = dY - diffY;
+ }
+ elmX += diffX;
+ elmY += diffY;
- drag(e);
+ // set new position
+ $el.css({
+ 'top': elmY + 'px',
+ 'left': elmX + 'px'
+ });
- return true;
- }
+ drag(e);
- function mouseUp(e) {
- if (!$el.hasClass('gridster-item-moving') || $el.hasClass('gridster-item-resizing')) {
- return false;
+ return true;
}
- mOffX = mOffY = 0;
-
- dragStop(e);
+ function mouseUp(e) {
+ if (!$el.hasClass('gridster-item-moving') || $el.hasClass('gridster-item-resizing')) {
+ return false;
+ }
- return true;
- }
+ mOffX = mOffY = 0;
- var enabled = null;
- var gridsterTouch = null;
+ dragStop(e);
- this.enable = function() {
- if (enabled === true) {
- return;
+ return true;
}
- enabled = true;
- if (gridsterTouch) {
+ var enabled = null;
+ var gridsterTouch = null;
+
+ this.enable = function() {
+ if (enabled === true) {
+ return;
+ }
+ enabled = true;
+
+ if (gridsterTouch) {
+ gridsterTouch.enable();
+ return;
+ }
+
+ gridsterTouch = new GridsterTouch($el[0], mouseDown, mouseMove, mouseUp);
gridsterTouch.enable();
- return;
- }
+ };
- gridsterTouch = new GridsterTouch($el[0], mouseDown, mouseMove, mouseUp);
- gridsterTouch.enable();
- };
+ this.disable = function() {
+ if (enabled === false) {
+ return;
+ }
- this.disable = function() {
- if (enabled === false) {
- return;
- }
+ enabled = false;
+ if (gridsterTouch) {
+ gridsterTouch.disable();
+ }
+ };
- enabled = false;
- if (gridsterTouch) {
- gridsterTouch.disable();
- }
- };
+ this.toggle = function(enabled) {
+ if (enabled) {
+ this.enable();
+ } else {
+ this.disable();
+ }
+ };
- this.toggle = function(enabled) {
- if (enabled) {
- this.enable();
- } else {
+ this.destroy = function() {
this.disable();
- }
- };
+ };
+ }
- this.destroy = function() {
- this.disable();
- };
+ return GridsterDraggable;
}
+ ])
- return GridsterDraggable;
- }
- ])
+ .factory('GridsterResizable', ['GridsterTouch', function(GridsterTouch) {
+ function GridsterResizable($el, scope, gridster, item, itemOptions) {
- .factory('GridsterResizable', ['GridsterTouch', function(GridsterTouch) {
- function GridsterResizable($el, scope, gridster, item, itemOptions) {
+ function ResizeHandle(handleClass) {
- function ResizeHandle(handleClass) {
+ var hClass = handleClass;
- var hClass = handleClass;
+ var elmX, elmY, elmW, elmH,
- var elmX, elmY, elmW, elmH,
+ mouseX = 0,
+ mouseY = 0,
+ lastMouseX = 0,
+ lastMouseY = 0,
+ mOffX = 0,
+ mOffY = 0,
- mouseX = 0,
- mouseY = 0,
- lastMouseX = 0,
- lastMouseY = 0,
- mOffX = 0,
- mOffY = 0,
+ minTop = 0,
+ maxTop = 9999,
+ minLeft = 0;
- minTop = 0,
- maxTop = 9999,
- minLeft = 0;
+ var getMinHeight = function() {
+ return (item.minSizeY ? item.minSizeY : 1) * gridster.curRowHeight - gridster.margins[0];
+ };
+ var getMinWidth = function() {
+ return (item.minSizeX ? item.minSizeX : 1) * gridster.curColWidth - gridster.margins[1];
+ };
- var getMinHeight = function() {
- return (item.minSizeY ? item.minSizeY : 1) * gridster.curRowHeight - gridster.margins[0];
- };
- var getMinWidth = function() {
- return (item.minSizeX ? item.minSizeX : 1) * gridster.curColWidth - gridster.margins[1];
- };
+ var originalWidth, originalHeight;
+ var savedDraggable;
- var originalWidth, originalHeight;
- var savedDraggable;
+ function resizeStart(e) {
+ $el.addClass('gridster-item-moving');
+ $el.addClass('gridster-item-resizing');
- function resizeStart(e) {
- $el.addClass('gridster-item-moving');
- $el.addClass('gridster-item-resizing');
+ gridster.movingItem = item;
- gridster.movingItem = item;
+ item.setElementSizeX();
+ item.setElementSizeY();
+ item.setElementPosition();
+ gridster.updateHeight(1);
+
+ scope.$apply(function() {
+ // callback
+ if (gridster.resizable && gridster.resizable.start) {
+ gridster.resizable.start(e, $el, itemOptions, item); // options is the item model
+ }
+ });
+ }
- item.setElementSizeX();
- item.setElementSizeY();
- item.setElementPosition();
- gridster.updateHeight(1);
+ function resize(e) {
+ var oldRow = item.row,
+ oldCol = item.col,
+ oldSizeX = item.sizeX,
+ oldSizeY = item.sizeY,
+ hasCallback = gridster.resizable && gridster.resizable.resize;
- scope.$apply(function() {
- // callback
- if (gridster.resizable && gridster.resizable.start) {
- gridster.resizable.start(e, $el, itemOptions, item); // options is the item model
+ var col = item.col;
+ // only change column if grabbing left edge
+ if (['w', 'nw', 'sw'].indexOf(handleClass) !== -1) {
+ col = gridster.pixelsToColumns(elmX, false);
}
- });
- }
- function resize(e) {
- var oldRow = item.row,
- oldCol = item.col,
- oldSizeX = item.sizeX,
- oldSizeY = item.sizeY,
- hasCallback = gridster.resizable && gridster.resizable.resize;
+ var row = item.row;
+ // only change row if grabbing top edge
+ if (['n', 'ne', 'nw'].indexOf(handleClass) !== -1) {
+ row = gridster.pixelsToRows(elmY, false);
+ }
- var col = item.col;
- // only change column if grabbing left edge
- if (['w', 'nw', 'sw'].indexOf(handleClass) !== -1) {
- col = gridster.pixelsToColumns(elmX, false);
- }
+ var sizeX = item.sizeX;
+ // only change row if grabbing left or right edge
+ if (['n', 's'].indexOf(handleClass) === -1) {
+ sizeX = gridster.pixelsToColumns(elmW, true);
+ }
- var row = item.row;
- // only change row if grabbing top edge
- if (['n', 'ne', 'nw'].indexOf(handleClass) !== -1) {
- row = gridster.pixelsToRows(elmY, false);
- }
+ var sizeY = item.sizeY;
+ // only change row if grabbing top or bottom edge
+ if (['e', 'w'].indexOf(handleClass) === -1) {
+ sizeY = gridster.pixelsToRows(elmH, true);
+ }
- var sizeX = item.sizeX;
- // only change row if grabbing left or right edge
- if (['n', 's'].indexOf(handleClass) === -1) {
- sizeX = gridster.pixelsToColumns(elmW, true);
- }
- var sizeY = item.sizeY;
- // only change row if grabbing top or bottom edge
- if (['e', 'w'].indexOf(handleClass) === -1) {
- sizeY = gridster.pixelsToRows(elmH, true);
+ var canOccupy = row > -1 && col > -1 && sizeX + col <= gridster.columns && sizeY + row <= gridster.maxRows;
+ if (canOccupy && (gridster.pushing !== false || gridster.getItems(row, col, sizeX, sizeY, item).length === 0)) {
+ item.row = row;
+ item.col = col;
+ item.sizeX = sizeX;
+ item.sizeY = sizeY;
+ }
+ var isChanged = item.row !== oldRow || item.col !== oldCol || item.sizeX !== oldSizeX || item.sizeY !== oldSizeY;
+
+ if (hasCallback || isChanged) {
+ scope.$apply(function() {
+ if (hasCallback) {
+ gridster.resizable.resize(e, $el, itemOptions, item); // options is the item model
+ }
+ });
+ }
}
+ function resizeStop(e) {
+ $el.removeClass('gridster-item-moving');
+ $el.removeClass('gridster-item-resizing');
- var canOccupy = row > -1 && col > -1 && sizeX + col <= gridster.columns && sizeY + row <= gridster.maxRows;
- if (canOccupy && (gridster.pushing !== false || gridster.getItems(row, col, sizeX, sizeY, item).length === 0)) {
- item.row = row;
- item.col = col;
- item.sizeX = sizeX;
- item.sizeY = sizeY;
- }
- var isChanged = item.row !== oldRow || item.col !== oldCol || item.sizeX !== oldSizeX || item.sizeY !== oldSizeY;
+ gridster.movingItem = null;
+
+ item.setPosition(item.row, item.col);
+ item.setSizeY(item.sizeY);
+ item.setSizeX(item.sizeX);
- if (hasCallback || isChanged) {
scope.$apply(function() {
- if (hasCallback) {
- gridster.resizable.resize(e, $el, itemOptions, item); // options is the item model
+ if (gridster.resizable && gridster.resizable.stop) {
+ gridster.resizable.stop(e, $el, itemOptions, item); // options is the item model
}
});
}
- }
-
- function resizeStop(e) {
- $el.removeClass('gridster-item-moving');
- $el.removeClass('gridster-item-resizing');
-
- gridster.movingItem = null;
- item.setPosition(item.row, item.col);
- item.setSizeY(item.sizeY);
- item.setSizeX(item.sizeX);
-
- scope.$apply(function() {
- if (gridster.resizable && gridster.resizable.stop) {
- gridster.resizable.stop(e, $el, itemOptions, item); // options is the item model
+ function mouseDown(e) {
+ switch (e.which) {
+ case 1:
+ // left mouse button
+ break;
+ case 2:
+ case 3:
+ // right or middle mouse button
+ return;
}
- });
- }
- function mouseDown(e) {
- switch (e.which) {
- case 1:
- // left mouse button
- break;
- case 2:
- case 3:
- // right or middle mouse button
- return;
- }
-
- // save the draggable setting to restore after resize
- savedDraggable = gridster.draggable.enabled;
- if (savedDraggable) {
- gridster.draggable.enabled = false;
- scope.$broadcast('gridster-draggable-changed', gridster);
- }
+ // save the draggable setting to restore after resize
+ savedDraggable = gridster.draggable.enabled;
+ if (savedDraggable) {
+ gridster.draggable.enabled = false;
+ scope.$broadcast('gridster-draggable-changed', gridster);
+ }
- // Get the current mouse position.
- lastMouseX = e.pageX;
- lastMouseY = e.pageY;
+ // Get the current mouse position.
+ lastMouseX = e.pageX;
+ lastMouseY = e.pageY;
- // Record current widget dimensions
- elmX = parseInt($el.css('left'), 10);
- elmY = parseInt($el.css('top'), 10);
- elmW = $el[0].offsetWidth;
- elmH = $el[0].offsetHeight;
+ // Record current widget dimensions
+ elmX = parseInt($el.css('left'), 10);
+ elmY = parseInt($el.css('top'), 10);
+ elmW = $el[0].offsetWidth;
+ elmH = $el[0].offsetHeight;
- originalWidth = item.sizeX;
- originalHeight = item.sizeY;
+ originalWidth = item.sizeX;
+ originalHeight = item.sizeY;
- resizeStart(e);
+ resizeStart(e);
- return true;
- }
+ return true;
+ }
- function mouseMove(e) {
- var maxLeft = gridster.curWidth - 1;
+ function mouseMove(e) {
+ var maxLeft = gridster.curWidth - 1;
- // Get the current mouse position.
- mouseX = e.pageX;
- mouseY = e.pageY;
+ // Get the current mouse position.
+ mouseX = e.pageX;
+ mouseY = e.pageY;
- // Get the deltas
- var diffX = mouseX - lastMouseX + mOffX;
- var diffY = mouseY - lastMouseY + mOffY;
- mOffX = mOffY = 0;
+ // Get the deltas
+ var diffX = mouseX - lastMouseX + mOffX;
+ var diffY = mouseY - lastMouseY + mOffY;
+ mOffX = mOffY = 0;
- // Update last processed mouse positions.
- lastMouseX = mouseX;
- lastMouseY = mouseY;
+ // Update last processed mouse positions.
+ lastMouseX = mouseX;
+ lastMouseY = mouseY;
- var dY = diffY,
- dX = diffX;
+ var dY = diffY,
+ dX = diffX;
- if (hClass.indexOf('n') >= 0) {
- if (elmH - dY < getMinHeight()) {
- diffY = elmH - getMinHeight();
- mOffY = dY - diffY;
- } else if (elmY + dY < minTop) {
- diffY = minTop - elmY;
- mOffY = dY - diffY;
+ if (hClass.indexOf('n') >= 0) {
+ if (elmH - dY < getMinHeight()) {
+ diffY = elmH - getMinHeight();
+ mOffY = dY - diffY;
+ } else if (elmY + dY < minTop) {
+ diffY = minTop - elmY;
+ mOffY = dY - diffY;
+ }
+ elmY += diffY;
+ elmH -= diffY;
}
- elmY += diffY;
- elmH -= diffY;
- }
- if (hClass.indexOf('s') >= 0) {
- if (elmH + dY < getMinHeight()) {
- diffY = getMinHeight() - elmH;
- mOffY = dY - diffY;
- } else if (elmY + elmH + dY > maxTop) {
- diffY = maxTop - elmY - elmH;
- mOffY = dY - diffY;
+ if (hClass.indexOf('s') >= 0) {
+ if (elmH + dY < getMinHeight()) {
+ diffY = getMinHeight() - elmH;
+ mOffY = dY - diffY;
+ } else if (elmY + elmH + dY > maxTop) {
+ diffY = maxTop - elmY - elmH;
+ mOffY = dY - diffY;
+ }
+ elmH += diffY;
}
- elmH += diffY;
- }
- if (hClass.indexOf('w') >= 0) {
- if (elmW - dX < getMinWidth()) {
- diffX = elmW - getMinWidth();
- mOffX = dX - diffX;
- } else if (elmX + dX < minLeft) {
- diffX = minLeft - elmX;
- mOffX = dX - diffX;
+ if (hClass.indexOf('w') >= 0) {
+ if (elmW - dX < getMinWidth()) {
+ diffX = elmW - getMinWidth();
+ mOffX = dX - diffX;
+ } else if (elmX + dX < minLeft) {
+ diffX = minLeft - elmX;
+ mOffX = dX - diffX;
+ }
+ elmX += diffX;
+ elmW -= diffX;
}
- elmX += diffX;
- elmW -= diffX;
- }
- if (hClass.indexOf('e') >= 0) {
- if (elmW + dX < getMinWidth()) {
- diffX = getMinWidth() - elmW;
- mOffX = dX - diffX;
- } else if (elmX + elmW + dX > maxLeft) {
- diffX = maxLeft - elmX - elmW;
- mOffX = dX - diffX;
+ if (hClass.indexOf('e') >= 0) {
+ if (elmW + dX < getMinWidth()) {
+ diffX = getMinWidth() - elmW;
+ mOffX = dX - diffX;
+ } else if (elmX + elmW + dX > maxLeft) {
+ diffX = maxLeft - elmX - elmW;
+ mOffX = dX - diffX;
+ }
+ elmW += diffX;
}
- elmW += diffX;
+
+ // set new position
+ $el.css({
+ 'top': elmY + 'px',
+ 'left': elmX + 'px',
+ 'width': elmW + 'px',
+ 'height': elmH + 'px'
+ });
+
+ resize(e);
+
+ return true;
}
- // set new position
- $el.css({
- 'top': elmY + 'px',
- 'left': elmX + 'px',
- 'width': elmW + 'px',
- 'height': elmH + 'px'
- });
+ function mouseUp(e) {
+ // restore draggable setting to its original state
+ if (gridster.draggable.enabled !== savedDraggable) {
+ gridster.draggable.enabled = savedDraggable;
+ scope.$broadcast('gridster-draggable-changed', gridster);
+ }
- resize(e);
+ mOffX = mOffY = 0;
- return true;
- }
+ resizeStop(e);
- function mouseUp(e) {
- // restore draggable setting to its original state
- if (gridster.draggable.enabled !== savedDraggable) {
- gridster.draggable.enabled = savedDraggable;
- scope.$broadcast('gridster-draggable-changed', gridster);
+ return true;
}
- mOffX = mOffY = 0;
+ var $dragHandle = null;
+ var unifiedInput;
+
+ this.enable = function() {
+ if (!$dragHandle) {
+ $dragHandle = angular.element('');
+ $el.append($dragHandle);
+ }
+
+ unifiedInput = new GridsterTouch($dragHandle[0], mouseDown, mouseMove, mouseUp);
+ unifiedInput.enable();
+ };
+
+ this.disable = function() {
+ if ($dragHandle) {
+ $dragHandle.remove();
+ $dragHandle = null;
+ }
+
+ unifiedInput.disable();
+ unifiedInput = undefined;
+ };
- resizeStop(e);
+ this.destroy = function() {
+ this.disable();
+ };
+ }
- return true;
+ var handles = [];
+ var handlesOpts = gridster.resizable.handles;
+ if (typeof handlesOpts === 'string') {
+ handlesOpts = gridster.resizable.handles.split(',');
}
+ var enabled = false;
- var $dragHandle = null;
- var unifiedInput;
+ for (var c = 0, l = handlesOpts.length; c < l; c++) {
+ handles.push(new ResizeHandle(handlesOpts[c]));
+ }
this.enable = function() {
- if (!$dragHandle) {
- $dragHandle = angular.element('');
- $el.append($dragHandle);
+ if (enabled) {
+ return;
}
-
- unifiedInput = new GridsterTouch($dragHandle[0], mouseDown, mouseMove, mouseUp);
- unifiedInput.enable();
+ for (var c = 0, l = handles.length; c < l; c++) {
+ handles[c].enable();
+ }
+ enabled = true;
};
this.disable = function() {
- if ($dragHandle) {
- $dragHandle.remove();
- $dragHandle = null;
+ if (!enabled) {
+ return;
}
+ for (var c = 0, l = handles.length; c < l; c++) {
+ handles[c].disable();
+ }
+ enabled = false;
+ };
- unifiedInput.disable();
- unifiedInput = undefined;
+ this.toggle = function(enabled) {
+ if (enabled) {
+ this.enable();
+ } else {
+ this.disable();
+ }
};
this.destroy = function() {
- this.disable();
+ for (var c = 0, l = handles.length; c < l; c++) {
+ handles[c].destroy();
+ }
};
}
-
- var handles = [];
- var handlesOpts = gridster.resizable.handles;
- if (typeof handlesOpts === 'string') {
- handlesOpts = gridster.resizable.handles.split(',');
- }
- var enabled = false;
-
- for (var c = 0, l = handlesOpts.length; c < l; c++) {
- handles.push(new ResizeHandle(handlesOpts[c]));
- }
-
- this.enable = function() {
- if (enabled) {
- return;
- }
- for (var c = 0, l = handles.length; c < l; c++) {
- handles[c].enable();
- }
- enabled = true;
- };
-
- this.disable = function() {
- if (!enabled) {
- return;
- }
- for (var c = 0, l = handles.length; c < l; c++) {
- handles[c].disable();
- }
- enabled = false;
- };
-
- this.toggle = function(enabled) {
- if (enabled) {
- this.enable();
- } else {
- this.disable();
- }
- };
-
- this.destroy = function() {
- for (var c = 0, l = handles.length; c < l; c++) {
- handles[c].destroy();
- }
- };
- }
- return GridsterResizable;
- }])
-
- .factory('gridsterDebounce', function() {
- return function gridsterDebounce(func, wait, immediate) {
- var timeout;
- return function() {
- var context = this,
- args = arguments;
- var later = function() {
- timeout = null;
- if (!immediate) {
+ return GridsterResizable;
+ }])
+
+ .factory('gridsterDebounce', function() {
+ return function gridsterDebounce(func, wait, immediate) {
+ var timeout;
+ return function() {
+ var context = this,
+ args = arguments;
+ var later = function() {
+ timeout = null;
+ if (!immediate) {
+ func.apply(context, args);
+ }
+ };
+ var callNow = immediate && !timeout;
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ if (callNow) {
func.apply(context, args);
}
};
- var callNow = immediate && !timeout;
- clearTimeout(timeout);
- timeout = setTimeout(later, wait);
- if (callNow) {
- func.apply(context, args);
- }
};
- };
- })
-
- /**
- * GridsterItem directive
- * @param $parse
- * @param GridsterDraggable
- * @param GridsterResizable
- * @param gridsterDebounce
- */
- .directive('gridsterItem', ['$parse', 'GridsterDraggable', 'GridsterResizable', 'gridsterDebounce',
- function($parse, GridsterDraggable, GridsterResizable, gridsterDebounce) {
- return {
- scope: true,
- restrict: 'EA',
- controller: 'GridsterItemCtrl',
- controllerAs: 'gridsterItem',
- require: ['^gridster', 'gridsterItem'],
- link: function(scope, $el, attrs, controllers) {
- var optionsKey = attrs.gridsterItem,
- options;
-
- var gridster = controllers[0],
- item = controllers[1];
-
- scope.gridster = gridster;
-
- // bind the item's position properties
- // options can be an object specified by gridster-item="object"
- // or the options can be the element html attributes object
- if (optionsKey) {
- var $optionsGetter = $parse(optionsKey);
- options = $optionsGetter(scope) || {};
- if (!options && $optionsGetter.assign) {
- options = {
- row: item.row,
- col: item.col,
- sizeX: item.sizeX,
- sizeY: item.sizeY,
- minSizeX: 0,
- minSizeY: 0,
- maxSizeX: null,
- maxSizeY: null
- };
- $optionsGetter.assign(scope, options);
- }
- } else {
- options = attrs;
- }
-
- item.init($el, gridster);
+ })
- $el.addClass('gridster-item');
+ /**
+ * GridsterItem directive
+ * @param $parse
+ * @param GridsterDraggable
+ * @param GridsterResizable
+ * @param gridsterDebounce
+ */
+ .directive('gridsterItem', ['$parse', 'GridsterDraggable', 'GridsterResizable', 'gridsterDebounce',
+ function($parse, GridsterDraggable, GridsterResizable, gridsterDebounce) {
+ return {
+ scope: true,
+ restrict: 'EA',
+ controller: 'GridsterItemCtrl',
+ controllerAs: 'gridsterItem',
+ require: ['^gridster', 'gridsterItem'],
+ link: function(scope, $el, attrs, controllers) {
+ var optionsKey = attrs.gridsterItem,
+ options;
+
+ var gridster = controllers[0],
+ item = controllers[1];
- var aspects = ['minSizeX', 'maxSizeX', 'minSizeY', 'maxSizeY', 'sizeX', 'sizeY', 'row', 'col'],
- $getters = {};
+ scope.gridster = gridster;
- var expressions = [];
- var aspectFn = function(aspect) {
- var expression;
- if (typeof options[aspect] === 'string') {
- // watch the expression in the scope
- expression = options[aspect];
- } else if (typeof options[aspect.toLowerCase()] === 'string') {
- // watch the expression in the scope
- expression = options[aspect.toLowerCase()];
- } else if (optionsKey) {
- // watch the expression on the options object in the scope
- expression = optionsKey + '.' + aspect;
+ // bind the item's position properties
+ // options can be an object specified by gridster-item="object"
+ // or the options can be the element html attributes object
+ if (optionsKey) {
+ var $optionsGetter = $parse(optionsKey);
+ options = $optionsGetter(scope) || {};
+ if (!options && $optionsGetter.assign) {
+ options = {
+ row: item.row,
+ col: item.col,
+ sizeX: item.sizeX,
+ sizeY: item.sizeY,
+ minSizeX: 0,
+ minSizeY: 0,
+ maxSizeX: null,
+ maxSizeY: null
+ };
+ $optionsGetter.assign(scope, options);
+ }
} else {
- return;
- }
- expressions.push('"' + aspect + '":' + expression);
- $getters[aspect] = $parse(expression);
-
- // initial set
- var val = $getters[aspect](scope);
- if (typeof val === 'number') {
- item[aspect] = val;
+ options = attrs;
}
- };
- for (var i = 0, l = aspects.length; i < l; ++i) {
- aspectFn(aspects[i]);
- }
-
- var watchExpressions = '{' + expressions.join(',') + '}';
- // when the value changes externally, update the internal item object
- scope.$watchCollection(watchExpressions, function(newVals, oldVals) {
- for (var aspect in newVals) {
- var newVal = newVals[aspect];
- var oldVal = oldVals[aspect];
- if (oldVal === newVal) {
- continue;
- }
- newVal = parseInt(newVal, 10);
- if (!isNaN(newVal)) {
- item[aspect] = newVal;
+ item.init($el, gridster);
+
+ $el.addClass('gridster-item');
+
+ var aspects = ['minSizeX', 'maxSizeX', 'minSizeY', 'maxSizeY', 'sizeX', 'sizeY', 'row', 'col'],
+ $getters = {};
+
+ var expressions = [];
+ var aspectFn = function(aspect) {
+ var expression;
+ if (typeof options[aspect] === 'string') {
+ // watch the expression in the scope
+ expression = options[aspect];
+ } else if (typeof options[aspect.toLowerCase()] === 'string') {
+ // watch the expression in the scope
+ expression = options[aspect.toLowerCase()];
+ } else if (optionsKey) {
+ // watch the expression on the options object in the scope
+ expression = optionsKey + '.' + aspect;
+ } else {
+ return;
}
- }
- });
+ expressions.push('"' + aspect + '":' + expression);
+ $getters[aspect] = $parse(expression);
- function positionChanged() {
- // call setPosition so the element and gridster controller are updated
- item.setPosition(item.row, item.col);
+ // initial set
+ var val = $getters[aspect](scope);
+ if (typeof val === 'number') {
+ item[aspect] = val;
+ }
+ };
- // when internal item position changes, update externally bound values
- if ($getters.row && $getters.row.assign) {
- $getters.row.assign(scope, item.row);
+ for (var i = 0, l = aspects.length; i < l; ++i) {
+ aspectFn(aspects[i]);
}
- if ($getters.col && $getters.col.assign) {
- $getters.col.assign(scope, item.col);
- }
- }
- scope.$watch(function() {
- return item.row + ',' + item.col;
- }, positionChanged);
- function sizeChanged() {
- var changedX = item.setSizeX(item.sizeX, true);
- if (changedX && $getters.sizeX && $getters.sizeX.assign) {
- $getters.sizeX.assign(scope, item.sizeX);
- }
- var changedY = item.setSizeY(item.sizeY, true);
- if (changedY && $getters.sizeY && $getters.sizeY.assign) {
- $getters.sizeY.assign(scope, item.sizeY);
- }
+ var watchExpressions = '{' + expressions.join(',') + '}';
+ // when the value changes externally, update the internal item object
+ scope.$watchCollection(watchExpressions, function(newVals, oldVals) {
+ for (var aspect in newVals) {
+ var newVal = newVals[aspect];
+ var oldVal = oldVals[aspect];
+ if (oldVal === newVal) {
+ continue;
+ }
+ newVal = parseInt(newVal, 10);
+ if (!isNaN(newVal)) {
+ item[aspect] = newVal;
+ }
+ }
+ });
+
+ function positionChanged() {
+ // call setPosition so the element and gridster controller are updated
+ item.setPosition(item.row, item.col);
- if (changedX || changedY) {
- item.gridster.moveOverlappingItems(item);
- gridster.layoutChanged();
- scope.$broadcast('gridster-item-resized', item);
+ // when internal item position changes, update externally bound values
+ if ($getters.row && $getters.row.assign) {
+ $getters.row.assign(scope, item.row);
+ }
+ if ($getters.col && $getters.col.assign) {
+ $getters.col.assign(scope, item.col);
+ }
}
- }
+ scope.$watch(function() {
+ return item.row + ',' + item.col;
+ }, positionChanged);
- scope.$watch(function() {
- return item.sizeY + ',' + item.sizeX + ',' + item.minSizeX + ',' + item.maxSizeX + ',' + item.minSizeY + ',' + item.maxSizeY;
- }, sizeChanged);
+ function sizeChanged() {
+ var changedX = item.setSizeX(item.sizeX, true);
+ if (changedX && $getters.sizeX && $getters.sizeX.assign) {
+ $getters.sizeX.assign(scope, item.sizeX);
+ }
+ var changedY = item.setSizeY(item.sizeY, true);
+ if (changedY && $getters.sizeY && $getters.sizeY.assign) {
+ $getters.sizeY.assign(scope, item.sizeY);
+ }
- var draggable = new GridsterDraggable($el, scope, gridster, item, options);
- var resizable = new GridsterResizable($el, scope, gridster, item, options);
+ if (changedX || changedY) {
+ item.gridster.moveOverlappingItems(item);
+ gridster.layoutChanged();
+ scope.$broadcast('gridster-item-resized', item);
+ }
+ }
- var updateResizable = function() {
- resizable.toggle(!gridster.isMobile && gridster.resizable && gridster.resizable.enabled);
- };
- updateResizable();
+ scope.$watch(function() {
+ return item.sizeY + ',' + item.sizeX + ',' + item.minSizeX + ',' + item.maxSizeX + ',' + item.minSizeY + ',' + item.maxSizeY;
+ }, sizeChanged);
- var updateDraggable = function() {
- draggable.toggle(!gridster.isMobile && gridster.draggable && gridster.draggable.enabled);
- };
- updateDraggable();
+ var draggable = new GridsterDraggable($el, scope, gridster, item, options);
+ var resizable = new GridsterResizable($el, scope, gridster, item, options);
- scope.$on('gridster-draggable-changed', updateDraggable);
- scope.$on('gridster-resizable-changed', updateResizable);
- scope.$on('gridster-resized', updateResizable);
- scope.$on('gridster-mobile-changed', function() {
+ var updateResizable = function() {
+ resizable.toggle(!gridster.isMobile && gridster.resizable && gridster.resizable.enabled);
+ };
updateResizable();
- updateDraggable();
- });
- function whichTransitionEvent() {
- var el = document.createElement('div');
- var transitions = {
- 'transition': 'transitionend',
- 'OTransition': 'oTransitionEnd',
- 'MozTransition': 'transitionend',
- 'WebkitTransition': 'webkitTransitionEnd'
+ var updateDraggable = function() {
+ draggable.toggle(!gridster.isMobile && gridster.draggable && gridster.draggable.enabled);
};
- for (var t in transitions) {
- if (el.style[t] !== undefined) {
- return transitions[t];
+ updateDraggable();
+
+ scope.$on('gridster-draggable-changed', updateDraggable);
+ scope.$on('gridster-resizable-changed', updateResizable);
+ scope.$on('gridster-resized', updateResizable);
+ scope.$on('gridster-mobile-changed', function() {
+ updateResizable();
+ updateDraggable();
+ });
+
+ function whichTransitionEvent() {
+ var el = document.createElement('div');
+ var transitions = {
+ 'transition': 'transitionend',
+ 'OTransition': 'oTransitionEnd',
+ 'MozTransition': 'transitionend',
+ 'WebkitTransition': 'webkitTransitionEnd'
+ };
+ for (var t in transitions) {
+ if (el.style[t] !== undefined) {
+ return transitions[t];
+ }
}
}
- }
- var debouncedTransitionEndPublisher = gridsterDebounce(function() {
- scope.$apply(function() {
- scope.$broadcast('gridster-item-transition-end', item);
- });
- }, 50);
+ var debouncedTransitionEndPublisher = gridsterDebounce(function() {
+ scope.$apply(function() {
+ scope.$broadcast('gridster-item-transition-end', item);
+ });
+ }, 50);
- $el.on(whichTransitionEvent(), debouncedTransitionEndPublisher);
+ $el.on(whichTransitionEvent(), debouncedTransitionEndPublisher);
- scope.$broadcast('gridster-item-initialized', item);
+ scope.$broadcast('gridster-item-initialized', item);
- return scope.$on('$destroy', function() {
- try {
- resizable.destroy();
- draggable.destroy();
- } catch (e) {}
+ return scope.$on('$destroy', function() {
+ try {
+ resizable.destroy();
+ draggable.destroy();
+ } catch (e) {}
- try {
- gridster.removeItem(item);
- } catch (e) {}
+ try {
+ gridster.removeItem(item);
+ } catch (e) {}
- try {
- item.destroy();
- } catch (e) {}
- });
+ try {
+ item.destroy();
+ } catch (e) {}
+ });
+ }
+ };
+ }
+ ])
+
+ .directive('gridsterNoDrag', function() {
+ return {
+ restrict: 'A',
+ link: function(scope, $element) {
+ $element.addClass('gridster-no-drag');
}
};
- }
- ])
-
- .directive('gridsterNoDrag', function() {
- return {
- restrict: 'A',
- link: function(scope, $element) {
- $element.addClass('gridster-no-drag');
- }
- };
- })
+ })
;