123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- /* Flot plugin for drawing legends.
- */
- (function($) {
- var defaultOptions = {
- legend: {
- show: false,
- labelFormatter: null, // fn: string -> string
- container: null, // container (as jQuery object) to put legend in, null means default on top of graph
- position: 'ne', // position of default legend container within plot
- margin: 5, // distance from grid edge to default legend container within plot
- sorted: null // default to no legend sorting
- }
- };
- function insertLegend(plot, options, placeholder, legendEntries) {
- // clear before redraw
- if (options.legend.container != null) {
- $(options.legend.container).html('');
- } else {
- placeholder.find('.legend').remove();
- }
- if (!options.legend.show) {
- return;
- }
- // Save the legend entries in legend options
- var entries = options.legend.legendEntries = legendEntries,
- plotOffset = options.legend.plotOffset = plot.getPlotOffset(),
- html = [],
- entry, labelHtml, iconHtml,
- maxLabelLength = 0,
- j = 0,
- pos = "",
- p = options.legend.position,
- m = options.legend.margin,
- shape = {
- name: '',
- label: '',
- xPos: '',
- yPos: ''
- };
- html[j++] = '<svg class="legendLayer" style="width:inherit;height:inherit;">';
- html[j++] = '<rect class="background" width="100%" height="100%"/>';
- html[j++] = svgShapeDefs;
- // Generate html for icons and labels from a list of entries
- for (var i = 0; i < entries.length; ++i) {
- entry = entries[i];
- iconHtml = '';
- shape.label = entry.label;
- shape.xPos = '0em';
- shape.yPos = i * 1.5 + 'em';
- // area
- if (entry.options.lines.show && entry.options.lines.fill) {
- shape.name = 'area';
- shape.fillColor = entry.color;
- iconHtml += getEntryIconHtml(shape);
- }
- // bars
- if (entry.options.bars.show) {
- shape.name = 'bar';
- shape.fillColor = entry.color;
- iconHtml += getEntryIconHtml(shape);
- }
- // lines
- if (entry.options.lines.show && !entry.options.lines.fill) {
- shape.name = 'line';
- shape.strokeColor = entry.color;
- shape.strokeWidth = entry.options.lines.lineWidth;
- iconHtml += getEntryIconHtml(shape);
- }
- // points
- if (entry.options.points.show) {
- shape.name = entry.options.points.symbol;
- shape.strokeColor = entry.color;
- shape.fillColor = entry.options.points.fillColor;
- shape.strokeWidth = entry.options.points.lineWidth;
- iconHtml += getEntryIconHtml(shape);
- }
- maxLabelLength = maxLabelLength < shape.label.length ? shape.label.length : maxLabelLength;
- labelHtml = '<text x="' + shape.xPos + '" y="' + shape.yPos + '" text-anchor="start"><tspan dx="2em" dy="1.2em">' + shape.label + '</tspan></text>'
- html[j++] = '<g>' + iconHtml + labelHtml + '</g>';
- }
- html[j++] = '</svg>';
- if (m[0] == null) {
- m = [m, m];
- }
- if (p.charAt(0) === 'n') {
- pos += 'top:' + (m[1] + plotOffset.top) + 'px;';
- } else if (p.charAt(0) === 's') {
- pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;';
- }
- if (p.charAt(1) === 'e') {
- pos += 'right:' + (m[0] + plotOffset.right) + 'px;';
- } else if (p.charAt(1) === 'w') {
- pos += 'left:' + (m[0] + plotOffset.left) + 'px;';
- }
- var legendEl,
- width = 3 + maxLabelLength / 2,
- height = entries.length * 1.6;
- if (!options.legend.container) {
- legendEl = $('<div class="legend" style="position:absolute;' + pos + '">' + html.join('') + '</div>').appendTo(placeholder);
- legendEl.css('width', width + 'em');
- legendEl.css('height', height + 'em');
- legendEl.css('pointerEvents', 'none');
- } else {
- legendEl = $(html.join('')).appendTo(options.legend.container)[0];
- options.legend.container.style.width = width + 'em';
- options.legend.container.style.height = height + 'em';
- }
- }
- // Generate html for a shape
- function getEntryIconHtml(shape) {
- var html = '',
- name = shape.name,
- x = shape.xPos,
- y = shape.yPos,
- fill = shape.fillColor,
- stroke = shape.strokeColor,
- width = shape.strokeWidth;
- switch (name) {
- case 'circle':
- html = '<use xlink:href="#circle" class="legendIcon" ' +
- 'x="' + x + '" ' +
- 'y="' + y + '" ' +
- 'fill="' + fill + '" ' +
- 'stroke="' + stroke + '" ' +
- 'stroke-width="' + width + '" ' +
- 'width="1.5em" height="1.5em"' +
- '/>';
- break;
- case 'diamond':
- html = '<use xlink:href="#diamond" class="legendIcon" ' +
- 'x="' + x + '" ' +
- 'y="' + y + '" ' +
- 'fill="' + fill + '" ' +
- 'stroke="' + stroke + '" ' +
- 'stroke-width="' + width + '" ' +
- 'width="1.5em" height="1.5em"' +
- '/>';
- break;
- case 'cross':
- html = '<use xlink:href="#cross" class="legendIcon" ' +
- 'x="' + x + '" ' +
- 'y="' + y + '" ' +
- // 'fill="' + fill + '" ' +
- 'stroke="' + stroke + '" ' +
- 'stroke-width="' + width + '" ' +
- 'width="1.5em" height="1.5em"' +
- '/>';
- break;
- case 'rectangle':
- html = '<use xlink:href="#rectangle" class="legendIcon" ' +
- 'x="' + x + '" ' +
- 'y="' + y + '" ' +
- 'fill="' + fill + '" ' +
- 'stroke="' + stroke + '" ' +
- 'stroke-width="' + width + '" ' +
- 'width="1.5em" height="1.5em"' +
- '/>';
- break;
- case 'plus':
- html = '<use xlink:href="#plus" class="legendIcon" ' +
- 'x="' + x + '" ' +
- 'y="' + y + '" ' +
- // 'fill="' + fill + '" ' +
- 'stroke="' + stroke + '" ' +
- 'stroke-width="' + width + '" ' +
- 'width="1.5em" height="1.5em"' +
- '/>';
- break;
- case 'bar':
- html = '<use xlink:href="#bars" class="legendIcon" ' +
- 'x="' + x + '" ' +
- 'y="' + y + '" ' +
- 'fill="' + fill + '" ' +
- // 'stroke="' + stroke + '" ' +
- // 'stroke-width="' + width + '" ' +
- 'width="1.5em" height="1.5em"' +
- '/>';
- break;
- case 'area':
- html = '<use xlink:href="#area" class="legendIcon" ' +
- 'x="' + x + '" ' +
- 'y="' + y + '" ' +
- 'fill="' + fill + '" ' +
- // 'stroke="' + stroke + '" ' +
- // 'stroke-width="' + width + '" ' +
- 'width="1.5em" height="1.5em"' +
- '/>';
- break;
- case 'line':
- html = '<use xlink:href="#line" class="legendIcon" ' +
- 'x="' + x + '" ' +
- 'y="' + y + '" ' +
- // 'fill="' + fill + '" ' +
- 'stroke="' + stroke + '" ' +
- 'stroke-width="' + width + '" ' +
- 'width="1.5em" height="1.5em"' +
- '/>';
- break;
- default:
- // default is circle
- html = '<use xlink:href="#circle" class="legendIcon" ' +
- 'x="' + x + '" ' +
- 'y="' + y + '" ' +
- 'fill="' + fill + '" ' +
- 'stroke="' + stroke + '" ' +
- 'stroke-width="' + width + '" ' +
- 'width="1.5em" height="1.5em"' +
- '/>';
- }
- return html;
- }
- // Define svg symbols for shapes
- var svgShapeDefs = '' +
- '<defs>' +
- '<symbol id="line" fill="none" viewBox="-5 -5 25 25">' +
- '<polyline points="0,15 5,5 10,10 15,0"/>' +
- '</symbol>' +
- '<symbol id="area" stroke-width="1" viewBox="-5 -5 25 25">' +
- '<polyline points="0,15 5,5 10,10 15,0, 15,15, 0,15"/>' +
- '</symbol>' +
- '<symbol id="bars" stroke-width="1" viewBox="-5 -5 25 25">' +
- '<polyline points="1.5,15.5 1.5,12.5, 4.5,12.5 4.5,15.5 6.5,15.5 6.5,3.5, 9.5,3.5 9.5,15.5 11.5,15.5 11.5,7.5 14.5,7.5 14.5,15.5 1.5,15.5"/>' +
- '</symbol>' +
- '<symbol id="circle" viewBox="-5 -5 25 25">' +
- '<circle cx="0" cy="15" r="2.5"/>' +
- '<circle cx="5" cy="5" r="2.5"/>' +
- '<circle cx="10" cy="10" r="2.5"/>' +
- '<circle cx="15" cy="0" r="2.5"/>' +
- '</symbol>' +
- '<symbol id="rectangle" viewBox="-5 -5 25 25">' +
- '<rect x="-2.1" y="12.9" width="4.2" height="4.2"/>' +
- '<rect x="2.9" y="2.9" width="4.2" height="4.2"/>' +
- '<rect x="7.9" y="7.9" width="4.2" height="4.2"/>' +
- '<rect x="12.9" y="-2.1" width="4.2" height="4.2"/>' +
- '</symbol>' +
- '<symbol id="diamond" viewBox="-5 -5 25 25">' +
- '<path d="M-3,15 L0,12 L3,15, L0,18 Z"/>' +
- '<path d="M2,5 L5,2 L8,5, L5,8 Z"/>' +
- '<path d="M7,10 L10,7 L13,10, L10,13 Z"/>' +
- '<path d="M12,0 L15,-3 L18,0, L15,3 Z"/>' +
- '</symbol>' +
- '<symbol id="cross" fill="none" viewBox="-5 -5 25 25">' +
- '<path d="M-2.1,12.9 L2.1,17.1, M2.1,12.9 L-2.1,17.1 Z"/>' +
- '<path d="M2.9,2.9 L7.1,7.1 M7.1,2.9 L2.9,7.1 Z"/>' +
- '<path d="M7.9,7.9 L12.1,12.1 M12.1,7.9 L7.9,12.1 Z"/>' +
- '<path d="M12.9,-2.1 L17.1,2.1 M17.1,-2.1 L12.9,2.1 Z"/>' +
- '</symbol>' +
- '<symbol id="plus" fill="none" viewBox="-5 -5 25 25">' +
- '<path d="M0,12 L0,18, M-3,15 L3,15 Z"/>' +
- '<path d="M5,2 L5,8 M2,5 L8,5 Z"/>' +
- '<path d="M10,7 L10,13 M7,10 L13,10 Z"/>' +
- '<path d="M15,-3 L15,3 M12,0 L18,0 Z"/>' +
- '</symbol>' +
- '</defs>';
- // Generate a list of legend entries in their final order
- function getLegendEntries(series, labelFormatter, sorted) {
- var lf = labelFormatter,
- legendEntries = series.map(function(s, i) {
- return {
- label: (lf ? lf(s.label, s) : s.label) || 'Plot ' + (i + 1),
- color: s.color,
- options: {
- lines: s.lines,
- points: s.points,
- bars: s.bars
- }
- };
- });
- // Sort the legend using either the default or a custom comparator
- if (sorted) {
- if ($.isFunction(sorted)) {
- legendEntries.sort(sorted);
- } else if (sorted === 'reverse') {
- legendEntries.reverse();
- } else {
- var ascending = (sorted !== 'descending');
- legendEntries.sort(function(a, b) {
- return a.label === b.label
- ? 0
- : ((a.label < b.label) !== ascending ? 1 : -1 // Logical XOR
- );
- });
- }
- }
- return legendEntries;
- }
- // return false if opts1 same as opts2
- function checkOptions(opts1, opts2) {
- for (var prop in opts1) {
- if (opts1.hasOwnProperty(prop)) {
- if (opts1[prop] !== opts2[prop]) {
- return true;
- }
- }
- }
- return false;
- }
- // Compare two lists of legend entries
- function shouldRedraw(oldEntries, newEntries) {
- if (!oldEntries || !newEntries) {
- return true;
- }
- if (oldEntries.length !== newEntries.length) {
- return true;
- }
- var i, newEntry, oldEntry, newOpts, oldOpts;
- for (i = 0; i < newEntries.length; i++) {
- newEntry = newEntries[i];
- oldEntry = oldEntries[i];
- if (newEntry.label !== oldEntry.label) {
- return true;
- }
- if (newEntry.color !== oldEntry.color) {
- return true;
- }
- // check for changes in lines options
- newOpts = newEntry.options.lines;
- oldOpts = oldEntry.options.lines;
- if (checkOptions(newOpts, oldOpts)) {
- return true;
- }
- // check for changes in points options
- newOpts = newEntry.options.points;
- oldOpts = oldEntry.options.points;
- if (checkOptions(newOpts, oldOpts)) {
- return true;
- }
- // check for changes in bars options
- newOpts = newEntry.options.bars;
- oldOpts = oldEntry.options.bars;
- if (checkOptions(newOpts, oldOpts)) {
- return true;
- }
- }
- return false;
- }
- function init(plot) {
- plot.hooks.setupGrid.push(function (plot) {
- var options = plot.getOptions();
- var series = plot.getData(),
- labelFormatter = options.legend.labelFormatter,
- oldEntries = options.legend.legendEntries,
- oldPlotOffset = options.legend.plotOffset,
- newEntries = getLegendEntries(series, labelFormatter, options.legend.sorted),
- newPlotOffset = plot.getPlotOffset();
- if (shouldRedraw(oldEntries, newEntries) ||
- checkOptions(oldPlotOffset, newPlotOffset)) {
- insertLegend(plot, options, plot.getPlaceholder(), newEntries);
- }
- });
- }
- $.plot.plugins.push({
- init: init,
- options: defaultOptions,
- name: 'legend',
- version: '1.0'
- });
- })(jQuery);
|