contour 0.5.7 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }());