contour 0.5.7 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,12 @@
1
+ == 0.6.0
2
+
3
+ * Enhancements
4
+ * Underlying HighCharts Library updated to 2.1.9 to work with jQuery 1.7.0
5
+ * Added jquery-rails ~> 1.0.17 as a dependency
6
+
7
+ * Bug Fix
8
+ * CSS classes negative and positive now properly apply color to their corresponding links
9
+
1
10
  == 0.5.7
2
11
 
3
12
  * Enhancements
@@ -42,7 +42,7 @@ In order to get registration working, you can use the modified Contour Authentic
42
42
 
43
43
  == Setting up a new project with quick authentication
44
44
 
45
- Make sure you have Rails 3.1.0
45
+ Make sure you have Rails 3.1.1
46
46
 
47
47
  rails -v
48
48
 
@@ -52,9 +52,9 @@ Make sure you have Rails 3.1.0
52
52
 
53
53
  Modify Gemfile and add
54
54
 
55
- gem 'contour', '~> 0.5.0' # Basic Layout and Assets
56
- gem 'devise', '~> 1.4.4' # User Authorization
57
- gem 'omniauth', '0.2.6' # User Multi-Authentication
55
+ gem 'contour', '~> 0.6.0' # Basic Layout and Assets
56
+ gem 'devise' # User Authorization
57
+ gem 'omniauth' # User Multi-Authentication
58
58
 
59
59
  Run Bundle install
60
60
 
@@ -11,7 +11,7 @@ require 'contour/version'
11
11
 
12
12
  Gem::Specification.new do |s|
13
13
  s.name = 'contour'
14
- s.version = Contour::VERSION.dup
14
+ s.version = Contour::VERSION::STRING
15
15
  s.platform = Gem::Platform::RUBY
16
16
  s.summary = 'Basic Rails framework files and assets for layout and authentication'
17
17
  s.email = 'remosm@gmail.com'
@@ -21,9 +21,7 @@ Gem::Specification.new do |s|
21
21
 
22
22
  s.add_dependency('devise', '~> 1.4.9')
23
23
  s.add_dependency('omniauth', '=0.2.6')
24
- # jquery-rails 1.0.17 switches to jQuery 1.7.0
25
- # HighCharts 2.1.4 requires jQuery 1.6.4
26
- s.add_dependency('jquery-rails', '>= 1.0.0', '<= 1.0.16')
24
+ s.add_dependency('jquery-rails', '~> 1.0.17')
27
25
 
28
26
  s.files = `git ls-files`.split("\n")
29
27
  end
@@ -1,3 +1,10 @@
1
1
  module Contour
2
- VERSION = "0.5.7"
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 6
5
+ TINY = 0
6
+ BUILD = nil # nil, "pre", "rc", "rc2"
7
+
8
+ STRING = [MAJOR, MINOR, TINY, BUILD].compact.join('.')
9
+ end
3
10
  end
@@ -3,6 +3,6 @@
3
3
  //
4
4
  //= require jquery
5
5
  //= require jquery_ujs
6
- //= require external/highcharts-2.1.4.src
7
- //= require external/exporting-2.1.4.src
6
+ //= require external/highcharts-2.1.9.src
7
+ //= require external/exporting-2.1.9.src
8
8
  //= require_tree .
@@ -1,21 +1,22 @@
1
- /**
2
- * @license Highcharts JS v2.1.4 (2011-03-02)
1
+ /**
2
+ * @license Highcharts JS v2.1.9 (2011-11-11)
3
3
  * Exporting module
4
- *
5
- * (c) 2010 Torstein Hønsi
6
- *
4
+ *
5
+ * (c) 2010-2011 Torstein Hønsi
6
+ *
7
7
  * License: www.highcharts.com/license
8
8
  */
9
9
 
10
10
  // JSLint options:
11
11
  /*global Highcharts, document, window, Math, setTimeout */
12
12
 
13
- (function() { // encapsulate
13
+ (function () { // encapsulate
14
14
 
15
15
  // create shortcuts
16
16
  var HC = Highcharts,
17
17
  Chart = HC.Chart,
18
18
  addEvent = HC.addEvent,
19
+ removeEvent = HC.removeEvent,
19
20
  createElement = HC.createElement,
20
21
  discardElement = HC.discardElement,
21
22
  css = HC.css,
@@ -26,7 +27,7 @@ var HC = Highcharts,
26
27
  mathMax = math.max,
27
28
  doc = document,
28
29
  win = window,
29
- hasTouch = 'ontouchstart' in doc.documentElement,
30
+ hasTouch = doc.documentElement.ontouchstart !== undefined,
30
31
  M = 'M',
31
32
  L = 'L',
32
33
  DIV = 'div',
@@ -35,19 +36,17 @@ var HC = Highcharts,
35
36
  PREFIX = 'highcharts-',
36
37
  ABSOLUTE = 'absolute',
37
38
  PX = 'px',
39
+ UNDEFINED,
40
+ defaultOptions = HC.getOptions();
38
41
 
39
-
40
-
41
- // Add language and get the defaultOptions
42
- defaultOptions = HC.setOptions({
43
- lang: {
44
- downloadPNG: 'Download PNG image',
45
- downloadJPEG: 'Download JPEG image',
46
- downloadPDF: 'Download PDF document',
47
- downloadSVG: 'Download SVG vector image',
48
- exportButtonTitle: 'Export to raster or vector image',
49
- printButtonTitle: 'Print the chart'
50
- }
42
+ // Add language
43
+ extend(defaultOptions.lang, {
44
+ downloadPNG: 'Download PNG image',
45
+ downloadJPEG: 'Download JPEG image',
46
+ downloadPDF: 'Download PDF document',
47
+ downloadSVG: 'Download SVG vector image',
48
+ exportButtonTitle: 'Export to raster or vector image',
49
+ printButtonTitle: 'Print the chart'
51
50
  });
52
51
 
53
52
  // Buttons and menus are collected in a separate config option set called 'navigation'.
@@ -67,7 +66,7 @@ defaultOptions.navigation = {
67
66
  background: '#4572A5',
68
67
  color: '#FFFFFF'
69
68
  },
70
-
69
+
71
70
  buttonOptions: {
72
71
  align: 'right',
73
72
  backgroundColor: {
@@ -93,7 +92,7 @@ defaultOptions.navigation = {
93
92
  symbolY: 10.5,
94
93
  verticalAlign: 'top',
95
94
  width: 24,
96
- y: 10
95
+ y: 10
97
96
  }
98
97
  };
99
98
 
@@ -106,6 +105,7 @@ defaultOptions.exporting = {
106
105
  type: 'image/png',
107
106
  url: 'http://export.highcharts.com/',
108
107
  width: 800,
108
+ enableImages: false,
109
109
  buttons: {
110
110
  exportButton: {
111
111
  //enabled: true,
@@ -113,29 +113,30 @@ defaultOptions.exporting = {
113
113
  x: -10,
114
114
  symbolFill: '#A8BF77',
115
115
  hoverSymbolFill: '#768F3E',
116
+ _id: 'exportButton',
116
117
  _titleKey: 'exportButtonTitle',
117
118
  menuItems: [{
118
119
  textKey: 'downloadPNG',
119
- onclick: function() {
120
+ onclick: function () {
120
121
  this.exportChart();
121
122
  }
122
123
  }, {
123
124
  textKey: 'downloadJPEG',
124
- onclick: function() {
125
+ onclick: function () {
125
126
  this.exportChart({
126
127
  type: 'image/jpeg'
127
128
  });
128
129
  }
129
130
  }, {
130
131
  textKey: 'downloadPDF',
131
- onclick: function() {
132
+ onclick: function () {
132
133
  this.exportChart({
133
134
  type: 'application/pdf'
134
135
  });
135
136
  }
136
137
  }, {
137
138
  textKey: 'downloadSVG',
138
- onclick: function() {
139
+ onclick: function () {
139
140
  this.exportChart({
140
141
  type: 'image/svg+xml'
141
142
  });
@@ -146,11 +147,11 @@ defaultOptions.exporting = {
146
147
  var svg = this.getSVG()
147
148
  .replace(/</g, '\n&lt;')
148
149
  .replace(/>/g, '&gt;');
149
-
150
+
150
151
  doc.body.innerHTML = '<pre>'+ svg +'</pre>';
151
152
  }
152
153
  }*/]
153
-
154
+
154
155
  },
155
156
  printButton: {
156
157
  //enabled: true,
@@ -158,8 +159,9 @@ defaultOptions.exporting = {
158
159
  x: -36,
159
160
  symbolFill: '#B5C9DF',
160
161
  hoverSymbolFill: '#779ABF',
162
+ _id: 'printButton',
161
163
  _titleKey: 'printButtonTitle',
162
- onclick: function() {
164
+ onclick: function () {
163
165
  this.print();
164
166
  }
165
167
  }
@@ -171,10 +173,10 @@ defaultOptions.exporting = {
171
173
  extend(Chart.prototype, {
172
174
  /**
173
175
  * Return an SVG representation of the chart
174
- *
176
+ *
175
177
  * @param additionalOptions {Object} Additional chart options for the generated SVG representation
176
- */
177
- getSVG: function(additionalOptions) {
178
+ */
179
+ getSVG: function (additionalOptions) {
178
180
  var chart = this,
179
181
  chartCopy,
180
182
  sandbox,
@@ -184,18 +186,20 @@ extend(Chart.prototype, {
184
186
  pointOptions,
185
187
  pointMarker,
186
188
  options = merge(chart.options, additionalOptions); // copy the options and add extra options
187
-
189
+
188
190
  // IE compatibility hack for generating SVG content that it doesn't really understand
189
191
  if (!doc.createElementNS) {
190
- doc.createElementNS = function(ns, tagName) {
192
+ /*jslint unparam: true*//* allow unused parameter ns in function below */
193
+ doc.createElementNS = function (ns, tagName) {
191
194
  var elem = doc.createElement(tagName);
192
- elem.getBBox = function() {
193
- return chart.renderer.Element.prototype.getBBox.apply({ element: elem });
195
+ elem.getBBox = function () {
196
+ return HC.Renderer.prototype.Element.prototype.getBBox.apply({ element: elem });
194
197
  };
195
198
  return elem;
196
199
  };
200
+ /*jslint unparam: false*/
197
201
  }
198
-
202
+
199
203
  // create a sandbox where a new chart will be generated
200
204
  sandbox = createElement(DIV, null, {
201
205
  position: ABSOLUTE,
@@ -203,31 +207,38 @@ extend(Chart.prototype, {
203
207
  width: chart.chartWidth + PX,
204
208
  height: chart.chartHeight + PX
205
209
  }, doc.body);
206
-
210
+
207
211
  // override some options
208
212
  extend(options.chart, {
209
213
  renderTo: sandbox,
210
214
  forExport: true
211
215
  });
212
216
  options.exporting.enabled = false; // hide buttons in print
213
- options.chart.plotBackgroundImage = null; // the converter doesn't handle images
217
+
218
+ if (!options.exporting.enableImages) {
219
+ options.chart.plotBackgroundImage = null; // the converter doesn't handle images
220
+ }
221
+
214
222
  // prepare for replicating the chart
215
223
  options.series = [];
216
- each(chart.series, function(serie) {
217
- seriesOptions = serie.options;
218
-
224
+ each(chart.series, function (serie) {
225
+ seriesOptions = serie.options;
226
+
219
227
  seriesOptions.animation = false; // turn off animation
220
228
  seriesOptions.showCheckbox = false;
221
-
222
- // remove image markers
223
- if (seriesOptions && seriesOptions.marker && /^url\(/.test(seriesOptions.marker.symbol)) {
224
- seriesOptions.marker.symbol = 'circle';
229
+ seriesOptions.visible = serie.visible;
230
+
231
+ if (!options.exporting.enableImages) {
232
+ // remove image markers
233
+ if (seriesOptions && seriesOptions.marker && /^url\(/.test(seriesOptions.marker.symbol)) {
234
+ seriesOptions.marker.symbol = 'circle';
235
+ }
225
236
  }
226
-
237
+
227
238
  seriesOptions.data = [];
228
-
229
- each(serie.data, function(point) {
230
-
239
+
240
+ each(serie.data, function (point) {
241
+
231
242
  // extend the options by those values that can be expressed in a number or array config
232
243
  config = point.config;
233
244
  pointOptions = {
@@ -236,81 +247,108 @@ extend(Chart.prototype, {
236
247
  name: point.name
237
248
  };
238
249
 
239
- if (typeof config == 'object' && point.config && config.constructor != Array) {
250
+ if (typeof config === 'object' && point.config && config.constructor !== Array) {
240
251
  extend(pointOptions, config);
241
252
  }
242
253
 
254
+ pointOptions.visible = point.visible;
243
255
  seriesOptions.data.push(pointOptions); // copy fresh updated data
244
-
245
- // remove image markers
246
- pointMarker = point.config && point.config.marker;
247
- if (pointMarker && /^url\(/.test(pointMarker.symbol)) {
248
- delete pointMarker.symbol;
256
+
257
+ if (!options.exporting.enableImages) {
258
+ // remove image markers
259
+ pointMarker = point.config && point.config.marker;
260
+ if (pointMarker && /^url\(/.test(pointMarker.symbol)) {
261
+ delete pointMarker.symbol;
262
+ }
249
263
  }
250
- });
251
-
264
+ });
265
+
252
266
  options.series.push(seriesOptions);
253
267
  });
254
-
268
+
255
269
  // generate the chart copy
256
270
  chartCopy = new Highcharts.Chart(options);
257
-
271
+
272
+ // reflect axis extremes in the export
273
+ each(['xAxis', 'yAxis'], function (axisType) {
274
+ each(chart[axisType], function (axis, i) {
275
+ var axisCopy = chartCopy[axisType][i],
276
+ extremes = axis.getExtremes(),
277
+ userMin = extremes.userMin,
278
+ userMax = extremes.userMax;
279
+
280
+ if (userMin !== UNDEFINED || userMax !== UNDEFINED) {
281
+ axisCopy.setExtremes(userMin, userMax, true, false);
282
+ }
283
+ });
284
+ });
285
+
258
286
  // get the SVG from the container's innerHTML
259
287
  svg = chartCopy.container.innerHTML;
260
-
288
+
261
289
  // free up memory
262
290
  options = null;
263
291
  chartCopy.destroy();
264
292
  discardElement(sandbox);
265
-
293
+
266
294
  // sanitize
267
295
  svg = svg
268
- .replace(/zIndex="[^"]+"/g, '')
296
+ .replace(/zIndex="[^"]+"/g, '')
269
297
  .replace(/isShadow="[^"]+"/g, '')
270
298
  .replace(/symbolName="[^"]+"/g, '')
271
299
  .replace(/jQuery[0-9]+="[^"]+"/g, '')
272
300
  .replace(/isTracker="[^"]+"/g, '')
273
301
  .replace(/url\([^#]+#/g, 'url(#')
274
- /*.replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
275
- .replace(/ href=/, ' xlink:href=')
276
- .replace(/preserveAspectRatio="none">/g, 'preserveAspectRatio="none"/>')*/
302
+ .replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
303
+ .replace(/ href=/g, ' xlink:href=')
304
+ /*.replace(/preserveAspectRatio="none">/g, 'preserveAspectRatio="none"/>')*/
277
305
  /* This fails in IE < 8
278
306
  .replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
279
307
  return s2 +'.'+ s3[0];
280
- })*/
281
-
308
+ })*/
309
+
310
+ // Replace HTML entities, issue #347
311
+ .replace(/&nbsp;/g, '\u00A0') // no-break space
312
+ .replace(/&shy;/g, '\u00AD') // soft hyphen
313
+
282
314
  // IE specific
283
- .replace(/id=([^" >]+)/g, 'id="$1"')
315
+ .replace(/id=([^" >]+)/g, 'id="$1"')
284
316
  .replace(/class=([^" ]+)/g, 'class="$1"')
285
317
  .replace(/ transform /g, ' ')
286
318
  .replace(/:(path|rect)/g, '$1')
287
- .replace(/style="([^"]+)"/g, function(s) {
319
+ .replace(/<img ([^>]*)>/gi, '<image $1 />')
320
+ .replace(/<\/image>/g, '') // remove closing tags for images as they'll never have any content
321
+ .replace(/<image ([^>]*)([^\/])>/gi, '<image $1$2 />') // closes image tags for firefox
322
+ .replace(/width=(\d+)/g, 'width="$1"')
323
+ .replace(/height=(\d+)/g, 'height="$1"')
324
+ .replace(/hc-svg-href="/g, 'xlink:href="')
325
+ .replace(/style="([^"]+)"/g, function (s) {
288
326
  return s.toLowerCase();
289
327
  });
290
-
328
+
291
329
  // IE9 beta bugs with innerHTML. Test again with final IE9.
292
330
  svg = svg.replace(/(url\(#highcharts-[0-9]+)&quot;/g, '$1')
293
331
  .replace(/&quot;/g, "'");
294
- if (svg.match(/ xmlns="/g).length == 2) {
332
+ if (svg.match(/ xmlns="/g).length === 2) {
295
333
  svg = svg.replace(/xmlns="[^"]+"/, '');
296
334
  }
297
-
335
+
298
336
  return svg;
299
337
  },
300
-
338
+
301
339
  /**
302
340
  * Submit the SVG representation of the chart to the server
303
341
  * @param {Object} options Exporting options. Possible members are url, type and width.
304
342
  * @param {Object} chartOptions Additional chart options for the SVG representation of the chart
305
343
  */
306
- exportChart: function(options, chartOptions) {
344
+ exportChart: function (options, chartOptions) {
307
345
  var form,
308
346
  chart = this,
309
347
  svg = chart.getSVG(chartOptions);
310
-
348
+
311
349
  // merge the options
312
350
  options = merge(chart.options.exporting, options);
313
-
351
+
314
352
  // create the form
315
353
  form = createElement('form', {
316
354
  method: 'post',
@@ -318,82 +356,82 @@ extend(Chart.prototype, {
318
356
  }, {
319
357
  display: NONE
320
358
  }, doc.body);
321
-
359
+
322
360
  // add the values
323
- each(['filename', 'type', 'width', 'svg'], function(name) {
361
+ each(['filename', 'type', 'width', 'svg'], function (name) {
324
362
  createElement('input', {
325
363
  type: HIDDEN,
326
364
  name: name,
327
- value: {
328
- filename: options.filename || 'chart',
329
- type: options.type,
330
- width: options.width,
331
- svg: svg
365
+ value: {
366
+ filename: options.filename || 'chart',
367
+ type: options.type,
368
+ width: options.width,
369
+ svg: svg
332
370
  }[name]
333
371
  }, null, form);
334
372
  });
335
-
373
+
336
374
  // submit
337
375
  form.submit();
338
-
376
+
339
377
  // clean up
340
378
  discardElement(form);
341
379
  },
342
-
380
+
343
381
  /**
344
382
  * Print the chart
345
383
  */
346
- print: function() {
347
-
384
+ print: function () {
385
+
348
386
  var chart = this,
349
387
  container = chart.container,
350
388
  origDisplay = [],
351
389
  origParent = container.parentNode,
352
390
  body = doc.body,
353
391
  childNodes = body.childNodes;
354
-
392
+
355
393
  if (chart.isPrinting) { // block the button while in printing mode
356
394
  return;
357
395
  }
358
-
396
+
359
397
  chart.isPrinting = true;
360
-
361
- // hide all body content
362
- each(childNodes, function(node, i) {
363
- if (node.nodeType == 1) {
398
+
399
+ // hide all body content
400
+ each(childNodes, function (node, i) {
401
+ if (node.nodeType === 1) {
364
402
  origDisplay[i] = node.style.display;
365
403
  node.style.display = NONE;
366
404
  }
367
405
  });
368
-
406
+
369
407
  // pull out the chart
370
408
  body.appendChild(container);
371
-
409
+
372
410
  // print
373
- win.print();
374
-
411
+ win.print();
412
+
375
413
  // allow the browser to prepare before reverting
376
- setTimeout(function() {
414
+ setTimeout(function () {
377
415
 
378
416
  // put the chart back in
379
417
  origParent.appendChild(container);
380
-
418
+
381
419
  // restore all body content
382
- each(childNodes, function(node, i) {
383
- if (node.nodeType == 1) {
420
+ each(childNodes, function (node, i) {
421
+ if (node.nodeType === 1) {
384
422
  node.style.display = origDisplay[i];
385
423
  }
386
424
  });
387
-
425
+
388
426
  chart.isPrinting = false;
389
-
427
+
390
428
  }, 1000);
391
429
 
392
430
  },
393
-
431
+
394
432
  /**
395
- * Display a popup menu for choosing the export type
396
- *
433
+ * Display a popup menu for choosing the export type
434
+ *
397
435
  * @param {String} name An identifier for the menu
398
436
  * @param {Array} items A collection with text and onclicks for the items
399
437
  * @param {Number} x The x position of the opener button
@@ -401,24 +439,24 @@ extend(Chart.prototype, {
401
439
  * @param {Number} width The width of the opener button
402
440
  * @param {Number} height The height of the opener button
403
441
  */
404
- contextMenu: function(name, items, x, y, width, height) {
442
+ contextMenu: function (name, items, x, y, width, height) {
405
443
  var chart = this,
406
444
  navOptions = chart.options.navigation,
407
445
  menuItemStyle = navOptions.menuItemStyle,
408
446
  chartWidth = chart.chartWidth,
409
447
  chartHeight = chart.chartHeight,
410
- cacheName = 'cache-'+ name,
448
+ cacheName = 'cache-' + name,
411
449
  menu = chart[cacheName],
412
450
  menuPadding = mathMax(width, height), // for mouse leave detection
413
451
  boxShadow = '3px 3px 10px #888',
414
452
  innerMenu,
415
453
  hide,
416
- menuStyle;
417
-
454
+ menuStyle;
455
+
418
456
  // create the menu only the first time
419
457
  if (!menu) {
420
-
421
- // create a HTML element above the SVG
458
+
459
+ // create a HTML element above the SVG
422
460
  chart[cacheName] = menu = createElement(DIV, {
423
461
  className: PREFIX + name
424
462
  }, {
@@ -426,51 +464,56 @@ extend(Chart.prototype, {
426
464
  zIndex: 1000,
427
465
  padding: menuPadding + PX
428
466
  }, chart.container);
429
-
430
- innerMenu = createElement(DIV, null,
467
+
468
+ innerMenu = createElement(DIV, null,
431
469
  extend({
432
470
  MozBoxShadow: boxShadow,
433
471
  WebkitBoxShadow: boxShadow,
434
472
  boxShadow: boxShadow
435
- }, navOptions.menuStyle) , menu);
436
-
473
+ }, navOptions.menuStyle), menu);
474
+
437
475
  // hide on mouse out
438
- hide = function() {
476
+ hide = function () {
439
477
  css(menu, { display: NONE });
440
478
  };
441
-
479
+
442
480
  addEvent(menu, 'mouseleave', hide);
443
-
444
-
481
+
482
+
445
483
  // create the items
446
- each(items, function(item) {
484
+ each(items, function (item) {
447
485
  if (item) {
448
486
  var div = createElement(DIV, {
449
- onmouseover: function() {
487
+ onmouseover: function () {
450
488
  css(this, navOptions.menuItemHoverStyle);
451
489
  },
452
- onmouseout: function() {
490
+ onmouseout: function () {
453
491
  css(this, menuItemStyle);
454
492
  },
455
- innerHTML: item.text || HC.getOptions().lang[item.textKey]
493
+ innerHTML: item.text || chart.options.lang[item.textKey]
456
494
  }, extend({
457
495
  cursor: 'pointer'
458
496
  }, menuItemStyle), innerMenu);
459
-
460
- div[hasTouch ? 'ontouchstart' : 'onclick'] = function() {
497
+
498
+ div[hasTouch ? 'ontouchstart' : 'onclick'] = function () {
461
499
  hide();
462
500
  item.onclick.apply(chart, arguments);
463
501
  };
464
-
502
+
503
+ // Keep references to menu divs to be able to destroy them
504
+ chart.exportDivElements.push(div);
465
505
  }
466
506
  });
467
-
507
+
508
+ // Keep references to menu and innerMenu div to be able to destroy them
509
+ chart.exportDivElements.push(innerMenu, menu);
510
+
468
511
  chart.exportMenuWidth = menu.offsetWidth;
469
512
  chart.exportMenuHeight = menu.offsetHeight;
470
513
  }
471
-
514
+
472
515
  menuStyle = { display: 'block' };
473
-
516
+
474
517
  // if outside right, right align it
475
518
  if (x + chart.exportMenuWidth > chartWidth) {
476
519
  menuStyle.right = (chartWidth - x - width - menuPadding) + PX;
@@ -483,14 +526,14 @@ extend(Chart.prototype, {
483
526
  } else {
484
527
  menuStyle.top = (y + height - menuPadding) + PX;
485
528
  }
486
-
529
+
487
530
  css(menu, menuStyle);
488
531
  },
489
-
532
+
490
533
  /**
491
534
  * Add the export button to the chart
492
535
  */
493
- addButton: function(options) {
536
+ addButton: function (options) {
494
537
  var chart = this,
495
538
  renderer = chart.renderer,
496
539
  btnOptions = merge(chart.options.navigation.buttonOptions, options),
@@ -503,32 +546,38 @@ extend(Chart.prototype, {
503
546
  buttonHeight = btnOptions.height,
504
547
  box,
505
548
  symbol,
506
- button,
549
+ button,
507
550
  borderWidth = btnOptions.borderWidth,
508
551
  boxAttr = {
509
552
  stroke: btnOptions.borderColor
510
-
553
+
511
554
  },
512
555
  symbolAttr = {
513
556
  stroke: btnOptions.symbolStroke,
514
557
  fill: btnOptions.symbolFill
515
558
  };
516
-
559
+
560
+ // Keeps references to the button elements
561
+ if (!chart.exportDivElements) {
562
+ chart.exportDivElements = [];
563
+ chart.exportSVGElements = [];
564
+ }
565
+
517
566
  if (btnOptions.enabled === false) {
518
567
  return;
519
568
  }
520
-
569
+
521
570
  // element to capture the click
522
571
  function revert() {
523
572
  symbol.attr(symbolAttr);
524
573
  box.attr(boxAttr);
525
574
  }
526
-
575
+
527
576
  // the box border
528
577
  box = renderer.rect(
529
578
  0,
530
579
  0,
531
- buttonWidth,
580
+ buttonWidth,
532
581
  buttonHeight,
533
582
  btnOptions.borderRadius,
534
583
  borderWidth
@@ -540,9 +589,9 @@ extend(Chart.prototype, {
540
589
  'stroke-width': borderWidth,
541
590
  zIndex: 19
542
591
  }, boxAttr)).add();
543
-
592
+
544
593
  // the invisible element to track the clicks
545
- button = renderer.rect(
594
+ button = renderer.rect(
546
595
  0,
547
596
  0,
548
597
  buttonWidth,
@@ -551,13 +600,14 @@ extend(Chart.prototype, {
551
600
  )
552
601
  .align(btnOptions)
553
602
  .attr({
603
+ id: btnOptions._id,
554
604
  fill: 'rgba(255, 255, 255, 0.001)',
555
- title: HC.getOptions().lang[btnOptions._titleKey],
605
+ title: chart.options.lang[btnOptions._titleKey],
556
606
  zIndex: 21
557
607
  }).css({
558
608
  cursor: 'pointer'
559
609
  })
560
- .on('mouseover', function() {
610
+ .on('mouseover', function () {
561
611
  symbol.attr({
562
612
  stroke: btnOptions.hoverSymbolStroke,
563
613
  fill: btnOptions.hoverSymbolFill
@@ -569,12 +619,12 @@ extend(Chart.prototype, {
569
619
  .on('mouseout', revert)
570
620
  .on('click', revert)
571
621
  .add();
572
-
622
+
573
623
  //addEvent(button.element, 'click', revert);
574
-
624
+
575
625
  // add the click event
576
626
  if (menuItems) {
577
- onclick = function(e) {
627
+ onclick = function () {
578
628
  revert();
579
629
  var bBox = button.getBBox();
580
630
  chart.contextMenu('export-menu', menuItems, bBox.x, bBox.y, buttonWidth, buttonHeight);
@@ -583,30 +633,61 @@ extend(Chart.prototype, {
583
633
  /*addEvent(button.element, 'click', function() {
584
634
  onclick.apply(chart, arguments);
585
635
  });*/
586
- button.on('click', function() {
636
+ button.on('click', function () {
587
637
  onclick.apply(chart, arguments);
588
638
  });
589
-
639
+
590
640
  // the icon
591
641
  symbol = renderer.symbol(
592
- btnOptions.symbol,
593
- btnOptions.symbolX,
594
- btnOptions.symbolY,
642
+ btnOptions.symbol,
643
+ btnOptions.symbolX,
644
+ btnOptions.symbolY,
595
645
  (btnOptions.symbolSize || 12) / 2
596
646
  )
597
647
  .align(btnOptions, true)
598
648
  .attr(extend(symbolAttr, {
599
649
  'stroke-width': btnOptions.symbolStrokeWidth || 1,
600
- zIndex: 20
650
+ zIndex: 20
601
651
  })).add();
602
-
603
-
604
-
652
+
653
+ // Keep references to the renderer element so to be able to destroy them later.
654
+ chart.exportSVGElements.push(box, button, symbol);
655
+ },
656
+
657
+ /**
658
+ * Destroy the buttons.
659
+ */
660
+ destroyExport: function () {
661
+ var i,
662
+ chart = this,
663
+ elem;
664
+
665
+ // Destroy the extra buttons added
666
+ for (i = 0; i < chart.exportSVGElements.length; i++) {
667
+ elem = chart.exportSVGElements[i];
668
+ // Destroy and null the svg/vml elements
669
+ elem.onclick = elem.ontouchstart = null;
670
+ chart.exportSVGElements[i] = elem.destroy();
671
+ }
672
+
673
+ // Destroy the divs for the menu
674
+ for (i = 0; i < chart.exportDivElements.length; i++) {
675
+ elem = chart.exportDivElements[i];
676
+
677
+ // Remove the event handler
678
+ removeEvent(elem, 'mouseleave');
679
+
680
+ // Remove inline events
681
+ chart.exportDivElements[i] = elem.onmouseout = elem.onmouseover = elem.ontouchstart = elem.onclick = null;
682
+
683
+ // Destroy the div by moving to garbage bin
684
+ discardElement(elem);
685
+ }
605
686
  }
606
687
  });
607
688
 
608
689
  // Create the export icon
609
- HC.Renderer.prototype.symbols.exportIcon = function(x, y, radius) {
690
+ HC.Renderer.prototype.symbols.exportIcon = function (x, y, radius) {
610
691
  return [
611
692
  M, // the disk
612
693
  x - radius, y + radius,
@@ -628,7 +709,7 @@ HC.Renderer.prototype.symbols.exportIcon = function(x, y, radius) {
628
709
  ];
629
710
  };
630
711
  // Create the print icon
631
- HC.Renderer.prototype.symbols.printIcon = function(x, y, radius) {
712
+ HC.Renderer.prototype.symbols.printIcon = function (x, y, radius) {
632
713
  return [
633
714
  M, // the printer
634
715
  x - radius, y + radius * 0.5,
@@ -656,7 +737,7 @@ HC.Renderer.prototype.symbols.printIcon = function(x, y, radius) {
656
737
 
657
738
 
658
739
  // Add the buttons on chart load
659
- Chart.prototype.callbacks.push(function(chart) {
740
+ Chart.prototype.callbacks.push(function (chart) {
660
741
  var n,
661
742
  exportingOptions = chart.options.exporting,
662
743
  buttons = exportingOptions.buttons;
@@ -666,8 +747,12 @@ Chart.prototype.callbacks.push(function(chart) {
666
747
  for (n in buttons) {
667
748
  chart.addButton(buttons[n]);
668
749
  }
750
+
751
+ // Destroy the export elements at chart destroy
752
+ addEvent(chart, 'destroy', chart.destroyExport);
669
753
  }
754
+
670
755
  });
671
756
 
672
757
 
673
- })();
758
+ }());