From a94fd82fb3a35a8e58c40fe7553c063272d7c092 Mon Sep 17 00:00:00 2001 From: Dor Kalev Date: Sun, 15 Mar 2020 14:53:04 +0000 Subject: [PATCH 1/4] Explicitly declare empty ChecksController#edit so hosting app won't break --- app/controllers/blazer/checks_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/blazer/checks_controller.rb b/app/controllers/blazer/checks_controller.rb index 580214c56..a28ea097b 100644 --- a/app/controllers/blazer/checks_controller.rb +++ b/app/controllers/blazer/checks_controller.rb @@ -1,7 +1,8 @@ module Blazer class ChecksController < BaseController before_action :set_check, only: [:edit, :update, :destroy, :run] - + def edit + end def index state_order = [nil, "disabled", "error", "timed out", "failing", "passing"] @checks = Blazer::Check.joins(:query).includes(:query).order("blazer_queries.name, blazer_checks.id").to_a.sort_by { |q| state_order.index(q.state) || 99 } From a66cf22d380073becefb4424b304f057415bf79f Mon Sep 17 00:00:00 2001 From: Dor Kalev Date: Sun, 15 Mar 2020 14:59:18 +0000 Subject: [PATCH 2/4] ignore vim tmp files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index ae3fdc298..5d3599168 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ *.o *.a mkmf.log +*~ +*.swp +*.swo From f5ec125cb2e3f625b724592897ef4fc67218df0e Mon Sep 17 00:00:00 2001 From: Dor Kalev Date: Sun, 15 Mar 2020 15:03:06 +0000 Subject: [PATCH 3/4] no dashboards_path route so DashboardsController#destroy re-routed to Blazer root --- app/controllers/blazer/dashboards_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/blazer/dashboards_controller.rb b/app/controllers/blazer/dashboards_controller.rb index 98a214bbd..bca02d64d 100644 --- a/app/controllers/blazer/dashboards_controller.rb +++ b/app/controllers/blazer/dashboards_controller.rb @@ -51,7 +51,7 @@ def update def destroy @dashboard.destroy - redirect_to dashboards_path + redirect_to :root end def refresh From 6e9eb1ac68534b71cba638585c9ae78df85e5950 Mon Sep 17 00:00:00 2001 From: Dor Kalev Date: Sun, 15 Mar 2020 15:07:31 +0000 Subject: [PATCH 4/4] gridstack integration - first commit --- app/assets/javascripts/blazer/application.js | 2 + .../javascripts/blazer/gridstack.all.js | 4 + app/assets/javascripts/blazer/gridstack.js | 2123 +++++++++++++++++ .../javascripts/blazer/gs_dashboards.js | 57 + app/assets/stylesheets/blazer/application.css | 3 + .../stylesheets/blazer/gridstack-extra.scss | 30 + app/assets/stylesheets/blazer/gridstack.scss | 139 ++ .../stylesheets/blazer/gs_dashboards.scss | 34 + .../blazer/dashboards_controller.rb | 75 +- app/controllers/blazer/queries_controller.rb | 2 +- app/models/blazer/dashboard.rb | 1 - app/views/blazer/dashboards/show.html.erb | 43 +- lib/generators/blazer/templates/install.rb.tt | 1 + 13 files changed, 2469 insertions(+), 45 deletions(-) create mode 100644 app/assets/javascripts/blazer/gridstack.all.js create mode 100644 app/assets/javascripts/blazer/gridstack.js create mode 100644 app/assets/javascripts/blazer/gs_dashboards.js create mode 100644 app/assets/stylesheets/blazer/gridstack-extra.scss create mode 100644 app/assets/stylesheets/blazer/gridstack.scss create mode 100644 app/assets/stylesheets/blazer/gs_dashboards.scss diff --git a/app/assets/javascripts/blazer/application.js b/app/assets/javascripts/blazer/application.js index 0cc418438..fadf94223 100644 --- a/app/assets/javascripts/blazer/application.js +++ b/app/assets/javascripts/blazer/application.js @@ -1,4 +1,6 @@ //= require ./jquery +//= require ./gridstack.all +//= require ./gs_dashboards //= require ./jquery-ujs //= require ./stupidtable //= require ./stupidtable-custom-settings diff --git a/app/assets/javascripts/blazer/gridstack.all.js b/app/assets/javascripts/blazer/gridstack.all.js new file mode 100644 index 000000000..cde177211 --- /dev/null +++ b/app/assets/javascripts/blazer/gridstack.all.js @@ -0,0 +1,4 @@ +/** gridstack.js 1.1.0 - IE and older browsers Polyfills for this library @preserve*/ +Number.isNaN=Number.isNaN||function(e){return"number"==typeof e&&e!=e},Array.prototype.find||Object.defineProperty(Array.prototype,"find",{value:function(e){if(null==this)throw TypeError('"this" is null or not defined');var t=Object(this),i=t.length>>>0;if("function"!=typeof e)throw TypeError("predicate must be a function");for(var n=arguments[1],o=0;o>>0;if("function"!=typeof e)throw new TypeError("predicate must be a function");for(var n=arguments[1],o=0;o>10|55296,1023&n|56320)}function o(){x()}var e,f,_,s,r,p,c,g,w,l,h,x,C,a,T,m,u,v,y,N="sizzle"+ +new Date,b=i.document,E=0,n=0,k=le(),H=le(),S=le(),z=le(),P=function(e,t){return e===t&&(h=!0),0},D={}.hasOwnProperty,t=[],R=t.pop,A=t.push,M=t.push,W=t.slice,O=function(e,t){for(var i=0,n=e.length;i+~]|"+j+")"+j+"*"),Y=new RegExp(j+"|>"),G=new RegExp(q),V=new RegExp("^"+L+"$"),Q={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+$),PSEUDO:new RegExp("^"+q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+j+"*(even|odd|(([+-]|)(\\d*)n|)"+j+"*(?:([+-]|)"+j+"*(\\d+)|))"+j+"*\\)|)","i"),bool:new RegExp("^(?:"+I+")$","i"),needsContext:new RegExp("^"+j+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+j+"*((?:-\\d)?\\d*)"+j+"*\\)|)(?=[^-]|$)","i")},K=/HTML$/i,J=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,ee=/^[^{]+\{\s*\[native \w/,te=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ie=/[+~]/,ne=new RegExp("\\\\([\\da-f]{1,6}"+j+"?|("+j+")|.)","ig"),oe=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,se=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=_e(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{M.apply(t=W.call(b.childNodes),b.childNodes),t[b.childNodes.length].nodeType}catch(e){M={apply:t.length?function(e,t){A.apply(e,W.call(t))}:function(e,t){for(var i=e.length,n=0;e[i++]=t[n++];);e.length=i-1}}}function ae(t,e,i,n){var o,s,r,a,l,h,u,d=e&&e.ownerDocument,c=e?e.nodeType:9;if(i=i||[],"string"!=typeof t||!t||1!==c&&9!==c&&11!==c)return i;if(!n&&((e?e.ownerDocument||e:b)!==C&&x(e),e=e||C,T)){if(11!==c&&(l=te.exec(t)))if(o=l[1]){if(9===c){if(!(r=e.getElementById(o)))return i;if(r.id===o)return i.push(r),i}else if(d&&(r=d.getElementById(o))&&y(e,r)&&r.id===o)return i.push(r),i}else{if(l[2])return M.apply(i,e.getElementsByTagName(t)),i;if((o=l[3])&&f.getElementsByClassName&&e.getElementsByClassName)return M.apply(i,e.getElementsByClassName(o)),i}if(f.qsa&&!z[t+" "]&&(!m||!m.test(t))&&(1!==c||"object"!==e.nodeName.toLowerCase())){if(u=t,d=e,1===c&&Y.test(t)){for((a=e.getAttribute("id"))?a=a.replace(oe,se):e.setAttribute("id",a=N),s=(h=p(t)).length;s--;)h[s]="#"+a+" "+be(h[s]);u=h.join(","),d=ie.test(t)&&ve(e.parentNode)||e}try{return M.apply(i,d.querySelectorAll(u)),i}catch(e){z(t,!0)}finally{a===N&&e.removeAttribute("id")}}}return g(t.replace(F,"$1"),e,i,n)}function le(){var n=[];return function e(t,i){return n.push(t+" ")>_.cacheLength&&delete e[n.shift()],e[t+" "]=i}}function he(e){return e[N]=!0,e}function ue(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function de(e,t){for(var i=e.split("|"),n=i.length;n--;)_.attrHandle[i[n]]=t}function ce(e,t){var i=t&&e,n=i&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(n)return n;if(i)for(;i=i.nextSibling;)if(i===t)return-1;return e?1:-1}function fe(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function pe(i){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===i}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&re(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function me(r){return he(function(s){return s=+s,he(function(e,t){for(var i,n=r([],e.length,s),o=n.length;o--;)e[i=n[o]]&&(e[i]=!(t[i]=e[i]))})})}function ve(e){return e&&void 0!==e.getElementsByTagName&&e}for(e in f=ae.support={},r=ae.isXML=function(e){var t=e.namespaceURI,i=(e.ownerDocument||e).documentElement;return!K.test(t||i&&i.nodeName||"HTML")},x=ae.setDocument=function(e){var t,i,n=e?e.ownerDocument||e:b;return n!==C&&9===n.nodeType&&n.documentElement&&(a=(C=n).documentElement,T=!r(C),b!==C&&(i=C.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",o,!1):i.attachEvent&&i.attachEvent("onunload",o)),f.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),f.getElementsByTagName=ue(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),f.getElementsByClassName=ee.test(C.getElementsByClassName),f.getById=ue(function(e){return a.appendChild(e).id=N,!C.getElementsByName||!C.getElementsByName(N).length}),f.getById?(_.filter.ID=function(e){var t=e.replace(ne,d);return function(e){return e.getAttribute("id")===t}},_.find.ID=function(e,t){if(void 0!==t.getElementById&&T){var i=t.getElementById(e);return i?[i]:[]}}):(_.filter.ID=function(e){var i=e.replace(ne,d);return function(e){var t=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===i}},_.find.ID=function(e,t){if(void 0!==t.getElementById&&T){var i,n,o,s=t.getElementById(e);if(s){if((i=s.getAttributeNode("id"))&&i.value===e)return[s];for(o=t.getElementsByName(e),n=0;s=o[n++];)if((i=s.getAttributeNode("id"))&&i.value===e)return[s]}return[]}}),_.find.TAG=f.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):f.qsa?t.querySelectorAll(e):void 0}:function(e,t){var i,n=[],o=0,s=t.getElementsByTagName(e);if("*"!==e)return s;for(;i=s[o++];)1===i.nodeType&&n.push(i);return n},_.find.CLASS=f.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&T)return t.getElementsByClassName(e)},u=[],m=[],(f.qsa=ee.test(C.querySelectorAll))&&(ue(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&m.push("[*^$]="+j+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||m.push("\\["+j+"*(?:value|"+I+")"),e.querySelectorAll("[id~="+N+"-]").length||m.push("~="),e.querySelectorAll(":checked").length||m.push(":checked"),e.querySelectorAll("a#"+N+"+*").length||m.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&m.push("name"+j+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&m.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&m.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),m.push(",.*:")})),(f.matchesSelector=ee.test(v=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ue(function(e){f.disconnectedMatch=v.call(e,"*"),v.call(e,"[s!='']:x"),u.push("!=",q)}),m=m.length&&new RegExp(m.join("|")),u=u.length&&new RegExp(u.join("|")),t=ee.test(a.compareDocumentPosition),y=t||ee.test(a.contains)?function(e,t){var i=9===e.nodeType?e.documentElement:e,n=t&&t.parentNode;return e===n||!(!n||1!==n.nodeType||!(i.contains?i.contains(n):e.compareDocumentPosition&&16&e.compareDocumentPosition(n)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},P=t?function(e,t){if(e===t)return h=!0,0;var i=!e.compareDocumentPosition-!t.compareDocumentPosition;return i||(1&(i=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!f.sortDetached&&t.compareDocumentPosition(e)===i?e===C||e.ownerDocument===b&&y(b,e)?-1:t===C||t.ownerDocument===b&&y(b,t)?1:l?O(l,e)-O(l,t):0:4&i?-1:1)}:function(e,t){if(e===t)return h=!0,0;var i,n=0,o=e.parentNode,s=t.parentNode,r=[e],a=[t];if(!o||!s)return e===C?-1:t===C?1:o?-1:s?1:l?O(l,e)-O(l,t):0;if(o===s)return ce(e,t);for(i=e;i=i.parentNode;)r.unshift(i);for(i=t;i=i.parentNode;)a.unshift(i);for(;r[n]===a[n];)n++;return n?ce(r[n],a[n]):r[n]===b?-1:a[n]===b?1:0}),C},ae.matches=function(e,t){return ae(e,null,null,t)},ae.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&x(e),f.matchesSelector&&T&&!z[t+" "]&&(!u||!u.test(t))&&(!m||!m.test(t)))try{var i=v.call(e,t);if(i||f.disconnectedMatch||e.document&&11!==e.document.nodeType)return i}catch(e){z(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(ne,d),e[3]=(e[3]||e[4]||e[5]||"").replace(ne,d),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||ae.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&ae.error(e[0]),e},PSEUDO:function(e){var t,i=!e[6]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":i&&G.test(i)&&(t=p(i,!0))&&(t=i.indexOf(")",i.length-t)-i.length)&&(e[0]=e[0].slice(0,t),e[2]=i.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(ne,d).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=k[e+" "];return t||(t=new RegExp("(^|"+j+")"+e+"("+j+"|$)"))&&k(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(i,n,o){return function(e){var t=ae.attr(e,i);return null==t?"!="===n:!n||(t+="","="===n?t===o:"!="===n?t!==o:"^="===n?o&&0===t.indexOf(o):"*="===n?o&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function z(e,i,n){return b(i)?N.grep(e,function(e,t){return!!i.call(e,t,e)!==n}):i.nodeType?N.grep(e,function(e){return e===i!==n}):"string"!=typeof i?N.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(N.fn.init=function(e,t,i){var n,o;if(!e)return this;if(i=i||P,"string"!=typeof e)return e.nodeType?(this[0]=e,this.length=1,this):b(e)?void 0!==i.ready?i.ready(e):e(N):N.makeArray(e,this);if(!(n="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:D.exec(e))||!n[1]&&t)return!t||t.jquery?(t||i).find(e):this.constructor(t).find(e);if(n[1]){if(t=t instanceof N?t[0]:t,N.merge(this,N.parseHTML(n[1],t&&t.nodeType?t.ownerDocument||t:T,!0)),S.test(n[1])&&N.isPlainObject(t))for(n in t)b(this[n])?this[n](t[n]):this.attr(n,t[n]);return this}return(o=T.getElementById(n[2]))&&(this[0]=o,this.length=1),this}).prototype=N.fn,P=N(T);var R=/^(?:parents|prev(?:Until|All))/,A={children:!0,contents:!0,next:!0,prev:!0};function M(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}N.fn.extend({has:function(e){var t=N(e,this),i=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,pe=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function me(e,t){var i;return i=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&H(e,t)?N.merge([e],i):i}function ve(e,t){for(var i=0,n=e.length;ix",y.noCloneChecked=!!ye.cloneNode(!0).lastChild.defaultValue;var xe=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ne(){return!0}function Ee(){return!1}function ke(e,t){return e===function(){try{return T.activeElement}catch(e){}}()==("focus"===t)}function He(e,t,i,n,o,s){var r,a;if("object"==typeof t){for(a in"string"!=typeof i&&(n=n||i,i=void 0),t)He(e,a,i,n,t[a],s);return e}if(null==n&&null==o?(o=i,n=i=void 0):null==o&&("string"==typeof i?(o=n,n=void 0):(o=n,n=i,i=void 0)),!1===o)o=Ee;else if(!o)return e;return 1===s&&(r=o,(o=function(e){return N().off(e),r.apply(this,arguments)}).guid=r.guid||(r.guid=N.guid++)),e.each(function(){N.event.add(this,t,o,n,i)})}function Se(e,o,s){s?(Q.set(e,o,!1),N.event.add(e,o,{namespace:!1,handler:function(e){var t,i,n=Q.get(this,o);if(1&e.isTrigger&&this[o]){if(n.length)(N.event.special[o]||{}).delegateType&&e.stopPropagation();else if(n=a.call(arguments),Q.set(this,o,n),t=s(this,o),this[o](),n!==(i=Q.get(this,o))||t?Q.set(this,o,!1):i={},n!==i)return e.stopImmediatePropagation(),e.preventDefault(),i.value}else n.length&&(Q.set(this,o,{value:N.event.trigger(N.extend(n[0],N.Event.prototype),n.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,o)&&N.event.add(e,o,Ne)}N.event={global:{},add:function(t,e,i,n,o){var s,r,a,l,h,u,d,c,f,p,g,m=Q.get(t);if(m)for(i.handler&&(i=(s=i).handler,o=s.selector),o&&N.find.matchesSelector(oe,o),i.guid||(i.guid=N.guid++),(l=m.events)||(l=m.events={}),(r=m.handle)||(r=m.handle=function(e){return void 0!==N&&N.event.triggered!==e.type?N.event.dispatch.apply(t,arguments):void 0}),h=(e=(e||"").match(W)||[""]).length;h--;)f=g=(a=Te.exec(e[h])||[])[1],p=(a[2]||"").split(".").sort(),f&&(d=N.event.special[f]||{},f=(o?d.delegateType:d.bindType)||f,d=N.event.special[f]||{},u=N.extend({type:f,origType:g,data:n,handler:i,guid:i.guid,selector:o,needsContext:o&&N.expr.match.needsContext.test(o),namespace:p.join(".")},s),(c=l[f])||((c=l[f]=[]).delegateCount=0,d.setup&&!1!==d.setup.call(t,n,p,r)||t.addEventListener&&t.addEventListener(f,r)),d.add&&(d.add.call(t,u),u.handler.guid||(u.handler.guid=i.guid)),o?c.splice(c.delegateCount++,0,u):c.push(u),N.event.global[f]=!0)},remove:function(e,t,i,n,o){var s,r,a,l,h,u,d,c,f,p,g,m=Q.hasData(e)&&Q.get(e);if(m&&(l=m.events)){for(h=(t=(t||"").match(W)||[""]).length;h--;)if(f=g=(a=Te.exec(t[h])||[])[1],p=(a[2]||"").split(".").sort(),f){for(d=N.event.special[f]||{},c=l[f=(n?d.delegateType:d.bindType)||f]||[],a=a[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),r=s=c.length;s--;)u=c[s],!o&&g!==u.origType||i&&i.guid!==u.guid||a&&!a.test(u.namespace)||n&&n!==u.selector&&("**"!==n||!u.selector)||(c.splice(s,1),u.selector&&c.delegateCount--,d.remove&&d.remove.call(e,u));r&&!c.length&&(d.teardown&&!1!==d.teardown.call(e,p,m.handle)||N.removeEvent(e,f,m.handle),delete l[f])}else for(f in l)N.event.remove(e,f+t[h],i,n,!0);N.isEmptyObject(l)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,i,n,o,s,r,a=N.event.fix(e),l=new Array(arguments.length),h=(Q.get(this,"events")||{})[a.type]||[],u=N.event.special[a.type]||{};for(l[0]=a,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,Pe=/\s*$/g;function Ae(e,t){return H(e,"table")&&H(11!==t.nodeType?t:t.firstChild,"tr")&&N(e).children("tbody")[0]||e}function Me(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var i,n,o,s,r,a,l,h;if(1===t.nodeType){if(Q.hasData(e)&&(s=Q.access(e),r=Q.set(t,s),h=s.events))for(o in delete r.handle,r.events={},h)for(i=0,n=h[o].length;i")},clone:function(e,t,i){var n,o,s,r,a,l,h,u=e.cloneNode(!0),d=se(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||N.isXMLDoc(e)))for(r=me(u),n=0,o=(s=me(e)).length;n").attr(i.scriptAttrs||{}).prop({charset:i.scriptCharset,src:i.url}).on("load error",o=function(e){n.remove(),o=null,e&&t("error"===e.type?404:200,e.type)}),T.head.appendChild(n[0])},abort:function(){o&&o()}}});var ii,ni=[],oi=/(=)\?(?=&|$)|\?\?/;N.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=ni.pop()||N.expando+"_"+At++;return this[e]=!0,e}}),N.ajaxPrefilter("json jsonp",function(e,t,i){var n,o,s,r=!1!==e.jsonp&&(oi.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&oi.test(e.data)&&"data");if(r||"jsonp"===e.dataTypes[0])return n=e.jsonpCallback=b(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,r?e[r]=e[r].replace(oi,"$1"+n):!1!==e.jsonp&&(e.url+=(Mt.test(e.url)?"&":"?")+e.jsonp+"="+n),e.converters["script json"]=function(){return s||N.error(n+" was not called"),s[0]},e.dataTypes[0]="json",o=C[n],C[n]=function(){s=arguments},i.always(function(){void 0===o?N(C).removeProp(n):C[n]=o,e[n]&&(e.jsonpCallback=t.jsonpCallback,ni.push(n)),s&&b(o)&&o(s[0]),s=o=void 0}),"script"}),y.createHTMLDocument=((ii=T.implementation.createHTMLDocument("").body).innerHTML="
",2===ii.childNodes.length),N.parseHTML=function(e,t,i){return"string"!=typeof e?[]:("boolean"==typeof t&&(i=t,t=!1),t||(y.createHTMLDocument?((n=(t=T.implementation.createHTMLDocument("")).createElement("base")).href=T.location.href,t.head.appendChild(n)):t=T),s=!i&&[],(o=S.exec(e))?[t.createElement(o[1])]:(o=we([e],t,s),s&&s.length&&N(s).remove(),N.merge([],o.childNodes)));var n,o,s},N.fn.load=function(e,t,i){var n,o,s,r=this,a=e.indexOf(" ");return-1").append(N.parseHTML(e)).find(n):e)}).always(i&&function(e,t){r.each(function(){i.apply(this,s||[e.responseText,t,e])})}),this},N.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){N.fn[t]=function(e){return this.on(t,e)}}),N.expr.pseudos.animated=function(t){return N.grep(N.timers,function(e){return t===e.elem}).length},N.offset={setOffset:function(e,t,i){var n,o,s,r,a,l,h=N.css(e,"position"),u=N(e),d={};"static"===h&&(e.style.position="relative"),a=u.offset(),s=N.css(e,"top"),l=N.css(e,"left"),o=("absolute"===h||"fixed"===h)&&-1<(s+l).indexOf("auto")?(r=(n=u.position()).top,n.left):(r=parseFloat(s)||0,parseFloat(l)||0),b(t)&&(t=t.call(e,i,N.extend({},a))),null!=t.top&&(d.top=t.top-a.top+r),null!=t.left&&(d.left=t.left-a.left+o),"using"in t?t.using.call(e,d):u.css(d)}},N.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){N.offset.setOffset(this,t,e)});var e,i,n=this[0];return n?n.getClientRects().length?(e=n.getBoundingClientRect(),i=n.ownerDocument.defaultView,{top:e.top+i.pageYOffset,left:e.left+i.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,i,n=this[0],o={top:0,left:0};if("fixed"===N.css(n,"position"))t=n.getBoundingClientRect();else{for(t=this.offset(),i=n.ownerDocument,e=n.offsetParent||i.documentElement;e&&(e===i.body||e===i.documentElement)&&"static"===N.css(e,"position");)e=e.parentNode;e&&e!==n&&1===e.nodeType&&((o=N(e).offset()).top+=N.css(e,"borderTopWidth",!0),o.left+=N.css(e,"borderLeftWidth",!0))}return{top:t.top-o.top-N.css(n,"marginTop",!0),left:t.left-o.left-N.css(n,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===N.css(e,"position");)e=e.offsetParent;return e||oe})}}),N.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,o){var s="pageYOffset"===o;N.fn[t]=function(e){return B(this,function(e,t,i){var n;if(g(e)?n=e:9===e.nodeType&&(n=e.defaultView),void 0===i)return n?n[o]:e[t];n?n.scrollTo(s?n.pageXOffset:i,s?i:n.pageYOffset):e[t]=i},t,e,arguments.length)}}),N.each(["top","left"],function(e,i){N.cssHooks[i]=Ze(y.pixelPosition,function(e,t){if(t)return t=Je(e,i),Ye.test(t)?N(e).position()[i]+"px":t})}),N.each({Height:"height",Width:"width"},function(r,a){N.each({padding:"inner"+r,content:a,"":"outer"+r},function(n,s){N.fn[s]=function(e,t){var i=arguments.length&&(n||"boolean"!=typeof e),o=n||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,i){var n;return g(e)?0===s.indexOf("outer")?e["inner"+r]:e.document.documentElement["client"+r]:9===e.nodeType?(n=e.documentElement,Math.max(e.body["scroll"+r],n["scroll"+r],e.body["offset"+r],n["offset"+r],n["client"+r])):void 0===i?N.css(e,t,o):N.style(e,t,i,o)},a,i?e:void 0,i)}})}),N.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,i){N.fn[i]=function(e,t){return 0n?1:-1})},defaults:function(i){var e=Array.prototype.slice.call(arguments,1);e.forEach(function(e){for(var t in e){if(e.hasOwnProperty(t)&&(!i.hasOwnProperty(t)||i[t]===undefined)){i[t]=e[t]}}});return i},clone:function(e){return g.extend({},e)},throttle:function(e,t){var i=false;return function(){if(!i){e.apply(this,arguments);i=true;setTimeout(function(){i=false},t)}}},removePositioningStyles:function(e){var t=e[0].style;if(t.position){t.removeProperty("position")}if(t.left){t.removeProperty("left")}if(t.top){t.removeProperty("top")}if(t.width){t.removeProperty("width")}if(t.height){t.removeProperty("height")}},getScrollParent:function(e){var t;if(e===null){t=null}else if(e.scrollHeight>e.clientHeight){t=e}else{t=v.getScrollParent(e.parentNode)}return t},updateScrollPosition:function(e,t,i){var n=e.getBoundingClientRect();var o=window.innerHeight||document.documentElement.clientHeight;if(n.top<0||n.bottom>o){var s=n.bottom-o;var r=n.top;var a=v.getScrollParent(e);if(a!==null){var l=a.scrollTop;if(n.top<0&&i<0){if(e.offsetHeight>o){a.scrollTop+=i}else{a.scrollTop+=Math.abs(r)>Math.abs(i)?i:r}}else if(i>0){if(e.offsetHeight>o){a.scrollTop+=i}else{a.scrollTop+=s>i?i:s}}t.position.top+=a.scrollTop-l}}}};function y(e){this.grid=e}y.registeredPlugins=[],y.registerPlugin=function(e){y.registeredPlugins.push(e)},y.prototype.resizable=function(e,t){return this},y.prototype.draggable=function(e,t){return this},y.prototype.droppable=function(e,t){return this},y.prototype.isDroppable=function(e){return false},y.prototype.on=function(e,t,i){return this};var s=0,b=function(e,t,i,n,o){this.column=e||12;this.float=i||false;this.maxRow=n||0;this.nodes=o||[];this.onchange=t||function(){};this._addedNodes=[];this._removedNodes=[];this._batchMode=false};b.prototype.batchUpdate=function(){if(this._batchMode)return;this._batchMode=true;this._prevFloat=this.float;this.float=true},b.prototype.commit=function(){if(!this._batchMode)return;this._batchMode=false;this.float=this._prevFloat;delete this._prevFloat;this._packNodes();this._notify()},b.prototype.getNodeDataByDOMEl=function(t){return this.nodes.find(function(e){return t===e.el})},b.prototype._fixCollisions=function(e){var t=this;this._sortNodes(-1);var i=e;var n=Boolean(this.nodes.find(function(e){return e.locked}));if(!this.float&&!n){i={x:0,y:e.y,width:this.column,height:e.height}}while(true){var o=this.nodes.find(v._collisionNodeCheck,{node:e,nn:i});if(!o){return}var s=this.moveNode(o,o.x,e.y+e.height,o.width,o.height,true);if(!s){return}}},b.prototype.isAreaEmpty=function(e,t,i,n){var o={x:e||0,y:t||0,width:i||1,height:n||1};var s=this.nodes.find(function(e){return v.isIntercepted(e,o)});return!s},b.prototype._sortNodes=function(e){this.nodes=v.sort(this.nodes,e,this.column)},b.prototype._packNodes=function(){this._sortNodes();if(this.float){this.nodes.forEach(function(e,t){if(e._updating||e._packY===undefined||e.y===e._packY){return}var i=e.y;while(i>=e._packY){var n=this.nodes.slice(0,t).find(v._didCollide,{n:e,newY:i});if(!n){e._dirty=true;e.y=i}--i}},this)}else{this.nodes.forEach(function(e,t){if(e.locked){return}while(e.y>0){var i=e.y-1;var n=t===0;if(t>0){var o=this.nodes.slice(0,t).find(v._didCollide,{n:e,newY:i});n=o===undefined}if(!n){break}e._dirty=e.y!==i;e.y=i}},this)}},b.prototype._prepareNode=function(e,t){e=e||{};if(e.x===undefined||e.y===undefined||e.x===null||e.y===null){e.autoPosition=true}var i={width:1,height:1,x:0,y:0};e=v.defaults(e,i);e.x=parseInt(e.x);e.y=parseInt(e.y);e.width=parseInt(e.width);e.height=parseInt(e.height);e.autoPosition=e.autoPosition||false;e.noResize=e.noResize||false;e.noMove=e.noMove||false;if(Number.isNaN(e.x)){e.x=i.x;e.autoPosition=true}if(Number.isNaN(e.y)){e.y=i.y;e.autoPosition=true}if(Number.isNaN(e.width)){e.width=i.width}if(Number.isNaN(e.height)){e.height=i.height}if(e.maxWidth!==undefined){e.width=Math.min(e.width,e.maxWidth)}if(e.maxHeight!==undefined){e.height=Math.min(e.height,e.maxHeight)}if(e.minWidth!==undefined){e.width=Math.max(e.width,e.minWidth)}if(e.minHeight!==undefined){e.height=Math.max(e.height,e.minHeight)}if(e.width>this.column){e.width=this.column}else if(e.width<1){e.width=1}if(this.maxRow&&e.height>this.maxRow){e.height=this.maxRow}else if(e.height<1){e.height=1}if(e.x<0){e.x=0}if(e.y<0){e.y=0}if(e.x+e.width>this.column){if(t){e.width=this.column-e.x}else{e.x=this.column-e.width}}if(this.maxRow&&e.y+e.height>this.maxRow){if(t){e.height=this.maxRow-e.y}else{e.y=this.maxRow-e.height}}return e},b.prototype._notify=function(){if(this._batchMode){return}var e=Array.prototype.slice.call(arguments,0);e[0]=e[0]===undefined?[]:Array.isArray(e[0])?e[0]:[e[0]];e[1]=e[1]===undefined?true:e[1];var t=e[0].concat(this.getDirtyNodes());this.onchange(t,e[1])},b.prototype.cleanNodes=function(){if(this._batchMode){return}this.nodes.forEach(function(e){delete e._dirty})},b.prototype.getDirtyNodes=function(e){if(e){var t=[];this.nodes.forEach(function(e){if(e._dirty){if(e.y===e._origY&&e.x===e._origX&&e.width===e._origW&&e.height===e._origH){delete e._dirty}else{t.push(e)}}});return t}return this.nodes.filter(function(e){return e._dirty})},b.prototype.addNode=function(e,t){e=this._prepareNode(e);e._id=e._id||++s;if(e.autoPosition){this._sortNodes();for(var i=0;;++i){var n=i%this.column;var o=Math.floor(i/this.column);if(n+e.width>this.column){continue}if(!this.nodes.find(v._isAddNodeIntercepted,{x:n,y:o,node:e})){e.x=n;e.y=o;delete e.autoPosition;break}}}this.nodes.push(e);if(t){this._addedNodes.push(e)}this._fixCollisions(e);this._packNodes();this._notify();return e},b.prototype.removeNode=function(e,t){t=t===undefined?true:t;this._removedNodes.push(e);e._id=null;this.nodes=v.without(this.nodes,e);this._packNodes();this._notify(e,t)},b.prototype.removeAll=function(e){delete this._layouts;if(this.nodes.length===0){return}e=e===undefined?true:e;this.nodes.forEach(function(e){e._id=null});this._removedNodes=this.nodes;this.nodes=[];this._notify(this._removedNodes,e)},b.prototype.canMoveNode=function(t,e,i,n,o){if(!this.isNodeChangedPosition(t,e,i,n,o)){return false}var s=Boolean(this.nodes.find(function(e){return e.locked}));if(!this.maxRow&&!s){return true}var r;var a=new b(this.column,null,this.float,0,this.nodes.map(function(e){if(e===t){r=g.extend({},e);return r}return g.extend({},e)}));if(!r){return true}a.moveNode(r,e,i,n,o);var l=true;if(s){l&=!Boolean(a.nodes.find(function(e){return e!==r&&Boolean(e.locked)&&Boolean(e._dirty)}))}if(this.maxRow){l&=a.getRow()<=this.maxRow}return l},b.prototype.canBePlacedWithRespectToHeight=function(e){if(!this.maxRow){return true}var t=new b(this.column,null,this.float,0,this.nodes.map(function(e){return g.extend({},e)}));t.addNode(e);return t.getRow()<=this.maxRow},b.prototype.isNodeChangedPosition=function(e,t,i,n,o){if(typeof t!=="number"){t=e.x}if(typeof i!=="number"){i=e.y}if(typeof n!=="number"){n=e.width}if(typeof o!=="number"){o=e.height}if(e.maxWidth!==undefined){n=Math.min(n,e.maxWidth)}if(e.maxHeight!==undefined){o=Math.min(o,e.maxHeight)}if(e.minWidth!==undefined){n=Math.max(n,e.minWidth)}if(e.minHeight!==undefined){o=Math.max(o,e.minHeight)}if(e.x===t&&e.y===i&&e.width===n&&e.height===o){return false}return true},b.prototype.moveNode=function(e,t,i,n,o,s){if(typeof t!=="number"){t=e.x}if(typeof i!=="number"){i=e.y}if(typeof n!=="number"){n=e.width}if(typeof o!=="number"){o=e.height}var r=e.width!==n||e.height!==o;var a={x:t,y:i,width:n,height:o,maxWidth:e.maxWidth,maxHeight:NodeIterator.maxHeight,minWidth:e.minWidth,minHeight:e.minHeight};a=this._prepareNode(a,r);if(e.x===a.x&&e.y===a.y&&e.width===a.width&&e.height===a.height){return null}e._dirty=true;e.x=e.lastTriedX=a.x;e.y=e.lastTriedY=a.y;e.width=e.lastTriedWidth=a.width;e.height=e.lastTriedHeight=a.height;this._fixCollisions(e);if(!s){this._packNodes();this._notify()}return e},b.prototype.getRow=function(){return this.nodes.reduce(function(e,t){return Math.max(e,t.y+t.height)},0)},b.prototype.beginUpdate=function(e){if(e._updating)return;e._updating=true;this.nodes.forEach(function(e){e._packY=e.y})},b.prototype.endUpdate=function(){var e=this.nodes.find(function(e){return e._updating});if(e){e._updating=false;this.nodes.forEach(function(e){delete e._packY})}};var o=function(e,t){var c=this;var i,n,o;t=t||{};this.$el=g(e);this.el=this.$el.get(0);u(t,"width","column","v0.5.3");u(t,"height","maxRow","v0.5.3");d(t,"oneColumnModeClass","v0.6.3",". Use class `.grid-stack-1` instead");m(this.$el,"data-gs-width","data-gs-column","v0.5.3");m(this.$el,"data-gs-height","data-gs-max-row","v0.5.3");m(this.$el,"data-gs-current-height","data-gs-current-row","v1.0.0");t.itemClass=t.itemClass||"grid-stack-item";var s=this.$el.closest("."+t.itemClass).length>0;if(t.row){t.minRow=t.maxRow=t.row;delete t.row}var r=parseInt(this.$el.attr("data-gs-row"));this.opts=v.defaults(t,{column:parseInt(this.$el.attr("data-gs-column"))||12,minRow:r?r:parseInt(this.$el.attr("data-gs-min-row"))||0,maxRow:r?r:parseInt(this.$el.attr("data-gs-max-row"))||0,itemClass:"grid-stack-item",placeholderClass:"grid-stack-placeholder",placeholderText:"",handle:".grid-stack-item-content",handleClass:null,cellHeight:60,verticalMargin:20,auto:true,minWidth:768,float:false,staticGrid:false,_class:"grid-stack-instance-"+(Math.random()*1e4).toFixed(0),animate:Boolean(this.$el.attr("data-gs-animate"))||false,alwaysShowResizeHandle:t.alwaysShowResizeHandle||false,resizable:v.defaults(t.resizable||{},{autoHide:!(t.alwaysShowResizeHandle||false),handles:"se"}),draggable:v.defaults(t.draggable||{},{handle:(t.handleClass?"."+t.handleClass:t.handle?t.handle:"")||".grid-stack-item-content",scroll:false,appendTo:"body"}),disableDrag:t.disableDrag||false,disableResize:t.disableResize||false,rtl:"auto",removable:false,removableOptions:v.defaults(t.removableOptions||{},{accept:"."+t.itemClass}),removeTimeout:2e3,verticalMarginUnit:"px",cellHeightUnit:"px",disableOneColumnMode:t.disableOneColumnMode||false,oneColumnModeDomSort:t.oneColumnModeDomSort,ddPlugin:null});if(this.opts.ddPlugin===false){this.opts.ddPlugin=y}else if(this.opts.ddPlugin===null){this.opts.ddPlugin=y.registeredPlugins[0]||y}this.dd=new this.opts.ddPlugin(this);if(this.opts.rtl==="auto"){this.opts.rtl=this.$el.css("direction")==="rtl"}if(this.opts.rtl){this.$el.addClass("grid-stack-rtl")}this.opts.isNested=s;o=this.opts.cellHeight==="auto";if(o){c.cellHeight(c.cellWidth(),true)}else{this.cellHeight(this.opts.cellHeight,true)}this.verticalMargin(this.opts.verticalMargin,true);this.$el.addClass(this.opts._class);this._setStaticClass();if(s){this.$el.addClass("grid-stack-nested")}this._initStyles();this.engine=new b(this.opts.column,function(e,t){t=t===undefined?true:t;var i=0;this.nodes.forEach(function(e){i=Math.max(i,e.y+e.height)});e.forEach(function(e){if(t&&e._id===null){if(e.el){g(e.el).remove()}}else{g(e.el).attr("data-gs-x",e.x).attr("data-gs-y",e.y).attr("data-gs-width",e.width).attr("data-gs-height",e.height)}});c._updateStyles(i+10)},this.opts.float,this.opts.maxRow);if(this.opts.auto){var a=[];var l=this;this.$el.children("."+this.opts.itemClass+":not(."+this.opts.placeholderClass+")").each(function(e,t){t=g(t);var i=parseInt(t.attr("data-gs-x"));var n=parseInt(t.attr("data-gs-y"));a.push({el:t.get(0),i:(Number.isNaN(i)?1e3:i)+(Number.isNaN(n)?1e3:n)*l.opts.column})});v.sortBy(a,function(e){return e.i}).forEach(function(e){this._prepareElement(e.el)},this)}this.engine._saveInitial();this.setAnimation(this.opts.animate);this.placeholder=g('
'+'
'+this.opts.placeholderText+"
").hide();this._updateContainerHeight();this._updateHeightsOnResize=v.throttle(function(){c.cellHeight(c.cellWidth(),false)},100);this.onResizeHandler=function(){if(o){c._updateHeightsOnResize()}if(c.opts.staticGrid){return}if(!c.opts.disableOneColumnMode&&(window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth)<=c.opts.minWidth){if(c.oneColumnMode){return}c.oneColumnMode=true;c.column(1)}else{if(!c.oneColumnMode){return}c.oneColumnMode=false;c.column(c._prevColumn)}};g(window).resize(this.onResizeHandler);this.onResizeHandler();if(!c.opts.staticGrid&&typeof c.opts.removable==="string"){var h=g(c.opts.removable);if(!this.dd.isDroppable(h)){this.dd.droppable(h,c.opts.removableOptions)}this.dd.on(h,"dropover",function(e,t){var i=g(t.draggable);var n=i.data("_gridstack_node");if(!n||n._grid!==c){return}i.data("inTrashZone",true);c._setupRemovingTimeout(i)}).on(h,"dropout",function(e,t){var i=g(t.draggable);var n=i.data("_gridstack_node");if(!n||n._grid!==c){return}i.data("inTrashZone",false);c._clearRemovingTimeout(i)})}if(!c.opts.staticGrid&&c.opts.acceptWidgets){var f=null;var p=function(e,t){var i=f;var n=i.data("_gridstack_node");var o=c.getCellFromPixel({left:e.pageX,top:e.pageY},true);var s=Math.max(0,o.x);var r=Math.max(0,o.y);if(!n._added){n._added=true;n.el=i.get(0);n.autoPosition=true;n.x=s;n.y=r;c.engine.cleanNodes();c.engine.beginUpdate(n);c.engine.addNode(n);c.$el.append(c.placeholder);c.placeholder.attr("data-gs-x",n.x).attr("data-gs-y",n.y).attr("data-gs-width",n.width).attr("data-gs-height",n.height).show();n.el=c.placeholder.get(0);n._beforeDragX=n.x;n._beforeDragY=n.y;c._updateContainerHeight()}if(!c.engine.canMoveNode(n,s,r)){return}c.engine.moveNode(n,s,r);c._updateContainerHeight()};this.dd.droppable(c.$el,{accept:function(e){e=g(e);var t=e.data("_gridstack_node");if(t&&t._grid===c){return false}return e.is(c.opts.acceptWidgets===true?".grid-stack-item":c.opts.acceptWidgets)}}).on(c.$el,"dropover",function(e,t){var i=g(t.draggable);var n,o;var s=i.data("_gridstack_node");if(!s||!s.width||!s.height){var r=parseInt(i.attr("data-gs-width"));if(r>0){s=s||{};s.width=r}var a=parseInt(i.attr("data-gs-height"));if(a>0){s=s||{};s.height=a}}var l=c.cellWidth();var h=c.cellHeight();var u=c.opts.verticalMargin;n=s&&s.width?s.width:Math.ceil(i.outerWidth()/l);o=s&&s.height?s.height:Math.round((i.outerHeight()+u)/(h+u));f=i;var d=c.engine._prepareNode({width:n,height:o,_added:false,_temporary:true});d.isOutOfGrid=true;i.data("_gridstack_node",d);i.data("_gridstack_node_orig",s);i.on("drag",p);return false}).on(c.$el,"dropout",function(e,t){var i=g(t.draggable);if(!i.data("_gridstack_node")){return}var n=i.data("_gridstack_node");if(!n.isOutOfGrid){return}i.unbind("drag",p);n.el=null;c.engine.removeNode(n);c.placeholder.detach();c._updateContainerHeight();i.data("_gridstack_node",i.data("_gridstack_node_orig"));return false}).on(c.$el,"drop",function(e,t){c.placeholder.detach();var i=g(t.draggable).data("_gridstack_node");i.isOutOfGrid=false;i._grid=c;var n=g(t.draggable).clone(false);n.data("_gridstack_node",i);var o=g(t.draggable).data("_gridstack_node_orig");if(o!==undefined&&o._grid!==undefined){o._grid._triggerRemoveEvent()}g(t.helper).remove();i.el=n.get(0);c.placeholder.hide();v.removePositioningStyles(n);n.find("div.ui-resizable-handle").remove();n.attr("data-gs-x",i.x).attr("data-gs-y",i.y).attr("data-gs-width",i.width).attr("data-gs-height",i.height).addClass(c.opts.itemClass).enableSelection().removeData("draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled").unbind("drag",p);c.$el.append(n);c._prepareElementsByNode(n,i);c._updateContainerHeight();c.engine._addedNodes.push(i);c._triggerAddEvent();c._triggerChangeEvent();c.engine.endUpdate();g(t.draggable).unbind("drag",p);g(t.draggable).removeData("_gridstack_node");g(t.draggable).removeData("_gridstack_node_orig");c.$el.trigger("dropped",[o,i]);return false})}};o.prototype._triggerChangeEvent=function(){if(!this.engine._batchMode){var e=this.engine.getDirtyNodes(!0);e&&e.length&&(this.engine._layoutsNodesChange(e),this._triggerEvent("change",e)),this.engine._saveInitial()}},o.prototype._triggerAddEvent=function(){this.engine._batchMode||this.engine._addedNodes&&0this._styles._max)){for(var o=this._styles._max;o=f.engine.column||s<0||!f.engine.float&&s>f.engine.getRow()){if(u._temporaryRemoved)return;!0===f.opts.removable&&f._setupRemovingTimeout(h),o=u._beforeDragX,s=u._beforeDragY,f.placeholder.detach(),f.placeholder.hide(),f.engine.removeNode(u),f._updateContainerHeight(),u._temporaryRemoved=!0}else f._clearRemovingTimeout(h),u._temporaryRemoved&&(f.engine.addNode(u),f.placeholder.attr("data-gs-x",o).attr("data-gs-y",s).attr("data-gs-width",i).attr("data-gs-height",n).show(),f.$el.append(f.placeholder),u.el=f.placeholder.get(0),u._temporaryRemoved=!1)}else if("resize"===e.type){if(o<0)return;i=Math.round(t.size.width/d),n=Math.round((t.size.height+f.verticalMargin())/c)}var a=void 0!==i?i:u.lastTriedWidth,l=void 0!==n?n:u.lastTriedHeight;!f.engine.canMoveNode(u,o,s,i,n)||u.lastTriedX===o&&u.lastTriedY===s&&u.lastTriedWidth===a&&u.lastTriedHeight===l||(u.lastTriedX=o,u.lastTriedY=s,u.lastTriedWidth=i,u.lastTriedHeight=n,f.engine.moveNode(u,o,s,i,n),f._updateContainerHeight(),"resize"===e.type&&g(e.target).trigger("gsresize",u))}function t(e,t){f.$el.append(f.placeholder);var i=g(this);f.engine.cleanNodes(),f.engine.beginUpdate(u),d=f.cellWidth();var n=f.cellHeight();c=(f.$el.height()+f.verticalMargin())/parseInt(f.$el.attr("data-gs-current-row")),f.placeholder.attr("data-gs-x",i.attr("data-gs-x")).attr("data-gs-y",i.attr("data-gs-y")).attr("data-gs-width",i.attr("data-gs-width")).attr("data-gs-height",i.attr("data-gs-height")).show(),u.el=f.placeholder.get(0),u._beforeDragX=u.x,u._beforeDragY=u.y,u._prevYPix=t.position.top;var o=u.minHeight||1,s=f.opts.verticalMargin;f.dd.resizable(h,"option","minWidth",d*(u.minWidth||1)),f.dd.resizable(h,"option","minHeight",n*o+(o-1)*s),"resizestart"===e.type&&i.find(".grid-stack-item").trigger("resizestart")}function i(e,t){var i=g(this);if(i.data("_gridstack_node")){if(f.placeholder.detach(),u.el=i.get(0),f.placeholder.hide(),u._isAboutToRemove)h.data("_gridstack_node")._grid._triggerRemoveEvent(),h.removeData("_gridstack_node"),h.remove();else f._clearRemovingTimeout(h),u._temporaryRemoved?(v.removePositioningStyles(i),i.attr("data-gs-x",u._beforeDragX).attr("data-gs-y",u._beforeDragY).attr("data-gs-width",u.width).attr("data-gs-height",u.height),u.x=u._beforeDragX,u.y=u._beforeDragY,u._temporaryRemoved=!1,f.engine.addNode(u)):(v.removePositioningStyles(i),i.attr("data-gs-x",u.x).attr("data-gs-y",u.y).attr("data-gs-width",u.width).attr("data-gs-height",u.height));f._updateContainerHeight(),f._triggerChangeEvent(),f.engine.endUpdate();var n=i.find(".grid-stack");n.length&&"resizestop"===e.type&&(n.each(function(e,t){t.gridstack.onResizeHandler()}),i.find(".grid-stack-item").trigger("resizestop"),i.find(".grid-stack-item").trigger("gsresizestop")),"resizestop"===e.type&&f.$el.trigger("gsresizestop",i)}}var d,c,f=this;this.dd.draggable(h,{start:t,stop:i,drag:e}).resizable(h,{start:t,stop:i,resize:e}),(u.noMove||this.opts.disableDrag||this.opts.staticGrid)&&this.dd.draggable(h,"disable"),(u.noResize||this.opts.disableResize||this.opts.staticGrid)&&this.dd.resizable(h,"disable"),this._writeAttr(h,u)},o.prototype._prepareElement=function(e,t){t=void 0!==t&&t;(e=g(e)).addClass(this.opts.itemClass);var i=this._readAttr(e,{el:e.get(0),_grid:this});i=this.engine.addNode(i,t),e.data("_gridstack_node",i),this._prepareElementsByNode(e,i)},o.prototype._writeAttr=function(e,t){e=g(e),void 0!==(t=t||{}).x&&e.attr("data-gs-x",t.x),void 0!==t.y&&e.attr("data-gs-y",t.y),void 0!==t.width&&e.attr("data-gs-width",t.width),void 0!==t.height&&e.attr("data-gs-height",t.height),void 0!==t.autoPosition&&e.attr("data-gs-auto-position",!!t.autoPosition||null),void 0!==t.minWidth&&e.attr("data-gs-min-width",t.minWidth),void 0!==t.maxWidth&&e.attr("data-gs-max-width",t.maxWidth),void 0!==t.minHeight&&e.attr("data-gs-min-height",t.minHeight),void 0!==t.maxHeight&&e.attr("data-gs-max-height",t.maxHeight),void 0!==t.noResize&&e.attr("data-gs-no-resize",!!t.noResize||null),void 0!==t.noMove&&e.attr("data-gs-no-move",!!t.noMove||null),void 0!==t.locked&&e.attr("data-gs-locked",!!t.locked||null),void 0!==t.resizeHandles&&e.attr("data-gs-resize-handles",t.resizeHandles),void 0!==t.id&&e.attr("data-gs-id",t.id)},o.prototype._readAttr=function(e,t){return e=g(e),(t=t||{}).x=e.attr("data-gs-x"),t.y=e.attr("data-gs-y"),t.width=e.attr("data-gs-width"),t.height=e.attr("data-gs-height"),t.autoPosition=v.toBool(e.attr("data-gs-auto-position")),t.maxWidth=e.attr("data-gs-max-width"),t.minWidth=e.attr("data-gs-min-width"),t.maxHeight=e.attr("data-gs-max-height"),t.minHeight=e.attr("data-gs-min-height"),t.noResize=v.toBool(e.attr("data-gs-no-resize")),t.noMove=v.toBool(e.attr("data-gs-no-move")),t.locked=v.toBool(e.attr("data-gs-locked")),t.resizeHandles=e.attr("data-gs-resize-handles"),t.id=e.attr("data-gs-id"),t},o.prototype.setAnimation=function(e){e?this.$el.addClass("grid-stack-animate"):this.$el.removeClass("grid-stack-animate")},o.prototype.addWidget=function(e,t,i,n,o,s,r,a,l,h,u){return void 0!==t&&"object"!=typeof t?this.addWidget(e,{x:t,y:i,width:n,height:o,autoPosition:s,minWidth:r,maxWidth:a,minHeight:l,maxHeight:h,id:u}):(e=g(e),t&&this.engine._prepareNode(t),this._writeAttr(e,t),this.$el.append(e),this.makeWidget(e))},o.prototype.makeWidget=function(e){return e=g(e),this._prepareElement(e,!0),this._updateContainerHeight(),this._triggerAddEvent(),this._triggerChangeEvent(!0),e.get(0)},o.prototype.willItFit=function(e,t,i,n,o){var s={x:e,y:t,width:i,height:n,autoPosition:o};return this.engine.canBePlacedWithRespectToHeight(s)},o.prototype.removeWidget=function(e,t){t=void 0===t||t;var i=(e=g(e)).data("_gridstack_node");i=i||this.engine.getNodeDataByDOMEl(e.get(0)),e.removeData("_gridstack_node"),this.engine.removeNode(i,t),this._triggerRemoveEvent(),this._triggerChangeEvent(!0)},o.prototype.removeAll=function(e){!1!==e&&this.engine.nodes.forEach(function(e){g(e.el).removeData("_gridstack_node")}),this.engine.removeAll(e),this._triggerRemoveEvent()},o.prototype.destroy=function(e){g(window).off("resize",this.onResizeHandler),this.disable(),void 0===e||e?this.$el.remove():(this.removeAll(!1),delete this.$el.get(0).gridstack),v.removeStylesheet(this._stylesId),this.engine&&(this.engine=null)},o.prototype.resizable=function(e,n){var o=this;return(e=g(e)).each(function(e,t){var i=(t=g(t)).data("_gridstack_node");i&&(i.noResize=!n,i.noResize?o.dd.resizable(t,"disable"):o.dd.resizable(t,"enable"))}),this},o.prototype.movable=function(e,n){var o=this;return(e=g(e)).each(function(e,t){var i=(t=g(t)).data("_gridstack_node");i&&(i.noMove=!n,i.noMove?(o.dd.draggable(t,"disable"),t.removeClass("ui-draggable-handle")):(o.dd.draggable(t,"enable"),t.addClass("ui-draggable-handle")))}),this},o.prototype.enableMove=function(e,t){this.movable(this.$el.children("."+this.opts.itemClass),e),t&&(this.opts.disableDrag=!e)},o.prototype.enableResize=function(e,t){this.resizable(this.$el.children("."+this.opts.itemClass),e),t&&(this.opts.disableResize=!e)},o.prototype.disable=function(){this.movable(this.$el.children("."+this.opts.itemClass),!1),this.resizable(this.$el.children("."+this.opts.itemClass),!1),this.$el.trigger("disable")},o.prototype.enable=function(){this.movable(this.$el.children("."+this.opts.itemClass),!0),this.resizable(this.$el.children("."+this.opts.itemClass),!0),this.$el.trigger("enable")},o.prototype.locked=function(e,n){return(e=g(e)).each(function(e,t){var i=(t=g(t)).data("_gridstack_node");i&&(i.locked=n||!1,t.attr("data-gs-locked",i.locked?"yes":null))}),this},o.prototype.maxHeight=function(e,n){return(e=g(e)).each(function(e,t){var i=(t=g(t)).data("_gridstack_node");i&&(isNaN(n)||(i.maxHeight=n||!1,t.attr("data-gs-max-height",n)))}),this},o.prototype.minHeight=function(e,n){return(e=g(e)).each(function(e,t){var i=(t=g(t)).data("_gridstack_node");i&&(isNaN(n)||(i.minHeight=n||!1,t.attr("data-gs-min-height",n)))}),this},o.prototype.maxWidth=function(e,n){return(e=g(e)).each(function(e,t){var i=(t=g(t)).data("_gridstack_node");i&&(isNaN(n)||(i.maxWidth=n||!1,t.attr("data-gs-max-width",n)))}),this},o.prototype.minWidth=function(e,n){return(e=g(e)).each(function(e,t){var i=(t=g(t)).data("_gridstack_node");i&&(isNaN(n)||(i.minWidth=n||!1,t.attr("data-gs-min-width",n)))}),this},o.prototype._updateElement=function(e,t){var i=(e=g(e).first()).data("_gridstack_node");if(i){var n=this;n.engine.cleanNodes(),n.engine.beginUpdate(i),t.call(this,e,i),n._updateContainerHeight(),n._triggerChangeEvent(),n.engine.endUpdate()}},o.prototype.resize=function(e,i,n){this._updateElement(e,function(e,t){i=null!=i?i:t.width,n=null!=n?n:t.height,this.engine.moveNode(t,t.x,t.y,i,n)})},o.prototype.move=function(e,i,n){this._updateElement(e,function(e,t){i=null!=i?i:t.x,n=null!=n?n:t.y,this.engine.moveNode(t,i,n,t.width,t.height)})},o.prototype.update=function(e,i,n,o,s){this._updateElement(e,function(e,t){i=null!=i?i:t.x,n=null!=n?n:t.y,o=null!=o?o:t.width,s=null!=s?s:t.height,this.engine.moveNode(t,i,n,o,s)})},o.prototype.compact=function(){if(0!==this.engine.nodes.length){this.batchUpdate(),this.engine._sortNodes();var e=this.engine.nodes;this.engine.nodes=[],e.forEach(function(e){e.noMove||e.locked||(e.autoPosition=!0),this.engine.addNode(e,!1),e._dirty=!0},this),this.commit()}},o.prototype.verticalMargin=function(e,t){if(void 0===e)return this.opts.verticalMargin;var i=v.parseHeight(e);this.opts.verticalMarginUnit===i.unit&&this.opts.maxRow===i.height||(this.opts.verticalMarginUnit=i.unit,this.opts.verticalMargin=i.height,t||this._updateStyles())},o.prototype.cellHeight=function(e,t){if(void 0===e){if(this.opts.cellHeight&&"auto"!==this.opts.cellHeight)return this.opts.cellHeight;var i=this.$el.children("."+this.opts.itemClass).first(),n=i.attr("data-gs-height"),o=this.opts.verticalMargin;return Math.round((i.outerHeight()-(n-1)*o)/n)}var s=v.parseHeight(e);this.opts.cellHeightUnit===s.unit&&this.opts.cellHeight===s.height||(this.opts.cellHeightUnit=s.unit,this.opts.cellHeight=s.height,t||this._updateStyles())},o.prototype.cellWidth=function(){return Math.round(this.$el.outerWidth()/this.opts.column)},o.prototype.getCellFromPixel=function(e,t){var i=void 0!==t&&t?this.$el.offset():this.$el.position(),n=e.left-i.left,o=e.top-i.top,s=Math.floor(this.$el.width()/this.opts.column),r=Math.floor(this.$el.height()/parseInt(this.$el.attr("data-gs-current-row")));return{x:Math.floor(n/s),y:Math.floor(o/r)}},o.prototype.batchUpdate=function(){this.engine.batchUpdate()},o.prototype.commit=function(){this.engine.commit(),this._triggerRemoveEvent(),this._triggerAddEvent(),this._triggerChangeEvent()},o.prototype.isAreaEmpty=function(e,t,i,n){return this.engine.isAreaEmpty(e,t,i,n)},o.prototype.setStatic=function(e){this.opts.staticGrid=!0===e,this.enableMove(!e),this.enableResize(!e),this._setStaticClass()},o.prototype._setStaticClass=function(){var e="grid-stack-static";!0===this.opts.staticGrid?this.$el.addClass(e):this.$el.removeClass(e)},b.prototype._layoutsNodesChange=function(e){this._layouts&&!this._ignoreLayoutsNodeChange&&this._layouts.forEach(function(n,o){n&&o!==this.column&&(o",options:{classes:{},disabled:false,create:null},_createWidget:function(e,t){t=b(t||this.defaultElement||this)[0];this.element=b(t);this.uuid=i++;this.eventNamespace="."+this.widgetName+this.uuid;this.bindings=b();this.hoverable=b();this.focusable=b();this.classesElementLookup={};if(t!==this){b.data(t,this.widgetFullName,this);this._on(true,this.element,{remove:function(e){if(e.target===t){this.destroy()}}});this.document=b(t.style?t.ownerDocument:t.document||t);this.window=b(this.document[0].defaultView||this.document[0].parentWindow)}this.options=b.widget.extend({},this.options,this._getCreateOptions(),e);this._create();if(this.options.disabled){this._setOptionDisabled(this.options.disabled)}this._trigger("create",null,this._getCreateEventData());this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:b.noop,_create:b.noop,_init:b.noop,destroy:function(){var i=this;this._destroy();b.each(this.classesElementLookup,function(e,t){i._removeClass(t,e)});this.element.off(this.eventNamespace).removeData(this.widgetFullName);this.widget().off(this.eventNamespace).removeAttr("aria-disabled");this.bindings.off(this.eventNamespace)},_destroy:b.noop,widget:function(){return this.element},option:function(e,t){var i=e;var n;var o;var s;if(arguments.length===0){return b.widget.extend({},this.options)}if(typeof e==="string"){i={};n=e.split(".");e=n.shift();if(n.length){o=i[e]=b.widget.extend({},this.options[e]);for(s=0;s=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}}),u=b.ui.plugin={add:function(e,t,i){var n,o=b.ui[e].prototype;for(n in i){o.plugins[n]=o.plugins[n]||[];o.plugins[n].push([t,i[n]])}},call:function(e,t,i,n){var o,s=e.plugins[t];if(!s){return}if(!n&&(!e.element[0].parentNode||e.element[0].parentNode.nodeType===11)){return}for(o=0;o0){return false}this.handle=this._getHandle(e);if(!this.handle){return false}this._blurActiveElement(e);this._blockFrames(t.iframeFix===true?"iframe":t.iframeFix);return true},_blockFrames:function(e){this.iframeBlocks=this.document.find(e).map(function(){var e=b(this);return b("
").css("position","absolute").appendTo(e.parent()).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()).offset(e.offset())[0]})},_unblockFrames:function(){if(this.iframeBlocks){this.iframeBlocks.remove();delete this.iframeBlocks}},_blurActiveElement:function(e){var t=b.ui.safeActiveElement(this.document[0]),i=b(e.target);if(i.closest(t).length){return}b.ui.safeBlur(t)},_mouseStart:function(e){var t=this.options;this.helper=this._createHelper(e);this._addClass(this.helper,"ui-draggable-dragging");this._cacheHelperProportions();if(b.ui.ddmanager){b.ui.ddmanager.current=this}this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent(true);this.offsetParent=this.helper.offsetParent();this.hasFixedAncestor=this.helper.parents().filter(function(){return b(this).css("position")==="fixed"}).length>0;this.positionAbs=this.element.offset();this._refreshOffsets(e);this.originalPosition=this.position=this._generatePosition(e,false);this.originalPageX=e.pageX;this.originalPageY=e.pageY;t.cursorAt&&this._adjustOffsetFromHelper(t.cursorAt);this._setContainment();if(this._trigger("start",e)===false){this._clear();return false}this._cacheHelperProportions();if(b.ui.ddmanager&&!t.dropBehaviour){b.ui.ddmanager.prepareOffsets(this,e)}this._mouseDrag(e,true);if(b.ui.ddmanager){b.ui.ddmanager.dragStart(this,e)}return true},_refreshOffsets:function(e){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:false,parent:this._getParentOffset(),relative:this._getRelativeOffset()};this.offset.click={left:e.pageX-this.offset.left,top:e.pageY-this.offset.top}},_mouseDrag:function(e,t){if(this.hasFixedAncestor){this.offset.parent=this._getParentOffset()}this.position=this._generatePosition(e,true);this.positionAbs=this._convertPositionTo("absolute");if(!t){var i=this._uiHash();if(this._trigger("drag",e,i)===false){this._mouseUp(new b.Event("mouseup",e));return false}this.position=i.position}this.helper[0].style.left=this.position.left+"px";this.helper[0].style.top=this.position.top+"px";if(b.ui.ddmanager){b.ui.ddmanager.drag(this,e)}return false},_mouseStop:function(e){var t=this,i=false;if(b.ui.ddmanager&&!this.options.dropBehaviour){i=b.ui.ddmanager.drop(this,e)}if(this.dropped){i=this.dropped;this.dropped=false}if(this.options.revert==="invalid"&&!i||this.options.revert==="valid"&&i||this.options.revert===true||b.isFunction(this.options.revert)&&this.options.revert.call(this.element,i)){b(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){if(t._trigger("stop",e)!==false){t._clear()}})}else{if(this._trigger("stop",e)!==false){this._clear()}}return false},_mouseUp:function(e){this._unblockFrames();if(b.ui.ddmanager){b.ui.ddmanager.dragStop(this,e)}if(this.handleElement.is(e.target)){this.element.trigger("focus")}return b.ui.mouse.prototype._mouseUp.call(this,e)},cancel:function(){if(this.helper.is(".ui-draggable-dragging")){this._mouseUp(new b.Event("mouseup",{target:this.element[0]}))}else{this._clear()}return this},_getHandle:function(e){return this.options.handle?!!b(e.target).closest(this.element.find(this.options.handle)).length:true},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element;this._addClass(this.handleElement,"ui-draggable-handle")},_removeHandleClassName:function(){this._removeClass(this.handleElement,"ui-draggable-handle")},_createHelper:function(e){var t=this.options,i=b.isFunction(t.helper),n=i?b(t.helper.apply(this.element[0],[e])):t.helper==="clone"?this.element.clone().removeAttr("id"):this.element;if(!n.parents("body").length){n.appendTo(t.appendTo==="parent"?this.element[0].parentNode:t.appendTo)}if(i&&n[0]===this.element[0]){this._setPositionRelative()}if(n[0]!==this.element[0]&&!/(fixed|absolute)/.test(n.css("position"))){n.css("position","absolute")}return n},_setPositionRelative:function(){if(!/^(?:r|a|f)/.test(this.element.css("position"))){this.element[0].style.position="relative"}},_adjustOffsetFromHelper:function(e){if(typeof e==="string"){e=e.split(" ")}if(b.isArray(e)){e={left:+e[0],top:+e[1]||0}}if("left"in e){this.offset.click.left=e.left+this.margins.left}if("right"in e){this.offset.click.left=this.helperProportions.width-e.right+this.margins.left}if("top"in e){this.offset.click.top=e.top+this.margins.top}if("bottom"in e){this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top}},_isRootNode:function(e){return/(html|body)/i.test(e.tagName)||e===this.document[0]},_getParentOffset:function(){var e=this.offsetParent.offset(),t=this.document[0];if(this.cssPosition==="absolute"&&this.scrollParent[0]!==t&&b.contains(this.scrollParent[0],this.offsetParent[0])){e.left+=this.scrollParent.scrollLeft();e.top+=this.scrollParent.scrollTop()}if(this._isRootNode(this.offsetParent[0])){e={top:0,left:0}}return{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition!=="relative"){return{top:0,left:0}}var e=this.element.position(),t=this._isRootNode(this.scrollParent[0]);return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+(!t?this.scrollParent.scrollTop():0),left:e.left-(parseInt(this.helper.css("left"),10)||0)+(!t?this.scrollParent.scrollLeft():0)}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,t,i,n=this.options,o=this.document[0];this.relativeContainer=null;if(!n.containment){this.containment=null;return}if(n.containment==="window"){this.containment=[b(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,b(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,b(window).scrollLeft()+b(window).width()-this.helperProportions.width-this.margins.left,b(window).scrollTop()+(b(window).height()||o.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];return}if(n.containment==="document"){this.containment=[0,0,b(o).width()-this.helperProportions.width-this.margins.left,(b(o).height()||o.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];return}if(n.containment.constructor===Array){this.containment=n.containment;return}if(n.containment==="parent"){n.containment=this.helper[0].parentNode}t=b(n.containment);i=t[0];if(!i){return}e=/(scroll|auto)/.test(t.css("overflow"));this.containment=[(parseInt(t.css("borderLeftWidth"),10)||0)+(parseInt(t.css("paddingLeft"),10)||0),(parseInt(t.css("borderTopWidth"),10)||0)+(parseInt(t.css("paddingTop"),10)||0),(e?Math.max(i.scrollWidth,i.offsetWidth):i.offsetWidth)-(parseInt(t.css("borderRightWidth"),10)||0)-(parseInt(t.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(e?Math.max(i.scrollHeight,i.offsetHeight):i.offsetHeight)-(parseInt(t.css("borderBottomWidth"),10)||0)-(parseInt(t.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relativeContainer=t},_convertPositionTo:function(e,t){if(!t){t=this.position}var i=e==="absolute"?1:-1,n=this._isRootNode(this.scrollParent[0]);return{top:t.top+this.offset.relative.top*i+this.offset.parent.top*i-(this.cssPosition==="fixed"?-this.offset.scroll.top:n?0:this.offset.scroll.top)*i,left:t.left+this.offset.relative.left*i+this.offset.parent.left*i-(this.cssPosition==="fixed"?-this.offset.scroll.left:n?0:this.offset.scroll.left)*i}},_generatePosition:function(e,t){var i,n,o,s,r=this.options,a=this._isRootNode(this.scrollParent[0]),l=e.pageX,h=e.pageY;if(!a||!this.offset.scroll){this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}}if(t){if(this.containment){if(this.relativeContainer){n=this.relativeContainer.offset();i=[this.containment[0]+n.left,this.containment[1]+n.top,this.containment[2]+n.left,this.containment[3]+n.top]}else{i=this.containment}if(e.pageX-this.offset.click.lefti[2]){l=i[2]+this.offset.click.left}if(e.pageY-this.offset.click.top>i[3]){h=i[3]+this.offset.click.top}}if(r.grid){o=r.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/r.grid[1])*r.grid[1]:this.originalPageY;h=i?o-this.offset.click.top>=i[1]||o-this.offset.click.top>i[3]?o:o-this.offset.click.top>=i[1]?o-r.grid[1]:o+r.grid[1]:o;s=r.grid[0]?this.originalPageX+Math.round((l-this.originalPageX)/r.grid[0])*r.grid[0]:this.originalPageX;l=i?s-this.offset.click.left>=i[0]||s-this.offset.click.left>i[2]?s:s-this.offset.click.left>=i[0]?s-r.grid[0]:s+r.grid[0]:s}if(r.axis==="y"){l=this.originalPageX}if(r.axis==="x"){h=this.originalPageY}}return{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(this.cssPosition==="fixed"?-this.offset.scroll.top:a?0:this.offset.scroll.top),left:l-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(this.cssPosition==="fixed"?-this.offset.scroll.left:a?0:this.offset.scroll.left)}},_clear:function(){this._removeClass(this.helper,"ui-draggable-dragging");if(this.helper[0]!==this.element[0]&&!this.cancelHelperRemoval){this.helper.remove()}this.helper=null;this.cancelHelperRemoval=false;if(this.destroyOnClear){this.destroy()}},_trigger:function(e,t,i){i=i||this._uiHash();b.ui.plugin.call(this,e,[t,i,this],true);if(/^(drag|start|stop)/.test(e)){this.positionAbs=this._convertPositionTo("absolute");i.offset=this.positionAbs}return b.Widget.prototype._trigger.call(this,e,t,i)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),b.ui.plugin.add("draggable","connectToSortable",{start:function(t,e,i){var n=b.extend({},e,{item:i.element});i.sortables=[];b(i.options.connectToSortable).each(function(){var e=b(this).sortable("instance");if(e&&!e.options.disabled){i.sortables.push(e);e.refreshPositions();e._trigger("activate",t,n)}})},stop:function(t,e,i){var n=b.extend({},e,{item:i.element});i.cancelHelperRemoval=false;b.each(i.sortables,function(){var e=this;if(e.isOver){e.isOver=0;i.cancelHelperRemoval=true;e.cancelHelperRemoval=false;e._storedCSS={position:e.placeholder.css("position"),top:e.placeholder.css("top"),left:e.placeholder.css("left")};e._mouseStop(t);e.options.helper=e.options._helper}else{e.cancelHelperRemoval=true;e._trigger("deactivate",t,n)}})},drag:function(i,n,o){b.each(o.sortables,function(){var e=false,t=this;t.positionAbs=o.positionAbs;t.helperProportions=o.helperProportions;t.offset.click=o.offset.click;if(t._intersectsWith(t.containerCache)){e=true;b.each(o.sortables,function(){this.positionAbs=o.positionAbs;this.helperProportions=o.helperProportions;this.offset.click=o.offset.click;if(this!==t&&this._intersectsWith(this.containerCache)&&b.contains(t.element[0],this.element[0])){e=false}return e})}if(e){if(!t.isOver){t.isOver=1;o._parent=n.helper.parent();t.currentItem=n.helper.appendTo(t.element).data("ui-sortable-item",true);t.options._helper=t.options.helper;t.options.helper=function(){return n.helper[0]};i.target=t.currentItem[0];t._mouseCapture(i,true);t._mouseStart(i,true,true);t.offset.click.top=o.offset.click.top;t.offset.click.left=o.offset.click.left;t.offset.parent.left-=o.offset.parent.left-t.offset.parent.left;t.offset.parent.top-=o.offset.parent.top-t.offset.parent.top;o._trigger("toSortable",i);o.dropped=t.element;b.each(o.sortables,function(){this.refreshPositions()});o.currentItem=o.element;t.fromOutside=o}if(t.currentItem){t._mouseDrag(i);n.position=t.position}}else{if(t.isOver){t.isOver=0;t.cancelHelperRemoval=true;t.options._revert=t.options.revert;t.options.revert=false;t._trigger("out",i,t._uiHash(t));t._mouseStop(i,true);t.options.revert=t.options._revert;t.options.helper=t.options._helper;if(t.placeholder){t.placeholder.remove()}n.helper.appendTo(o._parent);o._refreshOffsets(i);n.position=o._generatePosition(i,true);o._trigger("fromSortable",i);o.dropped=false;b.each(o.sortables,function(){this.refreshPositions()})}}})}}),b.ui.plugin.add("draggable","cursor",{start:function(e,t,i){var n=b("body"),o=i.options;if(n.css("cursor")){o._cursor=n.css("cursor")}n.css("cursor",o.cursor)},stop:function(e,t,i){var n=i.options;if(n._cursor){b("body").css("cursor",n._cursor)}}}),b.ui.plugin.add("draggable","opacity",{start:function(e,t,i){var n=b(t.helper),o=i.options;if(n.css("opacity")){o._opacity=n.css("opacity")}n.css("opacity",o.opacity)},stop:function(e,t,i){var n=i.options;if(n._opacity){b(t.helper).css("opacity",n._opacity)}}}),b.ui.plugin.add("draggable","scroll",{start:function(e,t,i){if(!i.scrollParentNotHidden){i.scrollParentNotHidden=i.helper.scrollParent(false)}if(i.scrollParentNotHidden[0]!==i.document[0]&&i.scrollParentNotHidden[0].tagName!=="HTML"){i.overflowOffset=i.scrollParentNotHidden.offset()}},drag:function(e,t,i){var n=i.options,o=false,s=i.scrollParentNotHidden[0],r=i.document[0];if(s!==r&&s.tagName!=="HTML"){if(!n.axis||n.axis!=="x"){if(i.overflowOffset.top+s.offsetHeight-e.pageY=0;d--){a=i.snapElements[d].left-i.margins.left;l=a+i.snapElements[d].width;h=i.snapElements[d].top-i.margins.top;u=h+i.snapElements[d].height;if(ml+p||yu+p||!b.contains(i.snapElements[d].item.ownerDocument,i.snapElements[d].item)){if(i.snapElements[d].snapping){i.options.snap.release&&i.options.snap.release.call(i.element,e,b.extend(i._uiHash(),{snapItem:i.snapElements[d].item}))}i.snapElements[d].snapping=false;continue}if(f.snapMode!=="inner"){n=Math.abs(h-y)<=p;o=Math.abs(u-v)<=p;s=Math.abs(a-m)<=p;r=Math.abs(l-g)<=p;if(n){t.position.top=i._convertPositionTo("relative",{top:h-i.helperProportions.height,left:0}).top}if(o){t.position.top=i._convertPositionTo("relative",{top:u,left:0}).top}if(s){t.position.left=i._convertPositionTo("relative",{top:0,left:a-i.helperProportions.width}).left}if(r){t.position.left=i._convertPositionTo("relative",{top:0,left:l}).left}}c=n||o||s||r;if(f.snapMode!=="outer"){n=Math.abs(h-v)<=p;o=Math.abs(u-y)<=p;s=Math.abs(a-g)<=p;r=Math.abs(l-m)<=p;if(n){t.position.top=i._convertPositionTo("relative",{top:h,left:0}).top}if(o){t.position.top=i._convertPositionTo("relative",{top:u-i.helperProportions.height,left:0}).top}if(s){t.position.left=i._convertPositionTo("relative",{top:0,left:a}).left}if(r){t.position.left=i._convertPositionTo("relative",{top:0,left:l-i.helperProportions.width}).left}}if(!i.snapElements[d].snapping&&(n||o||s||r||c)){i.options.snap.snap&&i.options.snap.snap.call(i.element,e,b.extend(i._uiHash(),{snapItem:i.snapElements[d].item}))}i.snapElements[d].snapping=n||o||s||r||c}}}),b.ui.plugin.add("draggable","stack",{start:function(e,t,i){var n,o=i.options,s=b.makeArray(b(o.stack)).sort(function(e,t){return(parseInt(b(e).css("zIndex"),10)||0)-(parseInt(b(t).css("zIndex"),10)||0)});if(!s.length){return}n=parseInt(b(s[0]).css("zIndex"),10)||0;b(s).each(function(e){b(this).css("zIndex",n+e)});this.css("zIndex",n+s.length)}}),b.ui.plugin.add("draggable","zIndex",{start:function(e,t,i){var n=b(t.helper),o=i.options;if(n.css("zIndex")){o._zIndex=n.css("zIndex")}n.css("zIndex",o.zIndex)},stop:function(e,t,i){var n=i.options;if(n._zIndex){b(t.helper).css("zIndex",n._zIndex)}}});var f=b.ui.draggable;b.widget("ui.droppable",{version:"1.12.1",widgetEventPrefix:"drop",options:{accept:"*",addClasses:true,greedy:false,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var e,t=this.options,i=t.accept;this.isover=false;this.isout=true;this.accept=b.isFunction(i)?i:function(e){return e.is(i)};this.proportions=function(){if(arguments.length){e=arguments[0]}else{return e?e:e={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight}}};this._addToManager(t.scope);t.addClasses&&this._addClass("ui-droppable")},_addToManager:function(e){b.ui.ddmanager.droppables[e]=b.ui.ddmanager.droppables[e]||[];b.ui.ddmanager.droppables[e].push(this)},_splice:function(e){var t=0;for(;t=t&&e=h&&s<=d||a>=h&&a<=d||sd)&&(o>=l&&o<=u||r>=l&&r<=u||ou);default:return false}}}();if(b.ui.ddmanager={current:null,droppables:{default:[]},prepareOffsets:function(e,t){var i,n,o=b.ui.ddmanager.droppables[e.options.scope]||[],s=t?t.type:null,r=(e.currentItem||e.element).find(":data(ui-droppable)").addBack();e:for(i=0;i0){return true}e[i]=1;n=e[i]>0;e[i]=0;return n},_create:function(){var e,t=this.options,i=this;this._addClass("ui-resizable");b.extend(this,{_aspectRatio:!!t.aspectRatio,aspectRatio:t.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:t.helper||t.ghost||t.animate?t.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)){this.element.wrap(b("
").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance"));this.elementIsWrapper=true;e={marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom"),marginLeft:this.originalElement.css("marginLeft")};this.element.css(e);this.originalElement.css("margin",0);this.originalResizeStyle=this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css(e);this._proportionallyResize()}this._setupHandles();if(t.autoHide){b(this.element).on("mouseenter",function(){if(t.disabled){return}i._removeClass("ui-resizable-autohide");i._handles.show()}).on("mouseleave",function(){if(t.disabled){return}if(!i.resizing){i._addClass("ui-resizable-autohide");i._handles.hide()}})}this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,t=function(e){b(e).removeData("resizable").removeData("ui-resizable").off(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){t(this.element);e=this.element;this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e);e.remove()}this.originalElement.css("resize",this.originalResizeStyle);t(this.originalElement);return this},_setOption:function(e,t){this._super(e,t);switch(e){case"handles":this._removeHandles();this._setupHandles();break;default:break}},_setupHandles:function(){var e=this.options,t,i,n,o,s,r=this;this.handles=e.handles||(!b(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"});this._handles=b();if(this.handles.constructor===String){if(this.handles==="all"){this.handles="n,e,s,w,se,sw,ne,nw"}n=this.handles.split(",");this.handles={};for(i=0;i");this._addClass(s,"ui-resizable-handle "+o);s.css({zIndex:e.zIndex});this.handles[t]=".ui-resizable-"+t;this.element.append(s)}}this._renderAxis=function(e){var t,i,n,o;e=e||this.element;for(t in this.handles){if(this.handles[t].constructor===String){this.handles[t]=this.element.children(this.handles[t]).first().show()}else if(this.handles[t].jquery||this.handles[t].nodeType){this.handles[t]=b(this.handles[t]);this._on(this.handles[t],{mousedown:r._mouseDown})}if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)){i=b(this.handles[t],this.element);o=/sw|ne|nw|se|n|s/.test(t)?i.outerHeight():i.outerWidth();n=["padding",/ne|nw|n/.test(t)?"Top":/se|sw|s/.test(t)?"Bottom":/^e$/.test(t)?"Right":"Left"].join("");e.css(n,o);this._proportionallyResize()}this._handles=this._handles.add(this.handles[t])}};this._renderAxis(this.element);this._handles=this._handles.add(this.element.find(".ui-resizable-handle"));this._handles.disableSelection();this._handles.on("mouseover",function(){if(!r.resizing){if(this.className){s=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)}r.axis=s&&s[1]?s[1]:"se"}});if(e.autoHide){this._handles.hide();this._addClass("ui-resizable-autohide")}},_removeHandles:function(){this._handles.remove()},_mouseCapture:function(e){var t,i,n=false;for(t in this.handles){i=b(this.handles[t])[0];if(i===e.target||b.contains(i,e.target)){n=true}}return!this.options.disabled&&n},_mouseStart:function(e){var t,i,n,o=this.options,s=this.element;this.resizing=true;this._renderProxy();t=this._num(this.helper.css("left"));i=this._num(this.helper.css("top"));if(o.containment){t+=b(o.containment).scrollLeft()||0;i+=b(o.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:t,top:i};this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:s.width(),height:s.height()};this.originalSize=this._helper?{width:s.outerWidth(),height:s.outerHeight()}:{width:s.width(),height:s.height()};this.sizeDiff={width:s.outerWidth()-s.width(),height:s.outerHeight()-s.height()};this.originalPosition={left:t,top:i};this.originalMousePosition={left:e.pageX,top:e.pageY};this.aspectRatio=typeof o.aspectRatio==="number"?o.aspectRatio:this.originalSize.width/this.originalSize.height||1;n=b(".ui-resizable-"+this.axis).css("cursor");b("body").css("cursor",n==="auto"?this.axis+"-resize":n);this._addClass("ui-resizable-resizing");this._propagate("start",e);return true},_mouseDrag:function(e){var t,i,n=this.originalMousePosition,o=this.axis,s=e.pageX-n.left||0,r=e.pageY-n.top||0,a=this._change[o];this._updatePrevProperties();if(!a){return false}t=a.apply(this,[e,s,r]);this._updateVirtualBoundaries(e.shiftKey);if(this._aspectRatio||e.shiftKey){t=this._updateRatio(t,e)}t=this._respectSize(t,e);this._updateCache(t);this._propagate("resize",e);i=this._applyChanges();if(!this._helper&&this._proportionallyResizeElements.length){this._proportionallyResize()}if(!b.isEmptyObject(i)){this._updatePrevProperties();this._trigger("resize",e,this.ui());this._applyChanges()}return false},_mouseStop:function(e){this.resizing=false;var t,i,n,o,s,r,a,l=this.options,h=this;if(this._helper){t=this._proportionallyResizeElements;i=t.length&&/textarea/i.test(t[0].nodeName);n=i&&this._hasScroll(t[0],"left")?0:h.sizeDiff.height;o=i?0:h.sizeDiff.width;s={width:h.helper.width()-o,height:h.helper.height()-n};r=parseFloat(h.element.css("left"))+(h.position.left-h.originalPosition.left)||null;a=parseFloat(h.element.css("top"))+(h.position.top-h.originalPosition.top)||null;if(!l.animate){this.element.css(b.extend(s,{top:a,left:r}))}h.helper.height(h.size.height);h.helper.width(h.size.width);if(this._helper&&!l.animate){this._proportionallyResize()}}b("body").css("cursor","auto");this._removeClass("ui-resizable-resizing");this._propagate("stop",e);if(this._helper){this.helper.remove()}return false},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left};this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var e={};if(this.position.top!==this.prevPosition.top){e.top=this.position.top+"px"}if(this.position.left!==this.prevPosition.left){e.left=this.position.left+"px"}if(this.size.width!==this.prevSize.width){e.width=this.size.width+"px"}if(this.size.height!==this.prevSize.height){e.height=this.size.height+"px"}this.helper.css(e);return e},_updateVirtualBoundaries:function(e){var t,i,n,o,s,r=this.options;s={minWidth:this._isNumber(r.minWidth)?r.minWidth:0,maxWidth:this._isNumber(r.maxWidth)?r.maxWidth:Infinity,minHeight:this._isNumber(r.minHeight)?r.minHeight:0,maxHeight:this._isNumber(r.maxHeight)?r.maxHeight:Infinity};if(this._aspectRatio||e){t=s.minHeight*this.aspectRatio;n=s.minWidth/this.aspectRatio;i=s.maxHeight*this.aspectRatio;o=s.maxWidth/this.aspectRatio;if(t>s.minWidth){s.minWidth=t}if(n>s.minHeight){s.minHeight=n}if(ie.width,r=this._isNumber(e.height)&&t.minHeight&&t.minHeight>e.height,a=this.originalPosition.left+this.originalSize.width,l=this.originalPosition.top+this.originalSize.height,h=/sw|nw|w/.test(i),u=/nw|ne|n/.test(i);if(s){e.width=t.minWidth}if(r){e.height=t.minHeight}if(n){e.width=t.maxWidth}if(o){e.height=t.maxHeight}if(s&&h){e.left=a-t.minWidth}if(n&&h){e.left=a-t.maxWidth}if(r&&u){e.top=l-t.minHeight}if(o&&u){e.top=l-t.maxHeight}if(!e.width&&!e.height&&!e.left&&e.top){e.top=null}else if(!e.width&&!e.height&&!e.top&&e.left){e.left=null}return e},_getPaddingPlusBorderDimensions:function(e){var t=0,i=[],n=[e.css("borderTopWidth"),e.css("borderRightWidth"),e.css("borderBottomWidth"),e.css("borderLeftWidth")],o=[e.css("paddingTop"),e.css("paddingRight"),e.css("paddingBottom"),e.css("paddingLeft")];for(;t<4;t++){i[t]=parseFloat(n[t])||0;i[t]+=parseFloat(o[t])||0}return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(!this._proportionallyResizeElements.length){return}var e,t=0,i=this.helper||this.element;for(;t
");this._addClass(this.helper,this._helper);this.helper.css({width:this.element.outerWidth(),height:this.element.outerHeight(),position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++t.zIndex});this.helper.appendTo("body").disableSelection()}else{this.helper=this.element}},_change:{e:function(e,t){return{width:this.originalSize.width+t}},w:function(e,t){var i=this.originalSize,n=this.originalPosition;return{left:n.left+t,width:i.width-t}},n:function(e,t,i){var n=this.originalSize,o=this.originalPosition;return{top:o.top+i,height:n.height-i}},s:function(e,t,i){return{height:this.originalSize.height+i}},se:function(e,t,i){return b.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,t,i]))},sw:function(e,t,i){return b.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,t,i]))},ne:function(e,t,i){return b.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,t,i]))},nw:function(e,t,i){return b.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,t,i]))}},_propagate:function(e,t){b.ui.plugin.call(this,e,[t,this.ui()]);e!=="resize"&&this._trigger(e,t,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),b.ui.plugin.add("resizable","animate",{stop:function(t){var i=b(this).resizable("instance"),e=i.options,n=i._proportionallyResizeElements,o=n.length&&/textarea/i.test(n[0].nodeName),s=o&&i._hasScroll(n[0],"left")?0:i.sizeDiff.height,r=o?0:i.sizeDiff.width,a={width:i.size.width-r,height:i.size.height-s},l=parseFloat(i.element.css("left"))+(i.position.left-i.originalPosition.left)||null,h=parseFloat(i.element.css("top"))+(i.position.top-i.originalPosition.top)||null;i.element.animate(b.extend(a,h&&l?{top:h,left:l}:{}),{duration:e.animateDuration,easing:e.animateEasing,step:function(){var e={width:parseFloat(i.element.css("width")),height:parseFloat(i.element.css("height")),top:parseFloat(i.element.css("top")),left:parseFloat(i.element.css("left"))};if(n&&n.length){b(n[0]).css({width:e.width,height:e.height})}i._updateCache(e);i._propagate("resize",t)}})}}),b.ui.plugin.add("resizable","containment",{start:function(){var i,n,e,t,o,s,r,a=b(this).resizable("instance"),l=a.options,h=a.element,u=l.containment,d=u instanceof b?u.get(0):/parent/.test(u)?h.parent().get(0):u;if(!d){return}a.containerElement=b(d);if(/document/.test(u)||u===document){a.containerOffset={left:0,top:0};a.containerPosition={left:0,top:0};a.parentData={element:b(document),left:0,top:0,width:b(document).width(),height:b(document).height()||document.body.parentNode.scrollHeight}}else{i=b(d);n=[];b(["Top","Right","Left","Bottom"]).each(function(e,t){n[e]=a._num(i.css("padding"+t))});a.containerOffset=i.offset();a.containerPosition=i.position();a.containerSize={height:i.innerHeight()-n[3],width:i.innerWidth()-n[1]};e=a.containerOffset;t=a.containerSize.height;o=a.containerSize.width;s=a._hasScroll(d,"left")?d.scrollWidth:o;r=a._hasScroll(d)?d.scrollHeight:t;a.parentData={element:d,left:e.left,top:e.top,width:s,height:r}}},resize:function(e){var t,i,n,o,s=b(this).resizable("instance"),r=s.options,a=s.containerOffset,l=s.position,h=s._aspectRatio||e.shiftKey,u={top:0,left:0},d=s.containerElement,c=true;if(d[0]!==document&&/static/.test(d.css("position"))){u=a}if(l.left<(s._helper?a.left:0)){s.size.width=s.size.width+(s._helper?s.position.left-a.left:s.position.left-u.left);if(h){s.size.height=s.size.width/s.aspectRatio;c=false}s.position.left=r.helper?a.left:0}if(l.top<(s._helper?a.top:0)){s.size.height=s.size.height+(s._helper?s.position.top-a.top:s.position.top);if(h){s.size.width=s.size.height*s.aspectRatio;c=false}s.position.top=s._helper?a.top:0}n=s.containerElement.get(0)===s.element.parent().get(0);o=/relative|absolute/.test(s.containerElement.css("position"));if(n&&o){s.offset.left=s.parentData.left+s.position.left;s.offset.top=s.parentData.top+s.position.top}else{s.offset.left=s.element.offset().left;s.offset.top=s.element.offset().top}t=Math.abs(s.sizeDiff.width+(s._helper?s.offset.left-u.left:s.offset.left-a.left));i=Math.abs(s.sizeDiff.height+(s._helper?s.offset.top-u.top:s.offset.top-a.top));if(t+s.size.width>=s.parentData.width){s.size.width=s.parentData.width-t;if(h){s.size.height=s.size.width/s.aspectRatio;c=false}}if(i+s.size.height>=s.parentData.height){s.size.height=s.parentData.height-i;if(h){s.size.width=s.size.height*s.aspectRatio;c=false}}if(!c){s.position.left=s.prevPosition.left;s.position.top=s.prevPosition.top;s.size.width=s.prevSize.width;s.size.height=s.prevSize.height}},stop:function(){var e=b(this).resizable("instance"),t=e.options,i=e.containerOffset,n=e.containerPosition,o=e.containerElement,s=b(e.helper),r=s.offset(),a=s.outerWidth()-e.sizeDiff.width,l=s.outerHeight()-e.sizeDiff.height;if(e._helper&&!t.animate&&/relative/.test(o.css("position"))){b(this).css({left:r.left-n.left-i.left,width:a,height:l})}if(e._helper&&!t.animate&&/static/.test(o.css("position"))){b(this).css({left:r.left-n.left-i.left,width:a,height:l})}}}),b.ui.plugin.add("resizable","alsoResize",{start:function(){var e=b(this).resizable("instance"),t=e.options;b(t.alsoResize).each(function(){var e=b(this);e.data("ui-resizable-alsoresize",{width:parseFloat(e.width()),height:parseFloat(e.height()),left:parseFloat(e.css("left")),top:parseFloat(e.css("top"))})})},resize:function(e,i){var t=b(this).resizable("instance"),n=t.options,o=t.originalSize,s=t.originalPosition,r={height:t.size.height-o.height||0,width:t.size.width-o.width||0,top:t.position.top-s.top||0,left:t.position.left-s.left||0};b(n.alsoResize).each(function(){var e=b(this),n=b(this).data("ui-resizable-alsoresize"),o={},t=e.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];b.each(t,function(e,t){var i=(n[t]||0)+(r[t]||0);if(i&&i>=0){o[t]=i||null}});e.css(o)})},stop:function(){b(this).removeData("ui-resizable-alsoresize")}}),b.ui.plugin.add("resizable","ghost",{start:function(){var e=b(this).resizable("instance"),t=e.size;e.ghost=e.originalElement.clone();e.ghost.css({opacity:.25,display:"block",position:"relative",height:t.height,width:t.width,margin:0,left:0,top:0});e._addClass(e.ghost,"ui-resizable-ghost");if(b.uiBackCompat!==false&&typeof e.options.ghost==="string"){e.ghost.addClass(this.options.ghost)}e.ghost.appendTo(e.helper)},resize:function(){var e=b(this).resizable("instance");if(e.ghost){e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})}},stop:function(){var e=b(this).resizable("instance");if(e.ghost&&e.helper){e.helper.get(0).removeChild(e.ghost.get(0))}}}),b.ui.plugin.add("resizable","grid",{resize:function(){var e,t=b(this).resizable("instance"),i=t.options,n=t.size,o=t.originalSize,s=t.originalPosition,r=t.axis,a=typeof i.grid==="number"?[i.grid,i.grid]:i.grid,l=a[0]||1,h=a[1]||1,u=Math.round((n.width-o.width)/l)*l,d=Math.round((n.height-o.height)/h)*h,c=o.width+u,f=o.height+d,p=i.maxWidth&&i.maxWidthc,v=i.minHeight&&i.minHeight>f;i.grid=a;if(m){c+=l}if(v){f+=h}if(p){c-=l}if(g){f-=h}if(/^(se|s|e)$/.test(r)){t.size.width=c;t.size.height=f}else if(/^(ne)$/.test(r)){t.size.width=c;t.size.height=f;t.position.top=s.top-d}else if(/^(sw)$/.test(r)){t.size.width=c;t.size.height=f;t.position.left=s.left-u}else{if(f-h<=0||c-l<=0){e=t._getPaddingPlusBorderDimensions(this)}if(f-h>0){t.size.height=f;t.position.top=s.top-d}else{f=h-e.height;t.size.height=f;t.position.top=s.top+o.height-f}if(c-l>0){t.size.width=c;t.position.left=s.left-u}else{c=l-e.width;t.size.width=c;t.position.left=s.left+o.width-c}}}});var m=b.ui.resizable}(jQuery),/** gridstack.js 1.1.0 - JQuery UI Drag&Drop plugin @preserve */ +function(){function e(e){t.DragDropPlugin.call(this,e)}var s,t,i;s=jQuery,t=GridStack,i=window,t.DragDropPlugin.registerPlugin(e),((e.prototype=Object.create(t.DragDropPlugin.prototype)).constructor=e).prototype.resizable=function(e,t){if(e=s(e),"disable"===t||"enable"===t)e.resizable(t);else if("option"===t){var i=arguments[2],n=arguments[3];e.resizable(t,i,n)}else{var o=e.data("gs-resize-handles")?e.data("gs-resize-handles"):this.grid.opts.resizable.handles;e.resizable(s.extend({},this.grid.opts.resizable,{handles:o},{start:t.start||function(){},stop:t.stop||function(){},resize:t.resize||function(){}}))}return this},e.prototype.draggable=function(e,t){return e=s(e),"disable"===t||"enable"===t?e.draggable(t):e.draggable(s.extend({},this.grid.opts.draggable,{containment:this.grid.opts.isNested&&!this.grid.opts.dragOut?this.grid.$el.parent():this.grid.opts.draggable.containment||null,start:t.start||function(){},stop:t.stop||function(){},drag:t.drag||function(){}})),this},e.prototype.droppable=function(e,t){return(e=s(e)).droppable(t),this},e.prototype.isDroppable=function(e,t){return e=s(e),Boolean(e.data("droppable"))},e.prototype.on=function(e,t,i){return s(e).on(t,i),this},i.JQueryUIGridStackDragDropPlugin=e}(); +//# sourceMappingURL=gridstack.min.map \ No newline at end of file diff --git a/app/assets/javascripts/blazer/gridstack.js b/app/assets/javascripts/blazer/gridstack.js new file mode 100644 index 000000000..e9fa17d66 --- /dev/null +++ b/app/assets/javascripts/blazer/gridstack.js @@ -0,0 +1,2123 @@ +/** + * gridstack.js 1.1.0-dev + * https://gridstackjs.com/ + * (c) 2014-2020 Alain Dumesny, Dylan Weiss, Pavel Reznikov + * gridstack.js may be freely distributed under the MIT license. + * @preserve +*/ +(function(factory) { + /* [alain] we compile jquery with our code, so no need to 'load' externally + if (typeof define === 'function' && define.amd) { + define(['jquery', 'exports'], factory); + } else if (typeof exports !== 'undefined') { + var jQueryModule; + + try { jQueryModule = require('jquery'); } catch (e) {} + + factory(jQueryModule || window.jQuery, exports); + } else */{ + factory(window.jQuery, window); + } +})(function($, scope) { + + // checks for obsolete method names + var obsolete = function(f, oldName, newName, rev) { + var wrapper = function() { + console.warn('gridstack.js: Function `' + oldName + '` is deprecated in ' + rev + ' and has been replaced ' + + 'with `' + newName + '`. It will be **completely** removed in v1.0'); + return f.apply(this, arguments); + }; + wrapper.prototype = f.prototype; + + return wrapper; + }; + + // checks for obsolete grid options (can be used for any fields, but msg is about options) + var obsoleteOpts = function(opts, oldName, newName, rev) { + if (opts[oldName] !== undefined) { + opts[newName] = opts[oldName]; + console.warn('gridstack.js: Option `' + oldName + '` is deprecated in ' + rev + ' and has been replaced with `' + + newName + '`. It will be **completely** removed in v1.0'); + } + }; + + // checks for obsolete grid options which are gone + var obsoleteOptsDel = function(opts, oldName, rev, info) { + if (opts[oldName] !== undefined) { + console.warn('gridstack.js: Option `' + oldName + '` is deprecated in ' + rev + info); + } + }; + + // checks for obsolete Jquery element attributes + var obsoleteAttr = function(el, oldName, newName, rev) { + var oldAttr = el.attr(oldName); + if (oldAttr !== undefined) { + el.attr(newName, oldAttr); + console.warn('gridstack.js: attribute `' + oldName + '`=' + oldAttr + ' is deprecated on this object in ' + rev + ' and has been replaced with `' + + newName + '`. It will be **completely** removed in v1.0'); + } + }; + + var Utils = { + + isIntercepted: function(a, b) { + return !(a.x + a.width <= b.x || b.x + b.width <= a.x || a.y + a.height <= b.y || b.y + b.height <= a.y); + }, + + sort: function(nodes, dir, column) { + if (!column) { + var widths = nodes.map(function(node) { return node.x + node.width; }); + column = Math.max.apply(Math, widths); + } + + if (dir === -1) + return Utils.sortBy(nodes, function(n) { return -(n.x + n.y * column); }); + else + return Utils.sortBy(nodes, function(n) { return (n.x + n.y * column); }); + }, + + createStylesheet: function(id, parent) { + var style = document.createElement('style'); + style.setAttribute('type', 'text/css'); + style.setAttribute('data-gs-style-id', id); + if (style.styleSheet) { + style.styleSheet.cssText = ''; + } else { + style.appendChild(document.createTextNode('')); + } + if (!parent) { parent = document.getElementsByTagName('head')[0]; } // default to head + parent.insertBefore(style, parent.firstChild); + return style.sheet; + }, + + removeStylesheet: function(id) { + $('STYLE[data-gs-style-id=' + id + ']').remove(); + }, + + insertCSSRule: function(sheet, selector, rules, index) { + if (typeof sheet.insertRule === 'function') { + sheet.insertRule(selector + '{' + rules + '}', index); + } else if (typeof sheet.addRule === 'function') { + sheet.addRule(selector, rules, index); + } + }, + + toBool: function(v) { + if (typeof v === 'boolean') { + return v; + } + if (typeof v === 'string') { + v = v.toLowerCase(); + return !(v === '' || v === 'no' || v === 'false' || v === '0'); + } + return Boolean(v); + }, + + _collisionNodeCheck: function(n) { + return n !== this.node && Utils.isIntercepted(n, this.nn); + }, + + _didCollide: function(bn) { + return Utils.isIntercepted({x: this.n.x, y: this.newY, width: this.n.width, height: this.n.height}, bn); + }, + + _isAddNodeIntercepted: function(n) { + return Utils.isIntercepted({x: this.x, y: this.y, width: this.node.width, height: this.node.height}, n); + }, + + parseHeight: function(val) { + var height = val; + var heightUnit = 'px'; + if (height && typeof height === 'string') { + var match = height.match(/^(-[0-9]+\.[0-9]+|[0-9]*\.[0-9]+|-[0-9]+|[0-9]+)(px|em|rem|vh|vw|%)?$/); + if (!match) { + throw new Error('Invalid height'); + } + heightUnit = match[2] || 'px'; + height = parseFloat(match[1]); + } + return {height: height, unit: heightUnit}; + }, + + without: function(array, item) { + var index = array.indexOf(item); + + if (index !== -1) { + array = array.slice(0); + array.splice(index, 1); + } + + return array; + }, + + sortBy: function(array, getter) { + return array.slice(0).sort(function(left, right) { + var valueLeft = getter(left); + var valueRight = getter(right); + + if (valueRight === valueLeft) { + return 0; + } + + return valueLeft > valueRight ? 1 : -1; + }); + }, + + defaults: function(target) { + var sources = Array.prototype.slice.call(arguments, 1); + + sources.forEach(function(source) { + for (var prop in source) { + if (source.hasOwnProperty(prop) && (!target.hasOwnProperty(prop) || target[prop] === undefined)) { + target[prop] = source[prop]; + } + } + }); + + return target; + }, + + clone: function(target) { + return $.extend({}, target); + }, + + throttle: function(callback, delay) { + var isWaiting = false; + + return function() { + if (!isWaiting) { + callback.apply(this, arguments); + isWaiting = true; + setTimeout(function() { isWaiting = false; }, delay); + } + }; + }, + + removePositioningStyles: function(el) { + var style = el[0].style; + if (style.position) { + style.removeProperty('position'); + } + if (style.left) { + style.removeProperty('left'); + } + if (style.top) { + style.removeProperty('top'); + } + if (style.width) { + style.removeProperty('width'); + } + if (style.height) { + style.removeProperty('height'); + } + }, + getScrollParent: function(el) { + var returnEl; + if (el === null) { + returnEl = null; + } else if (el.scrollHeight > el.clientHeight) { + returnEl = el; + } else { + returnEl = Utils.getScrollParent(el.parentNode); + } + return returnEl; + }, + updateScrollPosition: function(el, ui, distance) { + // is widget in view? + var rect = el.getBoundingClientRect(); + var innerHeightOrClientHeight = (window.innerHeight || document.documentElement.clientHeight); + if (rect.top < 0 || + rect.bottom > innerHeightOrClientHeight + ) { + // set scrollTop of first parent that scrolls + // if parent is larger than el, set as low as possible + // to get entire widget on screen + var offsetDiffDown = rect.bottom - innerHeightOrClientHeight; + var offsetDiffUp = rect.top; + var scrollEl = Utils.getScrollParent(el); + if (scrollEl !== null) { + var prevScroll = scrollEl.scrollTop; + if (rect.top < 0 && distance < 0) { + // moving up + if (el.offsetHeight > innerHeightOrClientHeight) { + scrollEl.scrollTop += distance; + } else { + scrollEl.scrollTop += Math.abs(offsetDiffUp) > Math.abs(distance) ? distance : offsetDiffUp; + } + } else if (distance > 0) { + // moving down + if (el.offsetHeight > innerHeightOrClientHeight) { + scrollEl.scrollTop += distance; + } else { + scrollEl.scrollTop += offsetDiffDown > distance ? distance : offsetDiffDown; + } + } + // move widget y by amount scrolled + ui.position.top += scrollEl.scrollTop - prevScroll; + } + } + } + }; + + /** + * @class GridStackDragDropPlugin + * Base class for drag'n'drop plugin. + */ + function GridStackDragDropPlugin(grid) { + this.grid = grid; + } + + GridStackDragDropPlugin.registeredPlugins = []; + + GridStackDragDropPlugin.registerPlugin = function(pluginClass) { + GridStackDragDropPlugin.registeredPlugins.push(pluginClass); + }; + + GridStackDragDropPlugin.prototype.resizable = function(el, opts) { + return this; + }; + + GridStackDragDropPlugin.prototype.draggable = function(el, opts) { + return this; + }; + + GridStackDragDropPlugin.prototype.droppable = function(el, opts) { + return this; + }; + + GridStackDragDropPlugin.prototype.isDroppable = function(el) { + return false; + }; + + GridStackDragDropPlugin.prototype.on = function(el, eventName, callback) { + return this; + }; + + + var idSeq = 0; + + var GridStackEngine = function(column, onchange, float, maxRow, items) { + this.column = column || 12; + this.float = float || false; + this.maxRow = maxRow || 0; + + this.nodes = items || []; + this.onchange = onchange || function() {}; + + this._addedNodes = []; + this._removedNodes = []; + this._batchMode = false; + }; + + GridStackEngine.prototype.batchUpdate = function() { + if (this._batchMode) return; + this._batchMode = true; + this._prevFloat = this.float; + this.float = true; // let things go anywhere for now... commit() will restore and possibly reposition + }; + + GridStackEngine.prototype.commit = function() { + if (!this._batchMode) return; + this._batchMode = false; + this.float = this._prevFloat; + delete this._prevFloat; + this._packNodes(); + this._notify(); + }; + + // For Meteor support: https://github.com/gridstack/gridstack.js/pull/272 + GridStackEngine.prototype.getNodeDataByDOMEl = function(el) { + return this.nodes.find(function(node) { return el === node.el }); + }; + + GridStackEngine.prototype._fixCollisions = function(node) { + var self = this; + this._sortNodes(-1); + + var nn = node; + var hasLocked = Boolean(this.nodes.find(function(n) { return n.locked; })); + if (!this.float && !hasLocked) { + nn = {x: 0, y: node.y, width: this.column, height: node.height}; + } + while (true) { + var collisionNode = this.nodes.find(Utils._collisionNodeCheck, {node: node, nn: nn}); + if (!collisionNode) { return; } + var moved = this.moveNode(collisionNode, collisionNode.x, node.y + node.height, + collisionNode.width, collisionNode.height, true); + if (!moved) { return; } // break inf loop if we couldn't move after all (ex: maxRow, fixed) + } + }; + + GridStackEngine.prototype.isAreaEmpty = function(x, y, width, height) { + var nn = {x: x || 0, y: y || 0, width: width || 1, height: height || 1}; + var collisionNode = this.nodes.find(function(n) { + return Utils.isIntercepted(n, nn); + }); + return !collisionNode; + }; + + GridStackEngine.prototype._sortNodes = function(dir) { + this.nodes = Utils.sort(this.nodes, dir, this.column); + }; + + GridStackEngine.prototype._packNodes = function() { + this._sortNodes(); + + if (this.float) { + this.nodes.forEach(function(n, i) { + if (n._updating || n._packY === undefined || n.y === n._packY) { + return; + } + + var newY = n.y; + while (newY >= n._packY) { + var collisionNode = this.nodes + .slice(0, i) + .find(Utils._didCollide, {n: n, newY: newY}); + + if (!collisionNode) { + n._dirty = true; + n.y = newY; + } + --newY; + } + }, this); + } else { + this.nodes.forEach(function(n, i) { + if (n.locked) { return; } + while (n.y > 0) { + var newY = n.y - 1; + var canBeMoved = i === 0; + + if (i > 0) { + var collisionNode = this.nodes + .slice(0, i) + .find(Utils._didCollide, {n: n, newY: newY}); + canBeMoved = collisionNode === undefined; + } + + if (!canBeMoved) { break; } + // Note: must be dirty (from last position) for GridStack::OnChange CB to update positions + // and move items back. The user 'change' CB should detect changes from the original + // starting position instead. + n._dirty = (n.y !== newY); + n.y = newY; + } + }, this); + } + }; + + GridStackEngine.prototype._prepareNode = function(node, resizing) { + node = node || {}; + // if we're missing position, have the grid position us automatically (before we set them to 0,0) + if (node.x === undefined || node.y === undefined || node.x === null || node.y === null) { + node.autoPosition = true; + } + + // assign defaults for missing required fields + var defaults = {width: 1, height: 1, x: 0, y: 0}; + node = Utils.defaults(node, defaults); + + // convert any strings over + node.x = parseInt(node.x); + node.y = parseInt(node.y); + node.width = parseInt(node.width); + node.height = parseInt(node.height); + node.autoPosition = node.autoPosition || false; + node.noResize = node.noResize || false; + node.noMove = node.noMove || false; + + // check for NaN (in case messed up strings were passed. can't do parseInt() || defaults.x above as 0 is valid #) + if (Number.isNaN(node.x)) { node.x = defaults.x; node.autoPosition = true; } + if (Number.isNaN(node.y)) { node.y = defaults.y; node.autoPosition = true; } + if (Number.isNaN(node.width)) { node.width = defaults.width; } + if (Number.isNaN(node.height)) { node.height = defaults.height; } + + if (node.maxWidth !== undefined) { node.width = Math.min(node.width, node.maxWidth); } + if (node.maxHeight !== undefined) { node.height = Math.min(node.height, node.maxHeight); } + if (node.minWidth !== undefined) { node.width = Math.max(node.width, node.minWidth); } + if (node.minHeight !== undefined) { node.height = Math.max(node.height, node.minHeight); } + + if (node.width > this.column) { + node.width = this.column; + } else if (node.width < 1) { + node.width = 1; + } + if (this.maxRow && node.height > this.maxRow) { + node.height = this.maxRow; + } else if (node.height < 1) { + node.height = 1; + } + + if (node.x < 0) { + node.x = 0; + } + if (node.y < 0) { + node.y = 0; + } + + if (node.x + node.width > this.column) { + if (resizing) { + node.width = this.column - node.x; + } else { + node.x = this.column - node.width; + } + } + if (this.maxRow && node.y + node.height > this.maxRow) { + if (resizing) { + node.height = this.maxRow - node.y; + } else { + node.y = this.maxRow - node.height; + } + } + + return node; + }; + + GridStackEngine.prototype._notify = function() { + if (this._batchMode) { return; } + var args = Array.prototype.slice.call(arguments, 0); + args[0] = (args[0] === undefined ? [] : (Array.isArray(args[0]) ? args[0] : [args[0]]) ); + args[1] = (args[1] === undefined ? true : args[1]); + var dirtyNodes = args[0].concat(this.getDirtyNodes()); + this.onchange(dirtyNodes, args[1]); + }; + + GridStackEngine.prototype.cleanNodes = function() { + if (this._batchMode) { return; } + this.nodes.forEach(function(n) { delete n._dirty; }); + }; + + GridStackEngine.prototype.getDirtyNodes = function(verify) { + // compare original X,Y,W,H (or entire node?) instead as _dirty can be a temporary state + if (verify) { + var dirtNodes = []; + this.nodes.forEach(function (n) { + if (n._dirty) { + if (n.y === n._origY && n.x === n._origX && n.width === n._origW && n.height === n._origH) { + delete n._dirty; + } else { + dirtNodes.push(n); + } + } + }); + return dirtNodes; + } + + return this.nodes.filter(function(n) { return n._dirty; }); + }; + + GridStackEngine.prototype.addNode = function(node, triggerAddEvent) { + node = this._prepareNode(node); + + node._id = node._id || ++idSeq; + + if (node.autoPosition) { + this._sortNodes(); + + for (var i = 0;; ++i) { + var x = i % this.column; + var y = Math.floor(i / this.column); + if (x + node.width > this.column) { + continue; + } + if (!this.nodes.find(Utils._isAddNodeIntercepted, {x: x, y: y, node: node})) { + node.x = x; + node.y = y; + delete node.autoPosition; // found our slot + break; + } + } + } + + this.nodes.push(node); + if (triggerAddEvent) { + this._addedNodes.push(node); + } + + this._fixCollisions(node); + this._packNodes(); + this._notify(); + return node; + }; + + GridStackEngine.prototype.removeNode = function(node, detachNode) { + detachNode = (detachNode === undefined ? true : detachNode); + this._removedNodes.push(node); + node._id = null; // hint that node is being removed + this.nodes = Utils.without(this.nodes, node); + this._packNodes(); + this._notify(node, detachNode); + }; + + GridStackEngine.prototype.removeAll = function(detachNode) { + delete this._layouts; + if (this.nodes.length === 0) { return; } + detachNode = (detachNode === undefined ? true : detachNode); + this.nodes.forEach(function(n) { n._id = null; }); // hint that node is being removed + this._removedNodes = this.nodes; + this.nodes = []; + this._notify(this._removedNodes, detachNode); + }; + + GridStackEngine.prototype.canMoveNode = function(node, x, y, width, height) { + if (!this.isNodeChangedPosition(node, x, y, width, height)) { + return false; + } + var hasLocked = Boolean(this.nodes.find(function(n) { return n.locked; })); + + if (!this.maxRow && !hasLocked) { + return true; + } + + var clonedNode; + var clone = new GridStackEngine( + this.column, + null, + this.float, + 0, + this.nodes.map(function(n) { + if (n === node) { + clonedNode = $.extend({}, n); + return clonedNode; + } + return $.extend({}, n); + })); + + if (!clonedNode) { return true;} + + clone.moveNode(clonedNode, x, y, width, height); + + var res = true; + + if (hasLocked) { + res &= !Boolean(clone.nodes.find(function(n) { + return n !== clonedNode && Boolean(n.locked) && Boolean(n._dirty); + })); + } + if (this.maxRow) { + res &= clone.getRow() <= this.maxRow; + } + + return res; + }; + + GridStackEngine.prototype.canBePlacedWithRespectToHeight = function(node) { + if (!this.maxRow) { + return true; + } + + var clone = new GridStackEngine( + this.column, + null, + this.float, + 0, + this.nodes.map(function(n) { return $.extend({}, n); })); + clone.addNode(node); + return clone.getRow() <= this.maxRow; + }; + + GridStackEngine.prototype.isNodeChangedPosition = function(node, x, y, width, height) { + if (typeof x !== 'number') { x = node.x; } + if (typeof y !== 'number') { y = node.y; } + if (typeof width !== 'number') { width = node.width; } + if (typeof height !== 'number') { height = node.height; } + + if (node.maxWidth !== undefined) { width = Math.min(width, node.maxWidth); } + if (node.maxHeight !== undefined) { height = Math.min(height, node.maxHeight); } + if (node.minWidth !== undefined) { width = Math.max(width, node.minWidth); } + if (node.minHeight !== undefined) { height = Math.max(height, node.minHeight); } + + if (node.x === x && node.y === y && node.width === width && node.height === height) { + return false; + } + return true; + }; + + GridStackEngine.prototype.moveNode = function(node, x, y, width, height, noPack) { + if (typeof x !== 'number') { x = node.x; } + if (typeof y !== 'number') { y = node.y; } + if (typeof width !== 'number') { width = node.width; } + if (typeof height !== 'number') { height = node.height; } + + // constrain the passed in values and check if we're still changing our node + var resizing = (node.width !== width || node.height !== height); + var nn = { x: x, y: y, width: width, height: height, + maxWidth: node.maxWidth, maxHeight: NodeIterator.maxHeight, minWidth: node.minWidth, minHeight: node.minHeight}; + nn = this._prepareNode(nn, resizing); + if (node.x === nn.x && node.y === nn.y && node.width === nn.width && node.height === nn.height) { + return null; + } + + node._dirty = true; + + node.x = node.lastTriedX = nn.x; + node.y = node.lastTriedY = nn.y; + node.width = node.lastTriedWidth = nn.width; + node.height = node.lastTriedHeight = nn.height; + + this._fixCollisions(node); + if (!noPack) { + this._packNodes(); + this._notify(); + } + return node; + }; + + GridStackEngine.prototype.getRow = function() { + return this.nodes.reduce(function(memo, n) { return Math.max(memo, n.y + n.height); }, 0); + }; + + GridStackEngine.prototype.beginUpdate = function(node) { + if (node._updating) return; + node._updating = true; + this.nodes.forEach(function(n) { n._packY = n.y; }); + }; + + GridStackEngine.prototype.endUpdate = function() { + var n = this.nodes.find(function(n) { return n._updating; }); + if (n) { + n._updating = false; + this.nodes.forEach(function(n) { delete n._packY; }); + } + }; + + /** + * Construct a grid item from the given element and options + * @param {GridStackElement} el + * @param {GridstackOptions} opts + */ + var GridStack = function(el, opts) { + var self = this; + var oneColumnMode, _prevColumn, isAutoCellHeight; + + opts = opts || {}; + + this.$el = $(el); // TODO: legacy code + this.el = this.$el.get(0); // exposed HTML element to the user + + obsoleteOpts(opts, 'width', 'column', 'v0.5.3'); + obsoleteOpts(opts, 'height', 'maxRow', 'v0.5.3'); + obsoleteOptsDel(opts, 'oneColumnModeClass', 'v0.6.3', '. Use class `.grid-stack-1` instead'); + + // container attributes + obsoleteAttr(this.$el, 'data-gs-width', 'data-gs-column', 'v0.5.3'); + obsoleteAttr(this.$el, 'data-gs-height', 'data-gs-max-row', 'v0.5.3'); + obsoleteAttr(this.$el, 'data-gs-current-height', 'data-gs-current-row', 'v1.0.0'); + + opts.itemClass = opts.itemClass || 'grid-stack-item'; + var isNested = this.$el.closest('.' + opts.itemClass).length > 0; + + // if row property exists, replace minRow and maxRow instead + if (opts.row) { + opts.minRow = opts.maxRow = opts.row; + delete opts.row; + } + var rowAttr = parseInt(this.$el.attr('data-gs-row')); + + // elements attributes override any passed options (like CSS style) - merge the two together + this.opts = Utils.defaults(opts, { + column: parseInt(this.$el.attr('data-gs-column')) || 12, + minRow: rowAttr ? rowAttr : parseInt(this.$el.attr('data-gs-min-row')) || 0, + maxRow: rowAttr ? rowAttr : parseInt(this.$el.attr('data-gs-max-row')) || 0, + itemClass: 'grid-stack-item', + placeholderClass: 'grid-stack-placeholder', + placeholderText: '', + handle: '.grid-stack-item-content', + handleClass: null, + cellHeight: 60, + verticalMargin: 20, + auto: true, + minWidth: 768, + float: false, + staticGrid: false, + _class: 'grid-stack-instance-' + (Math.random() * 10000).toFixed(0), + animate: Boolean(this.$el.attr('data-gs-animate')) || false, + alwaysShowResizeHandle: opts.alwaysShowResizeHandle || false, + resizable: Utils.defaults(opts.resizable || {}, { + autoHide: !(opts.alwaysShowResizeHandle || false), + handles: 'se' + }), + draggable: Utils.defaults(opts.draggable || {}, { + handle: (opts.handleClass ? '.' + opts.handleClass : (opts.handle ? opts.handle : '')) || + '.grid-stack-item-content', + scroll: false, + appendTo: 'body' + }), + disableDrag: opts.disableDrag || false, + disableResize: opts.disableResize || false, + rtl: 'auto', + removable: false, + removableOptions: Utils.defaults(opts.removableOptions || {}, { + accept: '.' + opts.itemClass + }), + removeTimeout: 2000, + verticalMarginUnit: 'px', + cellHeightUnit: 'px', + disableOneColumnMode: opts.disableOneColumnMode || false, + oneColumnModeDomSort: opts.oneColumnModeDomSort, + ddPlugin: null + }); + + if (this.opts.ddPlugin === false) { + this.opts.ddPlugin = GridStackDragDropPlugin; + } else if (this.opts.ddPlugin === null) { + this.opts.ddPlugin = GridStackDragDropPlugin.registeredPlugins[0] || GridStackDragDropPlugin; + } + + this.dd = new this.opts.ddPlugin(this); + + if (this.opts.rtl === 'auto') { + this.opts.rtl = this.$el.css('direction') === 'rtl'; + } + + if (this.opts.rtl) { + this.$el.addClass('grid-stack-rtl'); + } + + this.opts.isNested = isNested; + + isAutoCellHeight = (this.opts.cellHeight === 'auto'); + if (isAutoCellHeight) { + // make the cell square initially + self.cellHeight(self.cellWidth(), true); + } else { + this.cellHeight(this.opts.cellHeight, true); + } + this.verticalMargin(this.opts.verticalMargin, true); + + this.$el.addClass(this.opts._class); + + this._setStaticClass(); + + if (isNested) { + this.$el.addClass('grid-stack-nested'); + } + + this._initStyles(); + + this.engine = new GridStackEngine(this.opts.column, function(nodes, detachNode) { + detachNode = (detachNode === undefined ? true : detachNode); + var maxHeight = 0; + this.nodes.forEach(function(n) { + maxHeight = Math.max(maxHeight, n.y + n.height); + }); + nodes.forEach(function(n) { + if (detachNode && n._id === null) { + if (n.el) { + $(n.el).remove(); + } + } else { + $(n.el) + .attr('data-gs-x', n.x) + .attr('data-gs-y', n.y) + .attr('data-gs-width', n.width) + .attr('data-gs-height', n.height); + } + }); + self._updateStyles(maxHeight + 10); + }, this.opts.float, this.opts.maxRow); + + if (this.opts.auto) { + var elements = []; + var _this = this; + this.$el.children('.' + this.opts.itemClass + ':not(.' + this.opts.placeholderClass + ')') + .each(function(index, el) { + el = $(el); + var x = parseInt(el.attr('data-gs-x')); + var y = parseInt(el.attr('data-gs-y')); + elements.push({ + el: el.get(0), + // if x,y are missing (autoPosition) add them to end of list - but keep their respective DOM order + i: (Number.isNaN(x) ? 1000 : x) + (Number.isNaN(y) ? 1000 : y) * _this.opts.column + }); + }); + Utils.sortBy(elements, function(x) { return x.i; }).forEach(function(item) { + this._prepareElement(item.el); + }, this); + } + this.engine._saveInitial(); // initial start of items + + this.setAnimation(this.opts.animate); + + this.placeholder = $( + '
' + + '
' + this.opts.placeholderText + '
').hide(); + + this._updateContainerHeight(); + + this._updateHeightsOnResize = Utils.throttle(function() { + self.cellHeight(self.cellWidth(), false); + }, 100); + + /** + * called when we are being resized - check if the one Column Mode needs to be turned on/off + * and remember the prev columns we used. + */ + this.onResizeHandler = function() { + if (isAutoCellHeight) { + self._updateHeightsOnResize(); + } + + if (self.opts.staticGrid) { return; } + + if (!self.opts.disableOneColumnMode && (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) <= self.opts.minWidth) { + if (self.oneColumnMode) { return; } + self.oneColumnMode = true; + self.column(1); + } else { + if (!self.oneColumnMode) { return; } + self.oneColumnMode = false; + self.column(self._prevColumn); + } + }; + + $(window).resize(this.onResizeHandler); + this.onResizeHandler(); + + if (!self.opts.staticGrid && typeof self.opts.removable === 'string') { + var trashZone = $(self.opts.removable); + if (!this.dd.isDroppable(trashZone)) { + this.dd.droppable(trashZone, self.opts.removableOptions); + } + this.dd + .on(trashZone, 'dropover', function(event, ui) { + var el = $(ui.draggable); + var node = el.data('_gridstack_node'); + if (!node || node._grid !== self) { + return; + } + el.data('inTrashZone', true); + self._setupRemovingTimeout(el); + }) + .on(trashZone, 'dropout', function(event, ui) { + var el = $(ui.draggable); + var node = el.data('_gridstack_node'); + if (!node || node._grid !== self) { + return; + } + el.data('inTrashZone', false); + self._clearRemovingTimeout(el); + }); + } + + if (!self.opts.staticGrid && self.opts.acceptWidgets) { + var draggingElement = null; + + var onDrag = function(event, ui) { + var el = draggingElement; + var node = el.data('_gridstack_node'); + var pos = self.getCellFromPixel({left: event.pageX, top: event.pageY}, true); + var x = Math.max(0, pos.x); + var y = Math.max(0, pos.y); + if (!node._added) { + node._added = true; + + node.el = el.get(0); + node.autoPosition = true; + node.x = x; + node.y = y; + self.engine.cleanNodes(); + self.engine.beginUpdate(node); + self.engine.addNode(node); + + self.$el.append(self.placeholder); + self.placeholder + .attr('data-gs-x', node.x) + .attr('data-gs-y', node.y) + .attr('data-gs-width', node.width) + .attr('data-gs-height', node.height) + .show(); + node.el = self.placeholder.get(0); + node._beforeDragX = node.x; + node._beforeDragY = node.y; + + self._updateContainerHeight(); + } + if (!self.engine.canMoveNode(node, x, y)) { + return; + } + self.engine.moveNode(node, x, y); + self._updateContainerHeight(); + }; + + this.dd + .droppable(self.$el, { + accept: function(el) { + el = $(el); + var node = el.data('_gridstack_node'); + if (node && node._grid === self) { + return false; + } + return el.is(self.opts.acceptWidgets === true ? '.grid-stack-item' : self.opts.acceptWidgets); + } + }) + .on(self.$el, 'dropover', function(event, ui) { + var el = $(ui.draggable); + var width, height; + + // see if we already have a node with widget/height and check for attributes + var origNode = el.data('_gridstack_node'); + if (!origNode || !origNode.width || !origNode.height) { + var w = parseInt(el.attr('data-gs-width')); + if (w > 0) { origNode = origNode || {}; origNode.width = w; } + var h = parseInt(el.attr('data-gs-height')); + if (h > 0) { origNode = origNode || {}; origNode.height = h; } + } + + // if not calculate the grid size based on element outer size + // height: Each row is cellHeight + verticalMargin, until last one which has no margin below + var cellWidth = self.cellWidth(); + var cellHeight = self.cellHeight(); + var verticalMargin = self.opts.verticalMargin; + width = origNode && origNode.width ? origNode.width : Math.ceil(el.outerWidth() / cellWidth); + height = origNode && origNode.height ? origNode.height : Math.round((el.outerHeight() + verticalMargin) / (cellHeight + verticalMargin)); + + draggingElement = el; + + var node = self.engine._prepareNode({width: width, height: height, _added: false, _temporary: true}); + node.isOutOfGrid = true; + el.data('_gridstack_node', node); + el.data('_gridstack_node_orig', origNode); + + el.on('drag', onDrag); + return false; // prevent parent from receiving msg (which may be grid as well) + }) + .on(self.$el, 'dropout', function(event, ui) { + // jquery-ui bug. Must verify widget is being dropped out + // check node variable that gets set when widget is out of grid + var el = $(ui.draggable); + if (!el.data('_gridstack_node')) { + return; + } + var node = el.data('_gridstack_node'); + if (!node.isOutOfGrid) { + return; + } + el.unbind('drag', onDrag); + node.el = null; + self.engine.removeNode(node); + self.placeholder.detach(); + self._updateContainerHeight(); + el.data('_gridstack_node', el.data('_gridstack_node_orig')); + return false; // prevent parent from receiving msg (which may be grid as well) + }) + .on(self.$el, 'drop', function(event, ui) { + self.placeholder.detach(); + + var node = $(ui.draggable).data('_gridstack_node'); + node.isOutOfGrid = false; + node._grid = self; + var el = $(ui.draggable).clone(false); + el.data('_gridstack_node', node); + var originalNode = $(ui.draggable).data('_gridstack_node_orig'); + if (originalNode !== undefined && originalNode._grid !== undefined) { + originalNode._grid._triggerRemoveEvent(); + } + $(ui.helper).remove(); + node.el = el.get(0); + self.placeholder.hide(); + Utils.removePositioningStyles(el); + el.find('div.ui-resizable-handle').remove(); + + el + .attr('data-gs-x', node.x) + .attr('data-gs-y', node.y) + .attr('data-gs-width', node.width) + .attr('data-gs-height', node.height) + .addClass(self.opts.itemClass) + .enableSelection() + .removeData('draggable') + .removeClass('ui-draggable ui-draggable-dragging ui-draggable-disabled') + .unbind('drag', onDrag); + self.$el.append(el); + self._prepareElementsByNode(el, node); + self._updateContainerHeight(); + self.engine._addedNodes.push(node); + self._triggerAddEvent(); + self._triggerChangeEvent(); + + self.engine.endUpdate(); + $(ui.draggable).unbind('drag', onDrag); + $(ui.draggable).removeData('_gridstack_node'); + $(ui.draggable).removeData('_gridstack_node_orig'); + self.$el.trigger('dropped', [originalNode, node]); + return false; // prevent parent from receiving msg (which may be grid as well) + }); + } + }; + + GridStack.prototype._triggerChangeEvent = function(/*forceTrigger*/) { + if (this.engine._batchMode) { return; } + var elements = this.engine.getDirtyNodes(true); // verify they really changed + if (elements && elements.length) { + this.engine._layoutsNodesChange(elements); + this._triggerEvent('change', elements); + } + this.engine._saveInitial(); // we called, now reset initial values & dirty flags + }; + + GridStack.prototype._triggerAddEvent = function() { + if (this.engine._batchMode) { return; } + if (this.engine._addedNodes && this.engine._addedNodes.length > 0) { + this.engine._layoutsNodesChange(this.engine._addedNodes); + // prevent added nodes from also triggering 'change' event (which is called next) + this.engine._addedNodes.forEach(function (n) { delete n._dirty; }); + this._triggerEvent('added', this.engine._addedNodes); + this.engine._addedNodes = []; + } + }; + + GridStack.prototype._triggerRemoveEvent = function() { + if (this.engine._batchMode) { return; } + if (this.engine._removedNodes && this.engine._removedNodes.length > 0) { + this._triggerEvent('removed', this.engine._removedNodes); + this.engine._removedNodes = []; + } + }; + + GridStack.prototype._triggerEvent = function(name, data) { + var event = new CustomEvent(name, {detail: data}); + this.el.dispatchEvent(event); + }; + + GridStack.prototype._initStyles = function() { + if (this._stylesId) { + Utils.removeStylesheet(this._stylesId); + } + this._stylesId = 'gridstack-style-' + (Math.random() * 100000).toFixed(); + // insert style to parent (instead of 'head') to support WebComponent + this._styles = Utils.createStylesheet(this._stylesId, this.el.parentNode); + if (this._styles !== null) { + this._styles._max = 0; + } + }; + + GridStack.prototype._updateStyles = function(maxHeight) { + if (this._styles === null || this._styles === undefined) { + return; + } + + var prefix = '.' + this.opts._class + ' .' + this.opts.itemClass; + var self = this; + var getHeight; + + if (maxHeight === undefined) { + maxHeight = this._styles._max; + } + + this._initStyles(); + this._updateContainerHeight(); + if (!this.opts.cellHeight) { // The rest will be handled by CSS + return ; + } + if (this._styles._max !== 0 && maxHeight <= this._styles._max) { // Keep this._styles._max increasing + return ; + } + + if (!this.opts.verticalMargin || this.opts.cellHeightUnit === this.opts.verticalMarginUnit) { + getHeight = function(nbRows, nbMargins) { + return (self.opts.cellHeight * nbRows + self.opts.verticalMargin * nbMargins) + + self.opts.cellHeightUnit; + }; + } else { + getHeight = function(nbRows, nbMargins) { + if (!nbRows || !nbMargins) { + return (self.opts.cellHeight * nbRows + self.opts.verticalMargin * nbMargins) + + self.opts.cellHeightUnit; + } + return 'calc(' + ((self.opts.cellHeight * nbRows) + self.opts.cellHeightUnit) + ' + ' + + ((self.opts.verticalMargin * nbMargins) + self.opts.verticalMarginUnit) + ')'; + }; + } + + if (this._styles._max === 0) { + Utils.insertCSSRule(this._styles, prefix, 'min-height: ' + getHeight(1, 0) + ';', 0); + } + + if (maxHeight > this._styles._max) { + for (var i = this._styles._max; i < maxHeight; ++i) { + Utils.insertCSSRule(this._styles, + prefix + '[data-gs-height="' + (i + 1) + '"]', + 'height: ' + getHeight(i + 1, i) + ';', + i + ); + Utils.insertCSSRule(this._styles, + prefix + '[data-gs-min-height="' + (i + 1) + '"]', + 'min-height: ' + getHeight(i + 1, i) + ';', + i + ); + Utils.insertCSSRule(this._styles, + prefix + '[data-gs-max-height="' + (i + 1) + '"]', + 'max-height: ' + getHeight(i + 1, i) + ';', + i + ); + Utils.insertCSSRule(this._styles, + prefix + '[data-gs-y="' + i + '"]', + 'top: ' + getHeight(i, i) + ';', + i + ); + } + this._styles._max = maxHeight; + } + }; + + GridStack.prototype._updateContainerHeight = function() { + if (this.engine._batchMode) { return; } + var row = this.engine.getRow(); + if (row < this.opts.minRow) { + row = this.opts.minRow; + } + // check for css min height. Each row is cellHeight + verticalMargin, until last one which has no margin below + var cssMinHeight = parseInt(this.$el.css('min-height')); + if (cssMinHeight > 0) { + var verticalMargin = this.opts.verticalMargin; + var minRow = Math.round((cssMinHeight + verticalMargin) / (this.cellHeight() + verticalMargin)); + if (row < minRow) { + row = minRow; + } + } + this.$el.attr('data-gs-current-row', row); + if (!this.opts.cellHeight) { + return ; + } + if (!this.opts.verticalMargin) { + this.$el.css('height', (row * (this.opts.cellHeight)) + this.opts.cellHeightUnit); + } else if (this.opts.cellHeightUnit === this.opts.verticalMarginUnit) { + this.$el.css('height', (row * (this.opts.cellHeight + this.opts.verticalMargin) - + this.opts.verticalMargin) + this.opts.cellHeightUnit); + } else { + this.$el.css('height', 'calc(' + ((row * (this.opts.cellHeight)) + this.opts.cellHeightUnit) + + ' + ' + ((row * (this.opts.verticalMargin - 1)) + this.opts.verticalMarginUnit) + ')'); + } + }; + + GridStack.prototype._setupRemovingTimeout = function(el) { + var self = this; + var node = $(el).data('_gridstack_node'); + + if (node._removeTimeout || !self.opts.removable) { + return; + } + node._removeTimeout = setTimeout(function() { + el.addClass('grid-stack-item-removing'); + node._isAboutToRemove = true; + }, self.opts.removeTimeout); + }; + + GridStack.prototype._clearRemovingTimeout = function(el) { + var node = $(el).data('_gridstack_node'); + + if (!node._removeTimeout) { + return; + } + clearTimeout(node._removeTimeout); + node._removeTimeout = null; + el.removeClass('grid-stack-item-removing'); + node._isAboutToRemove = false; + }; + + GridStack.prototype._prepareElementsByNode = function(el, node) { + var self = this; + + var cellWidth; + var cellFullHeight; // internal cellHeight + v-margin + + var dragOrResize = function(event, ui) { + var x = Math.round(ui.position.left / cellWidth); + var y = Math.floor((ui.position.top + cellFullHeight / 2) / cellFullHeight); + var width; + var height; + + if (event.type === 'drag') { + var distance = ui.position.top - node._prevYPix; + node._prevYPix = ui.position.top; + Utils.updateScrollPosition(el[0], ui, distance); + if (el.data('inTrashZone') || x < 0 || x >= self.engine.column || y < 0 || + (!self.engine.float && y > self.engine.getRow())) { + if (!node._temporaryRemoved) { + if (self.opts.removable === true) { + self._setupRemovingTimeout(el); + } + + x = node._beforeDragX; + y = node._beforeDragY; + + self.placeholder.detach(); + self.placeholder.hide(); + self.engine.removeNode(node); + self._updateContainerHeight(); + + node._temporaryRemoved = true; + } else { + return; + } + } else { + self._clearRemovingTimeout(el); + + if (node._temporaryRemoved) { + self.engine.addNode(node); + self.placeholder + .attr('data-gs-x', x) + .attr('data-gs-y', y) + .attr('data-gs-width', width) + .attr('data-gs-height', height) + .show(); + self.$el.append(self.placeholder); + node.el = self.placeholder.get(0); + node._temporaryRemoved = false; + } + } + } else if (event.type === 'resize') { + if (x < 0) return; + width = Math.round(ui.size.width / cellWidth); + height = Math.round((ui.size.height + self.verticalMargin()) / cellFullHeight); + } + // width and height are undefined if not resizing + var lastTriedWidth = width !== undefined ? width : node.lastTriedWidth; + var lastTriedHeight = height !== undefined ? height : node.lastTriedHeight; + if (!self.engine.canMoveNode(node, x, y, width, height) || + (node.lastTriedX === x && node.lastTriedY === y && + node.lastTriedWidth === lastTriedWidth && node.lastTriedHeight === lastTriedHeight)) { + return; + } + node.lastTriedX = x; + node.lastTriedY = y; + node.lastTriedWidth = width; + node.lastTriedHeight = height; + self.engine.moveNode(node, x, y, width, height); + self._updateContainerHeight(); + + if (event.type === 'resize') { + $(event.target).trigger('gsresize', node); + } + }; + + var onStartMoving = function(event, ui) { + self.$el.append(self.placeholder); + var o = $(this); + self.engine.cleanNodes(); + self.engine.beginUpdate(node); + cellWidth = self.cellWidth(); + var strictCellHeight = self.cellHeight(); // heigh without v-margin + // compute height with v-margin (Note: we add 1 margin as last row is missing it) + cellFullHeight = (self.$el.height() + self.verticalMargin()) / parseInt(self.$el.attr('data-gs-current-row')); + self.placeholder + .attr('data-gs-x', o.attr('data-gs-x')) + .attr('data-gs-y', o.attr('data-gs-y')) + .attr('data-gs-width', o.attr('data-gs-width')) + .attr('data-gs-height', o.attr('data-gs-height')) + .show(); + node.el = self.placeholder.get(0); + node._beforeDragX = node.x; + node._beforeDragY = node.y; + node._prevYPix = ui.position.top; + var minHeight = (node.minHeight || 1); + var verticalMargin = self.opts.verticalMargin; + + // mineHeight - Each row is cellHeight + verticalMargin, until last one which has no margin below + self.dd.resizable(el, 'option', 'minWidth', cellWidth * (node.minWidth || 1)); + self.dd.resizable(el, 'option', 'minHeight', (strictCellHeight * minHeight) + (minHeight - 1) * verticalMargin); + + if (event.type === 'resizestart') { + o.find('.grid-stack-item').trigger('resizestart'); + } + }; + + var onEndMoving = function(event, ui) { + var o = $(this); + if (!o.data('_gridstack_node')) { + return; + } + + // var forceNotify = false; what is the point of calling 'change' event with no data, when the 'removed' event is already called ? + self.placeholder.detach(); + node.el = o.get(0); + self.placeholder.hide(); + + if (node._isAboutToRemove) { + // forceNotify = true; + var gridToNotify = el.data('_gridstack_node')._grid; + gridToNotify._triggerRemoveEvent(); + el.removeData('_gridstack_node'); + el.remove(); + } else { + self._clearRemovingTimeout(el); + if (!node._temporaryRemoved) { + Utils.removePositioningStyles(o); + o + .attr('data-gs-x', node.x) + .attr('data-gs-y', node.y) + .attr('data-gs-width', node.width) + .attr('data-gs-height', node.height); + } else { + Utils.removePositioningStyles(o); + o + .attr('data-gs-x', node._beforeDragX) + .attr('data-gs-y', node._beforeDragY) + .attr('data-gs-width', node.width) + .attr('data-gs-height', node.height); + node.x = node._beforeDragX; + node.y = node._beforeDragY; + node._temporaryRemoved = false; + self.engine.addNode(node); + } + } + self._updateContainerHeight(); + self._triggerChangeEvent(/*forceNotify*/); + + self.engine.endUpdate(); + + var nestedGrids = o.find('.grid-stack'); + if (nestedGrids.length && event.type === 'resizestop') { + nestedGrids.each(function(index, el) { + el.gridstack.onResizeHandler(); + }); + o.find('.grid-stack-item').trigger('resizestop'); + o.find('.grid-stack-item').trigger('gsresizestop'); + } + if (event.type === 'resizestop') { + self.$el.trigger('gsresizestop', o); + } + }; + + this.dd + .draggable(el, { + start: onStartMoving, + stop: onEndMoving, + drag: dragOrResize + }) + .resizable(el, { + start: onStartMoving, + stop: onEndMoving, + resize: dragOrResize + }); + + if (node.noMove || this.opts.disableDrag || this.opts.staticGrid) { + this.dd.draggable(el, 'disable'); + } + + if (node.noResize || this.opts.disableResize || this.opts.staticGrid) { + this.dd.resizable(el, 'disable'); + } + + this._writeAttr(el, node); + }; + + GridStack.prototype._prepareElement = function(el, triggerAddEvent) { + triggerAddEvent = triggerAddEvent !== undefined ? triggerAddEvent : false; + var self = this; + el = $(el); + + el.addClass(this.opts.itemClass); + var node = this._readAttr(el, {el: el.get(0), _grid: self}); + node = self.engine.addNode(node, triggerAddEvent); + el.data('_gridstack_node', node); + + this._prepareElementsByNode(el, node); + }; + + /** call to write any default attributes back to element */ + GridStack.prototype._writeAttr = function(el, node) { + el = $(el); + node = node || {}; + // Note: passing null removes the attr in jquery + if (node.x !== undefined) { el.attr('data-gs-x', node.x); } + if (node.y !== undefined) { el.attr('data-gs-y', node.y); } + if (node.width !== undefined) { el.attr('data-gs-width', node.width); } + if (node.height !== undefined) { el.attr('data-gs-height', node.height); } + if (node.autoPosition !== undefined) { el.attr('data-gs-auto-position', node.autoPosition ? true : null); } + if (node.minWidth !== undefined) { el.attr('data-gs-min-width', node.minWidth); } + if (node.maxWidth !== undefined) { el.attr('data-gs-max-width', node.maxWidth); } + if (node.minHeight !== undefined) { el.attr('data-gs-min-height', node.minHeight); } + if (node.maxHeight !== undefined) { el.attr('data-gs-max-height', node.maxHeight); } + if (node.noResize !== undefined) { el.attr('data-gs-no-resize', node.noResize ? true : null); } + if (node.noMove !== undefined) { el.attr('data-gs-no-move', node.noMove ? true : null); } + if (node.locked !== undefined) { el.attr('data-gs-locked', node.locked ? true : null); } + if (node.resizeHandles !== undefined) { el.attr('data-gs-resize-handles', node.resizeHandles); } + if (node.id !== undefined) { el.attr('data-gs-id', node.id); } + }; + + /** call to write any default attributes back to element */ + GridStack.prototype._readAttr = function(el, node) { + el = $(el); + node = node || {}; + node.x = el.attr('data-gs-x'); + node.y = el.attr('data-gs-y'); + node.width = el.attr('data-gs-width'); + node.height = el.attr('data-gs-height'); + node.autoPosition = Utils.toBool(el.attr('data-gs-auto-position')); + node.maxWidth = el.attr('data-gs-max-width'); + node.minWidth = el.attr('data-gs-min-width'); + node.maxHeight = el.attr('data-gs-max-height'); + node.minHeight = el.attr('data-gs-min-height'); + node.noResize = Utils.toBool(el.attr('data-gs-no-resize')); + node.noMove = Utils.toBool(el.attr('data-gs-no-move')); + node.locked = Utils.toBool(el.attr('data-gs-locked')); + node.resizeHandles = el.attr('data-gs-resize-handles'); + node.id = el.attr('data-gs-id'); + return node; + }; + + GridStack.prototype.setAnimation = function(enable) { + if (enable) { + this.$el.addClass('grid-stack-animate'); + } else { + this.$el.removeClass('grid-stack-animate'); + } + }; + + GridStack.prototype.addWidget = function(el, opt, y, width, height, autoPosition, minWidth, maxWidth, minHeight, maxHeight, id) { + + // new way of calling with an object - make sure all items have been properly initialized + if (opt === undefined || typeof opt === 'object') { + // Tempting to initialize the passed in opt with default and valid values, but this break knockout demos + // as the actual value are filled in when _prepareElement() calls el.attr('data-gs-xyz) before adding the node. + // opt = this.engine._prepareNode(opt); + } else { + // old legacy way of calling with items spelled out - call us back with single object instead (so we can properly initialized values) + return this.addWidget(el, {x: opt, y: y, width: width, height: height, autoPosition: autoPosition, + minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight, id: id}); + } + + el = $(el); + if (opt) { // see knockout above + this.engine._prepareNode(opt); + } + this._writeAttr(el, opt); + this.$el.append(el); + return this.makeWidget(el); + }; + + GridStack.prototype.makeWidget = function(el) { + el = $(el); + this._prepareElement(el, true); + this._updateContainerHeight(); + this._triggerAddEvent(); + this._triggerChangeEvent(true); // trigger any other changes + + return el.get(0); + }; + + GridStack.prototype.willItFit = function(x, y, width, height, autoPosition) { + var node = {x: x, y: y, width: width, height: height, autoPosition: autoPosition}; + return this.engine.canBePlacedWithRespectToHeight(node); + }; + + GridStack.prototype.removeWidget = function(el, detachNode) { + el = $(el); + var node = el.data('_gridstack_node'); + // For Meteor support: https://github.com/gridstack/gridstack.js/pull/272 + if (!node) { + node = this.engine.getNodeDataByDOMEl(el.get(0)); + } + // remove our DOM data (circular link) and drag&drop permanently + el.removeData('_gridstack_node'); + this.dd.draggable(el, 'destroy').resizable(el, 'destroy'); + + this.engine.removeNode(node, detachNode); + this._triggerRemoveEvent(); + this._triggerChangeEvent(true); // trigger any other changes + }; + + GridStack.prototype.removeAll = function(detachNode) { + // always remove our DOM data (circular link) before list gets emptied and drag&drop permanently + this.engine.nodes.forEach(function(node) { + var el = $(node.el); + el.removeData('_gridstack_node'); + this.dd.draggable(el, 'destroy').resizable(el, 'destroy'); + }, this); + + this.engine.removeAll(detachNode); + this._triggerRemoveEvent(); + }; + + GridStack.prototype.destroy = function(detachGrid) { + $(window).off('resize', this.onResizeHandler); + if (detachGrid === false) { + this.removeAll(false); + this.$el.removeClass(this.opts._class); + delete this.$el.get(0).gridstack; + } else { + this.$el.remove(); + } + Utils.removeStylesheet(this._stylesId); + if (this.engine) { + this.engine = null; + } + }; + + GridStack.prototype.resizable = function(el, val) { + var self = this; + el = $(el); + el.each(function(index, el) { + el = $(el); + var node = el.data('_gridstack_node'); + if (!node) { return; } + node.noResize = !(val || false); + if (node.noResize) { + self.dd.resizable(el, 'disable'); + } else { + self.dd.resizable(el, 'enable'); + } + }); + return this; + }; + + GridStack.prototype.movable = function(el, val) { + var self = this; + el = $(el); + el.each(function(index, el) { + el = $(el); + var node = el.data('_gridstack_node'); + if (!node) { return; } + node.noMove = !(val || false); + if (node.noMove) { + self.dd.draggable(el, 'disable'); + el.removeClass('ui-draggable-handle'); + } else { + self.dd.draggable(el, 'enable'); + el.addClass('ui-draggable-handle'); + } + }); + return this; + }; + + GridStack.prototype.enableMove = function(doEnable, includeNewWidgets) { + this.movable(this.$el.children('.' + this.opts.itemClass), doEnable); + if (includeNewWidgets) { + this.opts.disableDrag = !doEnable; + } + }; + + GridStack.prototype.enableResize = function(doEnable, includeNewWidgets) { + this.resizable(this.$el.children('.' + this.opts.itemClass), doEnable); + if (includeNewWidgets) { + this.opts.disableResize = !doEnable; + } + }; + + GridStack.prototype.disable = function() { + this.movable(this.$el.children('.' + this.opts.itemClass), false); + this.resizable(this.$el.children('.' + this.opts.itemClass), false); + this.$el.trigger('disable'); + }; + + GridStack.prototype.enable = function() { + this.movable(this.$el.children('.' + this.opts.itemClass), true); + this.resizable(this.$el.children('.' + this.opts.itemClass), true); + this.$el.trigger('enable'); + }; + + GridStack.prototype.locked = function(el, val) { + el = $(el); + el.each(function(index, el) { + el = $(el); + var node = el.data('_gridstack_node'); + if (!node) { return; } + node.locked = (val || false); + el.attr('data-gs-locked', node.locked ? 'yes' : null); + }); + return this; + }; + + GridStack.prototype.maxHeight = function(el, val) { + el = $(el); + el.each(function(index, el) { + el = $(el); + var node = el.data('_gridstack_node'); + if (!node) { return; } + if (!isNaN(val)) { + node.maxHeight = (val || false); + el.attr('data-gs-max-height', val); + } + }); + return this; + }; + + GridStack.prototype.minHeight = function(el, val) { + el = $(el); + el.each(function(index, el) { + el = $(el); + var node = el.data('_gridstack_node'); + if (!node) { return; } + if (!isNaN(val)) { + node.minHeight = (val || false); + el.attr('data-gs-min-height', val); + } + }); + return this; + }; + + GridStack.prototype.maxWidth = function(el, val) { + el = $(el); + el.each(function(index, el) { + el = $(el); + var node = el.data('_gridstack_node'); + if (!node) { return; } + if (!isNaN(val)) { + node.maxWidth = (val || false); + el.attr('data-gs-max-width', val); + } + }); + return this; + }; + + GridStack.prototype.minWidth = function(el, val) { + el = $(el); + el.each(function(index, el) { + el = $(el); + var node = el.data('_gridstack_node'); + if (!node) { return; } + if (!isNaN(val)) { + node.minWidth = (val || false); + el.attr('data-gs-min-width', val); + } + }); + return this; + }; + + GridStack.prototype._updateElement = function(el, callback) { + el = $(el).first(); + var node = el.data('_gridstack_node'); + if (!node) { return; } + var self = this; + + self.engine.cleanNodes(); + self.engine.beginUpdate(node); + + callback.call(this, el, node); + + self._updateContainerHeight(); + self._triggerChangeEvent(); + + self.engine.endUpdate(); + }; + + GridStack.prototype.resize = function(el, width, height) { + this._updateElement(el, function(el, node) { + width = (width !== null && width !== undefined) ? width : node.width; + height = (height !== null && height !== undefined) ? height : node.height; + + this.engine.moveNode(node, node.x, node.y, width, height); + }); + }; + + GridStack.prototype.move = function(el, x, y) { + this._updateElement(el, function(el, node) { + x = (x !== null && x !== undefined) ? x : node.x; + y = (y !== null && y !== undefined) ? y : node.y; + + this.engine.moveNode(node, x, y, node.width, node.height); + }); + }; + + GridStack.prototype.update = function(el, x, y, width, height) { + this._updateElement(el, function(el, node) { + x = (x !== null && x !== undefined) ? x : node.x; + y = (y !== null && y !== undefined) ? y : node.y; + width = (width !== null && width !== undefined) ? width : node.width; + height = (height !== null && height !== undefined) ? height : node.height; + + this.engine.moveNode(node, x, y, width, height); + }); + }; + + /** + * relayout grid items to reclaim any empty space + */ + GridStack.prototype.compact = function() { + if (this.engine.nodes.length === 0) { return; } + this.batchUpdate(); + this.engine._sortNodes(); + var nodes = this.engine.nodes; + this.engine.nodes = []; // pretend we have no nodes to conflict layout to start with... + nodes.forEach(function(node) { + if (!node.noMove && !node.locked) { + node.autoPosition = true; + } + this.engine.addNode(node, false); // 'false' for add event trigger + node._dirty = true; // force attr update + }, this); + this.commit(); + }; + + GridStack.prototype.verticalMargin = function(val, noUpdate) { + if (val === undefined) { + return this.opts.verticalMargin; + } + + var heightData = Utils.parseHeight(val); + + if (this.opts.verticalMarginUnit === heightData.unit && this.opts.maxRow === heightData.height) { + return ; + } + this.opts.verticalMarginUnit = heightData.unit; + this.opts.verticalMargin = heightData.height; + + if (!noUpdate) { + this._updateStyles(); + } + }; + + /** set/get the current cell height value */ + GridStack.prototype.cellHeight = function(val, noUpdate) { + // getter - returns the opts stored height else compute it... + if (val === undefined) { + if (this.opts.cellHeight && this.opts.cellHeight !== 'auto') { + return this.opts.cellHeight; + } + // compute the height taking margin into account (each row has margin other than last one) + var o = this.$el.children('.' + this.opts.itemClass).first(); + var height = o.attr('data-gs-height'); + var verticalMargin = this.opts.verticalMargin; + return Math.round((o.outerHeight() - (height - 1) * verticalMargin) / height); + } + + // setter - updates the cellHeight value if they changed + var heightData = Utils.parseHeight(val); + if (this.opts.cellHeightUnit === heightData.unit && this.opts.cellHeight === heightData.height) { + return ; + } + this.opts.cellHeightUnit = heightData.unit; + this.opts.cellHeight = heightData.height; + + if (!noUpdate) { + this._updateStyles(); + } + }; + + GridStack.prototype.cellWidth = function() { + // TODO: take margin into account ($horizontal_padding in .scss) to make cellHeight='auto' square ? (see 810-many-columns.html) + return Math.round(this.$el.outerWidth() / this.opts.column); + }; + + GridStack.prototype.getCellFromPixel = function(position, useOffset) { + var containerPos = (useOffset !== undefined && useOffset) ? + this.$el.offset() : this.$el.position(); + var relativeLeft = position.left - containerPos.left; + var relativeTop = position.top - containerPos.top; + + var columnWidth = Math.floor(this.$el.width() / this.opts.column); + var rowHeight = Math.floor(this.$el.height() / parseInt(this.$el.attr('data-gs-current-row'))); + + return {x: Math.floor(relativeLeft / columnWidth), y: Math.floor(relativeTop / rowHeight)}; + }; + + GridStack.prototype.batchUpdate = function() { + this.engine.batchUpdate(); + }; + + GridStack.prototype.commit = function() { + this.engine.commit(); + this._triggerRemoveEvent(); + this._triggerAddEvent(); + this._triggerChangeEvent(); + }; + + GridStack.prototype.isAreaEmpty = function(x, y, width, height) { + return this.engine.isAreaEmpty(x, y, width, height); + }; + + GridStack.prototype.setStatic = function(staticValue) { + this.opts.staticGrid = (staticValue === true); + this.enableMove(!staticValue); + this.enableResize(!staticValue); + this._setStaticClass(); + }; + + GridStack.prototype._setStaticClass = function() { + var staticClassName = 'grid-stack-static'; + + if (this.opts.staticGrid === true) { + this.$el.addClass(staticClassName); + } else { + this.$el.removeClass(staticClassName); + } + }; + + /** called whenever a node is added or moved - updates the cached layouts */ + GridStackEngine.prototype._layoutsNodesChange = function(nodes) { + if (!this._layouts || this._ignoreLayoutsNodeChange) return; + // remove smaller layouts - we will re-generate those on the fly... larger ones need to update + this._layouts.forEach(function(layout, column) { + if (!layout || column === this.column) return; + if (column < this.column) { + this._layouts[column] = undefined; + } + else { + // we save the original x,y,w (h isn't cached) to see what actually changed to propagate better. + // Note: we don't need to check against out of bound scaling/moving as that will be done when using those cache values. + nodes.forEach(function(node) { + var n = layout.find(function(l) { return l._id === node._id }); + if (!n) return; // no cache for new nodes. Will use those values. + var ratio = column / this.column; + // Y changed, push down same amount + // TODO: detect doing item 'swaps' will help instead of move (especially in 1 column mode) + if (node.y !== node._origY) { + n.y += (node.y - node._origY); + } + // X changed, scale from new position + if (node.x !== node._origX) { + n.x = Math.round(node.x * ratio); + } + // width changed, scale from new width + if (node.width !== node._origW) { + n.width = Math.round(node.width * ratio); + } + // ...height always carries over from cache + }, this); + } + }, this); + } + + /** + * Called to scale the widget width & position up/down based on the column change. + * Note we store previous layouts (especially original ones) to make it possible to go + * from say 12 -> 1 -> 12 and get back to where we were. + * + * oldColumn: previous number of columns + * column: new column number + * nodes?: different sorted list (ex: DOM order) instead of current list + */ + GridStackEngine.prototype._updateNodeWidths = function(oldColumn, column, nodes) { + if (!this.nodes.length || oldColumn === column) { return; } + + // cache the current layout in case they want to go back (like 12 -> 1 -> 12) as it requires original data + var copy = [this.nodes.length]; + this.nodes.forEach(function(n, i) {copy[i] = {x: n.x, y: n.y, width: n.width, _id: n._id}}); // only thing we change is x,y,w and id to find it back + this._layouts = this._layouts || []; // use array to find larger quick + this._layouts[oldColumn] = copy; + + // if we're going to 1 column and using DOM order rather than default sorting, then generate that layout + if (column === 1 && nodes && nodes.length) { + var top = 0; + nodes.forEach(function(n) { + n.x = 0; + n.width = 1; + n.y = Math.max(n.y, top); + top = n.y + n.height; + }); + } else { + nodes = Utils.sort(this.nodes, -1, oldColumn); // current column reverse sorting so we can insert last to front (limit collision) + } + + // see if we have cached previous layout. + var cacheNodes = this._layouts[column] || []; + // if not AND we are going up in size start with the largest layout as down-scaling is more accurate + var lastIndex = this._layouts.length - 1; + if (cacheNodes.length === 0 && column > oldColumn && column < lastIndex) { + cacheNodes = this._layouts[lastIndex] || []; + if (cacheNodes.length) { + // pretend we came from that larger column by assigning those values as starting point + oldColumn = lastIndex; + cacheNodes.forEach(function(cacheNode) { + var j = nodes.findIndex(function(n) {return n && n._id === cacheNode._id}); + if (j !== -1) { + // still current, use cache info positions + nodes[j].x = cacheNode.x; + nodes[j].y = cacheNode.y; + nodes[j].width = cacheNode.width; + } + }); + cacheNodes = []; // we still don't have new column cached data... will generate from larger one. + } + } + + // if we found cache re-use those nodes that are still current + var newNodes = []; + cacheNodes.forEach(function(cacheNode) { + var j = nodes.findIndex(function(n) {return n && n._id === cacheNode._id}); + if (j !== -1) { + // still current, use cache info positions + nodes[j].x = cacheNode.x; + nodes[j].y = cacheNode.y; + nodes[j].width = cacheNode.width; + newNodes.push(nodes[j]); + nodes[j] = null; // erase it so we know what's left + } + }); + // ...and add any extra non-cached ones + var ratio = column / oldColumn; + nodes.forEach(function(node) { + if (!node) return; + node.x = (column === 1 ? 0 : Math.round(node.x * ratio)); + node.width = ((column === 1 || oldColumn === 1) ? 1 : (Math.round(node.width * ratio) || 1)); + newNodes.push(node); + }); + + // finally relayout them in reverse order (to get correct placement) + newNodes = Utils.sort(newNodes, -1, column); + this._ignoreLayoutsNodeChange = true; + this.batchUpdate(); + this.nodes = []; // pretend we have no nodes to start with (we use same structures) to simplify layout + newNodes.forEach(function(node) { + this.addNode(node, false); // 'false' for add event trigger + node._dirty = true; // force attr update + }, this); + this.commit(); + delete this._ignoreLayoutsNodeChange; + } + + /** called to save initial position/size */ + GridStackEngine.prototype._saveInitial = function() { + this.nodes.forEach(function(n) { + n._origX = n.x; + n._origY = n.y; + n._origW = n.width; + n._origH = n.height; + delete n._dirty; + }); + } + + /** + * set/get number of columns in the grid. Will attempt to update existing widgets + * to conform to new number of columns. Requires `gridstack-extra.css` or `gridstack-extra.min.css` for [2-11], + * else you will need to generate correct CSS (see https://github.com/gridstack/gridstack.js#change-grid-columns) + * @param column - Integer > 0 (default 12). + * @param doNotPropagate if true existing widgets will not be updated (optional) + */ + GridStack.prototype.column = function(column, doNotPropagate) { + // getter - returns the opts stored mode + if (column === undefined) { + return this.opts.column; + } + // setter + if (this.opts.column === column) { return; } + var oldColumn = this.opts.column; + + // if we go into 1 column mode (which happens if we're sized less than minWidth unless disableOneColumnMode is on) + // then remember the original columns so we can restore. + if (column === 1) { + this._prevColumn = oldColumn; + } else { + delete this._prevColumn; + } + + this.$el.removeClass('grid-stack-' + oldColumn); + this.$el.addClass('grid-stack-' + column); + this.opts.column = this.engine.column = column; + + if (doNotPropagate === true) { return; } + + // update the items now - see if the dom order nodes should be passed instead (else default to current list) + var domNodes; + if (this.opts.oneColumnModeDomSort && column === 1) { + domNodes = []; + this.$el.children('.' + this.opts.itemClass).each(function(index, el) { + var node = $(el).data('_gridstack_node'); + if (node) { domNodes.push(node); } + }); + if (!domNodes.length) { domNodes = undefined; } + } + this.engine._updateNodeWidths(oldColumn, column, domNodes); + + // and trigger our event last... + this.engine._ignoreLayoutsNodeChange = true; + this._triggerChangeEvent(); + delete this.engine._ignoreLayoutsNodeChange; + }; + + GridStack.prototype.float = function(val) { + // getter - returns the opts stored mode + if (val === undefined) { + return this.opts.float || false; + } + // setter - updates the mode and relayout if gravity is back on + if (this.opts.float === val) { return; } + this.opts.float = this.engine.float = val || false; + if (!val) { + this.engine._packNodes(); + this.engine._notify(); + this._triggerChangeEvent(); + } + }; + + GridStack.prototype.getRow = function() { + return this.engine.getRow(); + } + + /** Event handler that extracts our CustomEvent data out automatically for receiving custom + * notifications (see doc for supported events) + */ + GridStack.prototype.on = function(eventName, callback) { + // check for array of names being passed instead + if (eventName.indexOf(' ') !== -1) { + var names = eventName.split(' '); + names.forEach(function(name) { this.on(name, callback) }, this); + return; + } + + if (eventName === 'change' || eventName === 'added' || eventName === 'removed') { + // native CustomEvent handlers - cash the generic handlers so we can remove + this._gsEventHandler = this._gsEventHandler || {}; + this._gsEventHandler[eventName] = function(event) { callback(event, event.detail) }; + this.el.addEventListener(eventName, this._gsEventHandler[eventName]); + } else { + // still JQuery events + this.$el.on(eventName, callback); + } + } + + /** unsubscribe from the 'on' event */ + GridStack.prototype.off = function(eventName) { + // check for array of names being passed instead + if (eventName.indexOf(' ') !== -1) { + var names = eventName.split(' '); + names.forEach(function(name) { this.off(name, callback) }, this); + return; + } + + if (eventName === 'change' || eventName === 'added' || eventName === 'removed') { + // remove native CustomEvent handlers + if (this._gsEventHandler && this._gsEventHandler[eventName]) { + this.el.removeEventListener(eventName, this._gsEventHandler[eventName]); + delete this._gsEventHandler[eventName]; + } + } else { + // still JQuery events + this.$el.off(eventName); + } + } + + // legacy method renames + GridStack.prototype.setGridWidth = obsolete(GridStack.prototype.column, 'setGridWidth', 'column', 'v0.5.3'); + GridStack.prototype.setColumn = obsolete(GridStack.prototype.column, 'setColumn', 'column', 'v0.6.4'); + GridStackEngine.prototype.getGridHeight = obsolete(GridStackEngine.prototype.getRow, 'getGridHeight', 'getRow', 'v1.0.0'); + + scope.GridStack = GridStack; + scope.GridStack.Utils = Utils; + scope.GridStack.Engine = GridStackEngine; + scope.GridStack.DragDropPlugin = GridStackDragDropPlugin; + + /** + * initializing the HTML element, or selector string, into a grid will return the grid. Calling it again will + * simply return the existing instance (ignore any passed options). + */ + GridStack.init = function(opts, elOrString) { + if (!elOrString) { elOrString = '.grid-stack' } + var el = $(elOrString).get(0); + if (!el) return; + if (!el.gridstack) { + el.gridstack = new GridStack(el, opts); + } + return el.gridstack + }; + + /** + * Will initialize a list of elements (given a selector) and return an array of grids. + */ + GridStack.initAll = function(opts, selector) { + if (!selector) { selector = '.grid-stack' } + var grids = []; + $(selector).each(function(index, el) { + if (!el.gridstack) { + el.gridstack = new GridStack(el, opts); + } + grids.push(el.gridstack); + }); + return grids; + }; + + return scope.GridStack; +}); diff --git a/app/assets/javascripts/blazer/gs_dashboards.js b/app/assets/javascripts/blazer/gs_dashboards.js new file mode 100644 index 000000000..ad9791fd4 --- /dev/null +++ b/app/assets/javascripts/blazer/gs_dashboards.js @@ -0,0 +1,57 @@ +function gsDashboards(serializedData, savePositionURL) { + var grid = GridStack.init(); + + grid.on('added', function(e, items) {log('added', items)}); + grid.on('removed', function(e, items) {log('removed', items)}); + grid.on('change', function(e, items) {log('changed', items)}); + function log(type, items) { + var str = ''; + items.forEach(function(item) { str += ' (x,y)=' + item.x + ',' + item.y; }); + console.log(type + ' ' + items.length + ' items.' + str ); + if (type === 'changed') saveGrid(); + } + + + loadGrid = function() { + grid.removeAll(); + var items = GridStack.Utils.sort(serializedData); + grid.batchUpdate(); + items.forEach(function (node, idx) { + grid.addWidget(`
`, node); + }); + grid.commit(); + $('.grid-stack-item-content').each(function(i,e) { + const $e = $(e); + $e.append($('#query-' + $e[0].id.split('-')[1])); + }); + }; + + saveGrid = function() { + serializedData = []; + grid.engine.nodes.forEach(function(node) { + const id = node.el.children[0].children[0].id.split('-')[1]; + serializedData.push({ + id: id, + x: node.x, + y: node.y, + width: node.width, + height: node.height + }); + }); + serializedDataFull = { + 'authenticity_token': document.querySelector('[name="csrf-token"]').content, + dashboard: { + positions: JSON.stringify(serializedData) + } + }; + const j = JSON.stringify(serializedDataFull); + fetch(savePositionURL, { method: 'PATCH', cache: 'no-cache', headers: { 'Content-Type': 'application/json' }, body: j}); + //document.querySelector('#saved-data').value = j; + }; + + clearGrid = function() { + grid.removeAll(); + } + + loadGrid(); +} diff --git a/app/assets/stylesheets/blazer/application.css b/app/assets/stylesheets/blazer/application.css index 4cb57fe82..b83a3d66d 100644 --- a/app/assets/stylesheets/blazer/application.css +++ b/app/assets/stylesheets/blazer/application.css @@ -3,6 +3,9 @@ *= require ./selectize *= require ./github *= require ./daterangepicker + *= require ./gridstack + *= require ./gridstack-extra + *= require ./gs_dashboards *= require_self */ diff --git a/app/assets/stylesheets/blazer/gridstack-extra.scss b/app/assets/stylesheets/blazer/gridstack-extra.scss new file mode 100644 index 000000000..5725e4475 --- /dev/null +++ b/app/assets/stylesheets/blazer/gridstack-extra.scss @@ -0,0 +1,30 @@ +/*! + * gridstack 1.1.0-dev extra CSS for [2-11] columns (non default) + * https://gridstackjs.com/ + * (c) 2014-2020 Alain Dumesny, Dylan Weiss, Pavel Reznikov + * gridstack.js may be freely distributed under the MIT license. +*/ + +// default to generate [2-11] columns as 1 (oneColumnMode) and 12 (default) are in the main css +$gridstack-columns: 11 !default; +$gridstack-columns-start: 2 !default; + +@mixin grid-stack-items($columns) { + .grid-stack.grid-stack-#{$columns} { + + > .grid-stack-item { + min-width: 100% / $columns; + + @for $i from 1 through $columns { + &[data-gs-width='#{$i}'] { width: (100% / $columns) * $i; } + &[data-gs-x='#{$i}'] { left: (100% / $columns) * $i; } + &[data-gs-min-width='#{$i}'] { min-width: (100% / $columns) * $i; } + &[data-gs-max-width='#{$i}'] { max-width: (100% / $columns) * $i; } + } + } + } +} + +@for $j from $gridstack-columns-start through $gridstack-columns { + @include grid-stack-items($j) +} diff --git a/app/assets/stylesheets/blazer/gridstack.scss b/app/assets/stylesheets/blazer/gridstack.scss new file mode 100644 index 000000000..07be076d0 --- /dev/null +++ b/app/assets/stylesheets/blazer/gridstack.scss @@ -0,0 +1,139 @@ +/*! + * required gridstack 1.1.0-dev CSS for default 12 and 1 columnMode size. Use gridstack-extra.css for others + * https://gridstackjs.com/ + * (c) 2014-2020 Alain Dumesny, Dylan Weiss, Pavel Reznikov + * gridstack.js may be freely distributed under the MIT license. +*/ + +$gridstack-columns: 12 !default; +$horizontal_padding: 20px !default; +$vertical_padding: 20px !default; +$animation_speed: .3s !default; + +@mixin vendor($property, $value...){ + -webkit-#{$property}: $value; + -moz-#{$property}: $value; + -ms-#{$property}: $value; + -o-#{$property}: $value; + #{$property}: $value; +} + +:root .grid-stack-item > .ui-resizable-handle { filter: none; } + +.grid-stack { + position: relative; + + &.grid-stack-rtl { + direction: ltr; + + > .grid-stack-item { + direction: rtl; + } + } + + .grid-stack-placeholder > .placeholder-content { + border: 1px dashed lightgray; + margin: 0; + position: absolute; + top: 0; + left: $horizontal_padding / 2; + right: $horizontal_padding / 2; + bottom: 0; + width: auto; + z-index: 0 !important; + text-align: center; + } + + > .grid-stack-item { + min-width: 100% / $gridstack-columns; + position: absolute; + padding: 0; + + > .grid-stack-item-content { + margin: 0; + position: absolute; + top: 0; + left: $horizontal_padding / 2; + right: $horizontal_padding / 2; + bottom: 0; + width: auto; + overflow-x: hidden; + overflow-y: auto; + } + + > .ui-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; + -ms-touch-action: none; + touch-action: none; + } + + &.ui-resizable-disabled > .ui-resizable-handle, + &.ui-resizable-autohide > .ui-resizable-handle { display: none; } + + &.ui-draggable-dragging, + &.ui-resizable-resizing { + z-index: 100; + + > .grid-stack-item-content, + > .grid-stack-item-content { + box-shadow: 1px 4px 6px rgba(0, 0, 0, 0.2); + opacity: 0.8; + } + } + + > .ui-resizable-se, + > .ui-resizable-sw { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTYuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjE2cHgiIGhlaWdodD0iMTZweCIgdmlld0JveD0iMCAwIDUxMS42MjYgNTExLjYyNyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNTExLjYyNiA1MTEuNjI3OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxnPgoJPHBhdGggZD0iTTMyOC45MDYsNDAxLjk5NGgtMzYuNTUzVjEwOS42MzZoMzYuNTUzYzQuOTQ4LDAsOS4yMzYtMS44MDksMTIuODQ3LTUuNDI2YzMuNjEzLTMuNjE1LDUuNDIxLTcuODk4LDUuNDIxLTEyLjg0NSAgIGMwLTQuOTQ5LTEuODAxLTkuMjMxLTUuNDI4LTEyLjg1MWwtNzMuMDg3LTczLjA5QzI2NS4wNDQsMS44MDksMjYwLjc2LDAsMjU1LjgxMywwYy00Ljk0OCwwLTkuMjI5LDEuODA5LTEyLjg0Nyw1LjQyNCAgIGwtNzMuMDg4LDczLjA5Yy0zLjYxOCwzLjYxOS01LjQyNCw3LjkwMi01LjQyNCwxMi44NTFjMCw0Ljk0NiwxLjgwNyw5LjIyOSw1LjQyNCwxMi44NDVjMy42MTksMy42MTcsNy45MDEsNS40MjYsMTIuODUsNS40MjYgICBoMzYuNTQ1djI5Mi4zNThoLTM2LjU0MmMtNC45NTIsMC05LjIzNSwxLjgwOC0xMi44NSw1LjQyMWMtMy42MTcsMy42MjEtNS40MjQsNy45MDUtNS40MjQsMTIuODU0ICAgYzAsNC45NDUsMS44MDcsOS4yMjcsNS40MjQsMTIuODQ3bDczLjA4OSw3My4wODhjMy42MTcsMy42MTcsNy44OTgsNS40MjQsMTIuODQ3LDUuNDI0YzQuOTUsMCw5LjIzNC0xLjgwNywxMi44NDktNS40MjQgICBsNzMuMDg3LTczLjA4OGMzLjYxMy0zLjYyLDUuNDIxLTcuOTAxLDUuNDIxLTEyLjg0N2MwLTQuOTQ4LTEuODA4LTkuMjMyLTUuNDIxLTEyLjg1NCAgIEMzMzguMTQyLDQwMy44MDIsMzMzLjg1Nyw0MDEuOTk0LDMyOC45MDYsNDAxLjk5NHoiIGZpbGw9IiM2NjY2NjYiLz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8L3N2Zz4K); + background-repeat: no-repeat; + background-position: center; + @include vendor(transform, rotate(45deg)); + } + + > .ui-resizable-se { + @include vendor(transform, rotate(-45deg)); + } + + > .ui-resizable-nw { cursor: nw-resize; width: 20px; height: 20px; left: 10px; top: 0; } + > .ui-resizable-n { cursor: n-resize; height: 10px; top: 0; left: 25px; right: 25px; } + > .ui-resizable-ne { cursor: ne-resize; width: 20px; height: 20px; right: 10px; top: 0; } + > .ui-resizable-e { cursor: e-resize; width: 10px; right: $horizontal_padding / 2; top: 15px; bottom: 15px; } + > .ui-resizable-se { cursor: se-resize; width: 20px; height: 20px; right: 10px; bottom: 0; } + > .ui-resizable-s { cursor: s-resize; height: 10px; left: 25px; bottom: 0; right: 25px; } + > .ui-resizable-sw { cursor: sw-resize; width: 20px; height: 20px; left: 10px; bottom: 0; } + > .ui-resizable-w { cursor: w-resize; width: 10px; left: $horizontal_padding / 2; top: 15px; bottom: 15px; } + + &.ui-draggable-dragging { + &> .ui-resizable-handle { + display: none !important; + } + } + + @for $i from 1 through $gridstack-columns { + &[data-gs-width='#{$i}'] { width: (100% / $gridstack-columns) * $i; } + &[data-gs-x='#{$i}'] { left: (100% / $gridstack-columns) * $i; } + &[data-gs-min-width='#{$i}'] { min-width: (100% / $gridstack-columns) * $i; } + &[data-gs-max-width='#{$i}'] { max-width: (100% / $gridstack-columns) * $i; } + } + } + + &.grid-stack-1>.grid-stack-item { + min-width: 100%; + &[data-gs-width='1'] { width: 100%; } + &[data-gs-x='1'] { left: 100%; } + &[data-gs-min-width='1'] { min-width: 100%; } + &[data-gs-max-width='1'] { max-width: 100%; } + } + + &.grid-stack-animate, + &.grid-stack-animate .grid-stack-item { + @include vendor(transition, left $animation_speed, top $animation_speed, height $animation_speed, width $animation_speed); + } + + &.grid-stack-animate .grid-stack-item.ui-draggable-dragging, + &.grid-stack-animate .grid-stack-item.ui-resizable-resizing, + &.grid-stack-animate .grid-stack-item.grid-stack-placeholder{ + @include vendor(transition, left .0s, top .0s, height .0s, width .0s); + } +} diff --git a/app/assets/stylesheets/blazer/gs_dashboards.scss b/app/assets/stylesheets/blazer/gs_dashboards.scss new file mode 100644 index 000000000..836a9aa21 --- /dev/null +++ b/app/assets/stylesheets/blazer/gs_dashboards.scss @@ -0,0 +1,34 @@ +.btn-primary { + color: #fff; + background-color: #007bff; +} + +.btn { + display: inline-block; + padding: .375rem .75rem; + line-height: 1.5; + border-radius: .25rem; +} + +a { + text-decoration: none; +} + +h1 { + font-size: 2.5rem; + margin-bottom: .5rem; +} +.gs-wrapper { + padding: 10px; + background: #efefed; +} +.grid-stack { + background: #efefed; + margin: 10px; +} + +.grid-stack-item-content { + color: #2c3e50; + text-align: center; + background-color: white; +} diff --git a/app/controllers/blazer/dashboards_controller.rb b/app/controllers/blazer/dashboards_controller.rb index bca02d64d..2f4a35cfe 100644 --- a/app/controllers/blazer/dashboards_controller.rb +++ b/app/controllers/blazer/dashboards_controller.rb @@ -36,16 +36,35 @@ def show @sql_errors << error if error end end + @dashboard_positions = JSON.load(@dashboard.positions) || [] + if @queries.size > @dashboard_positions.size + ids = @dashboard_positions.map { |x| x['id'].to_i } + new_queries = @queries.reject { |q| ids.include? q.id } + new_queries.each do |q| + @dashboard_positions.push({x: 0, y:0, width: 2, height: 2, 'id' => q.id}); + end + end + queries_ids = @queries.map(&:id) + @dashboard_positions.select! { |pos| queries_ids.include?(pos['id'].to_i) } + @dashboard_positions = @dashboard_positions.to_json end def edit end def update - if update_dashboard(@dashboard) - redirect_to dashboard_path(@dashboard, variable_params) - else - render_errors @dashboard + update_dashboard(@dashboard) + respond_to do |format| + format.html do + if @dashboard.errors.blank? + redirect_to dashboard_path(@dashboard, variable_params) + else + render_errors @dashboard + end + end + format.json do + render json: {success: @dashboard.errors.blank?, errors: @dashboard.errors} + end end end @@ -67,35 +86,35 @@ def refresh private - def dashboard_params - params.require(:dashboard).permit(:name) - end + def dashboard_params + params.require(:dashboard).permit(:name, :positions) + end - def set_dashboard - @dashboard = Blazer::Dashboard.find(params[:id]) - end + def set_dashboard + @dashboard = Blazer::Dashboard.find(params[:id]) + end - def update_dashboard(dashboard) - dashboard.assign_attributes(dashboard_params) - Blazer::Dashboard.transaction do - if params[:query_ids].is_a?(Array) - query_ids = params[:query_ids].map(&:to_i) - @queries = Blazer::Query.find(query_ids).sort_by { |q| query_ids.index(q.id) } - end - if dashboard.save - if @queries - @queries.each_with_index do |query, i| - dashboard_query = dashboard.dashboard_queries.where(query_id: query.id).first_or_initialize - dashboard_query.position = i - dashboard_query.save! - end - if dashboard.persisted? - dashboard.dashboard_queries.where.not(query_id: query_ids).destroy_all - end + def update_dashboard(dashboard) + dashboard.assign_attributes(dashboard_params) + Blazer::Dashboard.transaction do + if params[:query_ids].is_a?(Array) + query_ids = params[:query_ids].map(&:to_i) + @queries = Blazer::Query.find(query_ids).sort_by { |q| query_ids.index(q.id) } + end + if dashboard.save + if @queries + @queries.each_with_index do |query, i| + dashboard_query = dashboard.dashboard_queries.where(query_id: query.id).first_or_initialize + dashboard_query.position = i + dashboard_query.save! + end + if dashboard.persisted? + dashboard.dashboard_queries.where.not(query_id: query_ids).destroy_all end - true end + true end end + end end end diff --git a/app/controllers/blazer/queries_controller.rb b/app/controllers/blazer/queries_controller.rb index 6140460d7..47c068708 100644 --- a/app/controllers/blazer/queries_controller.rb +++ b/app/controllers/blazer/queries_controller.rb @@ -180,7 +180,7 @@ def destroy end def tables - render json: @data_source.tables + render json: @data_source.schema.map { |x| x[:schema] + '.' + x[:table] } end def docs diff --git a/app/models/blazer/dashboard.rb b/app/models/blazer/dashboard.rb index 941d95abc..93a9404bf 100644 --- a/app/models/blazer/dashboard.rb +++ b/app/models/blazer/dashboard.rb @@ -3,7 +3,6 @@ class Dashboard < Record belongs_to :creator, optional: true, class_name: Blazer.user_class.to_s if Blazer.user_class has_many :dashboard_queries, dependent: :destroy has_many :queries, through: :dashboard_queries - validates :name, presence: true def to_param diff --git a/app/views/blazer/dashboards/show.html.erb b/app/views/blazer/dashboards/show.html.erb index 29496f0fc..30af52b87 100644 --- a/app/views/blazer/dashboards/show.html.erb +++ b/app/views/blazer/dashboards/show.html.erb @@ -30,22 +30,35 @@ <% else %>
<% end %> - -<% @queries.each_with_index do |query, i| %> -
-

<%= link_to query.friendly_name, query_path(query, variable_params), target: "_blank" %>

-
-

Loading...

+
+ <% @queries.each_with_index do |query, i| %> +
+

<%= link_to query.friendly_name, query_path(query, variable_params), target: "_blank" %>

+
+

Loading...

+
+ <% end %> +
+ +
+
+
- -<% end %> + runQuery(data, function (data) { + $("#chart-<%= i %>").html(data) + $("#chart-<%= i %> table").stupidtable(stupidtableCustomSettings) + }, function (message) { + $("#chart-<%= i %>").addClass("query-error").html(message) + }); + <% end %> + const savePositionURL = '<%= dashboard_path(@dashboard, variable_params, format: :json) %>'; +let serializedData = <%= @dashboard_positions.html_safe %>; +gsDashboards(serializedData, savePositionURL); + diff --git a/lib/generators/blazer/templates/install.rb.tt b/lib/generators/blazer/templates/install.rb.tt index 34a7b5255..26d3d90bb 100644 --- a/lib/generators/blazer/templates/install.rb.tt +++ b/lib/generators/blazer/templates/install.rb.tt @@ -20,6 +20,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version create_table :blazer_dashboards do |t| t.references :creator t.text :name + t.text :positions t.timestamps null: false end