diff options
author | 2017-09-01 11:57:47 +0800 | |
---|---|---|
committer | 2017-09-01 11:57:58 +0800 | |
commit | 57dbba269d19bc59fad89160200bb2dbcccb9003 (patch) | |
tree | d466041ceffa2161124ca79a48b3e077777c74b8 /usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster | |
parent | 4ff32341a0af1972b44a7410e76e9b231131e7ab (diff) |
Upload Monitor function code
Change-Id: I33ad76221b4cb771a298ff240245fc24be664efb
Issue-Id: USECASEUI-6
Signed-off-by: shentao <shentao@chinamobile.com>
Diffstat (limited to 'usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster')
5 files changed, 2560 insertions, 0 deletions
diff --git a/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/dist/angular-gridster.css b/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/dist/angular-gridster.css new file mode 100644 index 00000000..f89af3e8 --- /dev/null +++ b/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/dist/angular-gridster.css @@ -0,0 +1,145 @@ +/** + * gridster.js - v0.2.1 - 2013-10-28 * http://gridster.net + * Copyright (c) 2013 ducksboard; Licensed MIT + */ +.gridster { + position: relative; + margin: auto; + height: 0; +} +.gridster > ul { + margin: 0; + list-style: none; + padding: 0; +} +.gridster-item { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + list-style: none; + z-index: 2; + position: absolute; + display: none; +} +.gridster-loaded { + -webkit-transition: height .3s; + -moz-transition: height .3s; + -o-transition: height .3s; + transition: height .3s; +} +.gridster-loaded .gridster-item { + display: block; + position: absolute; + -webkit-transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + -moz-transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + -o-transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + -webkit-transition-delay: 50ms; + -moz-transition-delay: 50ms; + -o-transition-delay: 50ms; + transition-delay: 50ms; +} +.gridster-loaded .gridster-preview-holder { + display: none; + z-index: 1; + position: absolute; + background-color: #ddd; + border-color: #fff; + opacity: 0.2; +} +.gridster-loaded .gridster-item.gridster-item-moving, +.gridster-loaded .gridster-preview-holder { + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; +} +.gridster-mobile { + height: auto !important; +} +.gridster-mobile .gridster-item { + height: auto; + position: static; + float: none; +} +.gridster-item.ng-leave.ng-leave-active { + opacity: 0; +} +.gridster-item.ng-enter { + opacity: 1; +} +.gridster-item-moving { + z-index: 3; +} +/* RESIZE */ +.gridster-item-resizable-handler { + position: absolute; + font-size: 1px; + display: block; + z-index: 5; +} +.handle-se { + cursor: se-resize; + width: 0; + height: 0; + right: 1px; + bottom: 1px; + border-style: solid; + border-width: 0 0 12px 12px; + border-color: transparent; +} +.handle-ne { + cursor: ne-resize; + width: 12px; + height: 12px; + right: 1px; + top: 1px; +} +.handle-nw { + cursor: nw-resize; + width: 12px; + height: 12px; + left: 1px; + top: 1px; +} +.handle-sw { + cursor: sw-resize; + width: 12px; + height: 12px; + left: 1px; + bottom: 1px; +} +.handle-e { + cursor: e-resize; + width: 12px; + bottom: 0; + right: 1px; + top: 0; +} +.handle-s { + cursor: s-resize; + height: 12px; + right: 0; + bottom: 1px; + left: 0; +} +.handle-n { + cursor: n-resize; + height: 12px; + right: 0; + top: 1px; + left: 0; +} +.handle-w { + cursor: w-resize; + width: 12px; + left: 1px; + top: 0; + bottom: 0; +} +.gridster .gridster-item:hover .gridster-box { + border: 1.5px solid #B3B2B3; +} +.gridster .gridster-item:hover .handle-se { + border-color: transparent transparent #ccc; +} diff --git a/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/dist/angular-gridster.min.css b/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/dist/angular-gridster.min.css new file mode 100644 index 00000000..1bb601d3 --- /dev/null +++ b/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/dist/angular-gridster.min.css @@ -0,0 +1 @@ +.gridster{position:relative;margin:auto;height:0}.gridster>ul{margin:0;list-style:none;padding:0}.gridster-item{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;list-style:none;z-index:2;position:absolute;display:none}.gridster-loaded{-webkit-transition:height .3s;-moz-transition:height .3s;-o-transition:height .3s;transition:height .3s}.gridster-loaded .gridster-item{display:block;position:absolute;-webkit-transition:opacity .3s,left .3s,top .3s,width .3s,height .3s;-moz-transition:opacity .3s,left .3s,top .3s,width .3s,height .3s;-o-transition:opacity .3s,left .3s,top .3s,width .3s,height .3s;transition:opacity .3s,left .3s,top .3s,width .3s,height .3s;-webkit-transition-delay:50ms;-moz-transition-delay:50ms;-o-transition-delay:50ms;transition-delay:50ms}.gridster-loaded .gridster-preview-holder{display:none;z-index:1;position:absolute;background-color:#ddd;border-color:#fff;opacity:.2}.gridster-loaded .gridster-item.gridster-item-moving,.gridster-loaded .gridster-preview-holder{-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.gridster-mobile{height:auto !important}.gridster-mobile .gridster-item{height:auto;position:static;float:none}.gridster-item.ng-leave.ng-leave-active{opacity:0}.gridster-item.ng-enter{opacity:1}.gridster-item-moving{z-index:3}.gridster-item-resizable-handler{position:absolute;font-size:1px;display:block;z-index:5}.handle-se{cursor:se-resize;width:0;height:0;right:1px;bottom:1px;border-style:solid;border-width:0 0 12px 12px;border-color:transparent}.handle-ne{cursor:ne-resize;width:12px;height:12px;right:1px;top:1px}.handle-nw{cursor:nw-resize;width:12px;height:12px;left:1px;top:1px}.handle-sw{cursor:sw-resize;width:12px;height:12px;left:1px;bottom:1px}.handle-e{cursor:e-resize;width:12px;bottom:0;right:1px;top:0}.handle-s{cursor:s-resize;height:12px;right:0;bottom:1px;left:0}.handle-n{cursor:n-resize;height:12px;right:0;top:1px;left:0}.handle-w{cursor:w-resize;width:12px;left:1px;top:0;bottom:0}.gridster .gridster-item:hover .gridster-box{border:1.5px solid #B3B2B3}.gridster .gridster-item:hover .handle-se{border-color:transparent transparent #ccc}
\ No newline at end of file diff --git a/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/dist/angular-gridster.min.js b/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/dist/angular-gridster.min.js new file mode 100644 index 00000000..07d7c878 --- /dev/null +++ b/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/dist/angular-gridster.min.js @@ -0,0 +1,8 @@ +/* + * angular-gridster + * http://manifestwebdesign.github.io/angular-gridster + * + * @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<d;++c)this.margins[c]=parseInt(this.margins[c],10),isNaN(this.margins[c])&&(this.margins[c]=0);else this.margins=[0,0]},this.canItemOccupy=function(a,b,c){return b>-1&&c>-1&&a.sizeX+c<=this.columns&&a.sizeY+b<=this.maxRows},this.autoSetItemPosition=function(a){for(var b=0;b<this.maxRows;++b)for(var c=0;c<this.columns;++c){var d=this.getItems(b,c,a.sizeX,a.sizeY,a);if(0===d.length&&this.canItemOccupy(a,b,c))return void this.putItem(a,b,c)}throw new Error("Unable to place item!")},this.getItems=function(a,b,c,d,e){var f=[];c&&d||(c=d=1),!e||e instanceof Array||(e=[e]);var g;if(this.sparse===!1)for(var h=0;h<d;++h)for(var i=0;i<c;++i)g=this.getItem(a+h,b+i,e),!g||e&&e.indexOf(g)!==-1||f.indexOf(g)!==-1||f.push(g);else for(var j=a+d-1,k=b+c-1,l=0;l<this.allItems.length;++l)g=this.allItems[l],!g||e&&e.indexOf(g)!==-1||f.indexOf(g)!==-1||!this.intersect(g,b,k,a,j)||f.push(g);return f},this.getBoundingBox=function(a){if(0===a.length)return null;if(1===a.length)return{row:a[0].row,col:a[0].col,sizeY:a[0].sizeY,sizeX:a[0].sizeX};for(var b=0,c=0,d=9999,e=9999,f=0,g=a.length;f<g;++f){var h=a[f];d=Math.min(h.row,d),e=Math.min(h.col,e),b=Math.max(h.row+h.sizeY,b),c=Math.max(h.col+h.sizeX,c)}return{row:d,col:e,sizeY:b-d,sizeX:c-e}},this.intersect=function(a,b,c,d,e){return b<=a.col+a.sizeX-1&&c>=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<d;++c){var e=this.grid[c];if(e&&(b=e.indexOf(a),b!==-1)){e[b]=null;break}}this.sparse&&(b=this.allItems.indexOf(a),b!==-1&&this.allItems.splice(b,1)),this.layoutChanged()},this.getItem=function(a,b,c){!c||c instanceof Array||(c=[c]);for(var d=1;a>-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<c;++b)this.putItem(a[b])},this.putItem=function(a,b,c,d){if(("undefined"==typeof b||null===b)&&(b=a.row,c=a.col,"undefined"==typeof b||null===b))return void this.autoSetItemPosition(a);if(this.canItemOccupy(a,b,c)||(c=Math.min(this.columns-a.sizeX,Math.max(0,c)),b=Math.min(this.maxRows-a.sizeY,Math.max(0,b))),null!==a.oldRow&&"undefined"!=typeof a.oldRow){var e=a.oldRow===b&&a.oldColumn===c,f=this.grid[b]&&this.grid[b][c]===a;if(e&&f)return a.row=b,void(a.col=c);var g=this.grid[a.oldRow];g&&g[a.oldColumn]===a&&delete g[a.oldColumn]}a.oldRow=a.row=b,a.oldColumn=a.col=c,this.moveOverlappingItems(a,d),this.grid[b]||(this.grid[b]=[]),this.grid[b][c]=a,this.sparse&&this.allItems.indexOf(a)===-1&&this.allItems.push(a),this.movingItem===a&&this.floatItemUp(a),this.layoutChanged()},this.swapItems=function(a,b){this.grid[a.row][a.col]=b,this.grid[b.row][b.col]=a;var c=a.row,d=a.col;a.row=b.row,a.col=b.col,b.row=c,b.col=d},this.moveOverlappingItems=function(a,b){b?b.indexOf(a)===-1&&(b=b.slice(0),b.push(a)):b=[a];var c=this.getItems(a.row,a.col,a.sizeX,a.sizeY,b);this.moveItemsDown(c,a.row+a.sizeY,b)},this.moveItemsDown=function(a,b,c){if(a&&0!==a.length){a.sort(function(a,b){return a.row-b.row}),c=c?c.slice(0):[];var d,e,f,g={};for(e=0,f=a.length;e<f;++e){d=a[e];var h=g[d.col];("undefined"==typeof h||d.row<h)&&(g[d.col]=d.row)}for(e=0,f=a.length;e<f;++e){d=a[e];var i=b-g[d.col];this.moveItemDown(d,d.row+i,c),c.push(d)}}},this.moveItemDown=function(a,b,c){if(!(a.row>=b)){for(;a.row<b;)++a.row,this.moveOverlappingItems(a,c);this.putItem(a,a.row,a.col,c)}},this.floatItemsUp=function(){if(this.floating!==!1)for(var a=0,b=this.grid.length;a<b;++a){var c=this.grid[a];if(c)for(var d=0,e=c.length;d<e;++d){var f=c[d];f&&this.floatItemUp(f)}}},this.floatItemUp=function(a){if(this.floating!==!1){for(var b=a.col,c=a.sizeY,d=a.sizeX,e=null,f=null,g=a.row-1;g>-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;e<f;++e)d[e]&&(b=Math.max(b,c+a+d[e].sizeY))}this.gridHeight=this.maxRows-b>0?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:'<div ng-style="previewStyle()" class="gridster-item gridster-preview-holder"></div>',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('<div ng-if="gridster.movingItem" gridster-preview></div>'),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;b<c;++b){var d=i.grid[b];if(d)for(var e=0,f=d.length;e<f;++e)if(d[e]){var h=d[e];h.setElementPosition(),h.setElementSizeY(),h.setElementSizeX()}}j()}}i.loaded=!1,i.$element=g,f.gridster=i,g.addClass("gridster");var l=function(a){return"hidden"!==a.style.visibility&&"none"!==a.style.display};f.$watch(function(){return i.gridHeight},j),f.$watch(function(){return i.movingItem},function(){i.updateHeight(i.movingItem?i.movingItem.sizeY:0)});var m=h.gridster;m?f.$parent.$watch(m,function(a){k(a)},!0):k({}),f.$watch(function(){return i.loaded},function(){i.loaded?(g.addClass("gridster-loaded"),d.$broadcast("gridster-loaded",i)):g.removeClass("gridster-loaded")}),f.$watch(function(){return i.isMobile},function(){i.isMobile?g.addClass("gridster-mobile").removeClass("gridster-desktop"):g.removeClass("gridster-mobile").addClass("gridster-desktop"),d.$broadcast("gridster-mobile-changed",i)}),f.$watch(function(){return i.draggable},function(){d.$broadcast("gridster-draggable-changed",i)},!0),f.$watch(function(){return i.resizable},function(){d.$broadcast("gridster-resizable-changed",i)},!0);var n=g[0].offsetWidth||parseInt(g.css("width"),10),o=function(){var a=g[0].offsetWidth||parseInt(g.css("width"),10);a&&a!==n&&!i.movingItem&&(n=a,i.loaded&&g.removeClass("gridster-loaded"),k(),i.loaded&&g.addClass("gridster-loaded"),d.$broadcast("gridster-resized",[a,g[0].offsetHeight],i))},p=e(function(){o(),b(function(){f.$apply()})},100);f.$watch(function(){return l(g[0])},p),"function"==typeof window.addResizeListener?window.addResizeListener(g[0],p):f.$watch(function(){return g[0].offsetWidth||parseInt(g.css("width"),10)},o);var q=a.element(c);q.on("resize",p),f.$on("$destroy",function(){i.destroy(),q.off("resize",p),"function"==typeof window.removeResizeListener&&window.removeResizeListener(g[0],p)}),b(function(){f.$watch("gridster.floating",function(){i.floatItemsUp()}),i.loaded=!0},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(a,b){this.$element=a,this.gridster=b,this.sizeX=b.defaultSizeX,this.sizeY=b.defaultSizeY},this.destroy=function(){this.gridster=null,this.$element=null},this.toJSON=function(){return{row:this.row,col:this.col,sizeY:this.sizeY,sizeX:this.sizeX}},this.isMoving=function(){return this.gridster.movingItem===this},this.setPosition=function(a,b){this.gridster.putItem(this,a,b),this.isMoving()||this.setElementPosition()},this.setSize=function(a,b,c){a=a.toUpperCase();var d="size"+a,e="Size"+a;if(""!==b){b=parseInt(b,10),(isNaN(b)||0===b)&&(b=this.gridster["default"+e]);var f="X"===a?this.gridster.columns:this.gridster.maxRows;this["max"+e]&&(f=Math.min(this["max"+e],f)),this.gridster["max"+e]&&(f=Math.min(this.gridster["max"+e],f)),"X"===a&&this.cols?f-=this.cols:"Y"===a&&this.rows&&(f-=this.rows);var g=0;this["min"+e]&&(g=Math.max(this["min"+e],g)),this.gridster["min"+e]&&(g=Math.max(this.gridster["min"+e],g)),b=Math.max(Math.min(b,f),g);var h=this[d]!==b||this["old"+e]&&this["old"+e]!==b;return this["old"+e]=this[d]=b,this.isMoving()||this["setElement"+e](),!c&&h&&(this.gridster.moveOverlappingItems(this),this.gridster.layoutChanged()),h}},this.setSizeY=function(a,b){return this.setSize("Y",a,b)},this.setSizeX=function(a,b){return this.setSize("X",a,b)},this.setElementPosition=function(){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:""}):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"})},this.setElementSizeY=function(){this.gridster.isMobile&&!this.gridster.saveGridItemCalculatedHeightInMobile?this.$element.css("height",""):this.$element.css("height",this.sizeY*this.gridster.curRowHeight-this.gridster.margins[0]+"px")},this.setElementSizeX=function(){this.gridster.isMobile?this.$element.css("width",""):this.$element.css("width",this.sizeX*this.gridster.curColWidth-this.gridster.margins[1]+"px")},this.getElementSizeX=function(){return this.sizeX*this.gridster.curColWidth-this.gridster.margins[1]},this.getElementSizeY=function(){return this.sizeY*this.gridster.curRowHeight-this.gridster.margins[0]}}).factory("GridsterTouch",[function(){return function(a,b,c,d){var e,f,g={},h=function(a){if(Object.keys)return Object.keys(a).length;var b,c=0;for(b in a)++c;return c},i=function(a){for(var b=0,c=0,d=navigator.userAgent.match(/\bMSIE\b/),e=a;null!=e;e=e.offsetParent)d&&(!document.documentMode||document.documentMode<8)&&"relative"===e.currentStyle.position&&e.offsetParent&&"relative"===e.offsetParent.currentStyle.position&&e.offsetLeft===e.offsetParent.offsetLeft?c+=e.offsetTop:(b+=e.offsetLeft,c+=e.offsetTop);return{x:b,y:c}},j=i(a),k=!1,l=function(e){if("mousemove"!==e.type||0!==h(g)){for(var f=!0,m=e.changedTouches?e.changedTouches:[e],n=0;n<m.length;++n){var o=m[n],p="undefined"!=typeof o.identifier?o.identifier:"undefined"!=typeof o.pointerId?o.pointerId:1;if("undefined"==typeof o.pageX)if(o.pageX=o.offsetX+j.x,o.pageY=o.offsetY+j.y,o.srcElement.offsetParent===a&&document.documentMode&&8===document.documentMode&&"mousedown"===o.type)o.pageX+=o.srcElement.offsetLeft,o.pageY+=o.srcElement.offsetTop;else if(o.srcElement!==a&&!document.documentMode||document.documentMode<8){for(var q=-2,r=-2,s=o.srcElement;null!==s;s=s.parentNode)q+=s.scrollLeft?s.scrollLeft:0,r+=s.scrollTop?s.scrollTop:0;o.pageX=o.clientX+q,o.pageY=o.clientY+r}var t=o.pageX,u=o.pageY;e.type.match(/(start|down)$/i)?(j=i(a),g[p]&&(d&&d({target:e.target,which:e.which,pointerId:p,pageX:t,pageY:u}),delete g[p]),b&&f&&(f=b({target:e.target,which:e.which,pointerId:p,pageX:t,pageY:u})),g[p]={x:t,y:u},a.msSetPointerCapture&&f?a.msSetPointerCapture(p):"mousedown"===e.type&&1===h(g)&&(k?a.setCapture(!0):(document.addEventListener("mousemove",l,!1),document.addEventListener("mouseup",l,!1)))):e.type.match(/move$/i)?!g[p]||g[p].x===t&&g[p].y===u||(c&&f&&(f=c({target:e.target,which:e.which,pointerId:p,pageX:t,pageY:u})),g[p].x=t,g[p].y=u):g[p]&&e.type.match(/(up|end|cancel)$/i)&&(d&&f&&(f=d({target:e.target,which:e.which,pointerId:p,pageX:t,pageY:u})),delete g[p],a.msReleasePointerCapture?a.msReleasePointerCapture(p):"mouseup"===e.type&&0===h(g)&&(k?a.releaseCapture():(document.removeEventListener("mousemove",l,!1),document.removeEventListener("mouseup",l,!1))))}f&&(e.preventDefault&&e.preventDefault(),e.preventManipulation&&e.preventManipulation(),e.preventMouseEvent&&e.preventMouseEvent())}};return this.enable=function(){window.navigator.msPointerEnabled?(a.addEventListener("MSPointerDown",l,!1),a.addEventListener("MSPointerMove",l,!1),a.addEventListener("MSPointerUp",l,!1),a.addEventListener("MSPointerCancel",l,!1),"undefined"!=typeof a.style.msContentZooming&&(e=a.style.msContentZooming,a.style.msContentZooming="none"),"undefined"!=typeof a.style.msTouchAction&&(f=a.style.msTouchAction,a.style.msTouchAction="none")):a.addEventListener?(a.addEventListener("touchstart",l,!1),a.addEventListener("touchmove",l,!1),a.addEventListener("touchend",l,!1),a.addEventListener("touchcancel",l,!1),a.addEventListener("mousedown",l,!1),a.setCapture&&!window.navigator.userAgent.match(/\bGecko\b/)&&(k=!0,a.addEventListener("mousemove",l,!1),a.addEventListener("mouseup",l,!1))):a.attachEvent&&a.setCapture&&(k=!0,a.attachEvent("onmousedown",function(){return l(window.event),window.event.returnValue=!1,!1}),a.attachEvent("onmousemove",function(){return l(window.event),window.event.returnValue=!1,!1}),a.attachEvent("onmouseup",function(){return l(window.event),window.event.returnValue=!1,!1}))},this.disable=function(){window.navigator.msPointerEnabled?(a.removeEventListener("MSPointerDown",l,!1),a.removeEventListener("MSPointerMove",l,!1),a.removeEventListener("MSPointerUp",l,!1),a.removeEventListener("MSPointerCancel",l,!1),e&&(a.style.msContentZooming=e),f&&(a.style.msTouchAction=f)):a.removeEventListener?(a.removeEventListener("touchstart",l,!1),a.removeEventListener("touchmove",l,!1),a.removeEventListener("touchend",l,!1),a.removeEventListener("touchcancel",l,!1),a.removeEventListener("mousedown",l,!1),a.setCapture&&!window.navigator.userAgent.match(/\bGecko\b/)&&(k=!0,a.removeEventListener("mousemove",l,!1),a.removeEventListener("mouseup",l,!1))):a.detachEvent&&a.setCapture&&(k=!0,a.detachEvent("onmousedown"),a.detachEvent("onmousemove"),a.detachEvent("onmouseup"))},this}}]).factory("GridsterDraggable",["$document","$window","GridsterTouch",function(b,c,d){function e(e,f,g,h,i){function j(a){e.addClass("gridster-item-moving"),g.movingItem=h,g.updateHeight(h.sizeY),f.$apply(function(){g.draggable&&g.draggable.start&&g.draggable.start(a,e,i,h)})}function k(a){var b=h.row,d=h.col,j=g.draggable&&g.draggable.drag,k=g.draggable.scrollSensitivity,l=g.draggable.scrollSpeed,m=Math.min(g.pixelsToRows(q),g.maxRows-1),n=Math.min(g.pixelsToColumns(p),g.columns-1),o=g.getItems(m,n,h.sizeX,h.sizeY,h),r=0!==o.length;if(g.swapping===!0&&r){var s=g.getBoundingBox(o),t=s.sizeX===h.sizeX&&s.sizeY===h.sizeY,u=s.row===b,v=s.col===d,w=s.row===m&&s.col===n,x=u||v;if(t&&1===o.length){if(w)g.swapItems(h,o[0]);else if(x)return}else if(s.sizeX<=h.sizeX&&s.sizeY<=h.sizeY&&x)for(var y=h.row<=m?h.row:m+h.sizeY,z=h.col<=n?h.col:n+h.sizeX,A=y-s.row,B=z-s.col,C=0,E=o.length;C<E;++C){var F=o[C],G=g.getItems(F.row+A,F.col+B,F.sizeX,F.sizeY,h);0===G.length&&g.putItem(F,F.row+A,F.col+B)}}g.pushing===!1&&r||(h.row=m,h.col=n),a.pageY-D.body.scrollTop<k?D.body.scrollTop=D.body.scrollTop-l:c.innerHeight-(a.pageY-D.body.scrollTop)<k&&(D.body.scrollTop=D.body.scrollTop+l),a.pageX-D.body.scrollLeft<k?D.body.scrollLeft=D.body.scrollLeft-l:c.innerWidth-(a.pageX-D.body.scrollLeft)<k&&(D.body.scrollLeft=D.body.scrollLeft+l),(j||b!==h.row||d!==h.col)&&f.$apply(function(){j&&g.draggable.drag(a,e,i,h)})}function l(a){e.removeClass("gridster-item-moving");var b=Math.min(g.pixelsToRows(q),g.maxRows-1),c=Math.min(g.pixelsToColumns(p),g.columns-1);g.pushing===!1&&0!==g.getItems(b,c,h.sizeX,h.sizeY,h).length||(h.row=b,h.col=c),g.movingItem=null,h.setPosition(h.row,h.col),f.$apply(function(){g.draggable&&g.draggable.stop&&g.draggable.stop(a,e,i,h)})}function m(b){if(E.indexOf(b.target.nodeName.toLowerCase())!==-1)return!1;var c=a.element(b.target);if(c.hasClass("gridster-item-resizable-handler"))return!1;if(c.attr("onclick")||c.attr("ng-click"))return!1;if(c.closest&&c.closest(".gridster-no-drag").length)return!1;if(g.draggable&&g.draggable.handle){var d=a.element(e[0].querySelectorAll(g.draggable.handle)),f=!1;a:for(var i=0,k=d.length;i<k;++i){var l=d[i];if(l===b.target){f=!0;break}for(var m=b.target,n=0;n<20;++n){var o=m.parentNode;if(o===e[0]||!o)break;if(o===l){f=!0;break a}m=o}}if(!f)return!1}switch(b.which){case 1:break;case 2:case 3:return}return x=b.pageX,y=b.pageY,p=parseInt(e.css("left"),10),q=parseInt(e.css("top"),10),r=e[0].offsetWidth,s=e[0].offsetHeight,t=h.col,u=h.row,j(b),!0}function n(a){if(!e.hasClass("gridster-item-moving")||e.hasClass("gridster-item-resizing"))return!1;var b=g.curWidth-1,c=g.curRowHeight*g.maxRows-1;v=a.pageX,w=a.pageY;var d=v-x+z,f=w-y+A;z=A=0,x=v,y=w;var h=d,i=f;return p+h<C?(d=C-p,z=h-d):p+r+h>b&&(d=b-p-r,z=h-d),q+i<B?(f=B-q,A=i-f):q+s+i>c&&(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<G()?(f=r-G(),C=g-f):p+g<D&&(f=D-p,C=g-f),p+=f,r-=f),w.indexOf("s")>=0&&(r+g<G()?(f=G()-r,C=g-f):p+r+g>E&&(f=E-p-r,C=g-f),r+=f),w.indexOf("w")>=0&&(q-h<H()?(d=q-H(),B=h-d):o+h<F&&(d=F-o,B=h-d),o+=d,q-=d),w.indexOf("e")>=0&&(q+h<H()?(d=H()-q,B=h-d):o+q+h>b&&(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('<div class="gridster-item-resizable-handler handle-'+w+'"></div>'),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<m;l++)i.push(new h(j[l]));this.enable=function(){if(!k){for(var a=0,b=i.length;a<b;a++)i[a].enable();k=!0}},this.disable=function(){if(k){for(var a=0,b=i.length;a<b;a++)i[a].disable();k=!1}},this.toggle=function(a){a?this.enable():this.disable()},this.destroy=function(){for(var a=0,b=i.length;a<b;a++)i[a].destroy()}}return c}]).factory("gridsterDebounce",function(){return function(a,b,c){var d;return function(){var e=this,f=arguments,g=function(){d=null,c||a.apply(e,f)},h=c&&!d;clearTimeout(d),d=setTimeout(g,b),h&&a.apply(e,f)}}}).directive("gridsterItem",["$parse","GridsterDraggable","GridsterResizable","gridsterDebounce",function(a,b,c,d){return{scope:!0,restrict:"EA",controller:"GridsterItemCtrl",controllerAs:"gridsterItem",require:["^gridster","gridsterItem"],link:function(e,f,g,h){function i(){o.setPosition(o.row,o.col),r.row&&r.row.assign&&r.row.assign(e,o.row),r.col&&r.col.assign&&r.col.assign(e,o.col)}function j(){var a=o.setSizeX(o.sizeX,!0);a&&r.sizeX&&r.sizeX.assign&&r.sizeX.assign(e,o.sizeX);var b=o.setSizeY(o.sizeY,!0);b&&r.sizeY&&r.sizeY.assign&&r.sizeY.assign(e,o.sizeY),(a||b)&&(o.gridster.moveOverlappingItems(o),n.layoutChanged(),e.$broadcast("gridster-item-resized",o))}function k(){var a=document.createElement("div"),b={transition:"transitionend",OTransition:"oTransitionEnd",MozTransition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(var c in b)if(void 0!==a.style[c])return b[c]}var l,m=g.gridsterItem,n=h[0],o=h[1];if(e.gridster=n,m){var p=a(m);l=p(e)||{},!l&&p.assign&&(l={row:o.row,col:o.col,sizeX:o.sizeX,sizeY:o.sizeY,minSizeX:0,minSizeY:0,maxSizeX:null,maxSizeY:null},p.assign(e,l))}else l=g;o.init(f,n),f.addClass("gridster-item");for(var q=["minSizeX","maxSizeX","minSizeY","maxSizeY","sizeX","sizeY","row","col"],r={},s=[],t=function(b){var c;if("string"==typeof l[b])c=l[b];else if("string"==typeof l[b.toLowerCase()])c=l[b.toLowerCase()];else{if(!m)return;c=m+"."+b}s.push('"'+b+'":'+c),r[b]=a(c);var d=r[b](e);"number"==typeof d&&(o[b]=d)},u=0,v=q.length;u<v;++u)t(q[u]);var w="{"+s.join(",")+"}";e.$watchCollection(w,function(a,b){for(var c in a){var d=a[c],e=b[c];e!==d&&(d=parseInt(d,10),isNaN(d)||(o[c]=d))}}),e.$watch(function(){return o.row+","+o.col},i),e.$watch(function(){return o.sizeY+","+o.sizeX+","+o.minSizeX+","+o.maxSizeX+","+o.minSizeY+","+o.maxSizeY},j);var x=new b(f,e,n,o,l),y=new c(f,e,n,o,l),z=function(){y.toggle(!n.isMobile&&n.resizable&&n.resizable.enabled)};z();var A=function(){x.toggle(!n.isMobile&&n.draggable&&n.draggable.enabled)};A(),e.$on("gridster-draggable-changed",A),e.$on("gridster-resizable-changed",z),e.$on("gridster-resized",z),e.$on("gridster-mobile-changed",function(){z(),A()});var B=d(function(){e.$apply(function(){e.$broadcast("gridster-item-transition-end",o)})},50);return f.on(k(),B),e.$broadcast("gridster-item-initialized",o),e.$on("$destroy",function(){try{y.destroy(),x.destroy()}catch(a){}try{n.removeItem(o)}catch(a){}try{o.destroy()}catch(a){}})}}}]).directive("gridsterNoDrag",function(){return{restrict:"A",link:function(a,b){b.addClass("gridster-no-drag")}}})});
\ No newline at end of file diff --git a/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/src/angular-gridster.js b/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/src/angular-gridster.js new file mode 100644 index 00000000..5fa8499f --- /dev/null +++ b/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/src/angular-gridster.js @@ -0,0 +1,2239 @@ +/*global define:true*/ +(function(root, factory) { + + 'use strict'; + + if (typeof define === 'function' && define.amd) { + // AMD + define(['angular'], factory); + } else if (typeof exports === 'object') { + // CommonJS + module.exports = factory(require('angular')); + } else { + // Browser, nothing "exported". Only registered as a module with angular. + factory(root.angular); + } +}(this, function(angular) { + + 'use strict'; + + // 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; + + /** + * Create options from gridsterConfig constant + */ + angular.extend(this, gridsterConfig); + + this.resizable = angular.extend({}, gridsterConfig.resizable || {}); + this.draggable = angular.extend({}, gridsterConfig.draggable || {}); + + 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); + }; + + /** + * A positional array of the items in the grid + */ + this.grid = []; + this.allItems = []; + + /** + * 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; + + if (this.allItems) { + this.allItems.length = 0; + this.allItems = null; + } + }; + + /** + * Overrides default options + * + * @param {Object} options The options to override + */ + this.setOptions = function(options) { + if (!options) { + return; + } + + options = angular.extend({}, options); + + // 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; + }; + + /** + * 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!'); + }; + + /** + * 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); + } + } + } + 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 + }; + } + + 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 + */ + + 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; + } + } + if (this.sparse) { + index = this.allItems.indexOf(item); + if (index !== -1) { + this.allItems.splice(index, 1); + } + } + 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; + } + } + ++sizeX; + --col; + } + --row; + ++sizeY; + } + 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 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; + 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)); + } + + // 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; + + this.moveOverlappingItems(item, ignoreItems); + + if (!this.grid[row]) { + this.grid[row] = []; + } + this.grid[row][column] = item; + + if (this.sparse && this.allItems.indexOf(item) === -1) { + this.allItems.push(item); + } + + if (this.movingItem === item) { + this.floatItemUp(item); + } + this.layoutChanged(); + }; + + /** + * 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; + }; + + /** + * 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; + }); + + 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; + } + } + + // 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); + }; + + /** + * 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; + } + for (var colIndex = 0, len = columns.length; colIndex < len; ++colIndex) { + var item = columns[colIndex]; + if (item) { + this.floatItemUp(item); + } + } + } + }; + + /** + * 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); + } + }; + + /** + * 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); + }; + + /** + * 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); + } + + return Math.round(pixels / this.curRowHeight); + }; + + /** + * 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; + } + + 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); + }; + } + ]) + + .directive('gridsterPreview', function() { + return { + replace: true, + scope: true, + require: '^gridster', + template: '<div ng-style="previewStyle()" class="gridster-item gridster-preview-holder"></div>', + link: function(scope, $el, attrs, gridster) { + + /** + * @returns {Object} style object for preview element + */ + scope.previewStyle = function() { + if (!gridster.movingItem) { + return { + display: 'none' + }; + } + + 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' + }; + }; + } + }; + }) + + /** + * 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) { + return { + scope: true, + restrict: 'EAC', + controller: 'GridsterCtrl', + controllerAs: 'gridster', + compile: function($tplElem) { + + $tplElem.prepend('<div ng-if="gridster.movingItem" gridster-preview></div>'); + + return function(scope, $elem, attrs, gridster) { + gridster.loaded = false; + + gridster.$element = $elem; + + scope.gridster = gridster; + + $elem.addClass('gridster'); + + var isVisible = function(ele) { + return ele.style.visibility !== 'hidden' && ele.style.display !== 'none'; + }; + + function updateHeight() { + $elem.css('height', (gridster.gridHeight * gridster.curRowHeight) + (gridster.outerMargin ? gridster.margins[0] : -gridster.margins[0]) + 'px'); + } + + scope.$watch(function() { + return gridster.gridHeight; + }, updateHeight); + + scope.$watch(function() { + return gridster.movingItem; + }, function() { + gridster.updateHeight(gridster.movingItem ? gridster.movingItem.sizeY : 0); + }); + + function refresh(config) { + gridster.setOptions(config); + + if (!isVisible($elem[0])) { + return; + } + + // resolve "auto" & "match" values + if (gridster.width === 'auto') { + gridster.curWidth = $elem[0].offsetWidth || parseInt($elem.css('width'), 10); + } else { + gridster.curWidth = gridster.width; + } + + if (gridster.colWidth === 'auto') { + gridster.curColWidth = (gridster.curWidth + (gridster.outerMargin ? -gridster.margins[1] : gridster.margins[1])) / gridster.columns; + } else { + gridster.curColWidth = gridster.colWidth; + } + + 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(' ', '')); + } + } + + gridster.isMobile = gridster.mobileModeEnabled && gridster.curWidth <= gridster.mobileBreakPoint; + + // 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; + } + + for (var colIndex = 0, len = columns.length; colIndex < len; ++colIndex) { + if (columns[colIndex]) { + var item = columns[colIndex]; + item.setElementPosition(); + item.setElementSizeY(); + item.setElementSizeX(); + } + } + } + + updateHeight(); + } + + var optionsKey = attrs.gridster; + if (optionsKey) { + scope.$parent.$watch(optionsKey, function(newConfig) { + refresh(newConfig); + }, true); + } else { + refresh({}); + } + + scope.$watch(function() { + return gridster.loaded; + }, function() { + if (gridster.loaded) { + $elem.addClass('gridster-loaded'); + $rootScope.$broadcast('gridster-loaded', gridster); + } else { + $elem.removeClass('gridster-loaded'); + } + }); + + 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); + }); + + 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); + + var resize = function() { + var width = $elem[0].offsetWidth || parseInt($elem.css('width'), 10); + + if (!width || width === prevWidth || gridster.movingItem) { + return; + } + prevWidth = width; + + if (gridster.loaded) { + $elem.removeClass('gridster-loaded'); + } + + refresh(); + + if (gridster.loaded) { + $elem.addClass('gridster-loaded'); + } + + $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); + + scope.$watch(function() { + return isVisible($elem[0]); + }, onResize); + + // 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); + } + }); + + // 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; + }; + + /** + * Returns the items most important attributes + */ + this.toJSON = function() { + return { + row: this.row, + col: this.col, + sizeY: this.sizeY, + sizeX: this.sizeX + }; + }; + + this.isMoving = function() { + return this.gridster.movingItem === this; + }; + + /** + * Set the items position + * + * @param {Number} row + * @param {Number} column + */ + this.setPosition = function(row, column) { + this.gridster.putItem(this, row, column); + + if (!this.isMoving()) { + this.setElementPosition(); + } + }; + + /** + * 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; + } + + 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); + } + + value = Math.max(Math.min(value, max), min); + + var changed = (this[camelCase] !== value || (this['old' + titleCase] && this['old' + titleCase] !== value)); + this['old' + titleCase] = this[camelCase] = value; + + if (!this.isMoving()) { + this['setElement' + titleCase](); + } + if (!preventMove && changed) { + this.gridster.moveOverlappingItems(this); + this.gridster.layoutChanged(); + } + + return changed; + }; + + /** + * Sets the items sizeY property + * + * @param {Number} rows + * @param {Boolean} preventMove + */ + this.setSizeY = function(rows, preventMove) { + return this.setSize('Y', rows, preventMove); + }; + + /** + * Sets the items sizeX property + * + * @param {Number} columns + * @param {Boolean} preventMove + */ + this.setSizeX = function(columns, preventMove) { + return this.setSize('X', columns, preventMove); + }; + + /** + * 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' + }); + } + }; + + /** + * 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'); + } + }; + + /** + * 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'); + } + }; + + /** + * Gets an element's width + */ + this.getElementSizeX = function() { + return (this.sizeX * this.gridster.curColWidth - this.gridster.margins[1]); + }; + + /** + * Gets an element's height + */ + this.getElementSizeY = function() { + return (this.sizeY * this.gridster.curRowHeight - this.gridster.margins[0]); + }; + + }) + + .factory('GridsterTouch', [function() { + return function GridsterTouch(target, startEvent, moveEvent, endEvent) { + var lastXYById = {}; + + // 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; + }; + + // 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 + }; + }; + + // cache the delta from the document to our event target (reinitialized each mousedown/MSPointerDown/touchstart) + var documentToTargetDelta = computeDocumentToElementDelta(target); + var useSetReleaseCapture = false; + + // 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; + } + + 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; + } + + pointerObj.pageX = pointerObj.clientX + sx; + pointerObj.pageY = pointerObj.clientY + sy; + } + } + + + 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]; + } + + if (startEvent) { + if (prevent) { + prevent = startEvent({ + target: theEvtObj.target, + which: theEvtObj.which, + pointerId: pointerId, + pageX: pageX, + pageY: pageY + }); + } + } + + // 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); + } + } + } 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 + }); + } + + // 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)$/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]; + + // 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); + } + } + } + } + + if (prevent) { + if (theEvtObj.preventDefault) { + theEvtObj.preventDefault(); + } + + if (theEvtObj.preventManipulation) { + theEvtObj.preventManipulation(); + } + + 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'; + } + + // 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); + + // mouse model + target.addEventListener('mousedown', doEvent, false); + + // 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, false); + target.addEventListener('mouseup', doEvent, 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); + + // 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, false); + target.removeEventListener('touchmove', doEvent, false); + target.removeEventListener('touchend', doEvent, false); + target.removeEventListener('touchcancel', doEvent, false); + + // mouse model + target.removeEventListener('mousedown', doEvent, false); + + // 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, 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'); + } + }; + + return this; + }; + }]) + + .factory('GridsterDraggable', ['$document', '$window', 'GridsterTouch', + function($document, $window, GridsterTouch) { + function GridsterDraggable($el, scope, gridster, item, itemOptions) { + + var elmX, elmY, elmW, elmH, + + mouseX = 0, + mouseY = 0, + lastMouseX = 0, + lastMouseY = 0, + mOffX = 0, + mOffY = 0, + + minTop = 0, + minLeft = 0, + realdocument = $document[0]; + + var originalCol, originalRow; + var inputTags = ['select', 'option', 'input', 'textarea', 'button']; + + function dragStart(event) { + $el.addClass('gridster-item-moving'); + gridster.movingItem = item; + + gridster.updateHeight(item.sizeY); + scope.$apply(function() { + if (gridster.draggable && gridster.draggable.start) { + gridster.draggable.start(event, $el, itemOptions, item); + } + }); + } + + 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 (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; + } + + if (hasCallback || oldRow !== item.row || oldCol !== item.col) { + scope.$apply(function() { + if (hasCallback) { + gridster.draggable.drag(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; + } + + var $target = angular.element(e.target); + + // 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; + } + + // 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) { + break; + } + if (parent === handle) { + match = true; + break outerloop; + } + target = parent; + } + } + 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; + + elmX = parseInt($el.css('left'), 10); + elmY = parseInt($el.css('top'), 10); + elmW = $el[0].offsetWidth; + elmH = $el[0].offsetHeight; + + originalCol = item.col; + originalRow = item.row; + + dragStart(e); + + return true; + } + + function mouseMove(e) { + if (!$el.hasClass('gridster-item-moving') || $el.hasClass('gridster-item-resizing')) { + return false; + } + + var maxLeft = gridster.curWidth - 1; + var maxTop = gridster.curRowHeight * gridster.maxRows - 1; + + // 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; + + // Update last processed mouse positions. + lastMouseX = mouseX; + lastMouseY = mouseY; + + 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; + } + + 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; + + // set new position + $el.css({ + 'top': elmY + 'px', + 'left': elmX + 'px' + }); + + drag(e); + + return true; + } + + function mouseUp(e) { + if (!$el.hasClass('gridster-item-moving') || $el.hasClass('gridster-item-resizing')) { + return false; + } + + mOffX = mOffY = 0; + + dragStop(e); + + return true; + } + + 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(); + }; + + this.disable = function() { + if (enabled === false) { + return; + } + + enabled = false; + if (gridsterTouch) { + gridsterTouch.disable(); + } + }; + + this.toggle = function(enabled) { + if (enabled) { + this.enable(); + } else { + this.disable(); + } + }; + + this.destroy = function() { + this.disable(); + }; + } + + return GridsterDraggable; + } + ]) + + .factory('GridsterResizable', ['GridsterTouch', function(GridsterTouch) { + function GridsterResizable($el, scope, gridster, item, itemOptions) { + + function ResizeHandle(handleClass) { + + var hClass = handleClass; + + var elmX, elmY, elmW, elmH, + + mouseX = 0, + mouseY = 0, + lastMouseX = 0, + lastMouseY = 0, + mOffX = 0, + mOffY = 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 originalWidth, originalHeight; + var savedDraggable; + + function resizeStart(e) { + $el.addClass('gridster-item-moving'); + $el.addClass('gridster-item-resizing'); + + 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 + } + }); + } + + function resize(e) { + var oldRow = item.row, + oldCol = item.col, + oldSizeX = item.sizeX, + oldSizeY = item.sizeY, + hasCallback = gridster.resizable && gridster.resizable.resize; + + var col = item.col; + // only change column if grabbing left edge + if (['w', 'nw', 'sw'].indexOf(handleClass) !== -1) { + col = gridster.pixelsToColumns(elmX, false); + } + + var row = item.row; + // only change row if grabbing top edge + if (['n', 'ne', 'nw'].indexOf(handleClass) !== -1) { + row = gridster.pixelsToRows(elmY, 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 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'); + + 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; + } + + // 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; + + // 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; + + resizeStart(e); + + return true; + } + + function mouseMove(e) { + var maxLeft = gridster.curWidth - 1; + + // 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; + + // Update last processed mouse positions. + lastMouseX = mouseX; + lastMouseY = mouseY; + + 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; + } + 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; + } + 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; + } + 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; + } + elmW += diffX; + } + + // set new position + $el.css({ + 'top': elmY + 'px', + 'left': elmX + 'px', + 'width': elmW + 'px', + 'height': elmH + 'px' + }); + + resize(e); + + return true; + } + + 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); + } + + mOffX = mOffY = 0; + + resizeStop(e); + + return true; + } + + var $dragHandle = null; + var unifiedInput; + + this.enable = function() { + if (!$dragHandle) { + $dragHandle = angular.element('<div class="gridster-item-resizable-handler handle-' + hClass + '"></div>'); + $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; + }; + + this.destroy = function() { + this.disable(); + }; + } + + 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) { + 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'); + + 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); + + // initial set + var val = $getters[aspect](scope); + if (typeof val === 'number') { + item[aspect] = val; + } + }; + + 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; + } + } + }); + + function positionChanged() { + // call setPosition so the element and gridster controller are updated + item.setPosition(item.row, item.col); + + // 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); + + 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); + } + + if (changedX || changedY) { + item.gridster.moveOverlappingItems(item); + gridster.layoutChanged(); + scope.$broadcast('gridster-item-resized', item); + } + } + + scope.$watch(function() { + return item.sizeY + ',' + item.sizeX + ',' + item.minSizeX + ',' + item.maxSizeX + ',' + item.minSizeY + ',' + item.maxSizeY; + }, sizeChanged); + + var draggable = new GridsterDraggable($el, scope, gridster, item, options); + var resizable = new GridsterResizable($el, scope, gridster, item, options); + + var updateResizable = function() { + resizable.toggle(!gridster.isMobile && gridster.resizable && gridster.resizable.enabled); + }; + updateResizable(); + + var updateDraggable = function() { + draggable.toggle(!gridster.isMobile && gridster.draggable && gridster.draggable.enabled); + }; + 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); + + $el.on(whichTransitionEvent(), debouncedTransitionEndPublisher); + + scope.$broadcast('gridster-item-initialized', item); + + return scope.$on('$destroy', function() { + try { + resizable.destroy(); + draggable.destroy(); + } catch (e) {} + + try { + gridster.removeItem(item); + } catch (e) {} + + try { + item.destroy(); + } catch (e) {} + }); + } + }; + } + ]) + + .directive('gridsterNoDrag', function() { + return { + restrict: 'A', + link: function(scope, $element) { + $element.addClass('gridster-no-drag'); + } + }; + }) + + ; + +})); diff --git a/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/src/angular-gridster.less b/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/src/angular-gridster.less new file mode 100644 index 00000000..e8160dd2 --- /dev/null +++ b/usecaseui-common/src/main/webapp/app/fusion/external/angular-gridster/src/angular-gridster.less @@ -0,0 +1,167 @@ +/** + * gridster.js - v0.2.1 - 2013-10-28 * http://gridster.net + * Copyright (c) 2013 ducksboard; Licensed MIT + */ + +.gridster { + position: relative; + margin: auto; + height: 0; +} + +.gridster > ul { + margin: 0; + list-style: none; + padding: 0; +} + +.gridster-item { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + list-style: none; + z-index: 2; + position: absolute; + display: none; +} + +.gridster-loaded { + -webkit-transition: height .3s; + -moz-transition: height .3s; + -o-transition: height .3s; + transition: height .3s; + + .gridster-item { + display: block; + position: absolute; + -webkit-transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + -moz-transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + -o-transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + -webkit-transition-delay: 50ms; + -moz-transition-delay: 50ms; + -o-transition-delay: 50ms; + transition-delay: 50ms; + } + + .gridster-preview-holder { + display: none; + z-index: 1; + position: absolute; + background-color: #ddd; + border-color: #fff; + opacity: 0.2; + } + + .gridster-item.gridster-item-moving, + .gridster-preview-holder { + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; + } +} + +.gridster-mobile { + height: auto !important; + + .gridster-item { + height: auto; + position: static; + float: none; + } +} + +.gridster-item.ng-leave.ng-leave-active { + opacity: 0; +} +.gridster-item.ng-enter { + opacity: 1; +} + +.gridster-item-moving { + z-index: 3; +} + +/* RESIZE */ +.gridster-item-resizable-handler { + position: absolute; + font-size: 1px; + display: block; + z-index: 5; +} + +.handle-se { + cursor: se-resize; + width: 0; + height: 0; + right: 1px; + bottom: 1px; + border-style: solid; + border-width: 0 0 12px 12px; + border-color: transparent; +} + +.handle-ne { + cursor: ne-resize; + width: 12px; + height: 12px; + right: 1px; + top: 1px; +} + +.handle-nw { + cursor: nw-resize; + width: 12px; + height: 12px; + left: 1px; + top: 1px; +} + +.handle-sw { + cursor: sw-resize; + width: 12px; + height: 12px; + left: 1px; + bottom: 1px; +} + +.handle-e { + cursor: e-resize; + width: 12px; + bottom: 0; + right: 1px; + top: 0; +} + +.handle-s { + cursor: s-resize; + height: 12px; + right: 0; + bottom: 1px; + left: 0; +} + +.handle-n { + cursor: n-resize; + height: 12px; + right: 0; + top: 1px; + left: 0; +} + +.handle-w { + cursor: w-resize; + width: 12px; + left: 1px; + top: 0; + bottom: 0; +} + +.gridster .gridster-item:hover .gridster-box { + border: 1.5px solid #B3B2B3; +} + +.gridster .gridster-item:hover .handle-se { + border-color: transparent transparent #ccc; +}
\ No newline at end of file |