mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-10 15:15:34 +00:00
Custom comparisons, performance improvements
This commit is contained in:
123
app/js/directives/directive-area-chart.js
Normal file
123
app/js/directives/directive-area-chart.js
Normal file
@@ -0,0 +1,123 @@
|
||||
angular.module('app').directive('areaChart', function () {
|
||||
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope:{
|
||||
config: '=',
|
||||
series: '=',
|
||||
height: '=',
|
||||
width: '='
|
||||
},
|
||||
link: function(scope, element) {
|
||||
var width = scope.width,
|
||||
height = scope.height,
|
||||
series = scope.series,
|
||||
config = scope.config,
|
||||
labels = config.labels,
|
||||
margin = {top: 15, right: 15, bottom: 35, left: 50},
|
||||
w = width - margin.left - margin.right,
|
||||
h = height - margin.top - margin.bottom,
|
||||
fmt = d3.format('.3r'),
|
||||
fmtLong = d3.format('.2f');
|
||||
|
||||
// Create chart
|
||||
var svg = d3.select(element[0]).append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
|
||||
// Define Axes
|
||||
var x = d3.scale.linear().range([0, w]);
|
||||
var y = d3.scale.linear().range([h, 0]);
|
||||
var xAxis = d3.svg.axis().outerTickSize(0).orient("bottom").tickFormat(fmt);
|
||||
var yAxis = d3.svg.axis().outerTickSize(0).orient("left").tickFormat(fmt);
|
||||
// Define Area
|
||||
var area = d3.svg.area().x(function(d) { return x(d[0]); }).y0(h).y1(function(d) { return y(d[1]); });
|
||||
|
||||
var gradient = svg.append("defs")
|
||||
.append("linearGradient")
|
||||
.attr("id", "gradient")
|
||||
.attr("x1", "0%").attr("y1", "0%")
|
||||
.attr("x2", "100%").attr("y2", "100%")
|
||||
.attr("spreadMethod", "pad");
|
||||
gradient.append("stop")
|
||||
.attr("offset", "0%")
|
||||
.attr("stop-color", "#ff8c0d")
|
||||
.attr("stop-opacity", 1);
|
||||
gradient.append("stop")
|
||||
.attr("offset", "100%")
|
||||
.attr("stop-color", "#ff3b00")
|
||||
.attr("stop-opacity", 1);
|
||||
|
||||
// Create Y Axis SVG Elements
|
||||
svg.append("g").attr("class", "y axis")
|
||||
.append("text")
|
||||
.attr("transform", "rotate(-90)")
|
||||
.attr("y", -40)
|
||||
.attr("x", -h/2)
|
||||
.attr("dy", ".1em")
|
||||
.style("text-anchor", "middle")
|
||||
.text(labels.yAxis.title + ' (' + labels.yAxis.unit + ')');
|
||||
// Create X Axis SVG Elements
|
||||
svg.append("g").attr("class", "x axis").attr("transform", "translate(0," + h + ")")
|
||||
.append("text")
|
||||
.attr("y", 30)
|
||||
.attr("x", w/2)
|
||||
.attr("dy", ".1em")
|
||||
.style("text-anchor", "middle")
|
||||
.text(labels.xAxis.title + ' (' + labels.xAxis.unit + ')');
|
||||
|
||||
// Create and Add tooltip
|
||||
var tip = svg.append("g").style("display", "none");
|
||||
tip.append("circle")
|
||||
.attr("class", "marker")
|
||||
.attr("r", 4);
|
||||
tip.append("text").attr("class", 'label x').attr("y", -2);
|
||||
tip.append("text").attr("class", 'label y').attr("y", '0.7em');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Watch for changes in the series data (mass changes, etc)
|
||||
*/
|
||||
scope.$watchCollection('series', render);
|
||||
scope.$watchCollection('config.watch', render);
|
||||
|
||||
function render() {
|
||||
var data = [];
|
||||
var func = series.func;
|
||||
for (var d = series.xMin; d <= series.xMax; d++) {
|
||||
data.push([ d, func(d) ]);
|
||||
}
|
||||
// Update domain and scale for axes;
|
||||
x.domain([series.xMin, series.xMax]);
|
||||
xAxis.scale(x);
|
||||
y.domain([data[data.length - 1][1], data[0][1]]);
|
||||
yAxis.scale(y);
|
||||
svg.selectAll(".y.axis").call(yAxis);
|
||||
svg.selectAll(".x.axis").call(xAxis);
|
||||
|
||||
// Remove existing elements
|
||||
svg.selectAll('path.area').remove();
|
||||
|
||||
svg.insert("path",':first-child') // Area/Path to appear behind everything else
|
||||
.datum(data)
|
||||
.attr("class", "area")
|
||||
.attr('fill', 'url(#gradient)')
|
||||
.attr("d", area)
|
||||
.on("mouseover", function() { tip.style("display", null); })
|
||||
.on("mouseout", function() { tip.style("display", "none"); })
|
||||
.on('mousemove', function() {
|
||||
var xPos = d3.mouse(this)[0], x0 = x.invert(xPos), y0 = func(x0), flip = (xPos > w * 0.75);
|
||||
tip.attr("transform", "translate(" + x(x0) + "," + y(y0) + ")");
|
||||
tip.selectAll('text.label').attr("x", flip? -10 : 10).style("text-anchor", flip? 'end' : 'start');
|
||||
tip.select('text.label.x').text(fmtLong(x0) + ' ' + labels.xAxis.unit);
|
||||
tip.select('text.label.y').text(fmtLong(y0) + ' ' + labels.yAxis.unit);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -25,76 +25,75 @@ angular.module('app').directive('barChart', ['$rootScope', function ($rootScope)
|
||||
width: '='
|
||||
},
|
||||
link: function(scope, element) {
|
||||
var color = d3.scale.ordinal().range([ "#7b6888", "#6b486b", "#3182bd", "#a05d56", "#d0743c"]),
|
||||
var color = d3.scale.ordinal().range([ '#7b6888', '#6b486b', '#3182bd', '#a05d56', '#d0743c']),
|
||||
width = scope.width,
|
||||
height = scope.height,
|
||||
labels = scope.facet.lbls,
|
||||
fmt = $rootScope[scope.facet.fmt],
|
||||
properties = scope.facet.prop? [scope.facet.prop] : scope.facet.props,
|
||||
fmt = scope.facet.fmt,
|
||||
properties = scope.facet.props,
|
||||
unit = scope.facet.unit,
|
||||
margin = {top: 10, right: 20, bottom: 35, left: 150},
|
||||
w = width - margin.left - margin.right,
|
||||
h = height - margin.top - margin.bottom;
|
||||
w = width - margin.left - margin.right;
|
||||
|
||||
// Create chart
|
||||
var svg = d3.select(element[0]).append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
var svg = d3.select(element[0]).append('svg').attr('width', width);
|
||||
var vis = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
||||
|
||||
// Create and Add tooltip
|
||||
var tip = d3.tip()
|
||||
.attr('class', 'd3-tip')
|
||||
.html(function(property, propertyIndex, buildIndex) {
|
||||
return (labels? (labels[propertyIndex] + ": ") : '') + fmt(property.value) + ' ' + unit;
|
||||
.html(function(property, propertyIndex) {
|
||||
return (labels? (labels[propertyIndex] + ': ') : '') + fmt(property.value) + ' ' + unit;
|
||||
});
|
||||
svg.call(tip);
|
||||
vis.call(tip);
|
||||
|
||||
// Create Y Axis SVG Elements
|
||||
svg.append("g").attr("class", "y axis");
|
||||
svg.selectAll('g.y.axis g text').each(insertLinebreaks);
|
||||
vis.append('g').attr('class', 'y axis');
|
||||
vis.selectAll('g.y.axis g text').each(insertLinebreaks);
|
||||
// Create X Axis SVG Elements
|
||||
svg.append("g")
|
||||
.attr("class", "x axis")
|
||||
.attr("transform", "translate(0," + h + ")")
|
||||
.append("text")
|
||||
.attr("y", 30)
|
||||
.attr("x", w/2)
|
||||
.attr("dy", ".1em")
|
||||
.style("text-anchor", "middle")
|
||||
vis.append('g')
|
||||
.attr('class', 'x axis')
|
||||
.append('text')
|
||||
.attr('y', 30)
|
||||
.attr('x', w/2)
|
||||
.attr('dy', '.1em')
|
||||
.style('text-anchor', 'middle')
|
||||
.text(scope.facet.title + (unit? (' (' + unit + ')') : ''));
|
||||
|
||||
|
||||
/**
|
||||
* Watch for changes in the comparison array (ships added/removed, sorting)
|
||||
*/
|
||||
scope.$watch('data', function() {
|
||||
var data = scope.data;
|
||||
var maxVal = d3.max(data, function(d) { return d3.max(properties, function(prop) {return d[prop]; }); });
|
||||
var y0 = d3.scale.ordinal().domain(data.map(bName)).rangeRoundBands([0, h],0.3);
|
||||
var y1 = d3.scale.ordinal().domain(properties).rangeRoundBands([0, y0.rangeBand()]);
|
||||
var x = d3.scale.linear().range([0, w]).domain([0, maxVal]);
|
||||
var yAxis = d3.svg.axis().scale(y0).outerTickSize(0).orient("left");
|
||||
var xAxis = d3.svg.axis().scale(x).outerTickSize(0).orient("bottom").tickFormat(d3.format('.2s'));
|
||||
scope.$watchCollection('data', function() {
|
||||
var data = scope.data,
|
||||
height = 45 + (25 * data.length),
|
||||
h = height - margin.top - margin.bottom,
|
||||
maxVal = d3.max(data, function(d) { return d3.max(properties, function(p) {return d[p]; }); }),
|
||||
y0 = d3.scale.ordinal().domain(data.map(bName)).rangeRoundBands([0, h],0.3),
|
||||
y1 = d3.scale.ordinal().domain(properties).rangeRoundBands([0, y0.rangeBand()]),
|
||||
x = d3.scale.linear().range([0, w]).domain([0, maxVal]),
|
||||
yAxis = d3.svg.axis().scale(y0).outerTickSize(0).orient('left'),
|
||||
xAxis = d3.svg.axis().scale(x).outerTickSize(0).orient('bottom').tickFormat(d3.format('.2s'));
|
||||
|
||||
// Update chart size
|
||||
svg.attr('height', height);
|
||||
|
||||
// Remove existing elements
|
||||
svg.selectAll('.ship').remove();
|
||||
svg.selectAll('rect').remove();
|
||||
vis.selectAll('.ship').remove();
|
||||
vis.selectAll('rect').remove();
|
||||
|
||||
// Update X & Y Axis
|
||||
svg.selectAll(".y.axis").call(yAxis);
|
||||
svg.selectAll(".x.axis").call(xAxis);
|
||||
vis.selectAll('.y.axis').call(yAxis);
|
||||
vis.selectAll('.x.axis').attr('transform', 'translate(0,' + h + ')').call(xAxis);
|
||||
// Update Y-Axis labels
|
||||
svg.selectAll('g.y.axis g text').each(insertLinebreaks);
|
||||
vis.selectAll('g.y.axis g text').each(insertLinebreaks);
|
||||
|
||||
var group = svg.selectAll(".ship")
|
||||
var group = vis.selectAll('.ship')
|
||||
.data(scope.data, bName)
|
||||
.enter().append("g")
|
||||
.attr("class", "g")
|
||||
.attr("transform", function(build) { return "translate(0," + y0(bName(build)) + ")"; });
|
||||
.enter().append('g')
|
||||
.attr('class', 'g')
|
||||
.attr('transform', function(build) { return 'translate(0,' + y0(bName(build)) + ')'; });
|
||||
|
||||
group.selectAll("rect")
|
||||
group.selectAll('rect')
|
||||
.data(function(build) {
|
||||
var o = [];
|
||||
for (var i = 0; i < properties.length; i++) {
|
||||
@@ -102,20 +101,20 @@ angular.module('app').directive('barChart', ['$rootScope', function ($rootScope)
|
||||
}
|
||||
return o;
|
||||
})
|
||||
.enter().append("rect")
|
||||
.attr("height", y1.rangeBand())
|
||||
.attr("x",0)
|
||||
.attr("y", function(d) {return y1(d.name); })
|
||||
.attr("width", function(d) { return x(d.value); })
|
||||
.enter().append('rect')
|
||||
.attr('height', y1.rangeBand())
|
||||
.attr('x',0)
|
||||
.attr('y', function(d) {return y1(d.name); })
|
||||
.attr('width', function(d) { return x(d.value); })
|
||||
.on('mouseover', tip.show)
|
||||
.on('mouseout', tip.hide)
|
||||
.style("fill", function(d) { return color(d.name); });
|
||||
.style('fill', function(d) { return color(d.name); });
|
||||
|
||||
});
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
tip.destroy(); // Remove the tooltip from the DOM
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
80
app/js/directives/directive-comparison-table.js
Normal file
80
app/js/directives/directive-comparison-table.js
Normal file
@@ -0,0 +1,80 @@
|
||||
angular.module('app').directive('comparisonTable', ['$state', function ($state) {
|
||||
|
||||
function tblHeader(facets) {
|
||||
var r1 = ['<tr class="main"><th rowspan="2" class="prop" prop="name">Ship</th><th rowspan="2" class="prop" prop="buildName">Build</th>'];
|
||||
var r2 = [];
|
||||
for (var i = 0, l = facets.length; i < l; i++) {
|
||||
if (facets[i].active) {
|
||||
var f = facets[i];
|
||||
var p = f.props;
|
||||
var pl = p.length;
|
||||
r1.push('<th rowspan="', f.props.length == 1 ? 2 : 1,'" colspan="',pl,'"');
|
||||
|
||||
if (pl == 1) {
|
||||
r1.push(' prop="',p[0],'" class="prop"');
|
||||
} else {
|
||||
for (var j = 0; j < pl; j++) {
|
||||
r2.push('<th prop="', p[j], '" class="prop ', j === 0? 'lft' : '', ' ">' , f.lbls[j], '</th>');
|
||||
}
|
||||
}
|
||||
|
||||
r1.push('>', f.title ,'</th>');
|
||||
}
|
||||
}
|
||||
r1.push('</tr><tr>');
|
||||
r1.push(r2.join(''));
|
||||
r1.push('</tr>');
|
||||
return r1.join('');
|
||||
}
|
||||
|
||||
function tblBody(facets, builds) {
|
||||
var body = [];
|
||||
|
||||
if(builds.length === 0) {
|
||||
return '<td colspan="100" class="cen">No builds added to comparison!</td';
|
||||
}
|
||||
|
||||
for (var i = 0, l = builds.length; i < l; i++) {
|
||||
var b = builds[i];
|
||||
body.push('<tr class="tr">');
|
||||
var href = $state.href('outfit',{shipId: b.id, code: b.code, bn: b.buildName});
|
||||
body.push('<td class="tl"><a href="', href,'">', b.name,'</a></td>');
|
||||
body.push('<td class="tl"><a href="', href,'">', b.buildName,'</a></td>');
|
||||
|
||||
for (var j = 0, fl = facets.length; j < fl; j++) {
|
||||
if (facets[j].active) {
|
||||
var f = facets[j];
|
||||
var p = f.props;
|
||||
for (var k = 0, pl = p.length; k < pl; k++) {
|
||||
body.push('<td>', f.fmt(b[p[k]]), '<u> ', f.unit, '</u></td>');
|
||||
}
|
||||
}
|
||||
}
|
||||
body.push('</tr>');
|
||||
}
|
||||
|
||||
return body.join('');
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
|
||||
link: function (scope, element) {
|
||||
var header = angular.element('<thead></thead>');
|
||||
var body = angular.element('<tbody></tbody>');
|
||||
element.append(header);
|
||||
element.append(body);
|
||||
|
||||
var updateAll = function (){
|
||||
header.html(tblHeader(scope.facets));
|
||||
body.html(tblBody(scope.facets, scope.builds));
|
||||
};
|
||||
|
||||
scope.$watchCollection('facets', updateAll);
|
||||
scope.$watch('tblUpdate', updateAll);
|
||||
scope.$watchCollection('builds', function() {
|
||||
body.html(tblBody(scope.facets, scope.builds));
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module('app').directive('componentSelect', function() {
|
||||
angular.module('app').directive('componentSelect', function () {
|
||||
|
||||
// Generting the HTML in this manner is MUCH faster than using an angular template.
|
||||
|
||||
@@ -8,20 +8,34 @@ angular.module('app').directive('componentSelect', function() {
|
||||
var o = opts[i];
|
||||
var id = o.id || (o.class + o.rating); // Common components' ID is their class and rating
|
||||
list.push('<li class="', o.name? 'lc' : 'c');
|
||||
if(wrap && o.class != prevClass) list.push(' cl');
|
||||
if (cid == o.id) list.push(' active');
|
||||
|
||||
if(wrap && o.class != prevClass) {
|
||||
list.push(' cl');
|
||||
}
|
||||
|
||||
if (cid == o.id) {
|
||||
list.push(' active');
|
||||
}
|
||||
|
||||
list.push((o.maxmass && mass > o.maxmass)? ' disabled"' : '" cpid="', id, '">');
|
||||
|
||||
if(o.mode) {
|
||||
list.push('<svg cpid="', id, '" class="icon lg"><use xlink:href="#mount-', o.mode , '"></use></svg> ');
|
||||
}
|
||||
|
||||
list.push(o.class, o.rating);
|
||||
|
||||
if(o.mode) {
|
||||
list.push('/' + o.mode);
|
||||
if(o.missile) {
|
||||
list.push(o.missile);
|
||||
}
|
||||
}
|
||||
if(o.name) list.push(' ' + o.name);
|
||||
|
||||
if(o.name) {
|
||||
list.push(' ' + o.name);
|
||||
}
|
||||
|
||||
list.push('</li>');
|
||||
prevClass = o.class;
|
||||
prevRating= o.rating;
|
||||
@@ -49,7 +63,7 @@ angular.module('app').directive('componentSelect', function() {
|
||||
if(groups) {
|
||||
// At present time slots with grouped options (Hardpoints and Internal) can be empty
|
||||
list.push('<div class="empty-c" cpid="empty">EMPTY</div>');
|
||||
for (g in groups) {
|
||||
for (var g in groups) {
|
||||
var grp = groups[g];
|
||||
var grpCode = grp[Object.keys(grp)[0]].grp; // Nasty operation to get the grp property of the first/any single component
|
||||
list.push('<div id="', grpCode ,'" class="select-group">', g, '</div><ul>');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module('app').directive('shipyardHeader', ['lodash','$rootScope', 'Persist', 'ShipsDB', function (_, $rootScope, Persist, ships) {
|
||||
angular.module('app').directive('shipyardHeader', ['lodash','$window','$rootScope', 'Persist', 'ShipsDB', function (_, $window, $rootScope, Persist, ships) {
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
@@ -8,6 +8,7 @@ angular.module('app').directive('shipyardHeader', ['lodash','$rootScope', 'Persi
|
||||
scope.openedMenu = null;
|
||||
scope.ships = ships;
|
||||
scope.allBuilds = Persist.builds;
|
||||
scope.allComparisons = Persist.comparisons;
|
||||
scope.bs = Persist.state;
|
||||
|
||||
// Insurance options and management here for now.
|
||||
@@ -17,9 +18,9 @@ angular.module('app').directive('shipyardHeader', ['lodash','$rootScope', 'Persi
|
||||
{ name:'Alpha', pct: 0.025 },
|
||||
{ name:'Beta', pct: 0.035 }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var insIndex = _.findIndex($rootScope.insurance.opts, 'name', localStorage.getItem('insurance'));
|
||||
var insIndex = _.findIndex($rootScope.insurance.opts, 'name', $window.localStorage.getItem('insurance'));
|
||||
$rootScope.insurance.current = $rootScope.insurance.opts[insIndex != -1? insIndex : 0];
|
||||
|
||||
// Close menus if a navigation change event occurs
|
||||
@@ -33,8 +34,8 @@ angular.module('app').directive('shipyardHeader', ['lodash','$rootScope', 'Persi
|
||||
});
|
||||
|
||||
scope.updateInsurance = function(){
|
||||
localStorage.setItem('insurance', $rootScope.insurance.current.name);
|
||||
}
|
||||
$window.localStorage.setItem('insurance', $rootScope.insurance.current.name);
|
||||
};
|
||||
|
||||
scope.openMenu = function (e, menu) {
|
||||
e.stopPropagation();
|
||||
@@ -59,8 +60,7 @@ angular.module('app').directive('shipyardHeader', ['lodash','$rootScope', 'Persi
|
||||
|
||||
$rootScope.hideAbout = function (){
|
||||
$rootScope.showAbout = false;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
};
|
||||
}]);
|
||||
Reference in New Issue
Block a user