From 0d28333456dc177ba78cfa3d6d8a6e303f897aae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn?= Date: Mon, 5 May 2014 17:18:44 +0200 Subject: [PATCH] Feature #1607: Add activity gantt chart --- install.sh | 2 + src/sunstone/public/js/sunstone.js | 107 +++- .../public/vendor/flot/jquery.flot.JUMlib.js | 520 ++++++++++++++++++ .../public/vendor/flot/jquery.flot.gantt.js | 201 +++++++ src/sunstone/views/index.erb | 2 + 5 files changed, 831 insertions(+), 1 deletion(-) create mode 100644 src/sunstone/public/vendor/flot/jquery.flot.JUMlib.js create mode 100644 src/sunstone/public/vendor/flot/jquery.flot.gantt.js diff --git a/install.sh b/install.sh index 6fbec3466f..1bdb5464c0 100755 --- a/install.sh +++ b/install.sh @@ -1498,6 +1498,8 @@ src/sunstone/public/vendor/flot/jquery.flot.pie.min.js \ src/sunstone/public/vendor/flot/jquery.flot.resize.min.js \ src/sunstone/public/vendor/flot/jquery.flot.stack.min.js \ src/sunstone/public/vendor/flot/jquery.flot.tooltip.min.js \ +src/sunstone/public/vendor/flot/jquery.flot.JUMlib.min.js \ +src/sunstone/public/vendor/flot/jquery.flot.gantt.min.js \ src/sunstone/public/vendor/flot/LICENSE.txt \ src/sunstone/public/vendor/flot/NOTICE" diff --git a/src/sunstone/public/js/sunstone.js b/src/sunstone/public/js/sunstone.js index 04a1d78aab..1ee7926d6a 100644 --- a/src/sunstone/public/js/sunstone.js +++ b/src/sunstone/public/js/sunstone.js @@ -3944,6 +3944,19 @@ function accountingGraphs(div, opt){
\ \
\ +
\ +
\ +

'+tr("Activity")+'

\ +
\ +
\ +
\ +
\ +
\ +
\ +
\ +
\ +
\ +
\
\
\

'+tr("CPU hours")+'

\ @@ -4379,5 +4392,97 @@ Download csv }); }); - $.plot($("#acct_mem_graph", div), plot_series, options); + var mem_plot = $.plot($("#acct_mem_graph", div), plot_series, options); + + // --- activity --- + + // TODO: refactor inefficient code + + series.GANTT = {}; + + var group_by_index = {}; + var group_by_rev_index = {}; + + var index = 0; + + $.each(response.HISTORY_RECORDS.HISTORY, function(index, history){ + + if(!filter_by_fn(history)){ + return true; //continue + } + + if(history.STIME == 0){ + return true; + } + + var group_by = group_by_fn(history); + + if(group_by_index[group_by] == undefined){ + group_by_index[group_by] = index; + group_by_rev_index[index] = group_by; + index++; + } + + var group_by_i = group_by_index[group_by]; + + if (series.GANTT[group_by_i] == undefined){ + series.GANTT[group_by_i] = []; + } + + var gantt_start = parseInt(history.STIME)*1000; + if(gantt_start < times[0]){ + gantt_start = times[0]; + } + + var gantt_end = parseInt(history.ETIME)*1000; + if(gantt_end == 0){ + gantt_end = times[times.length - 2]; + } + + var serie = series.GANTT[group_by_i]; + serie.push([ + gantt_start, // Start of Step + group_by_i, // number of resource + gantt_end, // End of step + "" // Name for step (used for tooltip) + ]); + }); + + + var plot_series = []; + var ticks = []; + + $.each(series.GANTT, function(key, val){ + data = val; + + var name = group_by_prefix+group_by_rev_index[key]; + + plot_series.push( + { + label: name, + data: data, + }); + + ticks.push([parseInt(key), name]); + }); + + options.series = { + gantt : { + active : true, + show : true, + barHeight : 1.0 + } + }; + + options.yaxis.ticks = ticks; + options.yaxis.min = -0.5; + options.yaxis.max = plot_series.length - 0.5; + + var plot_data = mem_plot.getData(); + if(plot_data.length != 0){ + options.xaxis.min = plot_data[0].xaxis.min; + options.xaxis.max = plot_data[0].xaxis.max; + } + + $.plot($("#acct_activity_graph", div), plot_series, options); } \ No newline at end of file diff --git a/src/sunstone/public/vendor/flot/jquery.flot.JUMlib.js b/src/sunstone/public/vendor/flot/jquery.flot.JUMlib.js new file mode 100644 index 0000000000..4c619d6950 --- /dev/null +++ b/src/sunstone/public/vendor/flot/jquery.flot.JUMlib.js @@ -0,0 +1,520 @@ +/* + * The MIT License + +Copyright (c) 2012, 2013 by Juergen Marsch + +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. +*/ +(function ($) { + "use strict"; + var pluginName = "JUMlib", pluginVersion = "0.5"; + function between(v,limit1,limit2){ + if(limit2 > limit1){ return (v >= limit1 && v <= limit2); } + else{ return(v >=limit2 && v <= limit1); } + } + function getMinMax(data,dataIndex){ + var mn,mx,df,dI; + if(dataIndex){ dI = dataIndex} else{ dI = 1;} + mn = Number.POSITIVE_INFINITY; + mx = Number.NEGATIVE_INFINITY; + for(var i = 0; i < data.length; i++){ + mn = Math.min(mn, data[i][dI]); + mx = Math.max(mx, data[i][dI]); + } + df = mx - mn; + return {min: mn, max: mx, diff: df}; + } + function showHover(event,pos,item,showAlways,createText){ + var txt; + if (item) { + var data = item.series.data[item.dataIndex]; + if(createText){ txt = createText(data);} + else { + txt = "X:" + data[0] + "
Y:" + data[1]; + if(data.length > 2) { for(var i = 2; i < data.length; i++){ txt += "
" + data[i]; } } + } + showTooltip(pos.pageX, pos.pageY,txt); + } + else { + if(showAlways === true){ + txt = pos.x1 + " / " + pos.y1; + showTooltip(pos.pageX,pos.pageY,txt); + } + else {$("#FLOTtooltip").remove();} + } + } + function showTooltip(x, y, contents){ + $("#FLOTtooltip").remove(); + $('
' + contents + '
').css( + { position: 'absolute',display: 'none',top: y + 5,left: x + 5, + border: '1px solid #fdd',padding: '2px','background-color': '#fee',opacity: 0.80 + }).appendTo("body").fadeIn(200); + } + function loadScripts(scripts,maxWait,callback){ + var loadedScripts = {},defs = []; + for(var i = 0; i < scripts.length; i++){ defs.push(loadScript(scripts[i].path,scripts[i].name)); } + $.when.apply(null,defs).then(function(){callback(loadedScripts);}); + function loadScript(path,name){ + var dfd = $.Deferred(),t; + t = setInterval(function(){clearInterval(t);dfd.reject();},maxWait); + $.getScript(path).done(loaded).fail(errorFound); + return dfd.promise(); + function loaded(){ loadedScripts[name] = true;dfd.resolve();} + function errorFound(e,f,g){console.log(path,e); loadedScripts[name] = false;dfd.reject();} + } + } + function loadJSON(scripts,maxWait,callback){ + var loadedJSON = {},defs = []; + for(var i = 0;i < scripts.length; i++){ defs.push(loadJSON(scripts[i].path,scripts[i].name)); } + $.when.apply(null,defs).then(function(){ callback(loadedJSON);}); + function loadJSON(path,name){ + var dfd = $.Deferred(),t; + t = setInterval(function(){clearInterval(t);dfd.reject();},maxWait); + $.getJSON(path,function(data){ + loadedJSON[name] = {data:data,name:name,loaded:true}; + }).done(loaded).fail(errorFound); + return dfd.promise(); + function loaded(){ dfd.resolve(); } + function errorFound(e,f,g){ loadedJSON[name] = {name:name,loaded:false};dfd.reject();} + } + } + + function createQuartile(data, index, indexName){ + var q0 = [], q1 = [],q2 = [],q3 = [],q4 = [], v = [], i1, i2, i3, i4, p; + i1 = (0.25 * data.length).toFixed(0); i2 = (0.5 * data.length).toFixed(0); + i3 = (0.75 * data.length).toFixed(0); i4 = data.length - 1; + for (var j = 0; j < data[0].length; j++){ + p = []; + for (var i = 0; i < data.length; i++) { p.push(data[i][j]); } + p.sort(function(a,b){return a - b;} ); + q1.push([j,p[i1]]); q2.push([j,p[i2]]); q3.push([j,p[i3]]); q4.push([j,p[i4]]); + q0.push([j,p[0]]); v.push([j,data[index][j]]); + } + var r = [ { data: q4}, { data: q3}, { data: q2}, { data: q1}, {data: q0, color: "#ffffff" }, + {label: indexName, points: {show:true}, lines: { fill: null, steps: false}, data: v}]; + return r; + } + function createPercentile(data, index, indexName, percentiles){ + var percentile = [], val = [], indexes = [], p,j,i; + if(percentiles.length){ + indexes.push([0]); + percentile.push([]); + for(j = 0;j < percentiles.length;j++){ + indexes.push(parseInt(data.length * percentiles[j],0)); + percentile.push([]); + } + indexes.push(data.length - 1); + } + else{ + for(j = 0;j < percentiles; j++){ + indexes.push(parseInt(data.length / percentiles * j,0)); + percentile.push([]); + } + indexes.push(data.length - 1); + } + percentile.push([]); + for(j = 0; j < data[0].length; j++){ + p = []; + for(i = 0; i < data.length; i++){ p.push(data[i][j]); } + p.sort(function(a,b){return a-b;}); + for(i = 0; i < percentile.length; i++ ) { percentile[i].push([j,p[indexes[i]]]); } + val.push([j,data[index][j]]); + } + var r = []; + for(j = percentile.length - 1; j > 0 ; j--){ r.push({ data: percentile[j] });} + r.push({data: percentile[0], color:"#ffffff"}); + r.push({label: indexName, points: {show: true}, lines: { fill: null, steps: false}, data:val}); + return r; + } + function createSimiliarity(data1, data2, mode){ + var r = []; + var d1 = normalize(data1); + var d2 = normalize(data2); + var d; + for (var i = 0; i < d1.length; i++){ + switch (mode){ + case "diff": + d = d1[i][1] - d2[i][1]; + break; + case "abs": + d = Math.abs(d1[i][1] - d2[i][1]); + break; + default: + d = 0; + } + r.push([d1[i][0],d]); + } + return r; + } + function createWaterfall(data, colors){ //convert waterfalldata to 4 Bars, d1 for fixed bars, d2 for invisible bar + var d1 = [], d2 = [], d3 = [], d4 = [], p = 0, mn = Number.POSITIVE_INFINITY,i; // d3 for negative bars and d4 for positive bars + var dx = data; + for(i = 0; i < dx.length; i++) { + if(dx[i][2]){ + if(typeof dx[i][1] === "undefined") { d1.push([i,p]); } + else { d1.push([i,dx[i][1]]); p = dx[i][1]; } + } + else{ + if(dx[i][1] > 0) { d4.push([i,- dx[i][1]]);p = p + dx[i][1];d2.push([i,p]);d3.push([i,0]);} + else {d3.push([i,- dx[i][1]]);p = p + dx[i][1];d2.push([i,p]);} + } + mn = Math.min(mn,p); + } + var ticks = []; + for(i = 0; i < data.length; i++){ ticks.push([i,data[i][0]]);} + var dr = { + data: [ + { data: d1, color: colors.fixed }, + { data: d2, bars: { show: false }, lines: { show: false } }, + { data: d3, color: colors.negative }, + { data: d4, color: colors.positive} + ], + ticks: ticks, + yaxismin: mn + }; + return dr; + } + function avg(data,range){ + var r = [],rd = [],i1,s; + for(var i = 0; i < data.length; i++){ + if(i < range){ i1 = 0;} else{ i1 = i - range + 1;} + rd = []; + rd.push(data[i][0]); + for(var k = 1; k < data[i].length; k++){ + s = 0; + for(var j = i1; j <= i; j++){ s += data[j][k];} + rd.push(s / (i - i1 + 1)); + } + r.push(rd); + } + return r; + } + function max(data,range){ + var r = [], rd = [], i1, mx; + for(var i = 0; i < data.length; i++){ + if(i < range) {i1 = 0;} else {i1 = i - range + 1;} + rd = []; + rd.push(data[i][0]); + for(var k = 1; k < data[i].length; k++){ + mx = - Number.MAX_VALUE; + for(var j = i1; j <= i; j++){ if(data[j][k] > mx){ mx = data[j][k]; } } + rd.push(mx); + } + r.push(rd); + } + return r; + } + function min(data,range){ + var r = [], rd = [], i1, mn; + for(var i = 0; i < data.length; i++){ + if(i < range){ i1 = 0;} else{ i1 = i - range + 1;} + rd = []; + rd.push(data[i][0]); + for(var k = 1; k < data[i].length; k++){ + mn = Number.MAX_VALUE; + for(var j = i1; j <= i; j++){ if(data[j][k] < mn){ mn = data[j][k]; } } + rd.push(mn); + } + r.push(rd); + } + return r; + } + function sort(data,sortOrder,sortfnc){ + var d = []; + for(var i = 0; i < data.length; i++){ d.push(data[i]);} + if(sortfnc){ d.sort(sortfnc); } + else{ + if (sortOrder === "a"){ d.sort(mysorta); } else { d.sort(mysortd); } + } + return d; + function mysorta(a,b){ return a[1] - b[1]; } + function mysortd(a,b){ return b[1] - a[1]; } + } + function sortTicks(data,sortOrder,sortfnc){ + var d = sort(data,sortOrder,sortfnc); + for(var i = 0; i < d.length; i++){d[i][0] = i; } + return { data:d, ticks:getTicks(d) }; + } + function pareto(data,otherLabel,showOthers,topN,topPercent){ + var d = [],othersLabel="Others",showothers = true,i,dn = []; + d = sort(data,"d"); + if(otherLabel.length > 0){ othersLabel = otherLabel;} + showothers = showOthers; + if(topN){ + var s; + for(i = 0;i < topN; i++){ dn.push(d[i]); } + s = 0; + for(i = topN;i < d.length; i++){ s+=d[i][1]; } + if(showothers){ dn.push([topN, s,othersLabel]);} + d = dn; + } + else if(topPercent){ + var datasum = 0, datar = 0, datao = 0,j; + for(i = 0; i < d.length;i++){ datasum += d[i][1];} + datasum = datasum * topPercent / 100; + for(i = 0; i < d.length; i++){ + if (datar < datasum) { + dn.push(d[i]); + datar += d[i][1]; + j = i; + } + else{ datao += d[i][1]; } + } + j++; + if(showothers){ dn.push([j,datao,othersLabel]);} + d = dn; + } + for(i = 0; i < d.length; i++){d[i][0] = i; } + return { data:d, ticks:getTicks(d) }; + } + function getTicks(d){ + var t = []; + for(var i = 0; i < d.length; i++){ + if(d[i][2]){ t.push([d[i][0], d[i][2]]);} else{ t.push(d[i][0], d[i][0]);} + } + return t; + } + function normalize(data){ + var minmax, d;var r = []; + minmax = getMinMax(data); + for(var i = 0; i < data.length; i++){ + d = (data[i][1] - minmax.min) / minmax.diff * 100; + r.push([data[i][0],d]); + } + return r; + } + function combineData(data,ticks){ + var r = []; + for(var i = 0; i < data.length; i++){ + var s = []; + for(var j = 0; j < data[i].length; j++){ + var d = [ ticks[j], data[i][j] ]; + s.push(d); + } + r.push(s); + } + return r; + } + + function createFont(placeholder){ + var f = { + style: placeholder.css("font-style"), + size: Math.round(0.8 * (+placeholder.css("font-size").replace("px", "") || 13)), + variant: placeholder.css("font-variant"), + weight: placeholder.css("font-weight"), + family: placeholder.css("font-family") + }; + return f; + } + function createColors(options,neededColors){ + // this is copied code from jquery.flot.js in fillInSeriesOptions + var c, colors = [], colorPool = options.colors, + colorPoolSize = colorPool.length, variation = 0; + for(var i = 0; i < colorPoolSize; i++){ colors[i] = colorPool[i]; } + if(colorPoolSize < neededColors){ + for (i = colorPoolSize; i < neededColors; i++) { + c = $.color.parse(colorPool[i % colorPoolSize] || "#666"); + if (i % colorPoolSize === 0 && i) { + if (variation >= 0) { + if (variation < 0.5) { variation = -variation - 0.2; } else{ variation = 0;} + } + else {variation = -variation;} + } + colors[i] = c.scale('rgb', 1 + variation).toString(); + } + } + return colors; + } + function getColor(colorData){ //based on a patch from Martin Thorsen Ranang from Nov 2012 + var c; + if(typeof colorData === "object"){ + if(typeof colorData.dataIndex !== "undefined"){ + if(typeof colorData.serie.data[colorData.dataIndex].color !== "undefined"){ + c = getColorL(colorData.serie.data[colorData.dataIndex].color); + } + else{ c = colorData.colors[colorData.dataIndex]; } + } + else{ + if(typeof colorData.serieIndex !== "undefined"){ + if(typeof colorData.serie.color !== "undefined"){ c = getColorL(colorData.serie.color);} + else{ c = colorData.colors[colorData.serieIndex];} + } + else{ + if(typeof colorData.color !== "undefined"){c = getColorL(colorData.color);} + else {c = "darkgreen"; } + } + } + } + else{ c = getColorL(colorData); } + return c; + function getColorL(color){ + var c; + if(typeof color === "object"){ + if(typeof color.image !== "undefined"){ + c = colorData.ctx.createPattern(color.image,color.repeat); + } + else{ + if(colorData.radius){ + c = colorData.ctx.createRadialGradient(colorData.left,colorData.top,0, + colorData.left,colorData.top,colorData.radius); + } + else { c = colorData.ctx.createLinearGradient(0,0,colorData.width,colorData.height);} + for(var i = 0; i < color.colors.length; i++){ + var cl = color.colors[i]; + if(typeof cl !== "string"){ + var co = $.color.parse(colorData.defaultColor); + if(color.brightness !== null){ cl = co.scale("rgb",color.brightness);} + if(color.opacity !== null){ co *= color.opacity;} + cl = co.toString(); + } + c.addColorStop(i / (color.colors.length - 1),cl); + } + } + } + else{ if(typeof color === "string"){ c = color; } else { c = colorData.colors[color]; } } + return c; + } + } + function loadImages(images,maxWait,callback){ + var loadedImg = {},defs = []; + for(var i = 0; i < images.length; i++){ defs.push(loadImage(images[i])); } + $.when.apply(null,defs).then(function(){callback(loadedImg);}); + function loadImage(img){ + var dfd = $.Deferred(),t,url; + url = img.path + img.name + "." + img.type; + t = setInterval(function(){clearInterval(t);dfd.reject();},maxWait); + $('').attr('src',url).load(loaded).error(errorFound); + return dfd.promise(); + function loaded(){ loadedImg[img.name] = this;dfd.resolve();} + function errorFound(e,f,g){console.log(url,e); loadedImg[img.name] = null;dfd.reject();} + } + } + function getCanvases(placeholder){ + var canvases = { + background:$(placeholder).children(".flot-background"), + base:$(placeholder).children(".flot-base"), + overlay:$(placeholder).children(".flot-overlay") + }; + return canvases; + } + function extendEmpty(org,ext){ + for(var i in ext){ + if(!org[i]){ org[i] = ext[i];} + else{ + if(typeof ext[i] === "object"){ + extendEmpty(org[i],ext[i]); + } + } + } + } + + function drawLines(plot,lines){ + var offset,series,ctx; + offset = plot.getPlotOffset(); + series = plot.getData(); + ctx = plot.getCanvas().getContext("2d"); + ctx.translate(offset.left,offset.top); + for(var i = 0; i < lines.length; i++){ + var from = series[lines[i].from.seriesIndex], to = series[lines[i].to.seriesIndex]; + var fl = lines[i].from, tl = lines[i].to; + if(!fl.dataFieldX){ fl.dataFieldX = 0;} + if(!fl.dataFieldY){ fl.dataFieldY = 1;} + if(!tl.dataFieldX){ tl.dataFieldX = 0;} + if(!tl.dataFieldY){ tl.dataFieldY = 1;} + var fromPos = {xaxis:from.xaxis, yaxis:from.yaxis, x:from.data[fl.dataIndex][fl.dataFieldX], y:from.data[fl.dataIndex][fl.dataFieldY]}, + toPos = {xaxis:to.xaxis, yaxis:to.yaxis, x:to.data[tl.dataIndex][tl.dataFieldX], y:to.data[tl.dataIndex][tl.dataFieldY]}, + lineStyle = {strokeStyle:"red", lineWidth:5}; + drawLine(plot,ctx,fromPos,toPos,lineStyle); + } + } + function drawLine(plot,ctx,fromPos,toPos,lineStyle){ + var xf,yf,xt,yt; + xf = fromPos.xaxis.p2c(fromPos.x); + yf = fromPos.yaxis.p2c(fromPos.y); + xt = toPos.xaxis.p2c(toPos.x); + yt = toPos.yaxis.p2c(toPos.y); + ctx.beginPath(); + ctx.strokeStyle = lineStyle.strokeStyle; + ctx.lineWidth = lineStyle.lineWidth; + ctx.moveTo(xf,yf); + ctx.lineTo(xt,yt); + ctx.stroke(); + } + function drawRect(plot,ctx,xaxis,yaxis,boxDim,boxStyle){ + var boxC = {x: xaxis.p2c(boxDim.x),y: yaxis.p2c(boxDim.y), + width: xaxis.p2c((boxDim.x + boxDim.width)) - xaxis.p2c(boxDim.x), + height: yaxis.p2c(boxDim.y + boxDim.height) - yaxis.p2c(boxDim.y)}; + switch(boxStyle.mode){ + case "image": drawImage(plot,ctx,boxC,boxStyle); break; + case "color": drawColor(plot,ctx,boxC,boxStyle); break; + case "userdefined": boxStyle.boxDraw(plot,ctx,boxC,boxStyle); break; + default: drawColor(plot,ctx,boxC,boxStyle); + } + function drawImage(plot,ctx,boxC,boxStyle){ + var image = boxStyle.image; + if(typeof image !== "undefined"){ctx.drawImage(image,boxC.x,boxC.y,boxC.width(),boxC.height());} + } + function drawColor(plot,ctx,boxC,boxStyle){ + color = getColor({ctx:ctx,color:boxStyle.color,left:boxC.x,top:boxC.y,height:boxC.height,width:boxC.width}); + ctx.fillStyle = color; + ctx.fillRect(boxC.x,boxC.y,boxC.width,boxC.height); + } + } + function drawCircle(plot,ctx,xaxis,yaxis,circle,circleStyle){ + var circleC = {x: xaxis.p2c(boxDim.x),y: yaxis.p2c(boxDim.y)} + } + $.plot.JUMlib = {}; + $.plot.JUMlib.library = {}; + $.plot.JUMlib.library.between = between; + $.plot.JUMlib.library.getMinMax = getMinMax; + $.plot.JUMlib.library.showHover = showHover; + $.plot.JUMlib.library.showTooltip = showTooltip; + $.plot.JUMlib.library.loadScripts = loadScripts; + $.plot.JUMlib.library.loadJSON = loadJSON; + $.plot.JUMlib.prepareData = {}; + $.plot.JUMlib.prepareData.createQuartile = createQuartile; + $.plot.JUMlib.prepareData.createPercentile = createPercentile; + $.plot.JUMlib.prepareData.createSimiliarity = createSimiliarity; + $.plot.JUMlib.prepareData.createWaterfall = createWaterfall; + $.plot.JUMlib.prepareData.avg = avg; + $.plot.JUMlib.prepareData.max = max; + $.plot.JUMlib.prepareData.min = min; + $.plot.JUMlib.prepareData.sort = sort; + $.plot.JUMlib.prepareData.sortTicks = sortTicks; + $.plot.JUMlib.prepareData.pareto = pareto; + $.plot.JUMlib.prepareData.normalize = normalize; + $.plot.JUMlib.prepareData.combineData = combineData; + $.plot.JUMlib.data = {}; + $.plot.JUMlib.data.createFont = createFont; + $.plot.JUMlib.data.createColors = createColors; + $.plot.JUMlib.data.getColor = getColor; + $.plot.JUMlib.data.loadImages = loadImages; + $.plot.JUMlib.data.getCanvases = getCanvases; + $.plot.JUMlib.data.extendEmpty = extendEmpty; + $.plot.JUMlib.drawing = {}; + $.plot.JUMlib.drawing.drawLine = drawLine; + $.plot.JUMlib.drawing.drawRect = drawRect; + $.plot.JUMlib.drawing.drawLines = drawLines; + $.plot.plugins.push({ + init: function(){}, + options: { }, + name: pluginName, + version: pluginVersion + }); +})(jQuery); \ No newline at end of file diff --git a/src/sunstone/public/vendor/flot/jquery.flot.gantt.js b/src/sunstone/public/vendor/flot/jquery.flot.gantt.js new file mode 100644 index 0000000000..16edaabf60 --- /dev/null +++ b/src/sunstone/public/vendor/flot/jquery.flot.gantt.js @@ -0,0 +1,201 @@ +/* + * The MIT License + +Copyright (c) 2010, 2011, 2012, 2013 by Juergen Marsch + +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. +*/ + +(function ($) { + "use strict"; + var pluginName = "gantt", pluginVersion = "0.3"; + var options = { + series: { + gantt: { + active: false, + show: false, + connectSteps: { show: false, lineWidth:2, color:"rgb(0,0,0)" }, + barHeight: 0.6, + highlight: { opacity: 0.5 }, + drawstep: drawStepDefault + } + } + }; + var replaceOptions = { series:{ lines: { show:false } } }; + var defaultOptions = { + series:{ + editMode: 'y', //could be none, x, y, xy, v + nearBy:{ + distance: 6, + findItem: null, + findMode: "circle", + drawHover: null + } + } + }; + function drawStepDefault(ctx,series,data,x,y,x2,color, isHighlight){ + if(isHighlight === false){ + ctx.beginPath(); + ctx.lineWidth = series.gantt.barheight; + ctx.strokeStyle = "rgb(0,0,0)"; + ctx.moveTo(x, y); + ctx.lineTo(x2, y); + ctx.stroke(); + } + ctx.beginPath(); + ctx.strokeStyle = color; + ctx.lineWidth = series.gantt.barheight - 2; + ctx.lineCap = "butt"; + ctx.moveTo(x + 1, y); + ctx.lineTo(x2 - 1, y); + ctx.stroke(); + } + function init(plot) { + var offset = null, opt = null, series = null,canvas,target,axes,data; + plot.hooks.processOptions.push(processOptions); + function processOptions(plot,options){ + if (options.series.gantt.active){ + $.extend(true,options,replaceOptions); + $.plot.JUMlib.data.extendEmpty(options,defaultOptions); + opt = options; + plot.hooks.processRawData.push(processRawData); + plot.hooks.draw.push(draw); + } + } + function processRawData(plot,s,data,datapoints){ + if(s.gantt.show === true){ + s.nearBy.findItem = findNearbyItemGantt; + s.nearBy.drawHover = drawHoverGantt; + } + } + function draw(plot, ctx){ + var serie; + canvas = plot.getCanvas(); + target = $(canvas).parent(); + axes = plot.getAxes(); + offset = plot.getPlotOffset(); + data = plot.getData(); + for (var i = 0; i < data.length; i++){ + serie = data[i]; + serie.gantt.barheight = axes.yaxis.p2c(1) / (axes.yaxis.max - axes.yaxis.min) * serie.gantt.barHeight; + if (serie.gantt.show) { + series = serie; + for (var j = 0; j < serie.data.length; j++){drawData(ctx,serie, serie.data[j], serie.color,false); } + if(serie.gantt.connectSteps.show){ drawConnections(ctx,serie); } + } + } + } + function drawData(ctx,series,data,color,isHighlight){ + var x,y,x2; + x = offset.left + axes.xaxis.p2c(data[0]); + x = Math.min(Math.max(offset.left,x),offset.left + plot.width()); + y = offset.top + axes.yaxis.p2c(data[1]); + x2 = offset.left + axes.xaxis.p2c(data[2]); + x2 = Math.min(Math.max(x2,offset.left),offset.left + plot.width()); + if(x2 > offset.left || x > offset.left){ + if (x < (offset.left + plot.width()) || x2 < (offset.left + plot.width())){ + if (data.length === 4) {drawStepDefault(ctx, series, data, x, y, x2, color, isHighlight);} + else{ series.gantt.drawstep(ctx,series,data,x,y,x2,color,isHighlight);} + } + } + } + function drawConnections(ctx,series){ + for(var i = 0; i < series.data.length; i++){ + for(var j = 0; j < series.data.length; j++){ + if(series.data[i][2] == series.data[j][0]){ + var x = offset.left + axes.xaxis.p2c(series.data[i][2]), + y = offset.top + axes.yaxis.p2c(series.data[i][1]), + y2 = offset.top + axes.yaxis.p2c(series.data[j][1]); + drawConnection(ctx,x,y,y2,series.gantt.connectSteps.lineWidth,series.gantt.connectSteps.color); + } + } + } + } + function drawConnection(ctx,x,y,y2,lineWidth,color){ + ctx.beginPath(); + ctx.lineWidth = lineWidth; + ctx.strokeStyle = color; + ctx.moveTo(x, y); + ctx.lineTo(x, y2); + ctx.stroke(); + } + function findNearbyItemGantt(mouseX, mouseY,i,serie){ + var item = null; + if(opt.series.justEditing){ + if(opt.series.justEditing[1].seriesIndex === i){item = findNearbyItemEdit(mouseX,mouseY,i,serie);} + } + else{ + if(opt.grid.editable){ item = findNearbyItemForEdit(mouseX,mouseY,i,serie);} + else{ item = findNearbyItem(mouseX,mouseY,i,serie);} + } + return item; + function findNearbyItem(mouseX,mouseY,i,serie){ + var item = null; + if(serie.gantt.show){ + for(var j = 0; j < serie.data.length; j++){ + var dataitem = serie.data[j]; + var dx = serie.xaxis.p2c(dataitem[0]),dx2 = serie.xaxis.p2c(dataitem[2]), + dy = Math.abs(serie.yaxis.p2c(dataitem[1]) - mouseY); + if(dy <= serie.gantt.barheight / 2){ if(between(mouseX,dx,dx2)){ item = [i,j]; } } + } + } + return item; + } + function findNearbyItemForEdit(mouseX,mouseY,i,serie){ + var item = null; + if(serie.gantt.show){ + for(var j = 0; j < serie.data.length; j++){ + var dataitem = serie.data[j]; + var dx = serie.xaxis.p2c(dataitem[0]),dx2 = serie.xaxis.p2c(dataitem[2]), + dy = Math.abs(serie.yaxis.p2c(dataitem[1]) - mouseY); + if(dy <= serie.gantt.barheight / 2){ + if(between(mouseX,dx,dx2)){ item = [i,j]; serie.editMode = 'y'; serie.nearBy.findMode = 'vertical';serie.nearBy.width = dataitem[2]-dataitem[0];} + if(between(mouseX,dx,dx + serie.nearBy.distance)) { item = [i,[j,1]];serie.editMode = 'x'; serie.nearBy.findMode = 'horizontal'; } + if(between(mouseX,dx2,dx2 + serie.nearBy.distance)) { item = [i,[j,2]];serie.editMode = 'x'; serie.nearBy.findMode = 'horizontal'; } + } + } + } + return item; + } + function findNearbyItemEdit(mouseX,mouseY,i,serie){ + var item = null; + var j = opt.series.justEditing[1].dataIndex; + var dataitem = serie.data[j]; + if(j.length){item = [i,j];}else{item = [i,j];} + return item; + } + } + function drawHoverGantt(octx,serie,dataIndex){ + var data; + octx.save(); + octx.translate(-offset.left,-offset.top); + var c = "rgba(255,255,255, " + serie.gantt.highlight.opacity + ")"; + if(dataIndex.length){ data = serie.data[dataIndex[0]];} else{ data = serie.data[dataIndex];} + drawData(octx,serie,data,c,true); + octx.restore(); + } + } + var between = $.plot.JUMlib.library.between; + $.plot.plugins.push({ + init: init, + options: options, + name: pluginName, + version: pluginVersion + }); +})(jQuery); diff --git a/src/sunstone/views/index.erb b/src/sunstone/views/index.erb index 6f084a53bf..058be2aa60 100644 --- a/src/sunstone/views/index.erb +++ b/src/sunstone/views/index.erb @@ -13,6 +13,8 @@ + +