From eb880757b244858e984822abeef265c9e082ca69 Mon Sep 17 00:00:00 2001 From: Matt Gilman Date: Wed, 10 Dec 2014 10:12:43 -0500 Subject: [PATCH] NIFI-27: - Latest version of slickgrid. --- .../src/main/webapp/WEB-INF/pages/canvas.jsp | 2 +- .../src/main/webapp/WEB-INF/pages/cluster.jsp | 2 +- .../main/webapp/WEB-INF/pages/counters.jsp | 2 +- .../src/main/webapp/WEB-INF/pages/history.jsp | 2 +- .../main/webapp/WEB-INF/pages/provenance.jsp | 2 +- .../src/main/webapp/WEB-INF/pages/summary.jsp | 2 +- .../main/webapp/WEB-INF/pages/templates.jsp | 2 +- .../src/main/webapp/WEB-INF/pages/users.jsp | 2 +- .../js/jquery/jquery.event.drag-2.0.min.js | 194 - .../js/jquery/jquery.event.drag-2.2.min.js | 6 + .../js/jquery/slickgrid/MIT-LICENSE.txt | 20 + .../jquery/slickgrid/css/images/collapse.gif | Bin 0 -> 846 bytes .../js/jquery/slickgrid/css/images/expand.gif | Bin 0 -> 851 bytes .../slickgrid/css/slick-default-theme.css | 68 +- .../js/jquery/slickgrid/css/slick.grid.css | 173 +- .../slickgrid/plugins/slick.autotooltips.js | 125 +- .../plugins/slick.cellrangedecorator.js | 126 +- .../plugins/slick.cellrangeselector.js | 218 +- .../plugins/slick.cellselectionmodel.js | 240 +- .../plugins/slick.rowselectionmodel.js | 365 +- .../webapp/js/jquery/slickgrid/slick.core.js | 846 +-- .../js/jquery/slickgrid/slick.dataview.js | 1982 +++--- .../js/jquery/slickgrid/slick.editors.js | 1004 +-- .../js/jquery/slickgrid/slick.formatters.js | 86 +- .../webapp/js/jquery/slickgrid/slick.grid.js | 6254 +++++++++-------- 25 files changed, 6262 insertions(+), 5461 deletions(-) delete mode 100755 nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/jquery.event.drag-2.0.min.js create mode 100644 nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/jquery.event.drag-2.2.min.js create mode 100755 nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/MIT-LICENSE.txt create mode 100755 nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/images/collapse.gif create mode 100755 nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/images/expand.gif diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp index c8f2ff735d..09c8207098 100644 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp @@ -48,7 +48,7 @@ - + diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp index e3e7b86bbe..a0bda249dc 100644 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp @@ -36,7 +36,7 @@ - + diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp index 21ee50862c..2678bf4235 100644 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp @@ -36,7 +36,7 @@ - + diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp index 61be72761f..19d9e90230 100644 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp @@ -36,7 +36,7 @@ - + diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp index 2f5191886e..f3c9a881ee 100644 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp @@ -38,7 +38,7 @@ - + diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp index b34f0e6db5..e8a8e4c7b0 100644 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp @@ -38,7 +38,7 @@ - + diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp index c7c0293d63..889485f4b7 100644 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp @@ -37,7 +37,7 @@ - + diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp index 770dad577b..9ff887b802 100644 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp @@ -38,7 +38,7 @@ - + diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/jquery.event.drag-2.0.min.js b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/jquery.event.drag-2.0.min.js deleted file mode 100755 index 146b20aa17..0000000000 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/jquery.event.drag-2.0.min.js +++ /dev/null @@ -1,194 +0,0 @@ -/*! - * jquery.event.drag - v 2.0.0 - * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com - * Open Source MIT License - http://threedubmedia.com/code/license - */ -; -(function (f) { - f.fn.drag = function (b, a, d) { - var e = typeof b == "string" ? b : "", k = f.isFunction(b) ? b : f.isFunction(a) ? a : null; - if (e.indexOf("drag") !== 0) - e = "drag" + e; - d = (b == k ? a : d) || {}; - return k ? this.bind(e, d, k) : this.trigger(e) - }; - var i = f.event, h = i.special, c = h.drag = {defaults: {which: 1, distance: 0, not: ":input", handle: null, relative: false, drop: true, click: false}, datakey: "dragdata", livekey: "livedrag", add: function (b) { - var a = f.data(this, c.datakey), d = b.data || {}; - a.related += 1; - if (!a.live && b.selector) { - a.live = true; - i.add(this, "draginit." + c.livekey, c.delegate) - } - f.each(c.defaults, function (e) { - if (d[e] !== undefined) - a[e] = d[e] - }) - }, remove: function () { - f.data(this, c.datakey).related -= 1 - }, setup: function () { - if (!f.data(this, c.datakey)) { - var b = f.extend({related: 0}, c.defaults); - f.data(this, c.datakey, b); - i.add(this, "mousedown", c.init, b); - this.attachEvent && this.attachEvent("ondragstart", c.dontstart) - } - }, teardown: function () { - if (!f.data(this, c.datakey).related) { - f.removeData(this, c.datakey); - i.remove(this, "mousedown", c.init); - i.remove(this, "draginit", c.delegate); - c.textselect(true); - this.detachEvent && this.detachEvent("ondragstart", c.dontstart) - } - }, init: function (b) { - var a = b.data, d; - if (!(a.which > 0 && b.which != a.which)) - if (!f(b.target).is(a.not)) - if (!(a.handle && !f(b.target).closest(a.handle, b.currentTarget).length)) { - a.propagates = 1; - a.interactions = [c.interaction(this, a)]; - a.target = b.target; - a.pageX = b.pageX; - a.pageY = b.pageY; - a.dragging = null; - d = c.hijack(b, "draginit", a); - if (a.propagates) { - if ((d = c.flatten(d)) && d.length) { - a.interactions = []; - f.each(d, function () { - a.interactions.push(c.interaction(this, a)) - }) - } - a.propagates = a.interactions.length; - a.drop !== false && h.drop && h.drop.handler(b, a); - c.textselect(false); - i.add(document, "mousemove mouseup", c.handler, a); - return false - } - } - }, interaction: function (b, a) { - return{drag: b, callback: new c.callback, droppable: [], offset: f(b)[a.relative ? "position" : "offset"]() || {top: 0, left: 0}} - }, handler: function (b) { - var a = b.data; - switch (b.type) { - case !a.dragging && "mousemove": - if (Math.pow(b.pageX - a.pageX, 2) + Math.pow(b.pageY - a.pageY, 2) < Math.pow(a.distance, 2)) - break; - b.target = a.target; - c.hijack(b, "dragstart", a); - if (a.propagates) - a.dragging = true; - case "mousemove": - if (a.dragging) { - c.hijack(b, "drag", a); - if (a.propagates) { - a.drop !== false && h.drop && h.drop.handler(b, a); - break - } - b.type = "mouseup" - } - case "mouseup": - i.remove(document, "mousemove mouseup", c.handler); - if (a.dragging) { - a.drop !== false && h.drop && h.drop.handler(b, a); - c.hijack(b, "dragend", a) - } - c.textselect(true); - if (a.click === false && a.dragging) { - jQuery.event.triggered = true; - setTimeout(function () { - jQuery.event.triggered = false - }, 20); - a.dragging = false - } - break - } - }, delegate: function (b) { - var a = [], d, e = f.data(this, "events") || {}; - f.each(e.live || [], function (k, j) { - if (j.preType.indexOf("drag") === 0) - if (d = f(b.target).closest(j.selector, b.currentTarget)[0]) { - i.add(d, j.origType + "." + c.livekey, j.origHandler, j.data); - f.inArray(d, a) < 0 && a.push(d) - } - }); - if (!a.length) - return false; - return f(a).bind("dragend." + c.livekey, function () { - i.remove(this, "." + c.livekey) - }) - }, hijack: function (b, a, d, e, k) { - if (d) { - var j = {event: b.originalEvent, type: b.type}, n = a.indexOf("drop") ? "drag" : "drop", l, o = e || 0, g, m; - e = !isNaN(e) ? e : d.interactions.length; - b.type = a; - b.originalEvent = null; - d.results = []; - do - if (g = d.interactions[o]) - if (!(a !== "dragend" && g.cancelled)) { - m = c.properties(b, d, g); - g.results = []; - f(k || g[n] || d.droppable).each(function (q, p) { - l = (m.target = p) ? i.handle.call(p, b, m) : null; - if (l === false) { - if (n == "drag") { - g.cancelled = true; - d.propagates -= 1 - } - if (a == "drop") - g[n][q] = null - } else if (a == "dropinit") - g.droppable.push(c.element(l) || p); - if (a == "dragstart") - g.proxy = f(c.element(l) || g.drag)[0]; - g.results.push(l); - delete b.result; - if (a !== "dropinit") - return l - }); - d.results[o] = c.flatten(g.results); - if (a == "dropinit") - g.droppable = c.flatten(g.droppable); - a == "dragstart" && !g.cancelled && m.update() - } - while (++o < e); - b.type = j.type; - b.originalEvent = j.event; - return c.flatten(d.results) - } - }, properties: function (b, a, d) { - var e = d.callback; - e.drag = d.drag; - e.proxy = d.proxy || d.drag; - e.startX = a.pageX; - e.startY = a.pageY; - e.deltaX = b.pageX - a.pageX; - e.deltaY = b.pageY - a.pageY; - e.originalX = d.offset.left; - e.originalY = d.offset.top; - e.offsetX = b.pageX - (a.pageX - e.originalX); - e.offsetY = b.pageY - (a.pageY - e.originalY); - e.drop = c.flatten((d.drop || []).slice()); - e.available = c.flatten((d.droppable || []).slice()); - return e - }, element: function (b) { - if (b && (b.jquery || b.nodeType == 1)) - return b - }, flatten: function (b) { - return f.map(b, function (a) { - return a && a.jquery ? f.makeArray(a) : a && a.length ? c.flatten(a) : a - }) - }, textselect: function (b) { - f(document)[b ? "unbind" : "bind"]("selectstart", c.dontstart).attr("unselectable", b ? "off" : "on").css("MozUserSelect", b ? "" : "none") - }, dontstart: function () { - return false - }, callback: function () { - }}; - c.callback.prototype = {update: function () { - h.drop && this.available.length && f.each(this.available, function (b) { - h.drop.locate(this, b) - }) - }}; - h.draginit = h.dragstart = h.dragend = c -})(jQuery); \ No newline at end of file diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/jquery.event.drag-2.2.min.js b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/jquery.event.drag-2.2.min.js new file mode 100644 index 0000000000..cff8ed6538 --- /dev/null +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/jquery.event.drag-2.2.min.js @@ -0,0 +1,6 @@ +/*! + * jquery.event.drag - v 2.2 + * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com + * Open Source MIT License - http://threedubmedia.com/code/license + */ +;(function(e){e.fn.drag=function(k,g,j){var i=typeof k=="string"?k:"",h=e.isFunction(k)?k:e.isFunction(g)?g:null;if(i.indexOf("drag")!==0){i="drag"+i}j=(k==h?g:j)||{};return h?this.bind(i,j,h):this.trigger(i)};var b=e.event,a=b.special,d=a.drag={defaults:{which:1,distance:0,not:":input",handle:null,relative:false,drop:true,click:false},datakey:"dragdata",noBubble:true,add:function(i){var h=e.data(this,d.datakey),g=i.data||{};h.related+=1;e.each(d.defaults,function(j,k){if(g[j]!==undefined){h[j]=g[j]}})},remove:function(){e.data(this,d.datakey).related-=1},setup:function(){if(e.data(this,d.datakey)){return}var g=e.extend({related:0},d.defaults);e.data(this,d.datakey,g);b.add(this,"touchstart mousedown",d.init,g);if(this.attachEvent){this.attachEvent("ondragstart",d.dontstart)}},teardown:function(){var g=e.data(this,d.datakey)||{};if(g.related){return}e.removeData(this,d.datakey);b.remove(this,"touchstart mousedown",d.init);d.textselect(true);if(this.detachEvent){this.detachEvent("ondragstart",d.dontstart)}},init:function(i){if(d.touched){return}var g=i.data,h;if(i.which!=0&&g.which>0&&i.which!=g.which){return}if(e(i.target).is(g.not)){return}if(g.handle&&!e(i.target).closest(g.handle,i.currentTarget).length){return}d.touched=i.type=="touchstart"?this:null;g.propagates=1;g.mousedown=this;g.interactions=[d.interaction(this,g)];g.target=i.target;g.pageX=i.pageX;g.pageY=i.pageY;g.dragging=null;h=d.hijack(i,"draginit",g);if(!g.propagates){return}h=d.flatten(h);if(h&&h.length){g.interactions=[];e.each(h,function(){g.interactions.push(d.interaction(this,g))})}g.propagates=g.interactions.length;if(g.drop!==false&&a.drop){a.drop.handler(i,g)}d.textselect(false);if(d.touched){b.add(d.touched,"touchmove touchend",d.handler,g)}else{b.add(document,"mousemove mouseup",d.handler,g)}if(!d.touched||g.live){return false}},interaction:function(h,g){var i=e(h)[g.relative?"position":"offset"]()||{top:0,left:0};return{drag:h,callback:new d.callback(),droppable:[],offset:i}},handler:function(h){var g=h.data;switch(h.type){case !g.dragging&&"touchmove":h.preventDefault();case !g.dragging&&"mousemove":if(Math.pow(h.pageX-g.pageX,2)+Math.pow(h.pageY-g.pageY,2)0){e.removeData(this,"suppress."+g.type);return}return f.apply(this,arguments)};var c=b.fixHooks.touchstart=b.fixHooks.touchmove=b.fixHooks.touchend=b.fixHooks.touchcancel={props:"clientX clientY pageX pageY screenX screenY".split(" "),filter:function(h,i){if(i){var g=(i.touches&&i.touches[0])||(i.changedTouches&&i.changedTouches[0])||null;if(g){e.each(c.props,function(j,k){h[k]=g[k]})}}return h}};a.draginit=a.dragstart=a.dragend=d})(jQuery); \ No newline at end of file diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/MIT-LICENSE.txt b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/MIT-LICENSE.txt new file mode 100755 index 0000000000..60f65425c0 --- /dev/null +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/MIT-LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2010 Michael Leibman, http://github.com/mleibman/slickgrid + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/images/collapse.gif b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/images/collapse.gif new file mode 100755 index 0000000000000000000000000000000000000000..01e691450c48632dc92a40a8c2b509bf99987225 GIT binary patch literal 846 zcmZ?wbhEHb$FyaIl$)g-gQVfI}k_i-?$k!^A~S3=)P?0Tm31hZvXy UI%HM~I5JIC=Ul}jAi!V^09i~Z*8l(j literal 0 HcmV?d00001 diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/images/expand.gif b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/images/expand.gif new file mode 100755 index 0000000000000000000000000000000000000000..1b24ef1248de55c6140895f6760758a1579f14cf GIT binary patch literal 851 zcmZ?wbhEHb$FyaIl$)g-gPK!J(0fMMMlpH!w1DF=kxpFl=aGX3~-h X>0od?I!Q%XL14k81*aJp85yhr3QsCm literal 0 HcmV?d00001 diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/slick-default-theme.css b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/slick-default-theme.css index c68b703282..6b45f3e821 100755 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/slick-default-theme.css +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/slick-default-theme.css @@ -31,24 +31,24 @@ classes should alter those! } .slick-headerrow { - background: #fafafa; + background: #fafafa; } .slick-headerrow-column { - background: #fafafa; - border-bottom: 0; - height: 100%; + background: #fafafa; + border-bottom: 0; + height: 100%; } .slick-row.ui-state-active { - background: #F5F7D7; + background: #F5F7D7; } .slick-row { - position: absolute; - background: white; - border: 0px; - line-height: 20px; + position: absolute; + background: white; + border: 0px; + line-height: 20px; } .slick-row:hover { @@ -56,37 +56,37 @@ classes should alter those! } .slick-row.selected { - z-index: 10; + z-index: 10; background: #DFE8F6 !important; } .slick-cell { - padding-left: 4px; - padding-right: 4px; + padding-left: 4px; + padding-right: 4px; border-style: solid !important; } .slick-group { - border-bottom: 2px solid silver; + border-bottom: 2px solid silver; } .slick-group-toggle { - width: 9px; - height: 9px; - margin-right: 5px; + width: 9px; + height: 9px; + margin-right: 5px; } .slick-group-toggle.expanded { - background: url(../../../../images/collapse.gif) no-repeat center center; + background: url(images/collapse.gif) no-repeat center center; } .slick-group-toggle.collapsed { - background: url(../../../../images/expand.gif) no-repeat center center; + background: url(images/expand.gif) no-repeat center center; } .slick-group-totals { - color: gray; - background: white; + color: gray; + background: white; } .slick-cell.selected { @@ -100,22 +100,36 @@ classes should alter those! } .slick-sortable-placeholder { - background: silver !important; + background: silver !important; } -.slick-row[row$="1"], .slick-row[row$="3"], .slick-row[row$="5"], .slick-row[row$="7"], .slick-row[row$="9"] { - background: #fafafa; +.slick-row.odd { + background: #fafafa; } .slick-row.ui-state-active { - background: #F5F7D7; + background: #F5F7D7; } .slick-row.loading { - opacity: 0.5; - filter: alpha(opacity = 50); + opacity: 0.5; + filter: alpha(opacity = 50); } .slick-cell.invalid { - border-color: red; + border-color: red; + -moz-animation-duration: 0.2s; + -webkit-animation-duration: 0.2s; + -moz-animation-name: slickgrid-invalid-hilite; + -webkit-animation-name: slickgrid-invalid-hilite; +} + +@-moz-keyframes slickgrid-invalid-hilite { + from { box-shadow: 0 0 6px red; } + to { box-shadow: none; } +} + +@-webkit-keyframes slickgrid-invalid-hilite { + from { box-shadow: 0 0 6px red; } + to { box-shadow: none; } } \ No newline at end of file diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/slick.grid.css b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/slick.grid.css index 2e8f390594..de94dbfe45 100755 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/slick.grid.css +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/css/slick.grid.css @@ -6,153 +6,152 @@ classes should alter those! */ .slick-header.ui-state-default, .slick-headerrow.ui-state-default { - width: 100%; - overflow: hidden; - border-left: 0px; + width: 100%; + overflow: hidden; + border-left: 0px; } .slick-header-columns, .slick-headerrow-columns { - width: 999999px; - position: relative; - white-space: nowrap; - cursor: default; - overflow: hidden; + position: relative; + white-space: nowrap; + cursor: default; + overflow: hidden; } .slick-header-column.ui-state-default { - position: relative; - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - height: 16px; - line-height: 16px; - margin: 0; - padding: 4px; - border-right: 1px solid silver; - border-left: 0px; - border-top: 0px; - border-bottom: 0px; - float: left; + position: relative; + display: inline-block; + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + height: 16px; + line-height: 16px; + margin: 0; + padding: 4px; + border-right: 1px solid silver; + border-left: 0px; + border-top: 0px; + border-bottom: 0px; + float: left; } .slick-headerrow-column.ui-state-default { - padding: 4px; + padding: 4px; } .slick-header-column-sorted { - font-style: italic; + font-style: italic; } .slick-sort-indicator { - display: inline-block; - width: 8px; - height: 5px; - margin-left: 4px; + display: inline-block; + width: 8px; + height: 5px; + margin-left: 4px; + margin-top: 6px; + /*float: left;*/ } .slick-sort-indicator-desc { - background: url(images/sort-desc.gif); + background: url(images/sort-desc.gif); } .slick-sort-indicator-asc { - background: url(images/sort-asc.gif); + background: url(images/sort-asc.gif); } .slick-resizable-handle { - position: absolute; - font-size: 0.1px; - display: block; - cursor: col-resize; - width: 4px; - right: 0px; - top: 0; - height: 100%; + position: absolute; + font-size: 0.1px; + display: block; + cursor: col-resize; + width: 4px; + right: 0px; + top: 0; + height: 100%; } .slick-sortable-placeholder { - background: silver; + background: silver; } .grid-canvas { - position: relative; - outline: 0; + position: relative; + outline: 0; } .slick-row.ui-widget-content, .slick-row.ui-state-active { - position: absolute; - border: 0px; - width: 100%; + position: absolute; + border: 0px; + width: 100%; } .slick-cell, .slick-headerrow-column { - position: absolute; - - border: 1px solid transparent; - border-right: 1px dotted silver; - border-bottom-color: silver; - - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - vertical-align: middle; - z-index: 1; - padding: 1px 2px 2px 1px; - margin: 0; - - white-space: nowrap; - - cursor: default; + position: absolute; + border: 1px solid transparent; + border-right: 1px dotted silver; + border-bottom-color: silver; + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + vertical-align: middle; + z-index: 1; + padding: 1px 2px 2px 1px; + margin: 0; + white-space: nowrap; + cursor: default; } .slick-group { } .slick-group-toggle { - display: inline-block; + display: inline-block; } .slick-cell.highlighted { - background: lightskyblue; - background: rgba(0, 0, 255, 0.2); - -webkit-transition: all 0.5s; - -moz-transition: all 0.5s; - transition: all 0.5s; + background: lightskyblue; + background: rgba(0, 0, 255, 0.2); + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + -o-transition: all 0.5s; + transition: all 0.5s; } .slick-cell.flashing { - border: 1px solid red !important; + border: 1px solid red !important; } .slick-cell.editable { - z-index: 11; - overflow: visible; - background: white; - border-color: black; - border-style: solid; + z-index: 11; + overflow: visible; + background: white; + border-color: black; + border-style: solid; } .slick-cell:focus { - outline: none; + outline: none; } .slick-reorder-proxy { - display: inline-block; - background: blue; - opacity: 0.15; - filter: alpha(opacity = 15); - cursor: move; + display: inline-block; + background: blue; + opacity: 0.15; + filter: alpha(opacity = 15); + cursor: move; } .slick-reorder-guide { - display: inline-block; - height: 2px; - background: blue; - opacity: 0.7; - filter: alpha(opacity = 70); + display: inline-block; + height: 2px; + background: blue; + opacity: 0.7; + filter: alpha(opacity = 70); } .slick-selection { - z-index: 10; - position: absolute; - border: 2px dashed black; -} \ No newline at end of file + z-index: 10; + position: absolute; + border: 2px dashed black; +} diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.autotooltips.js b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.autotooltips.js index bce0beab6c..955684f2aa 100755 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.autotooltips.js +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.autotooltips.js @@ -1,48 +1,83 @@ (function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "AutoTooltips": AutoTooltips - } - }); - - - function AutoTooltips(options) { - var _grid; - var _self = this; - var _defaults = { - maxToolTipLength: null - }; - - function init(grid) { - options = $.extend(true, {}, _defaults, options); - _grid = grid; - _grid.onMouseEnter.subscribe(handleMouseEnter); - } - - function destroy() { - _grid.onMouseEnter.unsubscribe(handleMouseEnter); - } - - function handleMouseEnter(e, args) { - var cell = _grid.getCellFromEvent(e); - if (cell) { - var node = _grid.getCellNode(cell.row, cell.cell); - if ($(node).innerWidth() < node.scrollWidth) { - var text = $.trim($(node).text()); - if (options.maxToolTipLength && text.length > options.maxToolTipLength) { - text = text.substr(0, options.maxToolTipLength - 3) + "..."; - } - $(node).attr("title", text); - } else { - $(node).attr("title", ""); - } - } - } - - $.extend(this, { - "init": init, - "destroy": destroy - }); + // Register namespace + $.extend(true, window, { + "Slick": { + "AutoTooltips": AutoTooltips } + }); + + /** + * AutoTooltips plugin to show/hide tooltips when columns are too narrow to fit content. + * @constructor + * @param {boolean} [options.enableForCells=true] - Enable tooltip for grid cells + * @param {boolean} [options.enableForHeaderCells=false] - Enable tooltip for header cells + * @param {number} [options.maxToolTipLength=null] - The maximum length for a tooltip + */ + function AutoTooltips(options) { + var _grid; + var _self = this; + var _defaults = { + enableForCells: true, + enableForHeaderCells: false, + maxToolTipLength: null + }; + + /** + * Initialize plugin. + */ + function init(grid) { + options = $.extend(true, {}, _defaults, options); + _grid = grid; + if (options.enableForCells) _grid.onMouseEnter.subscribe(handleMouseEnter); + if (options.enableForHeaderCells) _grid.onHeaderMouseEnter.subscribe(handleHeaderMouseEnter); + } + + /** + * Destroy plugin. + */ + function destroy() { + if (options.enableForCells) _grid.onMouseEnter.unsubscribe(handleMouseEnter); + if (options.enableForHeaderCells) _grid.onHeaderMouseEnter.unsubscribe(handleHeaderMouseEnter); + } + + /** + * Handle mouse entering grid cell to add/remove tooltip. + * @param {jQuery.Event} e - The event + */ + function handleMouseEnter(e) { + var cell = _grid.getCellFromEvent(e); + if (cell) { + var $node = $(_grid.getCellNode(cell.row, cell.cell)); + var text; + if ($node.innerWidth() < $node[0].scrollWidth) { + text = $.trim($node.text()); + if (options.maxToolTipLength && text.length > options.maxToolTipLength) { + text = text.substr(0, options.maxToolTipLength - 3) + "..."; + } + } else { + text = ""; + } + $node.attr("title", text); + } + } + + /** + * Handle mouse entering header cell to add/remove tooltip. + * @param {jQuery.Event} e - The event + * @param {object} args.column - The column definition + */ + function handleHeaderMouseEnter(e, args) { + var column = args.column, + $node = $(e.target).closest(".slick-header-column"); + if (!column.toolTip) { + $node.attr("title", ($node.innerWidth() < $node[0].scrollWidth) ? column.name : ""); + } + } + + // Public API + $.extend(this, { + "init": init, + "destroy": destroy + }); + } })(jQuery); \ No newline at end of file diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.cellrangedecorator.js b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.cellrangedecorator.js index eb2d506deb..0cbe71d48d 100755 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.cellrangedecorator.js +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.cellrangedecorator.js @@ -1,64 +1,66 @@ (function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "CellRangeDecorator": CellRangeDecorator - } - }); - - /*** - * Displays an overlay on top of a given cell range. - * - * TODO: - * Currently, it blocks mouse events to DOM nodes behind it. - * Use FF and WebKit-specific "pointer-events" CSS style, or some kind of event forwarding. - * Could also construct the borders separately using 4 individual DIVs. - * - * @param {Grid} grid - * @param {Object} options - */ - function CellRangeDecorator(grid, options) { - var _elem; - var _defaults = { - selectionCss: { - "zIndex": "9999", - "border": "2px dashed red" - } - }; - - options = $.extend(true, {}, _defaults, options); - - - function show(range) { - if (!_elem) { - _elem = $("
", {css: options.selectionCss}) - .css("position", "absolute") - .appendTo(grid.getCanvasNode()); - } - - var from = grid.getCellNodeBox(range.fromRow, range.fromCell); - var to = grid.getCellNodeBox(range.toRow, range.toCell); - - _elem.css({ - top: from.top - 1, - left: from.left - 1, - height: to.bottom - from.top - 2, - width: to.right - from.left - 2 - }); - - return _elem; - } - - function hide() { - if (_elem) { - _elem.remove(); - _elem = null; - } - } - - $.extend(this, { - "show": show, - "hide": hide - }); + // register namespace + $.extend(true, window, { + "Slick": { + "CellRangeDecorator": CellRangeDecorator } -})(jQuery); \ No newline at end of file + }); + + /*** + * Displays an overlay on top of a given cell range. + * + * TODO: + * Currently, it blocks mouse events to DOM nodes behind it. + * Use FF and WebKit-specific "pointer-events" CSS style, or some kind of event forwarding. + * Could also construct the borders separately using 4 individual DIVs. + * + * @param {Grid} grid + * @param {Object} options + */ + function CellRangeDecorator(grid, options) { + var _elem; + var _defaults = { + selectionCssClass: 'slick-range-decorator', + selectionCss: { + "zIndex": "9999", + "border": "2px dashed red" + } + }; + + options = $.extend(true, {}, _defaults, options); + + + function show(range) { + if (!_elem) { + _elem = $("
", {css: options.selectionCss}) + .addClass(options.selectionCssClass) + .css("position", "absolute") + .appendTo(grid.getCanvasNode()); + } + + var from = grid.getCellNodeBox(range.fromRow, range.fromCell); + var to = grid.getCellNodeBox(range.toRow, range.toCell); + + _elem.css({ + top: from.top - 1, + left: from.left - 1, + height: to.bottom - from.top - 2, + width: to.right - from.left - 2 + }); + + return _elem; + } + + function hide() { + if (_elem) { + _elem.remove(); + _elem = null; + } + } + + $.extend(this, { + "show": show, + "hide": hide + }); + } +})(jQuery); diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.cellrangeselector.js b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.cellrangeselector.js index 04d2f90dc0..520b17f3c4 100755 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.cellrangeselector.js +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.cellrangeselector.js @@ -1,111 +1,113 @@ (function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "CellRangeSelector": CellRangeSelector - } - }); - - - function CellRangeSelector(options) { - var _grid; - var _canvas; - var _dragging; - var _decorator; - var _self = this; - var _defaults = { - selectionCss: { - "border": "2px dashed blue" - } - }; - - - function init(grid) { - options = $.extend(true, {}, _defaults, options); - _decorator = new Slick.CellRangeDecorator(grid, options); - _grid = grid; - _canvas = _grid.getCanvasNode(); - _grid.onDragInit.subscribe(handleDragInit); - _grid.onDragStart.subscribe(handleDragStart); - _grid.onDrag.subscribe(handleDrag); - _grid.onDragEnd.subscribe(handleDragEnd); - } - - function destroy() { - _grid.onDragInit.unsubscribe(handleDragInit); - _grid.onDragStart.unsubscribe(handleDragStart); - _grid.onDrag.unsubscribe(handleDrag); - _grid.onDragEnd.unsubscribe(handleDragEnd); - } - - function handleDragInit(e, dd) { - // prevent the grid from cancelling drag'n'drop by default - e.stopImmediatePropagation(); - } - - function handleDragStart(e, dd) { - var cell = _grid.getCellFromEvent(e); - if (_self.onBeforeCellRangeSelected.notify(cell) !== false) { - if (_grid.canCellBeSelected(cell.row, cell.cell)) { - _dragging = true; - e.stopImmediatePropagation(); - } - } - if (!_dragging) { - return; - } - - var start = _grid.getCellFromPoint( - dd.startX - $(_canvas).offset().left, - dd.startY - $(_canvas).offset().top); - - dd.range = {start: start, end: {}}; - - return _decorator.show(new Slick.Range(start.row, start.cell)); - } - - function handleDrag(e, dd) { - if (!_dragging) { - return; - } - e.stopImmediatePropagation(); - - var end = _grid.getCellFromPoint( - e.pageX - $(_canvas).offset().left, - e.pageY - $(_canvas).offset().top); - - if (!_grid.canCellBeSelected(end.row, end.cell)) { - return; - } - - dd.range.end = end; - _decorator.show(new Slick.Range(dd.range.start.row, dd.range.start.cell, end.row, end.cell)); - } - - function handleDragEnd(e, dd) { - if (!_dragging) { - return; - } - - _dragging = false; - e.stopImmediatePropagation(); - - _decorator.hide(); - _self.onCellRangeSelected.notify({ - range: new Slick.Range( - dd.range.start.row, - dd.range.start.cell, - dd.range.end.row, - dd.range.end.cell - ) - }); - } - - $.extend(this, { - "init": init, - "destroy": destroy, - "onBeforeCellRangeSelected": new Slick.Event(), - "onCellRangeSelected": new Slick.Event() - }); + // register namespace + $.extend(true, window, { + "Slick": { + "CellRangeSelector": CellRangeSelector } + }); + + + function CellRangeSelector(options) { + var _grid; + var _canvas; + var _dragging; + var _decorator; + var _self = this; + var _handler = new Slick.EventHandler(); + var _defaults = { + selectionCss: { + "border": "2px dashed blue" + } + }; + + + function init(grid) { + options = $.extend(true, {}, _defaults, options); + _decorator = new Slick.CellRangeDecorator(grid, options); + _grid = grid; + _canvas = _grid.getCanvasNode(); + _handler + .subscribe(_grid.onDragInit, handleDragInit) + .subscribe(_grid.onDragStart, handleDragStart) + .subscribe(_grid.onDrag, handleDrag) + .subscribe(_grid.onDragEnd, handleDragEnd); + } + + function destroy() { + _handler.unsubscribeAll(); + } + + function handleDragInit(e, dd) { + // prevent the grid from cancelling drag'n'drop by default + e.stopImmediatePropagation(); + } + + function handleDragStart(e, dd) { + var cell = _grid.getCellFromEvent(e); + if (_self.onBeforeCellRangeSelected.notify(cell) !== false) { + if (_grid.canCellBeSelected(cell.row, cell.cell)) { + _dragging = true; + e.stopImmediatePropagation(); + } + } + if (!_dragging) { + return; + } + + _grid.focus(); + + var start = _grid.getCellFromPoint( + dd.startX - $(_canvas).offset().left, + dd.startY - $(_canvas).offset().top); + + dd.range = {start: start, end: {}}; + + return _decorator.show(new Slick.Range(start.row, start.cell)); + } + + function handleDrag(e, dd) { + if (!_dragging) { + return; + } + e.stopImmediatePropagation(); + + var end = _grid.getCellFromPoint( + e.pageX - $(_canvas).offset().left, + e.pageY - $(_canvas).offset().top); + + if (!_grid.canCellBeSelected(end.row, end.cell)) { + return; + } + + dd.range.end = end; + _decorator.show(new Slick.Range(dd.range.start.row, dd.range.start.cell, end.row, end.cell)); + } + + function handleDragEnd(e, dd) { + if (!_dragging) { + return; + } + + _dragging = false; + e.stopImmediatePropagation(); + + _decorator.hide(); + _self.onCellRangeSelected.notify({ + range: new Slick.Range( + dd.range.start.row, + dd.range.start.cell, + dd.range.end.row, + dd.range.end.cell + ) + }); + } + + $.extend(this, { + "init": init, + "destroy": destroy, + + "onBeforeCellRangeSelected": new Slick.Event(), + "onCellRangeSelected": new Slick.Event() + }); + } })(jQuery); \ No newline at end of file diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.cellselectionmodel.js b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.cellselectionmodel.js index 9f0730a12c..74bc3eb70e 100755 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.cellselectionmodel.js +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.cellselectionmodel.js @@ -1,90 +1,154 @@ (function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "CellSelectionModel": CellSelectionModel - } - }); - - - function CellSelectionModel(options) { - var _grid; - var _canvas; - var _ranges = []; - var _self = this; - var _selector = new Slick.CellRangeSelector({ - "selectionCss": { - "border": "2px solid black" - } - }); - var _options; - var _defaults = { - selectActiveCell: true - }; - - - function init(grid) { - _options = $.extend(true, {}, _defaults, options); - _grid = grid; - _canvas = _grid.getCanvasNode(); - _grid.onActiveCellChanged.subscribe(handleActiveCellChange); - grid.registerPlugin(_selector); - _selector.onCellRangeSelected.subscribe(handleCellRangeSelected); - _selector.onBeforeCellRangeSelected.subscribe(handleBeforeCellRangeSelected); - } - - function destroy() { - _grid.onActiveCellChanged.unsubscribe(handleActiveCellChange); - _selector.onCellRangeSelected.unsubscribe(handleCellRangeSelected); - _selector.onBeforeCellRangeSelected.unsubscribe(handleBeforeCellRangeSelected); - _grid.unregisterPlugin(_selector); - } - - function removeInvalidRanges(ranges) { - var result = []; - - for (var i = 0; i < ranges.length; i++) { - var r = ranges[i]; - if (_grid.canCellBeSelected(r.fromRow, r.fromCell) && _grid.canCellBeSelected(r.toRow, r.toCell)) { - result.push(r); - } - } - - return result; - } - - function setSelectedRanges(ranges) { - _ranges = removeInvalidRanges(ranges); - _self.onSelectedRangesChanged.notify(_ranges); - } - - function getSelectedRanges() { - return _ranges; - } - - function handleBeforeCellRangeSelected(e, args) { - if (_grid.getEditorLock().isActive()) { - e.stopPropagation(); - return false; - } - } - - function handleCellRangeSelected(e, args) { - setSelectedRanges([args.range]); - } - - function handleActiveCellChange(e, args) { - if (_options.selectActiveCell) { - setSelectedRanges([new Slick.Range(args.row, args.cell)]); - } - } - - $.extend(this, { - "getSelectedRanges": getSelectedRanges, - "setSelectedRanges": setSelectedRanges, - "init": init, - "destroy": destroy, - "onSelectedRangesChanged": new Slick.Event() - }); + // register namespace + $.extend(true, window, { + "Slick": { + "CellSelectionModel": CellSelectionModel } -})(jQuery); \ No newline at end of file + }); + + + function CellSelectionModel(options) { + var _grid; + var _canvas; + var _ranges = []; + var _self = this; + var _selector = new Slick.CellRangeSelector({ + "selectionCss": { + "border": "2px solid black" + } + }); + var _options; + var _defaults = { + selectActiveCell: true + }; + + + function init(grid) { + _options = $.extend(true, {}, _defaults, options); + _grid = grid; + _canvas = _grid.getCanvasNode(); + _grid.onActiveCellChanged.subscribe(handleActiveCellChange); + _grid.onKeyDown.subscribe(handleKeyDown); + grid.registerPlugin(_selector); + _selector.onCellRangeSelected.subscribe(handleCellRangeSelected); + _selector.onBeforeCellRangeSelected.subscribe(handleBeforeCellRangeSelected); + } + + function destroy() { + _grid.onActiveCellChanged.unsubscribe(handleActiveCellChange); + _grid.onKeyDown.unsubscribe(handleKeyDown); + _selector.onCellRangeSelected.unsubscribe(handleCellRangeSelected); + _selector.onBeforeCellRangeSelected.unsubscribe(handleBeforeCellRangeSelected); + _grid.unregisterPlugin(_selector); + } + + function removeInvalidRanges(ranges) { + var result = []; + + for (var i = 0; i < ranges.length; i++) { + var r = ranges[i]; + if (_grid.canCellBeSelected(r.fromRow, r.fromCell) && _grid.canCellBeSelected(r.toRow, r.toCell)) { + result.push(r); + } + } + + return result; + } + + function setSelectedRanges(ranges) { + _ranges = removeInvalidRanges(ranges); + _self.onSelectedRangesChanged.notify(_ranges); + } + + function getSelectedRanges() { + return _ranges; + } + + function handleBeforeCellRangeSelected(e, args) { + if (_grid.getEditorLock().isActive()) { + e.stopPropagation(); + return false; + } + } + + function handleCellRangeSelected(e, args) { + setSelectedRanges([args.range]); + } + + function handleActiveCellChange(e, args) { + if (_options.selectActiveCell && args.row != null && args.cell != null) { + setSelectedRanges([new Slick.Range(args.row, args.cell)]); + } + } + + function handleKeyDown(e) { + /*** + * Кey codes + * 37 left + * 38 up + * 39 right + * 40 down + */ + var ranges, last; + var active = _grid.getActiveCell(); + + if ( active && e.shiftKey && !e.ctrlKey && !e.altKey && + (e.which == 37 || e.which == 39 || e.which == 38 || e.which == 40) ) { + + ranges = getSelectedRanges(); + if (!ranges.length) + ranges.push(new Slick.Range(active.row, active.cell)); + + // keyboard can work with last range only + last = ranges.pop(); + + // can't handle selection out of active cell + if (!last.contains(active.row, active.cell)) + last = new Slick.Range(active.row, active.cell); + + var dRow = last.toRow - last.fromRow, + dCell = last.toCell - last.fromCell, + // walking direction + dirRow = active.row == last.fromRow ? 1 : -1, + dirCell = active.cell == last.fromCell ? 1 : -1; + + if (e.which == 37) { + dCell -= dirCell; + } else if (e.which == 39) { + dCell += dirCell ; + } else if (e.which == 38) { + dRow -= dirRow; + } else if (e.which == 40) { + dRow += dirRow; + } + + // define new selection range + var new_last = new Slick.Range(active.row, active.cell, active.row + dirRow*dRow, active.cell + dirCell*dCell); + if (removeInvalidRanges([new_last]).length) { + ranges.push(new_last); + var viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow; + var viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell; + _grid.scrollRowIntoView(viewRow); + _grid.scrollCellIntoView(viewRow, viewCell); + } + else + ranges.push(last); + + setSelectedRanges(ranges); + + e.preventDefault(); + e.stopPropagation(); + } + } + + $.extend(this, { + "getSelectedRanges": getSelectedRanges, + "setSelectedRanges": setSelectedRanges, + + "init": init, + "destroy": destroy, + + "onSelectedRangesChanged": new Slick.Event() + }); + } +})(jQuery); diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.rowselectionmodel.js b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.rowselectionmodel.js index af966c0b73..0de8dd3a40 100755 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.rowselectionmodel.js +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/plugins/slick.rowselectionmodel.js @@ -1,184 +1,187 @@ (function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "RowSelectionModel": RowSelectionModel - } - }); - - function RowSelectionModel(options) { - var _grid; - var _ranges = []; - var _self = this; - var _handler = new Slick.EventHandler(); - var _inHandler; - var _options; - var _defaults = { - selectActiveRow: true - }; - - function init(grid) { - _options = $.extend(true, {}, _defaults, options); - _grid = grid; - _handler.subscribe(_grid.onActiveCellChanged, - wrapHandler(handleActiveCellChange)); - _handler.subscribe(_grid.onKeyDown, - wrapHandler(handleKeyDown)); - _handler.subscribe(_grid.onClick, - wrapHandler(handleClick)); - } - - function destroy() { - _handler.unsubscribeAll(); - } - - function wrapHandler(handler) { - return function () { - if (!_inHandler) { - _inHandler = true; - handler.apply(this, arguments); - _inHandler = false; - } - }; - } - - function rangesToRows(ranges) { - var rows = []; - for (var i = 0; i < ranges.length; i++) { - for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { - rows.push(j); - } - } - return rows; - } - - function rowsToRanges(rows) { - var ranges = []; - var lastCell = _grid.getColumns().length - 1; - for (var i = 0; i < rows.length; i++) { - ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell)); - } - return ranges; - } - - function getRowsRange(from, to) { - var i, rows = []; - for (i = from; i <= to; i++) { - rows.push(i); - } - for (i = to; i < from; i++) { - rows.push(i); - } - return rows; - } - - function getSelectedRows() { - return rangesToRows(_ranges); - } - - function setSelectedRows(rows) { - setSelectedRanges(rowsToRanges(rows)); - } - - function setSelectedRanges(ranges) { - _ranges = ranges; - _self.onSelectedRangesChanged.notify(_ranges); - } - - function getSelectedRanges() { - return _ranges; - } - - function handleActiveCellChange(e, data) { - if (_options.selectActiveRow) { - setSelectedRanges([new Slick.Range(data.row, 0, data.row, _grid.getColumns().length - 1)]); - } - } - - function handleKeyDown(e) { - var activeRow = _grid.getActiveCell(); - if (activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.which == 38 || e.which == 40)) { - var selectedRows = getSelectedRows(); - selectedRows.sort(function (x, y) { - return x - y - }); - - if (!selectedRows.length) { - selectedRows = [activeRow.row]; - } - - var top = selectedRows[0]; - var bottom = selectedRows[selectedRows.length - 1]; - var active; - - if (e.which == 40) { - active = activeRow.row < bottom || top == bottom ? ++bottom : ++top; - } else { - active = activeRow.row < bottom ? --bottom : --top; - } - - if (active >= 0 && active < _grid.getDataLength()) { - _grid.scrollRowIntoView(active); - _ranges = rowsToRanges(getRowsRange(top, bottom)); - setSelectedRanges(_ranges); - } - - e.preventDefault(); - e.stopPropagation(); - } - } - - function handleClick(e) { - var cell = _grid.getCellFromEvent(e); - if (!cell || !_grid.canCellBeActive(cell.row, cell.cell)) { - return false; - } - - var selection = rangesToRows(_ranges); - var idx = $.inArray(cell.row, selection); - - if (!e.ctrlKey && !e.shiftKey && !e.metaKey) { - return false; - } - else if (_grid.getOptions().multiSelect) { - if (idx === -1 && (e.ctrlKey || e.metaKey)) { - selection.push(cell.row); - _grid.setActiveCell(cell.row, cell.cell); - } else if (idx !== -1 && (e.ctrlKey || e.metaKey)) { - selection = $.grep(selection, function (o, i) { - return (o !== cell.row); - }); - _grid.setActiveCell(cell.row, cell.cell); - } else if (selection.length && e.shiftKey) { - var last = selection.pop(); - var from = Math.min(cell.row, last); - var to = Math.max(cell.row, last); - selection = []; - for (var i = from; i <= to; i++) { - if (i !== last) { - selection.push(i); - } - } - selection.push(last); - _grid.setActiveCell(cell.row, cell.cell); - } - } - - _ranges = rowsToRanges(selection); - setSelectedRanges(_ranges); - e.stopImmediatePropagation(); - - return true; - } - - $.extend(this, { - "getSelectedRows": getSelectedRows, - "setSelectedRows": setSelectedRows, - "getSelectedRanges": getSelectedRanges, - "setSelectedRanges": setSelectedRanges, - "init": init, - "destroy": destroy, - "onSelectedRangesChanged": new Slick.Event() - }); + // register namespace + $.extend(true, window, { + "Slick": { + "RowSelectionModel": RowSelectionModel } + }); + + function RowSelectionModel(options) { + var _grid; + var _ranges = []; + var _self = this; + var _handler = new Slick.EventHandler(); + var _inHandler; + var _options; + var _defaults = { + selectActiveRow: true + }; + + function init(grid) { + _options = $.extend(true, {}, _defaults, options); + _grid = grid; + _handler.subscribe(_grid.onActiveCellChanged, + wrapHandler(handleActiveCellChange)); + _handler.subscribe(_grid.onKeyDown, + wrapHandler(handleKeyDown)); + _handler.subscribe(_grid.onClick, + wrapHandler(handleClick)); + } + + function destroy() { + _handler.unsubscribeAll(); + } + + function wrapHandler(handler) { + return function () { + if (!_inHandler) { + _inHandler = true; + handler.apply(this, arguments); + _inHandler = false; + } + }; + } + + function rangesToRows(ranges) { + var rows = []; + for (var i = 0; i < ranges.length; i++) { + for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { + rows.push(j); + } + } + return rows; + } + + function rowsToRanges(rows) { + var ranges = []; + var lastCell = _grid.getColumns().length - 1; + for (var i = 0; i < rows.length; i++) { + ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell)); + } + return ranges; + } + + function getRowsRange(from, to) { + var i, rows = []; + for (i = from; i <= to; i++) { + rows.push(i); + } + for (i = to; i < from; i++) { + rows.push(i); + } + return rows; + } + + function getSelectedRows() { + return rangesToRows(_ranges); + } + + function setSelectedRows(rows) { + setSelectedRanges(rowsToRanges(rows)); + } + + function setSelectedRanges(ranges) { + _ranges = ranges; + _self.onSelectedRangesChanged.notify(_ranges); + } + + function getSelectedRanges() { + return _ranges; + } + + function handleActiveCellChange(e, data) { + if (_options.selectActiveRow && data.row != null) { + setSelectedRanges([new Slick.Range(data.row, 0, data.row, _grid.getColumns().length - 1)]); + } + } + + function handleKeyDown(e) { + var activeRow = _grid.getActiveCell(); + if (activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.which == 38 || e.which == 40)) { + var selectedRows = getSelectedRows(); + selectedRows.sort(function (x, y) { + return x - y + }); + + if (!selectedRows.length) { + selectedRows = [activeRow.row]; + } + + var top = selectedRows[0]; + var bottom = selectedRows[selectedRows.length - 1]; + var active; + + if (e.which == 40) { + active = activeRow.row < bottom || top == bottom ? ++bottom : ++top; + } else { + active = activeRow.row < bottom ? --bottom : --top; + } + + if (active >= 0 && active < _grid.getDataLength()) { + _grid.scrollRowIntoView(active); + _ranges = rowsToRanges(getRowsRange(top, bottom)); + setSelectedRanges(_ranges); + } + + e.preventDefault(); + e.stopPropagation(); + } + } + + function handleClick(e) { + var cell = _grid.getCellFromEvent(e); + if (!cell || !_grid.canCellBeActive(cell.row, cell.cell)) { + return false; + } + + if (!_grid.getOptions().multiSelect || ( + !e.ctrlKey && !e.shiftKey && !e.metaKey)) { + return false; + } + + var selection = rangesToRows(_ranges); + var idx = $.inArray(cell.row, selection); + + if (idx === -1 && (e.ctrlKey || e.metaKey)) { + selection.push(cell.row); + _grid.setActiveCell(cell.row, cell.cell); + } else if (idx !== -1 && (e.ctrlKey || e.metaKey)) { + selection = $.grep(selection, function (o, i) { + return (o !== cell.row); + }); + _grid.setActiveCell(cell.row, cell.cell); + } else if (selection.length && e.shiftKey) { + var last = selection.pop(); + var from = Math.min(cell.row, last); + var to = Math.max(cell.row, last); + selection = []; + for (var i = from; i <= to; i++) { + if (i !== last) { + selection.push(i); + } + } + selection.push(last); + _grid.setActiveCell(cell.row, cell.cell); + } + + _ranges = rowsToRanges(selection); + setSelectedRanges(_ranges); + e.stopImmediatePropagation(); + + return true; + } + + $.extend(this, { + "getSelectedRows": getSelectedRows, + "setSelectedRows": setSelectedRows, + + "getSelectedRanges": getSelectedRanges, + "setSelectedRanges": setSelectedRanges, + + "init": init, + "destroy": destroy, + + "onSelectedRangesChanged": new Slick.Event() + }); + } })(jQuery); \ No newline at end of file diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/slick.core.js b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/slick.core.js index aea3567e9f..2f097b1db6 100755 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/slick.core.js +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/slick.core.js @@ -5,419 +5,463 @@ */ (function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "Event": Event, - "EventData": EventData, - "EventHandler": EventHandler, - "Range": Range, - "NonDataRow": NonDataItem, - "Group": Group, - "GroupTotals": GroupTotals, - "EditorLock": EditorLock, - /*** - * A global singleton editor lock. - * @class GlobalEditorLock - * @static - * @constructor - */ - "GlobalEditorLock": new EditorLock() - } - }); + // register namespace + $.extend(true, window, { + "Slick": { + "Event": Event, + "EventData": EventData, + "EventHandler": EventHandler, + "Range": Range, + "NonDataRow": NonDataItem, + "Group": Group, + "GroupTotals": GroupTotals, + "EditorLock": EditorLock, + + /*** + * A global singleton editor lock. + * @class GlobalEditorLock + * @static + * @constructor + */ + "GlobalEditorLock": new EditorLock() + } + }); + + /*** + * An event object for passing data to event handlers and letting them control propagation. + *

This is pretty much identical to how W3C and jQuery implement events.

+ * @class EventData + * @constructor + */ + function EventData() { + var isPropagationStopped = false; + var isImmediatePropagationStopped = false; /*** - * An event object for passing data to event handlers and letting them control propagation. - *

This is pretty much identical to how W3C and jQuery implement events.

- * @class EventData - * @constructor + * Stops event from propagating up the DOM tree. + * @method stopPropagation */ - function EventData() { - var isPropagationStopped = false; - var isImmediatePropagationStopped = false; - - /*** - * Stops event from propagating up the DOM tree. - * @method stopPropagation - */ - this.stopPropagation = function () { - isPropagationStopped = true; - }; - - /*** - * Returns whether stopPropagation was called on this event object. - * @method isPropagationStopped - * @return {Boolean} - */ - this.isPropagationStopped = function () { - return isPropagationStopped; - }; - - /*** - * Prevents the rest of the handlers from being executed. - * @method stopImmediatePropagation - */ - this.stopImmediatePropagation = function () { - isImmediatePropagationStopped = true; - }; - - /*** - * Returns whether stopImmediatePropagation was called on this event object.\ - * @method isImmediatePropagationStopped - * @return {Boolean} - */ - this.isImmediatePropagationStopped = function () { - return isImmediatePropagationStopped; - } - } - - /*** - * A simple publisher-subscriber implementation. - * @class Event - * @constructor - */ - function Event() { - var handlers = []; - - /*** - * Adds an event handler to be called when the event is fired. - *

Event handler will receive two arguments - an EventData and the data - * object the event was fired with.

- * @method subscribe - * @param fn {Function} Event handler. - */ - this.subscribe = function (fn) { - handlers.push(fn); - }; - - /*** - * Removes an event handler added with subscribe(fn). - * @method unsubscribe - * @param fn {Function} Event handler to be removed. - */ - this.unsubscribe = function (fn) { - for (var i = handlers.length - 1; i >= 0; i--) { - if (handlers[i] === fn) { - handlers.splice(i, 1); - } - } - }; - - /*** - * Fires an event notifying all subscribers. - * @method notify - * @param args {Object} Additional data object to be passed to all handlers. - * @param e {EventData} - * Optional. - * An EventData object to be passed to all handlers. - * For DOM events, an existing W3C/jQuery event object can be passed in. - * @param scope {Object} - * Optional. - * The scope ("this") within which the handler will be executed. - * If not specified, the scope will be set to the Event instance. - */ - this.notify = function (args, e, scope) { - e = e || new EventData(); - scope = scope || this; - - var returnValue; - for (var i = 0; i < handlers.length && !(e.isPropagationStopped() || e.isImmediatePropagationStopped()); i++) { - returnValue = handlers[i].call(scope, e, args); - } - - return returnValue; - }; - } - - function EventHandler() { - var handlers = []; - - this.subscribe = function (event, handler) { - handlers.push({ - event: event, - handler: handler - }); - event.subscribe(handler); - }; - - this.unsubscribe = function (event, handler) { - var i = handlers.length; - while (i--) { - if (handlers[i].event === event && - handlers[i].handler === handler) { - handlers.splice(i, 1); - event.unsubscribe(handler); - return; - } - } - }; - - this.unsubscribeAll = function () { - var i = handlers.length; - while (i--) { - handlers[i].event.unsubscribe(handlers[i].handler); - } - handlers = []; - } - } - - /*** - * A structure containing a range of cells. - * @class Range - * @constructor - * @param fromRow {Integer} Starting row. - * @param fromCell {Integer} Starting cell. - * @param toRow {Integer} Optional. Ending row. Defaults to fromRow. - * @param toCell {Integer} Optional. Ending cell. Defaults to fromCell. - */ - function Range(fromRow, fromCell, toRow, toCell) { - if (toRow === undefined && toCell === undefined) { - toRow = fromRow; - toCell = fromCell; - } - - /*** - * @property fromRow - * @type {Integer} - */ - this.fromRow = Math.min(fromRow, toRow); - - /*** - * @property fromCell - * @type {Integer} - */ - this.fromCell = Math.min(fromCell, toCell); - - /*** - * @property toRow - * @type {Integer} - */ - this.toRow = Math.max(fromRow, toRow); - - /*** - * @property toCell - * @type {Integer} - */ - this.toCell = Math.max(fromCell, toCell); - - /*** - * Returns whether a range represents a single row. - * @method isSingleRow - * @return {Boolean} - */ - this.isSingleRow = function () { - return this.fromRow == this.toRow; - }; - - /*** - * Returns whether a range represents a single cell. - * @method isSingleCell - * @return {Boolean} - */ - this.isSingleCell = function () { - return this.fromRow == this.toRow && this.fromCell == this.toCell; - }; - - /*** - * Returns whether a range contains a given cell. - * @method contains - * @param row {Integer} - * @param cell {Integer} - * @return {Boolean} - */ - this.contains = function (row, cell) { - return row >= this.fromRow && row <= this.toRow && - cell >= this.fromCell && cell <= this.toCell; - }; - - /*** - * Returns a readable representation of a range. - * @method toString - * @return {String} - */ - this.toString = function () { - if (this.isSingleCell()) { - return "(" + this.fromRow + ":" + this.fromCell + ")"; - } - else { - return "(" + this.fromRow + ":" + this.fromCell + " - " + this.toRow + ":" + this.toCell + ")"; - } - } - } - - - /*** - * A base class that all special / non-data rows (like Group and GroupTotals) derive from. - * @class NonDataItem - * @constructor - */ - function NonDataItem() { - this.__nonDataRow = true; - } - - - /*** - * Information about a group of rows. - * @class Group - * @extends Slick.NonDataItem - * @constructor - */ - function Group() { - this.__group = true; - this.__updated = false; - - /*** - * Number of rows in the group. - * @property count - * @type {Integer} - */ - this.count = 0; - - /*** - * Grouping value. - * @property value - * @type {Object} - */ - this.value = null; - - /*** - * Formatted display value of the group. - * @property title - * @type {String} - */ - this.title = null; - - /*** - * Whether a group is collapsed. - * @property collapsed - * @type {Boolean} - */ - this.collapsed = false; - - /*** - * GroupTotals, if any. - * @property totals - * @type {GroupTotals} - */ - this.totals = null; - } - - Group.prototype = new NonDataItem(); - - /*** - * Compares two Group instances. - * @method equals - * @return {Boolean} - * @param group {Group} Group instance to compare to. - */ - Group.prototype.equals = function (group) { - return this.value === group.value && - this.count === group.count && - this.collapsed === group.collapsed; + this.stopPropagation = function () { + isPropagationStopped = true; }; /*** - * Information about group totals. - * An instance of GroupTotals will be created for each totals row and passed to the aggregators - * so that they can store arbitrary data in it. That data can later be accessed by group totals - * formatters during the display. - * @class GroupTotals - * @extends Slick.NonDataItem - * @constructor + * Returns whether stopPropagation was called on this event object. + * @method isPropagationStopped + * @return {Boolean} */ - function GroupTotals() { - this.__groupTotals = true; - - /*** - * Parent Group. - * @param group - * @type {Group} - */ - this.group = null; - } - - GroupTotals.prototype = new NonDataItem(); + this.isPropagationStopped = function () { + return isPropagationStopped; + }; /*** - * A locking helper to track the active edit controller and ensure that only a single controller - * can be active at a time. This prevents a whole class of state and validation synchronization - * issues. An edit controller (such as SlickGrid) can query if an active edit is in progress - * and attempt a commit or cancel before proceeding. - * @class EditorLock - * @constructor + * Prevents the rest of the handlers from being executed. + * @method stopImmediatePropagation */ - function EditorLock() { - var activeEditController = null; + this.stopImmediatePropagation = function () { + isImmediatePropagationStopped = true; + }; - /*** - * Returns true if a specified edit controller is active (has the edit lock). - * If the parameter is not specified, returns true if any edit controller is active. - * @method isActive - * @param editController {EditController} - * @return {Boolean} - */ - this.isActive = function (editController) { - return (editController ? activeEditController === editController : activeEditController !== null); - }; - - /*** - * Sets the specified edit controller as the active edit controller (acquire edit lock). - * If another edit controller is already active, and exception will be thrown. - * @method activate - * @param editController {EditController} edit controller acquiring the lock - */ - this.activate = function (editController) { - if (editController === activeEditController) { // already activated? - return; - } - if (activeEditController !== null) { - throw "SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController"; - } - if (!editController.commitCurrentEdit) { - throw "SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()"; - } - if (!editController.cancelCurrentEdit) { - throw "SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()"; - } - activeEditController = editController; - }; - - /*** - * Unsets the specified edit controller as the active edit controller (release edit lock). - * If the specified edit controller is not the active one, an exception will be thrown. - * @method deactivate - * @param editController {EditController} edit controller releasing the lock - */ - this.deactivate = function (editController) { - if (activeEditController !== editController) { - throw "SlickGrid.EditorLock.deactivate: specified editController is not the currently active one"; - } - activeEditController = null; - }; - - /*** - * Attempts to commit the current edit by calling "commitCurrentEdit" method on the active edit - * controller and returns whether the commit attempt was successful (commit may fail due to validation - * errors, etc.). Edit controller's "commitCurrentEdit" must return true if the commit has succeeded - * and false otherwise. If no edit controller is active, returns true. - * @method commitCurrentEdit - * @return {Boolean} - */ - this.commitCurrentEdit = function () { - return (activeEditController ? activeEditController.commitCurrentEdit() : true); - }; - - /*** - * Attempts to cancel the current edit by calling "cancelCurrentEdit" method on the active edit - * controller and returns whether the edit was successfully cancelled. If no edit controller is - * active, returns true. - * @method cancelCurrentEdit - * @return {Boolean} - */ - this.cancelCurrentEdit = function cancelCurrentEdit() { - return (activeEditController ? activeEditController.cancelCurrentEdit() : true); - }; + /*** + * Returns whether stopImmediatePropagation was called on this event object.\ + * @method isImmediatePropagationStopped + * @return {Boolean} + */ + this.isImmediatePropagationStopped = function () { + return isImmediatePropagationStopped; } + } + + /*** + * A simple publisher-subscriber implementation. + * @class Event + * @constructor + */ + function Event() { + var handlers = []; + + /*** + * Adds an event handler to be called when the event is fired. + *

Event handler will receive two arguments - an EventData and the data + * object the event was fired with.

+ * @method subscribe + * @param fn {Function} Event handler. + */ + this.subscribe = function (fn) { + handlers.push(fn); + }; + + /*** + * Removes an event handler added with subscribe(fn). + * @method unsubscribe + * @param fn {Function} Event handler to be removed. + */ + this.unsubscribe = function (fn) { + for (var i = handlers.length - 1; i >= 0; i--) { + if (handlers[i] === fn) { + handlers.splice(i, 1); + } + } + }; + + /*** + * Fires an event notifying all subscribers. + * @method notify + * @param args {Object} Additional data object to be passed to all handlers. + * @param e {EventData} + * Optional. + * An EventData object to be passed to all handlers. + * For DOM events, an existing W3C/jQuery event object can be passed in. + * @param scope {Object} + * Optional. + * The scope ("this") within which the handler will be executed. + * If not specified, the scope will be set to the Event instance. + */ + this.notify = function (args, e, scope) { + e = e || new EventData(); + scope = scope || this; + + var returnValue; + for (var i = 0; i < handlers.length && !(e.isPropagationStopped() || e.isImmediatePropagationStopped()); i++) { + returnValue = handlers[i].call(scope, e, args); + } + + return returnValue; + }; + } + + function EventHandler() { + var handlers = []; + + this.subscribe = function (event, handler) { + handlers.push({ + event: event, + handler: handler + }); + event.subscribe(handler); + + return this; // allow chaining + }; + + this.unsubscribe = function (event, handler) { + var i = handlers.length; + while (i--) { + if (handlers[i].event === event && + handlers[i].handler === handler) { + handlers.splice(i, 1); + event.unsubscribe(handler); + return; + } + } + + return this; // allow chaining + }; + + this.unsubscribeAll = function () { + var i = handlers.length; + while (i--) { + handlers[i].event.unsubscribe(handlers[i].handler); + } + handlers = []; + + return this; // allow chaining + } + } + + /*** + * A structure containing a range of cells. + * @class Range + * @constructor + * @param fromRow {Integer} Starting row. + * @param fromCell {Integer} Starting cell. + * @param toRow {Integer} Optional. Ending row. Defaults to fromRow. + * @param toCell {Integer} Optional. Ending cell. Defaults to fromCell. + */ + function Range(fromRow, fromCell, toRow, toCell) { + if (toRow === undefined && toCell === undefined) { + toRow = fromRow; + toCell = fromCell; + } + + /*** + * @property fromRow + * @type {Integer} + */ + this.fromRow = Math.min(fromRow, toRow); + + /*** + * @property fromCell + * @type {Integer} + */ + this.fromCell = Math.min(fromCell, toCell); + + /*** + * @property toRow + * @type {Integer} + */ + this.toRow = Math.max(fromRow, toRow); + + /*** + * @property toCell + * @type {Integer} + */ + this.toCell = Math.max(fromCell, toCell); + + /*** + * Returns whether a range represents a single row. + * @method isSingleRow + * @return {Boolean} + */ + this.isSingleRow = function () { + return this.fromRow == this.toRow; + }; + + /*** + * Returns whether a range represents a single cell. + * @method isSingleCell + * @return {Boolean} + */ + this.isSingleCell = function () { + return this.fromRow == this.toRow && this.fromCell == this.toCell; + }; + + /*** + * Returns whether a range contains a given cell. + * @method contains + * @param row {Integer} + * @param cell {Integer} + * @return {Boolean} + */ + this.contains = function (row, cell) { + return row >= this.fromRow && row <= this.toRow && + cell >= this.fromCell && cell <= this.toCell; + }; + + /*** + * Returns a readable representation of a range. + * @method toString + * @return {String} + */ + this.toString = function () { + if (this.isSingleCell()) { + return "(" + this.fromRow + ":" + this.fromCell + ")"; + } + else { + return "(" + this.fromRow + ":" + this.fromCell + " - " + this.toRow + ":" + this.toCell + ")"; + } + } + } + + + /*** + * A base class that all special / non-data rows (like Group and GroupTotals) derive from. + * @class NonDataItem + * @constructor + */ + function NonDataItem() { + this.__nonDataRow = true; + } + + + /*** + * Information about a group of rows. + * @class Group + * @extends Slick.NonDataItem + * @constructor + */ + function Group() { + this.__group = true; + + /** + * Grouping level, starting with 0. + * @property level + * @type {Number} + */ + this.level = 0; + + /*** + * Number of rows in the group. + * @property count + * @type {Integer} + */ + this.count = 0; + + /*** + * Grouping value. + * @property value + * @type {Object} + */ + this.value = null; + + /*** + * Formatted display value of the group. + * @property title + * @type {String} + */ + this.title = null; + + /*** + * Whether a group is collapsed. + * @property collapsed + * @type {Boolean} + */ + this.collapsed = false; + + /*** + * GroupTotals, if any. + * @property totals + * @type {GroupTotals} + */ + this.totals = null; + + /** + * Rows that are part of the group. + * @property rows + * @type {Array} + */ + this.rows = []; + + /** + * Sub-groups that are part of the group. + * @property groups + * @type {Array} + */ + this.groups = null; + + /** + * A unique key used to identify the group. This key can be used in calls to DataView + * collapseGroup() or expandGroup(). + * @property groupingKey + * @type {Object} + */ + this.groupingKey = null; + } + + Group.prototype = new NonDataItem(); + + /*** + * Compares two Group instances. + * @method equals + * @return {Boolean} + * @param group {Group} Group instance to compare to. + */ + Group.prototype.equals = function (group) { + return this.value === group.value && + this.count === group.count && + this.collapsed === group.collapsed && + this.title === group.title; + }; + + /*** + * Information about group totals. + * An instance of GroupTotals will be created for each totals row and passed to the aggregators + * so that they can store arbitrary data in it. That data can later be accessed by group totals + * formatters during the display. + * @class GroupTotals + * @extends Slick.NonDataItem + * @constructor + */ + function GroupTotals() { + this.__groupTotals = true; + + /*** + * Parent Group. + * @param group + * @type {Group} + */ + this.group = null; + + /*** + * Whether the totals have been fully initialized / calculated. + * Will be set to false for lazy-calculated group totals. + * @param initialized + * @type {Boolean} + */ + this.initialized = false; + } + + GroupTotals.prototype = new NonDataItem(); + + /*** + * A locking helper to track the active edit controller and ensure that only a single controller + * can be active at a time. This prevents a whole class of state and validation synchronization + * issues. An edit controller (such as SlickGrid) can query if an active edit is in progress + * and attempt a commit or cancel before proceeding. + * @class EditorLock + * @constructor + */ + function EditorLock() { + var activeEditController = null; + + /*** + * Returns true if a specified edit controller is active (has the edit lock). + * If the parameter is not specified, returns true if any edit controller is active. + * @method isActive + * @param editController {EditController} + * @return {Boolean} + */ + this.isActive = function (editController) { + return (editController ? activeEditController === editController : activeEditController !== null); + }; + + /*** + * Sets the specified edit controller as the active edit controller (acquire edit lock). + * If another edit controller is already active, and exception will be thrown. + * @method activate + * @param editController {EditController} edit controller acquiring the lock + */ + this.activate = function (editController) { + if (editController === activeEditController) { // already activated? + return; + } + if (activeEditController !== null) { + throw "SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController"; + } + if (!editController.commitCurrentEdit) { + throw "SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()"; + } + if (!editController.cancelCurrentEdit) { + throw "SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()"; + } + activeEditController = editController; + }; + + /*** + * Unsets the specified edit controller as the active edit controller (release edit lock). + * If the specified edit controller is not the active one, an exception will be thrown. + * @method deactivate + * @param editController {EditController} edit controller releasing the lock + */ + this.deactivate = function (editController) { + if (activeEditController !== editController) { + throw "SlickGrid.EditorLock.deactivate: specified editController is not the currently active one"; + } + activeEditController = null; + }; + + /*** + * Attempts to commit the current edit by calling "commitCurrentEdit" method on the active edit + * controller and returns whether the commit attempt was successful (commit may fail due to validation + * errors, etc.). Edit controller's "commitCurrentEdit" must return true if the commit has succeeded + * and false otherwise. If no edit controller is active, returns true. + * @method commitCurrentEdit + * @return {Boolean} + */ + this.commitCurrentEdit = function () { + return (activeEditController ? activeEditController.commitCurrentEdit() : true); + }; + + /*** + * Attempts to cancel the current edit by calling "cancelCurrentEdit" method on the active edit + * controller and returns whether the edit was successfully cancelled. If no edit controller is + * active, returns true. + * @method cancelCurrentEdit + * @return {Boolean} + */ + this.cancelCurrentEdit = function cancelCurrentEdit() { + return (activeEditController ? activeEditController.cancelCurrentEdit() : true); + }; + } })(jQuery); diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/slick.dataview.js b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/slick.dataview.js index 07f5900b7f..f1c1b5e34f 100755 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/slick.dataview.js +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/slick.dataview.js @@ -1,914 +1,1126 @@ (function ($) { - $.extend(true, window, { - Slick: { - Data: { - DataView: DataView, - Aggregators: { - Avg: AvgAggregator, - Min: MinAggregator, - Max: MaxAggregator, - Sum: SumAggregator - } - } + $.extend(true, window, { + Slick: { + Data: { + DataView: DataView, + Aggregators: { + Avg: AvgAggregator, + Min: MinAggregator, + Max: MaxAggregator, + Sum: SumAggregator } - }); + } + } + }); + /*** + * A sample Model implementation. + * Provides a filtered view of the underlying data. + * + * Relies on the data item having an "id" property uniquely identifying it. + */ + function DataView(options) { + var self = this; + + var defaults = { + groupItemMetadataProvider: null, + inlineFilters: false + }; + + + // private + var idProperty = "id"; // property holding a unique row id + var items = []; // data by index + var rows = []; // data by row + var idxById = {}; // indexes by id + var rowsById = null; // rows by id; lazy-calculated + var filter = null; // filter function + var updated = null; // updated item ids + var suspend = false; // suspends the recalculation + var sortAsc = true; + var fastSortField; + var sortComparer; + var refreshHints = {}; + var prevRefreshHints = {}; + var filterArgs; + var filteredItems = []; + var compiledFilter; + var compiledFilterWithCaching; + var filterCache = []; + + // grouping + var groupingInfoDefaults = { + getter: null, + formatter: null, + comparer: function(a, b) { return a.value - b.value; }, + predefinedValues: [], + aggregators: [], + aggregateEmpty: false, + aggregateCollapsed: false, + aggregateChildGroups: false, + collapsed: false, + displayTotalsRow: true, + lazyTotalsCalculation: false + }; + var groupingInfos = []; + var groups = []; + var toggledGroupsByLevel = []; + var groupingDelimiter = ':|:'; + + var pagesize = 0; + var pagenum = 0; + var totalRows = 0; + + // events + var onRowCountChanged = new Slick.Event(); + var onRowsChanged = new Slick.Event(); + var onPagingInfoChanged = new Slick.Event(); + + options = $.extend(true, {}, defaults, options); + + + function beginUpdate() { + suspend = true; + } + + function endUpdate() { + suspend = false; + refresh(); + } + + function setRefreshHints(hints) { + refreshHints = hints; + } + + function setFilterArgs(args) { + filterArgs = args; + } + + function updateIdxById(startingIndex) { + startingIndex = startingIndex || 0; + var id; + for (var i = startingIndex, l = items.length; i < l; i++) { + id = items[i][idProperty]; + if (id === undefined) { + throw "Each data element must implement a unique 'id' property"; + } + idxById[id] = i; + } + } + + function ensureIdUniqueness() { + var id; + for (var i = 0, l = items.length; i < l; i++) { + id = items[i][idProperty]; + if (id === undefined || idxById[id] !== i) { + throw "Each data element must implement a unique 'id' property"; + } + } + } + + function getItems() { + return items; + } + + function setItems(data, objectIdProperty) { + if (objectIdProperty !== undefined) { + idProperty = objectIdProperty; + } + items = filteredItems = data; + idxById = {}; + updateIdxById(); + ensureIdUniqueness(); + refresh(); + } + + function setPagingOptions(args) { + if (args.pageSize != undefined) { + pagesize = args.pageSize; + pagenum = pagesize ? Math.min(pagenum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)) : 0; + } + + if (args.pageNum != undefined) { + pagenum = Math.min(args.pageNum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)); + } + + onPagingInfoChanged.notify(getPagingInfo(), null, self); + + refresh(); + } + + function getPagingInfo() { + var totalPages = pagesize ? Math.max(1, Math.ceil(totalRows / pagesize)) : 1; + return {pageSize: pagesize, pageNum: pagenum, totalRows: totalRows, totalPages: totalPages}; + } + + function sort(comparer, ascending) { + sortAsc = ascending; + sortComparer = comparer; + fastSortField = null; + if (ascending === false) { + items.reverse(); + } + items.sort(comparer); + if (ascending === false) { + items.reverse(); + } + idxById = {}; + updateIdxById(); + refresh(); + } + /*** - * A sample Model implementation. - * Provides a filtered view of the underlying data. - * - * Relies on the data item having an "id" property uniquely identifying it. + * Provides a workaround for the extremely slow sorting in IE. + * Does a [lexicographic] sort on a give column by temporarily overriding Object.prototype.toString + * to return the value of that field and then doing a native Array.sort(). */ - function DataView(options) { - var self = this; + function fastSort(field, ascending) { + sortAsc = ascending; + fastSortField = field; + sortComparer = null; + var oldToString = Object.prototype.toString; + Object.prototype.toString = (typeof field == "function") ? field : function () { + return this[field] + }; + // an extra reversal for descending sort keeps the sort stable + // (assuming a stable native sort implementation, which isn't true in some cases) + if (ascending === false) { + items.reverse(); + } + items.sort(); + Object.prototype.toString = oldToString; + if (ascending === false) { + items.reverse(); + } + idxById = {}; + updateIdxById(); + refresh(); + } - var defaults = { - groupItemMetadataProvider: null, - inlineFilters: false - }; + function reSort() { + if (sortComparer) { + sort(sortComparer, sortAsc); + } else if (fastSortField) { + fastSort(fastSortField, sortAsc); + } + } + function setFilter(filterFn) { + filter = filterFn; + if (options.inlineFilters) { + compiledFilter = compileFilter(); + compiledFilterWithCaching = compileFilterWithCaching(); + } + refresh(); + } - // private - var idProperty = "id"; // property holding a unique row id - var items = []; // data by index - var rows = []; // data by row - var idxById = {}; // indexes by id - var rowsById = null; // rows by id; lazy-calculated - var filter = null; // filter function - var updated = null; // updated item ids - var suspend = false; // suspends the recalculation - var sortAsc = true; - var fastSortField; - var sortComparer; - var refreshHints = {}; - var prevRefreshHints = {}; - var filterArgs; - var filteredItems = []; - var compiledFilter; - var compiledFilterWithCaching; - var filterCache = []; + function getGrouping() { + return groupingInfos; + } - // grouping - var groupingGetter; - var groupingGetterIsAFn; - var groupingFormatter; - var groupingComparer; - var groups = []; - var collapsedGroups = {}; - var aggregators; - var aggregateCollapsed = false; - var compiledAccumulators; + function setGrouping(groupingInfo) { + if (!options.groupItemMetadataProvider) { + options.groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider(); + } - var pagesize = 0; - var pagenum = 0; - var totalRows = 0; + groups = []; + toggledGroupsByLevel = []; + groupingInfo = groupingInfo || []; + groupingInfos = (groupingInfo instanceof Array) ? groupingInfo : [groupingInfo]; - // events - var onRowCountChanged = new Slick.Event(); - var onRowsChanged = new Slick.Event(); - var onPagingInfoChanged = new Slick.Event(); + for (var i = 0; i < groupingInfos.length; i++) { + var gi = groupingInfos[i] = $.extend(true, {}, groupingInfoDefaults, groupingInfos[i]); + gi.getterIsAFn = typeof gi.getter === "function"; - options = $.extend(true, {}, defaults, options); - - - function beginUpdate() { - suspend = true; + // pre-compile accumulator loops + gi.compiledAccumulators = []; + var idx = gi.aggregators.length; + while (idx--) { + gi.compiledAccumulators[idx] = compileAccumulatorLoop(gi.aggregators[idx]); } - function endUpdate() { - suspend = false; - refresh(); + toggledGroupsByLevel[i] = {}; + } + + refresh(); + } + + /** + * @deprecated Please use {@link setGrouping}. + */ + function groupBy(valueGetter, valueFormatter, sortComparer) { + if (valueGetter == null) { + setGrouping([]); + return; + } + + setGrouping({ + getter: valueGetter, + formatter: valueFormatter, + comparer: sortComparer + }); + } + + /** + * @deprecated Please use {@link setGrouping}. + */ + function setAggregators(groupAggregators, includeCollapsed) { + if (!groupingInfos.length) { + throw new Error("At least one grouping must be specified before calling setAggregators()."); + } + + groupingInfos[0].aggregators = groupAggregators; + groupingInfos[0].aggregateCollapsed = includeCollapsed; + + setGrouping(groupingInfos); + } + + function getItemByIdx(i) { + return items[i]; + } + + function getIdxById(id) { + return idxById[id]; + } + + function ensureRowsByIdCache() { + if (!rowsById) { + rowsById = {}; + for (var i = 0, l = rows.length; i < l; i++) { + rowsById[rows[i][idProperty]] = i; + } + } + } + + function getRowById(id) { + ensureRowsByIdCache(); + return rowsById[id]; + } + + function getItemById(id) { + return items[idxById[id]]; + } + + function mapIdsToRows(idArray) { + var rows = []; + ensureRowsByIdCache(); + for (var i = 0, l = idArray.length; i < l; i++) { + var row = rowsById[idArray[i]]; + if (row != null) { + rows[rows.length] = row; + } + } + return rows; + } + + function mapRowsToIds(rowArray) { + var ids = []; + for (var i = 0, l = rowArray.length; i < l; i++) { + if (rowArray[i] < rows.length) { + ids[ids.length] = rows[rowArray[i]][idProperty]; + } + } + return ids; + } + + function updateItem(id, item) { + if (idxById[id] === undefined || id !== item[idProperty]) { + throw "Invalid or non-matching id"; + } + items[idxById[id]] = item; + if (!updated) { + updated = {}; + } + updated[id] = true; + refresh(); + } + + function insertItem(insertBefore, item) { + items.splice(insertBefore, 0, item); + updateIdxById(insertBefore); + refresh(); + } + + function addItem(item) { + items.push(item); + updateIdxById(items.length - 1); + refresh(); + } + + function deleteItem(id) { + var idx = idxById[id]; + if (idx === undefined) { + throw "Invalid id"; + } + delete idxById[id]; + items.splice(idx, 1); + updateIdxById(idx); + refresh(); + } + + function getLength() { + return rows.length; + } + + function getItem(i) { + var item = rows[i]; + + // if this is a group row, make sure totals are calculated and update the title + if (item && item.__group && item.totals && !item.totals.initialized) { + var gi = groupingInfos[item.level]; + if (!gi.displayTotalsRow) { + calculateTotals(item.totals); + item.title = gi.formatter ? gi.formatter(item) : item.value; + } + } + // if this is a totals row, make sure it's calculated + else if (item && item.__groupTotals && !item.initialized) { + calculateTotals(item); + } + + return item; + } + + function getItemMetadata(i) { + var item = rows[i]; + if (item === undefined) { + return null; + } + + // overrides for grouping rows + if (item.__group) { + return options.groupItemMetadataProvider.getGroupRowMetadata(item); + } + + // overrides for totals rows + if (item.__groupTotals) { + return options.groupItemMetadataProvider.getTotalsRowMetadata(item); + } + + return null; + } + + function expandCollapseAllGroups(level, collapse) { + if (level == null) { + for (var i = 0; i < groupingInfos.length; i++) { + toggledGroupsByLevel[i] = {}; + groupingInfos[i].collapsed = collapse; + } + } else { + toggledGroupsByLevel[level] = {}; + groupingInfos[level].collapsed = collapse; + } + refresh(); + } + + /** + * @param level {Number} Optional level to collapse. If not specified, applies to all levels. + */ + function collapseAllGroups(level) { + expandCollapseAllGroups(level, true); + } + + /** + * @param level {Number} Optional level to expand. If not specified, applies to all levels. + */ + function expandAllGroups(level) { + expandCollapseAllGroups(level, false); + } + + function expandCollapseGroup(level, groupingKey, collapse) { + toggledGroupsByLevel[level][groupingKey] = groupingInfos[level].collapsed ^ collapse; + refresh(); + } + + /** + * @param varArgs Either a Slick.Group's "groupingKey" property, or a + * variable argument list of grouping values denoting a unique path to the row. For + * example, calling collapseGroup('high', '10%') will collapse the '10%' subgroup of + * the 'high' group. + */ + function collapseGroup(varArgs) { + var args = Array.prototype.slice.call(arguments); + var arg0 = args[0]; + if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) { + expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, true); + } else { + expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), true); + } + } + + /** + * @param varArgs Either a Slick.Group's "groupingKey" property, or a + * variable argument list of grouping values denoting a unique path to the row. For + * example, calling expandGroup('high', '10%') will expand the '10%' subgroup of + * the 'high' group. + */ + function expandGroup(varArgs) { + var args = Array.prototype.slice.call(arguments); + var arg0 = args[0]; + if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) { + expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, false); + } else { + expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), false); + } + } + + function getGroups() { + return groups; + } + + function extractGroups(rows, parentGroup) { + var group; + var val; + var groups = []; + var groupsByVal = {}; + var r; + var level = parentGroup ? parentGroup.level + 1 : 0; + var gi = groupingInfos[level]; + + for (var i = 0, l = gi.predefinedValues.length; i < l; i++) { + val = gi.predefinedValues[i]; + group = groupsByVal[val]; + if (!group) { + group = new Slick.Group(); + group.value = val; + group.level = level; + group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val; + groups[groups.length] = group; + groupsByVal[val] = group; + } + } + + for (var i = 0, l = rows.length; i < l; i++) { + r = rows[i]; + val = gi.getterIsAFn ? gi.getter(r) : r[gi.getter]; + group = groupsByVal[val]; + if (!group) { + group = new Slick.Group(); + group.value = val; + group.level = level; + group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val; + groups[groups.length] = group; + groupsByVal[val] = group; } - function setRefreshHints(hints) { - refreshHints = hints; + group.rows[group.count++] = r; + } + + if (level < groupingInfos.length - 1) { + for (var i = 0; i < groups.length; i++) { + group = groups[i]; + group.groups = extractGroups(group.rows, group); + } + } + + groups.sort(groupingInfos[level].comparer); + + return groups; + } + + function calculateTotals(totals) { + var group = totals.group; + var gi = groupingInfos[group.level]; + var isLeafLevel = (group.level == groupingInfos.length); + var agg, idx = gi.aggregators.length; + + if (!isLeafLevel && gi.aggregateChildGroups) { + // make sure all the subgroups are calculated + var i = group.groups.length; + while (i--) { + if (!group.groups[i].initialized) { + calculateTotals(group.groups[i]); + } + } + } + + while (idx--) { + agg = gi.aggregators[idx]; + agg.init(); + if (!isLeafLevel && gi.aggregateChildGroups) { + gi.compiledAccumulators[idx].call(agg, group.groups); + } else { + gi.compiledAccumulators[idx].call(agg, group.rows); + } + agg.storeResult(totals); + } + totals.initialized = true; + } + + function addGroupTotals(group) { + var gi = groupingInfos[group.level]; + var totals = new Slick.GroupTotals(); + totals.group = group; + group.totals = totals; + if (!gi.lazyTotalsCalculation) { + calculateTotals(totals); + } + } + + function addTotals(groups, level) { + level = level || 0; + var gi = groupingInfos[level]; + var groupCollapsed = gi.collapsed; + var toggledGroups = toggledGroupsByLevel[level]; + var idx = groups.length, g; + while (idx--) { + g = groups[idx]; + + if (g.collapsed && !gi.aggregateCollapsed) { + continue; } - function setFilterArgs(args) { - filterArgs = args; + // Do a depth-first aggregation so that parent group aggregators can access subgroup totals. + if (g.groups) { + addTotals(g.groups, level + 1); } - function updateIdxById(startingIndex) { - startingIndex = startingIndex || 0; - var id; - for (var i = startingIndex, l = items.length; i < l; i++) { - id = items[i][idProperty]; - if (id === undefined) { - throw "Each data element must implement a unique 'id' property"; - } - idxById[id] = i; + if (gi.aggregators.length && ( + gi.aggregateEmpty || g.rows.length || (g.groups && g.groups.length))) { + addGroupTotals(g); + } + + g.collapsed = groupCollapsed ^ toggledGroups[g.groupingKey]; + g.title = gi.formatter ? gi.formatter(g) : g.value; + } + } + + function flattenGroupedRows(groups, level) { + level = level || 0; + var gi = groupingInfos[level]; + var groupedRows = [], rows, gl = 0, g; + for (var i = 0, l = groups.length; i < l; i++) { + g = groups[i]; + groupedRows[gl++] = g; + + if (!g.collapsed) { + rows = g.groups ? flattenGroupedRows(g.groups, level + 1) : g.rows; + for (var j = 0, jj = rows.length; j < jj; j++) { + groupedRows[gl++] = rows[j]; + } + } + + if (g.totals && gi.displayTotalsRow && (!g.collapsed || gi.aggregateCollapsed)) { + groupedRows[gl++] = g.totals; + } + } + return groupedRows; + } + + function getFunctionInfo(fn) { + var fnRegex = /^function[^(]*\(([^)]*)\)\s*{([\s\S]*)}$/; + var matches = fn.toString().match(fnRegex); + return { + params: matches[1].split(","), + body: matches[2] + }; + } + + function compileAccumulatorLoop(aggregator) { + var accumulatorInfo = getFunctionInfo(aggregator.accumulate); + var fn = new Function( + "_items", + "for (var " + accumulatorInfo.params[0] + ", _i=0, _il=_items.length; _i<_il; _i++) {" + + accumulatorInfo.params[0] + " = _items[_i]; " + + accumulatorInfo.body + + "}" + ); + fn.displayName = fn.name = "compiledAccumulatorLoop"; + return fn; + } + + function compileFilter() { + var filterInfo = getFunctionInfo(filter); + + var filterBody = filterInfo.body + .replace(/return false\s*([;}]|$)/gi, "{ continue _coreloop; }$1") + .replace(/return true\s*([;}]|$)/gi, "{ _retval[_idx++] = $item$; continue _coreloop; }$1") + .replace(/return ([^;}]+?)\s*([;}]|$)/gi, + "{ if ($1) { _retval[_idx++] = $item$; }; continue _coreloop; }$2"); + + // This preserves the function template code after JS compression, + // so that replace() commands still work as expected. + var tpl = [ + //"function(_items, _args) { ", + "var _retval = [], _idx = 0; ", + "var $item$, $args$ = _args; ", + "_coreloop: ", + "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ", + "$item$ = _items[_i]; ", + "$filter$; ", + "} ", + "return _retval; " + //"}" + ].join(""); + tpl = tpl.replace(/\$filter\$/gi, filterBody); + tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]); + tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]); + + var fn = new Function("_items,_args", tpl); + fn.displayName = fn.name = "compiledFilter"; + return fn; + } + + function compileFilterWithCaching() { + var filterInfo = getFunctionInfo(filter); + + var filterBody = filterInfo.body + .replace(/return false\s*([;}]|$)/gi, "{ continue _coreloop; }$1") + .replace(/return true\s*([;}]|$)/gi, "{ _cache[_i] = true;_retval[_idx++] = $item$; continue _coreloop; }$1") + .replace(/return ([^;}]+?)\s*([;}]|$)/gi, + "{ if ((_cache[_i] = $1)) { _retval[_idx++] = $item$; }; continue _coreloop; }$2"); + + // This preserves the function template code after JS compression, + // so that replace() commands still work as expected. + var tpl = [ + //"function(_items, _args, _cache) { ", + "var _retval = [], _idx = 0; ", + "var $item$, $args$ = _args; ", + "_coreloop: ", + "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ", + "$item$ = _items[_i]; ", + "if (_cache[_i]) { ", + "_retval[_idx++] = $item$; ", + "continue _coreloop; ", + "} ", + "$filter$; ", + "} ", + "return _retval; " + //"}" + ].join(""); + tpl = tpl.replace(/\$filter\$/gi, filterBody); + tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]); + tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]); + + var fn = new Function("_items,_args,_cache", tpl); + fn.displayName = fn.name = "compiledFilterWithCaching"; + return fn; + } + + function uncompiledFilter(items, args) { + var retval = [], idx = 0; + + for (var i = 0, ii = items.length; i < ii; i++) { + if (filter(items[i], args)) { + retval[idx++] = items[i]; + } + } + + return retval; + } + + function uncompiledFilterWithCaching(items, args, cache) { + var retval = [], idx = 0, item; + + for (var i = 0, ii = items.length; i < ii; i++) { + item = items[i]; + if (cache[i]) { + retval[idx++] = item; + } else if (filter(item, args)) { + retval[idx++] = item; + cache[i] = true; + } + } + + return retval; + } + + function getFilteredAndPagedItems(items) { + if (filter) { + var batchFilter = options.inlineFilters ? compiledFilter : uncompiledFilter; + var batchFilterWithCaching = options.inlineFilters ? compiledFilterWithCaching : uncompiledFilterWithCaching; + + if (refreshHints.isFilterNarrowing) { + filteredItems = batchFilter(filteredItems, filterArgs); + } else if (refreshHints.isFilterExpanding) { + filteredItems = batchFilterWithCaching(items, filterArgs, filterCache); + } else if (!refreshHints.isFilterUnchanged) { + filteredItems = batchFilter(items, filterArgs); + } + } else { + // special case: if not filtering and not paging, the resulting + // rows collection needs to be a copy so that changes due to sort + // can be caught + filteredItems = pagesize ? items : items.concat(); + } + + // get the current page + var paged; + if (pagesize) { + if (filteredItems.length < pagenum * pagesize) { + pagenum = Math.floor(filteredItems.length / pagesize); + } + paged = filteredItems.slice(pagesize * pagenum, pagesize * pagenum + pagesize); + } else { + paged = filteredItems; + } + + return {totalRows: filteredItems.length, rows: paged}; + } + + function getRowDiffs(rows, newRows) { + var item, r, eitherIsNonData, diff = []; + var from = 0, to = newRows.length; + + if (refreshHints && refreshHints.ignoreDiffsBefore) { + from = Math.max(0, + Math.min(newRows.length, refreshHints.ignoreDiffsBefore)); + } + + if (refreshHints && refreshHints.ignoreDiffsAfter) { + to = Math.min(newRows.length, + Math.max(0, refreshHints.ignoreDiffsAfter)); + } + + for (var i = from, rl = rows.length; i < to; i++) { + if (i >= rl) { + diff[diff.length] = i; + } else { + item = newRows[i]; + r = rows[i]; + + if ((groupingInfos.length && (eitherIsNonData = (item.__nonDataRow) || (r.__nonDataRow)) && + item.__group !== r.__group || + item.__group && !item.equals(r)) + || (eitherIsNonData && + // no good way to compare totals since they are arbitrary DTOs + // deep object comparison is pretty expensive + // always considering them 'dirty' seems easier for the time being + (item.__groupTotals || r.__groupTotals)) + || item[idProperty] != r[idProperty] + || (updated && updated[item[idProperty]]) + ) { + diff[diff.length] = i; + } + } + } + return diff; + } + + function recalc(_items) { + rowsById = null; + + if (refreshHints.isFilterNarrowing != prevRefreshHints.isFilterNarrowing || + refreshHints.isFilterExpanding != prevRefreshHints.isFilterExpanding) { + filterCache = []; + } + + var filteredItems = getFilteredAndPagedItems(_items); + totalRows = filteredItems.totalRows; + var newRows = filteredItems.rows; + + groups = []; + if (groupingInfos.length) { + groups = extractGroups(newRows); + if (groups.length) { + addTotals(groups); + newRows = flattenGroupedRows(groups); + } + } + + var diff = getRowDiffs(rows, newRows); + + rows = newRows; + + return diff; + } + + function refresh() { + if (suspend) { + return; + } + + var countBefore = rows.length; + var totalRowsBefore = totalRows; + + var diff = recalc(items, filter); // pass as direct refs to avoid closure perf hit + + // if the current page is no longer valid, go to last page and recalc + // we suffer a performance penalty here, but the main loop (recalc) remains highly optimized + if (pagesize && totalRows < pagenum * pagesize) { + pagenum = Math.max(0, Math.ceil(totalRows / pagesize) - 1); + diff = recalc(items, filter); + } + + updated = null; + prevRefreshHints = refreshHints; + refreshHints = {}; + + if (totalRowsBefore != totalRows) { + onPagingInfoChanged.notify(getPagingInfo(), null, self); + } + if (countBefore != rows.length) { + onRowCountChanged.notify({previous: countBefore, current: rows.length}, null, self); + } + if (diff.length > 0) { + onRowsChanged.notify({rows: diff}, null, self); + } + } + + /*** + * Wires the grid and the DataView together to keep row selection tied to item ids. + * This is useful since, without it, the grid only knows about rows, so if the items + * move around, the same rows stay selected instead of the selection moving along + * with the items. + * + * NOTE: This doesn't work with cell selection model. + * + * @param grid {Slick.Grid} The grid to sync selection with. + * @param preserveHidden {Boolean} Whether to keep selected items that go out of the + * view due to them getting filtered out. + * @param preserveHiddenOnSelectionChange {Boolean} Whether to keep selected items + * that are currently out of the view (see preserveHidden) as selected when selection + * changes. + * @return {Slick.Event} An event that notifies when an internal list of selected row ids + * changes. This is useful since, in combination with the above two options, it allows + * access to the full list selected row ids, and not just the ones visible to the grid. + * @method syncGridSelection + */ + function syncGridSelection(grid, preserveHidden, preserveHiddenOnSelectionChange) { + var self = this; + var inHandler; + var selectedRowIds = self.mapRowsToIds(grid.getSelectedRows()); + var onSelectedRowIdsChanged = new Slick.Event(); + + function setSelectedRowIds(rowIds) { + if (selectedRowIds.join(",") == rowIds.join(",")) { + return; + } + + selectedRowIds = rowIds; + + onSelectedRowIdsChanged.notify({ + "grid": grid, + "ids": selectedRowIds + }, new Slick.EventData(), self); + } + + function update() { + if (selectedRowIds.length > 0) { + inHandler = true; + var selectedRows = self.mapIdsToRows(selectedRowIds); + if (!preserveHidden) { + setSelectedRowIds(self.mapRowsToIds(selectedRows)); + } + grid.setSelectedRows(selectedRows); + inHandler = false; + } + } + + grid.onSelectedRowsChanged.subscribe(function(e, args) { + if (inHandler) { return; } + var newSelectedRowIds = self.mapRowsToIds(grid.getSelectedRows()); + if (!preserveHiddenOnSelectionChange || !grid.getOptions().multiSelect) { + setSelectedRowIds(newSelectedRowIds); + } else { + // keep the ones that are hidden + var existing = $.grep(selectedRowIds, function(id) { return self.getRowById(id) === undefined; }); + // add the newly selected ones + setSelectedRowIds(existing.concat(newSelectedRowIds)); + } + }); + + this.onRowsChanged.subscribe(update); + + this.onRowCountChanged.subscribe(update); + + return onSelectedRowIdsChanged; + } + + function syncGridCellCssStyles(grid, key) { + var hashById; + var inHandler; + + // since this method can be called after the cell styles have been set, + // get the existing ones right away + storeCellCssStyles(grid.getCellCssStyles(key)); + + function storeCellCssStyles(hash) { + hashById = {}; + for (var row in hash) { + var id = rows[row][idProperty]; + hashById[id] = hash[row]; + } + } + + function update() { + if (hashById) { + inHandler = true; + ensureRowsByIdCache(); + var newHash = {}; + for (var id in hashById) { + var row = rowsById[id]; + if (row != undefined) { + newHash[row] = hashById[id]; } + } + grid.setCellCssStyles(key, newHash); + inHandler = false; } + } - function ensureIdUniqueness() { - var id; - for (var i = 0, l = items.length; i < l; i++) { - id = items[i][idProperty]; - if (id === undefined || idxById[id] !== i) { - throw "Each data element must implement a unique 'id' property"; - } - } + grid.onCellCssStylesChanged.subscribe(function(e, args) { + if (inHandler) { return; } + if (key != args.key) { return; } + if (args.hash) { + storeCellCssStyles(args.hash); } + }); - function getItems() { - return items; + this.onRowsChanged.subscribe(update); + + this.onRowCountChanged.subscribe(update); + } + + $.extend(this, { + // methods + "beginUpdate": beginUpdate, + "endUpdate": endUpdate, + "setPagingOptions": setPagingOptions, + "getPagingInfo": getPagingInfo, + "getItems": getItems, + "setItems": setItems, + "setFilter": setFilter, + "sort": sort, + "fastSort": fastSort, + "reSort": reSort, + "setGrouping": setGrouping, + "getGrouping": getGrouping, + "groupBy": groupBy, + "setAggregators": setAggregators, + "collapseAllGroups": collapseAllGroups, + "expandAllGroups": expandAllGroups, + "collapseGroup": collapseGroup, + "expandGroup": expandGroup, + "getGroups": getGroups, + "getIdxById": getIdxById, + "getRowById": getRowById, + "getItemById": getItemById, + "getItemByIdx": getItemByIdx, + "mapRowsToIds": mapRowsToIds, + "mapIdsToRows": mapIdsToRows, + "setRefreshHints": setRefreshHints, + "setFilterArgs": setFilterArgs, + "refresh": refresh, + "updateItem": updateItem, + "insertItem": insertItem, + "addItem": addItem, + "deleteItem": deleteItem, + "syncGridSelection": syncGridSelection, + "syncGridCellCssStyles": syncGridCellCssStyles, + + // data provider methods + "getLength": getLength, + "getItem": getItem, + "getItemMetadata": getItemMetadata, + + // events + "onRowCountChanged": onRowCountChanged, + "onRowsChanged": onRowsChanged, + "onPagingInfoChanged": onPagingInfoChanged + }); + } + + function AvgAggregator(field) { + this.field_ = field; + + this.init = function () { + this.count_ = 0; + this.nonNullCount_ = 0; + this.sum_ = 0; + }; + + this.accumulate = function (item) { + var val = item[this.field_]; + this.count_++; + if (val != null && val !== "" && val !== NaN) { + this.nonNullCount_++; + this.sum_ += parseFloat(val); + } + }; + + this.storeResult = function (groupTotals) { + if (!groupTotals.avg) { + groupTotals.avg = {}; + } + if (this.nonNullCount_ != 0) { + groupTotals.avg[this.field_] = this.sum_ / this.nonNullCount_; + } + }; + } + + function MinAggregator(field) { + this.field_ = field; + + this.init = function () { + this.min_ = null; + }; + + this.accumulate = function (item) { + var val = item[this.field_]; + if (val != null && val !== "" && val !== NaN) { + if (this.min_ == null || val < this.min_) { + this.min_ = val; } + } + }; - function setItems(data, objectIdProperty) { - if (objectIdProperty !== undefined) { - idProperty = objectIdProperty; - } - items = filteredItems = data; - idxById = {}; - updateIdxById(); - ensureIdUniqueness(); - refresh(); + this.storeResult = function (groupTotals) { + if (!groupTotals.min) { + groupTotals.min = {}; + } + groupTotals.min[this.field_] = this.min_; + } + } + + function MaxAggregator(field) { + this.field_ = field; + + this.init = function () { + this.max_ = null; + }; + + this.accumulate = function (item) { + var val = item[this.field_]; + if (val != null && val !== "" && val !== NaN) { + if (this.max_ == null || val > this.max_) { + this.max_ = val; } - - function setPagingOptions(args) { - if (args.pageSize != undefined) { - pagesize = args.pageSize; - pagenum = pagesize ? Math.min(pagenum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)) : 0; - } - - if (args.pageNum != undefined) { - pagenum = Math.min(args.pageNum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)); - } - - onPagingInfoChanged.notify(getPagingInfo(), null, self); - - refresh(); - } - - function getPagingInfo() { - var totalPages = pagesize ? Math.max(1, Math.ceil(totalRows / pagesize)) : 1; - return {pageSize: pagesize, pageNum: pagenum, totalRows: totalRows, totalPages: totalPages}; - } - - function sort(comparer, ascending) { - sortAsc = ascending; - sortComparer = comparer; - fastSortField = null; - if (ascending === false) { - items.reverse(); - } - items.sort(comparer); - if (ascending === false) { - items.reverse(); - } - idxById = {}; - updateIdxById(); - refresh(); - } - - /*** - * Provides a workaround for the extremely slow sorting in IE. - * Does a [lexicographic] sort on a give column by temporarily overriding Object.prototype.toString - * to return the value of that field and then doing a native Array.sort(). - */ - function fastSort(field, ascending) { - sortAsc = ascending; - fastSortField = field; - sortComparer = null; - var oldToString = Object.prototype.toString; - Object.prototype.toString = (typeof field == "function") ? field : function () { - return this[field] - }; - // an extra reversal for descending sort keeps the sort stable - // (assuming a stable native sort implementation, which isn't true in some cases) - if (ascending === false) { - items.reverse(); - } - items.sort(); - Object.prototype.toString = oldToString; - if (ascending === false) { - items.reverse(); - } - idxById = {}; - updateIdxById(); - refresh(); - } - - function reSort() { - if (sortComparer) { - sort(sortComparer, sortAsc); - } else if (fastSortField) { - fastSort(fastSortField, sortAsc); - } - } - - function setFilter(filterFn) { - filter = filterFn; - if (options.inlineFilters) { - compiledFilter = compileFilter(); - compiledFilterWithCaching = compileFilterWithCaching(); - } - refresh(); - } - - function groupBy(valueGetter, valueFormatter, sortComparer) { - if (!options.groupItemMetadataProvider) { - options.groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider(); - } - - groupingGetter = valueGetter; - groupingGetterIsAFn = typeof groupingGetter === "function"; - groupingFormatter = valueFormatter; - groupingComparer = sortComparer; - collapsedGroups = {}; - groups = []; - refresh(); - } - - function setAggregators(groupAggregators, includeCollapsed) { - aggregators = groupAggregators; - aggregateCollapsed = (includeCollapsed !== undefined) - ? includeCollapsed : aggregateCollapsed; - - // pre-compile accumulator loops - compiledAccumulators = []; - var idx = aggregators.length; - while (idx--) { - compiledAccumulators[idx] = compileAccumulatorLoop(aggregators[idx]); - } - - refresh(); - } - - function getItemByIdx(i) { - return items[i]; - } - - function getIdxById(id) { - return idxById[id]; - } - - function ensureRowsByIdCache() { - if (!rowsById) { - rowsById = {}; - for (var i = 0, l = rows.length; i < l; i++) { - rowsById[rows[i][idProperty]] = i; - } - } - } - - function getRowById(id) { - ensureRowsByIdCache(); - return rowsById[id]; - } - - function getItemById(id) { - return items[idxById[id]]; - } - - function mapIdsToRows(idArray) { - var rows = []; - ensureRowsByIdCache(); - for (var i = 0; i < idArray.length; i++) { - var row = rowsById[idArray[i]]; - if (row != null) { - rows[rows.length] = row; - } - } - return rows; - } - - function mapRowsToIds(rowArray) { - var ids = []; - for (var i = 0; i < rowArray.length; i++) { - if (rowArray[i] < rows.length) { - ids[ids.length] = rows[rowArray[i]][idProperty]; - } - } - return ids; - } - - function updateItem(id, item) { - if (idxById[id] === undefined || id !== item[idProperty]) { - throw "Invalid or non-matching id"; - } - items[idxById[id]] = item; - if (!updated) { - updated = {}; - } - updated[id] = true; - refresh(); - } - - function insertItem(insertBefore, item) { - items.splice(insertBefore, 0, item); - updateIdxById(insertBefore); - refresh(); - } - - function addItem(item) { - items.push(item); - updateIdxById(items.length - 1); - refresh(); - } - - function deleteItem(id) { - var idx = idxById[id]; - if (idx === undefined) { - throw "Invalid id"; - } - delete idxById[id]; - items.splice(idx, 1); - updateIdxById(idx); - refresh(); - } - - function getLength() { - return rows.length; - } - - function getItem(i) { - return rows[i]; - } - - function getItemMetadata(i) { - var item = rows[i]; - if (item === undefined) { - return null; - } - - // overrides for group rows - if (item.__group) { - return options.groupItemMetadataProvider.getGroupRowMetadata(item); - } - - // overrides for totals rows - if (item.__groupTotals) { - return options.groupItemMetadataProvider.getTotalsRowMetadata(item); - } - - return null; - } - - function collapseGroup(groupingValue) { - collapsedGroups[groupingValue] = true; - refresh(); - } - - function expandGroup(groupingValue) { - delete collapsedGroups[groupingValue]; - refresh(); - } - - function getGroups() { - return groups; - } - - function extractGroups(rows) { - var group; - var val; - var groups = []; - var groupsByVal = []; - var r; - - for (var i = 0, l = rows.length; i < l; i++) { - r = rows[i]; - val = (groupingGetterIsAFn) ? groupingGetter(r) : r[groupingGetter]; - val = val || 0; - group = groupsByVal[val]; - if (!group) { - group = new Slick.Group(); - group.count = 0; - group.value = val; - group.rows = []; - groups[groups.length] = group; - groupsByVal[val] = group; - } - - group.rows[group.count++] = r; - } - - return groups; - } - - // TODO: lazy totals calculation - function calculateGroupTotals(group) { - if (group.collapsed && !aggregateCollapsed) { - return; - } - - // TODO: try moving iterating over groups into compiled accumulator - var totals = new Slick.GroupTotals(); - var agg, idx = aggregators.length; - while (idx--) { - agg = aggregators[idx]; - agg.init(); - compiledAccumulators[idx].call(agg, group.rows); - agg.storeResult(totals); - } - totals.group = group; - group.totals = totals; - } - - function calculateTotals(groups) { - var idx = groups.length; - while (idx--) { - calculateGroupTotals(groups[idx]); - } - } - - function finalizeGroups(groups) { - var idx = groups.length, g; - while (idx--) { - g = groups[idx]; - g.collapsed = (g.value in collapsedGroups); - g.title = groupingFormatter ? groupingFormatter(g) : g.value; - } - } - - function flattenGroupedRows(groups) { - var groupedRows = [], gl = 0, g; - for (var i = 0, l = groups.length; i < l; i++) { - g = groups[i]; - groupedRows[gl++] = g; - - if (!g.collapsed) { - for (var j = 0, jj = g.rows.length; j < jj; j++) { - groupedRows[gl++] = g.rows[j]; - } - } - - if (g.totals && (!g.collapsed || aggregateCollapsed)) { - groupedRows[gl++] = g.totals; - } - } - return groupedRows; - } - - function getFunctionInfo(fn) { - var fnRegex = /^function[^(]*\(([^)]*)\)\s*{([\s\S]*)}$/; - var matches = fn.toString().match(fnRegex); - return { - params: matches[1].split(","), - body: matches[2] - }; - } - - function compileAccumulatorLoop(aggregator) { - var accumulatorInfo = getFunctionInfo(aggregator.accumulate); - var fn = new Function( - "_items", - "for (var " + accumulatorInfo.params[0] + ", _i=0, _il=_items.length; _i<_il; _i++) {" + - accumulatorInfo.params[0] + " = _items[_i]; " + - accumulatorInfo.body + - "}" - ); - fn.displayName = fn.name = "compiledAccumulatorLoop"; - return fn; - } - - function compileFilter() { - var filterInfo = getFunctionInfo(filter); - - var filterBody = filterInfo.body - .replace(/return false[;}]/gi, "{ continue _coreloop; }") - .replace(/return true[;}]/gi, "{ _retval[_idx++] = $item$; continue _coreloop; }") - .replace(/return ([^;}]+?);/gi, - "{ if ($1) { _retval[_idx++] = $item$; }; continue _coreloop; }"); - - // This preserves the function template code after JS compression, - // so that replace() commands still work as expected. - var tpl = [ - //"function(_items, _args) { ", - "var _retval = [], _idx = 0; ", - "var $item$, $args$ = _args; ", - "_coreloop: ", - "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ", - "$item$ = _items[_i]; ", - "$filter$; ", - "} ", - "return _retval; " - //"}" - ].join(""); - tpl = tpl.replace(/\$filter\$/gi, filterBody); - tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]); - tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]); - - var fn = new Function("_items,_args", tpl); - fn.displayName = fn.name = "compiledFilter"; - return fn; - } - - function compileFilterWithCaching() { - var filterInfo = getFunctionInfo(filter); - - var filterBody = filterInfo.body - .replace(/return false[;}]/gi, "{ continue _coreloop; }") - .replace(/return true[;}]/gi, "{ _cache[_i] = true;_retval[_idx++] = $item$; continue _coreloop; }") - .replace(/return ([^;}]+?);/gi, - "{ if ((_cache[_i] = $1)) { _retval[_idx++] = $item$; }; continue _coreloop; }"); - - // This preserves the function template code after JS compression, - // so that replace() commands still work as expected. - var tpl = [ - //"function(_items, _args, _cache) { ", - "var _retval = [], _idx = 0; ", - "var $item$, $args$ = _args; ", - "_coreloop: ", - "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ", - "$item$ = _items[_i]; ", - "if (_cache[_i]) { ", - "_retval[_idx++] = $item$; ", - "continue _coreloop; ", - "} ", - "$filter$; ", - "} ", - "return _retval; " - //"}" - ].join(""); - tpl = tpl.replace(/\$filter\$/gi, filterBody); - tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]); - tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]); - - var fn = new Function("_items,_args,_cache", tpl); - fn.displayName = fn.name = "compiledFilterWithCaching"; - return fn; - } - - function uncompiledFilter(items, args) { - var retval = [], idx = 0; - - for (var i = 0, ii = items.length; i < ii; i++) { - if (filter(items[i], args)) { - retval[idx++] = items[i]; - } - } - - return retval; - } - - function uncompiledFilterWithCaching(items, args, cache) { - var retval = [], idx = 0, item; - - for (var i = 0, ii = items.length; i < ii; i++) { - item = items[i]; - if (cache[i]) { - retval[idx++] = item; - } else if (filter(item, args)) { - retval[idx++] = item; - cache[i] = true; - } - } - - return retval; - } - - function getFilteredAndPagedItems(items) { - if (filter) { - var batchFilter = options.inlineFilters ? compiledFilter : uncompiledFilter; - var batchFilterWithCaching = options.inlineFilters ? compiledFilterWithCaching : uncompiledFilterWithCaching; - - if (refreshHints.isFilterNarrowing) { - filteredItems = batchFilter(filteredItems, filterArgs); - } else if (refreshHints.isFilterExpanding) { - filteredItems = batchFilterWithCaching(items, filterArgs, filterCache); - } else if (!refreshHints.isFilterUnchanged) { - filteredItems = batchFilter(items, filterArgs); - } - } else { - // special case: if not filtering and not paging, the resulting - // rows collection needs to be a copy so that changes due to sort - // can be caught - filteredItems = pagesize ? items : items.concat(); - } - - // get the current page - var paged; - if (pagesize) { - if (filteredItems.length < pagenum * pagesize) { - pagenum = Math.floor(filteredItems.length / pagesize); - } - paged = filteredItems.slice(pagesize * pagenum, pagesize * pagenum + pagesize); - } else { - paged = filteredItems; - } - - return {totalRows: filteredItems.length, rows: paged}; - } - - function getRowDiffs(rows, newRows) { - var item, r, eitherIsNonData, diff = []; - var from = 0, to = newRows.length; - - if (refreshHints && refreshHints.ignoreDiffsBefore) { - from = Math.max(0, - Math.min(newRows.length, refreshHints.ignoreDiffsBefore)); - } - - if (refreshHints && refreshHints.ignoreDiffsAfter) { - to = Math.min(newRows.length, - Math.max(0, refreshHints.ignoreDiffsAfter)); - } - - for (var i = from, rl = rows.length; i < to; i++) { - if (i >= rl) { - diff[diff.length] = i; - } else { - item = newRows[i]; - r = rows[i]; - - if ((groupingGetter && (eitherIsNonData = (item.__nonDataRow) || (r.__nonDataRow)) && - item.__group !== r.__group || - item.__updated || - item.__group && !item.equals(r)) - || (aggregators && eitherIsNonData && - // no good way to compare totals since they are arbitrary DTOs - // deep object comparison is pretty expensive - // always considering them 'dirty' seems easier for the time being - (item.__groupTotals || r.__groupTotals)) - || item[idProperty] != r[idProperty] - || (updated && updated[item[idProperty]]) - ) { - diff[diff.length] = i; - } - } - } - return diff; - } - - function recalc(_items) { - rowsById = null; - - if (refreshHints.isFilterNarrowing != prevRefreshHints.isFilterNarrowing || - refreshHints.isFilterExpanding != prevRefreshHints.isFilterExpanding) { - filterCache = []; - } - - var filteredItems = getFilteredAndPagedItems(_items); - totalRows = filteredItems.totalRows; - var newRows = filteredItems.rows; - - groups = []; - if (groupingGetter != null) { - groups = extractGroups(newRows); - if (groups.length) { - finalizeGroups(groups); - if (aggregators) { - calculateTotals(groups); - } - groups.sort(groupingComparer); - newRows = flattenGroupedRows(groups); - } - } - - var diff = getRowDiffs(rows, newRows); - - rows = newRows; - - return diff; - } - - function refresh() { - if (suspend) { - return; - } - - var countBefore = rows.length; - var totalRowsBefore = totalRows; - - var diff = recalc(items, filter); // pass as direct refs to avoid closure perf hit - - // if the current page is no longer valid, go to last page and recalc - // we suffer a performance penalty here, but the main loop (recalc) remains highly optimized - if (pagesize && totalRows < pagenum * pagesize) { - pagenum = Math.max(0, Math.ceil(totalRows / pagesize) - 1); - diff = recalc(items, filter); - } - - updated = null; - prevRefreshHints = refreshHints; - refreshHints = {}; - - if (totalRowsBefore != totalRows) { - onPagingInfoChanged.notify(getPagingInfo(), null, self); - } - if (countBefore != rows.length) { - onRowCountChanged.notify({previous: countBefore, current: rows.length}, null, self); - } - if (diff.length > 0) { - onRowsChanged.notify({rows: diff}, null, self); - } - } - - function syncGridSelection(grid, preserveHidden) { - var self = this; - var selectedRowIds = self.mapRowsToIds(grid.getSelectedRows()); - ; - var inHandler; - - grid.onSelectedRowsChanged.subscribe(function (e, args) { - if (inHandler) { - return; - } - selectedRowIds = self.mapRowsToIds(grid.getSelectedRows()); - }); - - this.onRowsChanged.subscribe(function (e, args) { - if (selectedRowIds.length > 0) { - inHandler = true; - var selectedRows = self.mapIdsToRows(selectedRowIds); - if (!preserveHidden) { - selectedRowIds = self.mapRowsToIds(selectedRows); - } - grid.setSelectedRows(selectedRows); - inHandler = false; - } - }); - } - - function syncGridCellCssStyles(grid, key) { - var hashById; - var inHandler; - - // since this method can be called after the cell styles have been set, - // get the existing ones right away - storeCellCssStyles(grid.getCellCssStyles(key)); - - function storeCellCssStyles(hash) { - hashById = {}; - for (var row in hash) { - var id = rows[row][idProperty]; - hashById[id] = hash[row]; - } - } - - grid.onCellCssStylesChanged.subscribe(function (e, args) { - if (inHandler) { - return; - } - if (key != args.key) { - return; - } - if (args.hash) { - storeCellCssStyles(args.hash); - } - }); - - this.onRowsChanged.subscribe(function (e, args) { - if (hashById) { - inHandler = true; - ensureRowsByIdCache(); - var newHash = {}; - for (var id in hashById) { - var row = rowsById[id]; - if (row != undefined) { - newHash[row] = hashById[id]; - } - } - grid.setCellCssStyles(key, newHash); - inHandler = false; - } - }); - } - - return { - // methods - "beginUpdate": beginUpdate, - "endUpdate": endUpdate, - "setPagingOptions": setPagingOptions, - "getPagingInfo": getPagingInfo, - "getItems": getItems, - "setItems": setItems, - "setFilter": setFilter, - "sort": sort, - "fastSort": fastSort, - "reSort": reSort, - "groupBy": groupBy, - "setAggregators": setAggregators, - "collapseGroup": collapseGroup, - "expandGroup": expandGroup, - "getGroups": getGroups, - "getIdxById": getIdxById, - "getRowById": getRowById, - "getItemById": getItemById, - "getItemByIdx": getItemByIdx, - "mapRowsToIds": mapRowsToIds, - "mapIdsToRows": mapIdsToRows, - "setRefreshHints": setRefreshHints, - "setFilterArgs": setFilterArgs, - "refresh": refresh, - "updateItem": updateItem, - "insertItem": insertItem, - "addItem": addItem, - "deleteItem": deleteItem, - "syncGridSelection": syncGridSelection, - "syncGridCellCssStyles": syncGridCellCssStyles, - // data provider methods - "getLength": getLength, - "getItem": getItem, - "getItemMetadata": getItemMetadata, - // events - "onRowCountChanged": onRowCountChanged, - "onRowsChanged": onRowsChanged, - "onPagingInfoChanged": onPagingInfoChanged - }; - } - - function AvgAggregator(field) { - this.field_ = field; - - this.init = function () { - this.count_ = 0; - this.nonNullCount_ = 0; - this.sum_ = 0; - }; - - this.accumulate = function (item) { - var val = item[this.field_]; - this.count_++; - if (val != null && val != "" && val != NaN) { - this.nonNullCount_++; - this.sum_ += parseFloat(val); - } - }; - - this.storeResult = function (groupTotals) { - if (!groupTotals.avg) { - groupTotals.avg = {}; - } - if (this.nonNullCount_ != 0) { - groupTotals.avg[this.field_] = this.sum_ / this.nonNullCount_; - } - }; - } - - function MinAggregator(field) { - this.field_ = field; - - this.init = function () { - this.min_ = null; - }; - - this.accumulate = function (item) { - var val = item[this.field_]; - if (val != null && val != "" && val != NaN) { - if (this.min_ == null || val < this.min_) { - this.min_ = val; - } - } - }; - - this.storeResult = function (groupTotals) { - if (!groupTotals.min) { - groupTotals.min = {}; - } - groupTotals.min[this.field_] = this.min_; - } - } - - function MaxAggregator(field) { - this.field_ = field; - - this.init = function () { - this.max_ = null; - }; - - this.accumulate = function (item) { - var val = item[this.field_]; - if (val != null && val != "" && val != NaN) { - if (this.max_ == null || val > this.max_) { - this.max_ = val; - } - } - }; - - this.storeResult = function (groupTotals) { - if (!groupTotals.max) { - groupTotals.max = {}; - } - groupTotals.max[this.field_] = this.max_; - } - } - - function SumAggregator(field) { - this.field_ = field; - - this.init = function () { - this.sum_ = null; - }; - - this.accumulate = function (item) { - var val = item[this.field_]; - if (val != null && val != "" && val != NaN) { - this.sum_ += parseFloat(val); - } - }; - - this.storeResult = function (groupTotals) { - if (!groupTotals.sum) { - groupTotals.sum = {}; - } - groupTotals.sum[this.field_] = this.sum_; - } - } - - // TODO: add more built-in aggregators - // TODO: merge common aggregators in one to prevent needles iterating - - })(jQuery); \ No newline at end of file + } + }; + + this.storeResult = function (groupTotals) { + if (!groupTotals.max) { + groupTotals.max = {}; + } + groupTotals.max[this.field_] = this.max_; + } + } + + function SumAggregator(field) { + this.field_ = field; + + this.init = function () { + this.sum_ = null; + }; + + this.accumulate = function (item) { + var val = item[this.field_]; + if (val != null && val !== "" && val !== NaN) { + this.sum_ += parseFloat(val); + } + }; + + this.storeResult = function (groupTotals) { + if (!groupTotals.sum) { + groupTotals.sum = {}; + } + groupTotals.sum[this.field_] = this.sum_; + } + } + + // TODO: add more built-in aggregators + // TODO: merge common aggregators in one to prevent needles iterating + +})(jQuery); diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/slick.editors.js b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/slick.editors.js index 2d8a48205a..04b20d2fad 100755 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/slick.editors.js +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/jquery/slickgrid/slick.editors.js @@ -5,508 +5,508 @@ */ (function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "Editors": { - "Text": TextEditor, - "Integer": IntegerEditor, - "Date": DateEditor, - "YesNoSelect": YesNoSelectEditor, - "Checkbox": CheckboxEditor, - "PercentComplete": PercentCompleteEditor, - "LongText": LongTextEditor + // register namespace + $.extend(true, window, { + "Slick": { + "Editors": { + "Text": TextEditor, + "Integer": IntegerEditor, + "Date": DateEditor, + "YesNoSelect": YesNoSelectEditor, + "Checkbox": CheckboxEditor, + "PercentComplete": PercentCompleteEditor, + "LongText": LongTextEditor + } + } + }); + + function TextEditor(args) { + var $input; + var defaultValue; + var scope = this; + + this.init = function () { + $input = $("") + .appendTo(args.container) + .bind("keydown.nav", function (e) { + if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) { + e.stopImmediatePropagation(); } + }) + .focus() + .select(); + }; + + this.destroy = function () { + $input.remove(); + }; + + this.focus = function () { + $input.focus(); + }; + + this.getValue = function () { + return $input.val(); + }; + + this.setValue = function (val) { + $input.val(val); + }; + + this.loadValue = function (item) { + defaultValue = item[args.column.field] || ""; + $input.val(defaultValue); + $input[0].defaultValue = defaultValue; + $input.select(); + }; + + this.serializeValue = function () { + return $input.val(); + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); + }; + + this.validate = function () { + if (args.column.validator) { + var validationResults = args.column.validator($input.val()); + if (!validationResults.valid) { + return validationResults; } - }); - - function TextEditor(args) { - var $input; - var defaultValue; - var scope = this; - - this.init = function () { - $input = $("") - .appendTo(args.container) - .bind("keydown.nav", function (e) { - if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) { - e.stopImmediatePropagation(); - } - }) - .focus() - .select(); - }; - - this.destroy = function () { - $input.remove(); - }; - - this.focus = function () { - $input.focus(); - }; - - this.getValue = function () { - return $input.val(); - }; - - this.setValue = function (val) { - $input.val(val); - }; - - this.loadValue = function (item) { - defaultValue = item[args.column.field] || ""; - $input.val(defaultValue); - $input[0].defaultValue = defaultValue; - $input.select(); - }; - - this.serializeValue = function () { - return $input.val(); - }; - - this.applyValue = function (item, state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function () { - return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); - }; - - this.validate = function () { - if (args.column.validator) { - var validationResults = args.column.validator($input.val()); - if (!validationResults.valid) { - return validationResults; - } - } - - return { - valid: true, - msg: null - }; - }; - - this.init(); - } - - function IntegerEditor(args) { - var $input; - var defaultValue; - var scope = this; - - this.init = function () { - $input = $(""); - - $input.bind("keydown.nav", function (e) { - if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) { - e.stopImmediatePropagation(); - } - }); - - $input.appendTo(args.container); - $input.focus().select(); - }; - - this.destroy = function () { - $input.remove(); - }; - - this.focus = function () { - $input.focus(); - }; - - this.loadValue = function (item) { - defaultValue = item[args.column.field]; - $input.val(defaultValue); - $input[0].defaultValue = defaultValue; - $input.select(); - }; - - this.serializeValue = function () { - return parseInt($input.val(), 10) || 0; - }; - - this.applyValue = function (item, state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function () { - return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); - }; - - this.validate = function () { - if (isNaN($input.val())) { - return { - valid: false, - msg: "Please enter a valid integer" - }; - } - - return { - valid: true, - msg: null - }; - }; - - this.init(); - } - - function DateEditor(args) { - var $input; - var defaultValue; - var scope = this; - var calendarOpen = false; - - this.init = function () { - $input = $(""); - $input.appendTo(args.container); - $input.focus().select(); - $input.datepicker({ - showOn: "button", - buttonImageOnly: true, - buttonImage: "../images/calendar.gif", - beforeShow: function () { - calendarOpen = true - }, - onClose: function () { - calendarOpen = false - } - }); - $input.width($input.width() - 18); - }; - - this.destroy = function () { - $.datepicker.dpDiv.stop(true, true); - $input.datepicker("hide"); - $input.datepicker("destroy"); - $input.remove(); - }; - - this.show = function () { - if (calendarOpen) { - $.datepicker.dpDiv.stop(true, true).show(); - } - }; - - this.hide = function () { - if (calendarOpen) { - $.datepicker.dpDiv.stop(true, true).hide(); - } - }; - - this.position = function (position) { - if (!calendarOpen) { - return; - } - $.datepicker.dpDiv - .css("top", position.top + 30) - .css("left", position.left); - }; - - this.focus = function () { - $input.focus(); - }; - - this.loadValue = function (item) { - defaultValue = item[args.column.field]; - $input.val(defaultValue); - $input[0].defaultValue = defaultValue; - $input.select(); - }; - - this.serializeValue = function () { - return $input.val(); - }; - - this.applyValue = function (item, state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function () { - return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); - }; - - this.validate = function () { - return { - valid: true, - msg: null - }; - }; - - this.init(); - } - - function YesNoSelectEditor(args) { - var $select; - var defaultValue; - var scope = this; - - this.init = function () { - $select = $(""); - $select.appendTo(args.container); - $select.focus(); - }; - - this.destroy = function () { - $select.remove(); - }; - - this.focus = function () { - $select.focus(); - }; - - this.loadValue = function (item) { - $select.val((defaultValue = item[args.column.field]) ? "yes" : "no"); - $select.select(); - }; - - this.serializeValue = function () { - return ($select.val() == "yes"); - }; - - this.applyValue = function (item, state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function () { - return ($select.val() != defaultValue); - }; - - this.validate = function () { - return { - valid: true, - msg: null - }; - }; - - this.init(); - } - - function CheckboxEditor(args) { - var $select; - var defaultValue; - var scope = this; - - this.init = function () { - $select = $(""); - $select.appendTo(args.container); - $select.focus(); - }; - - this.destroy = function () { - $select.remove(); - }; - - this.focus = function () { - $select.focus(); - }; - - this.loadValue = function (item) { - defaultValue = item[args.column.field]; - if (defaultValue) { - $select.attr("checked", "checked"); - } else { - $select.removeAttr("checked"); - } - }; - - this.serializeValue = function () { - return $select.attr("checked"); - }; - - this.applyValue = function (item, state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function () { - return ($select.attr("checked") != defaultValue); - }; - - this.validate = function () { - return { - valid: true, - msg: null - }; - }; - - this.init(); - } - - function PercentCompleteEditor(args) { - var $input, $picker; - var defaultValue; - var scope = this; - - this.init = function () { - $input = $(""); - $input.width($(args.container).innerWidth() - 25); - $input.appendTo(args.container); - - $picker = $("

").appendTo(args.container); - $picker.append("
"); - - $picker.find(".editor-percentcomplete-buttons").append("

"); - - $input.focus().select(); - - $picker.find(".editor-percentcomplete-slider").slider({ - orientation: "vertical", - range: "min", - value: defaultValue, - slide: function (event, ui) { - $input.val(ui.value) - } - }); - - $picker.find(".editor-percentcomplete-buttons button").bind("click", function (e) { - $input.val($(this).attr("val")); - $picker.find(".editor-percentcomplete-slider").slider("value", $(this).attr("val")); - }) - }; - - this.destroy = function () { - $input.remove(); - $picker.remove(); - }; - - this.focus = function () { - $input.focus(); - }; - - this.loadValue = function (item) { - $input.val(defaultValue = item[args.column.field]); - $input.select(); - }; - - this.serializeValue = function () { - return parseInt($input.val(), 10) || 0; - }; - - this.applyValue = function (item, state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function () { - return (!($input.val() == "" && defaultValue == null)) && ((parseInt($input.val(), 10) || 0) != defaultValue); - }; - - this.validate = function () { - if (isNaN(parseInt($input.val(), 10))) { - return { - valid: false, - msg: "Please enter a valid positive number" - }; - } - - return { - valid: true, - msg: null - }; - }; - - this.init(); - } - - /* - * An example of a "detached" editor. - * The UI is added onto document BODY and .position(), .show() and .hide() are implemented. - * KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter. - */ - function LongTextEditor(args) { - var $input, $wrapper; - var defaultValue; - var scope = this; - - this.init = function () { - var $container = $("body"); - - $wrapper = $("
") - .appendTo($container); - - $input = $("