ses-proxy 0.1.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.
- data/app/public/bootstrap/css/bootstrap-responsive.css +1092 -0
- data/app/public/bootstrap/css/bootstrap-responsive.min.css +9 -0
- data/app/public/bootstrap/css/bootstrap.css +6039 -0
- data/app/public/bootstrap/css/bootstrap.min.css +9 -0
- data/app/public/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/app/public/bootstrap/img/glyphicons-halflings.png +0 -0
- data/app/public/bootstrap/js/bootstrap.js +2159 -0
- data/app/public/bootstrap/js/bootstrap.min.js +6 -0
- data/app/public/css/application.css +16 -0
- data/app/public/datepicker/css/datepicker.css +7 -0
- data/app/public/datepicker/js/bootstrap-datepicker.js +454 -0
- data/app/public/datepicker/less/datepicker.less +119 -0
- data/app/public/highcharts/adapters/mootools-adapter.js +13 -0
- data/app/public/highcharts/adapters/mootools-adapter.src.js +327 -0
- data/app/public/highcharts/adapters/prototype-adapter.js +16 -0
- data/app/public/highcharts/adapters/prototype-adapter.src.js +385 -0
- data/app/public/highcharts/highcharts-more.js +35 -0
- data/app/public/highcharts/highcharts.js +246 -0
- data/app/public/highcharts/highcharts.src.js +15111 -0
- data/app/public/highcharts/modules/canvas-tools.js +133 -0
- data/app/public/highcharts/modules/canvas-tools.src.js +3113 -0
- data/app/public/highcharts/modules/data.js +11 -0
- data/app/public/highcharts/modules/data.src.js +277 -0
- data/app/public/highcharts/modules/exporting.js +23 -0
- data/app/public/highcharts/modules/exporting.src.js +736 -0
- data/app/public/highcharts/themes/dark-blue.js +263 -0
- data/app/public/highcharts/themes/dark-green.js +263 -0
- data/app/public/highcharts/themes/gray.js +262 -0
- data/app/public/highcharts/themes/grid.js +95 -0
- data/app/public/highcharts/themes/skies.js +89 -0
- data/app/public/images/loader.gif +0 -0
- data/app/public/js/application.js +81 -0
- data/app/views/_chart.haml +2 -0
- data/app/views/_search_form.haml +23 -0
- data/app/views/bounces.haml +23 -0
- data/app/views/kaminari/_first_page.html.erb +3 -0
- data/app/views/kaminari/_gap.html.erb +3 -0
- data/app/views/kaminari/_last_page.html.erb +3 -0
- data/app/views/kaminari/_next_page.html.erb +3 -0
- data/app/views/kaminari/_page.html.erb +3 -0
- data/app/views/kaminari/_paginator.html.erb +17 -0
- data/app/views/kaminari/_prev_page.html.erb +3 -0
- data/app/views/layout.haml +25 -0
- data/app/views/mails.haml +26 -0
- data/app/web_panel.rb +149 -0
- data/bin/ses_proxy +20 -0
- data/lib/ses_proxy/conf.rb +9 -0
- data/lib/ses_proxy/main_command.rb +154 -0
- data/lib/ses_proxy/models/bounce.rb +14 -0
- data/lib/ses_proxy/models/complaint.rb +13 -0
- data/lib/ses_proxy/models/email.rb +15 -0
- data/lib/ses_proxy/smtp_server.rb +122 -0
- data/lib/ses_proxy/sns_endpoint.rb +199 -0
- data/ses_proxy.rb +10 -0
- data/template/mongoid.yml +12 -0
- data/template/ses-proxy.yml +19 -0
- metadata +294 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
/*
|
2
|
+
Data plugin for Highcharts v0.1
|
3
|
+
|
4
|
+
(c) 2012 Torstein Hønsi
|
5
|
+
|
6
|
+
License: www.highcharts.com/license
|
7
|
+
*/
|
8
|
+
(function(m){var l=m.each,n=function(a){this.init(a)};m.extend(n.prototype,{init:function(a){this.options=a;this.columns=[];this.parseCSV();this.parseTable();this.parseTypes();this.findHeaderRow();this.parsed();this.complete()},parseCSV:function(){var a=this.options,b=a.csv,d=this.columns,c=a.startRow||0,f=a.endRow||Number.MAX_VALUE,e=a.startColumn||0,j=a.endColumn||Number.MAX_VALUE;b&&(b=b.split(a.lineDelimiter||"\n"),l(b,function(b,k){if(k>=c&&k<=f){var h=b.split(a.itemDelimiter||",");l(h,function(a,
|
9
|
+
b){b>=e&&b<=j&&(d[b-e]||(d[b-e]=[]),d[b-e][k-c]=a)})}}))},parseTable:function(){var a=this.options,b=a.table,d=this.columns,c=a.startRow||0,f=a.endRow||Number.MAX_VALUE,e=a.startColumn||0,j=a.endColumn||Number.MAX_VALUE,g;b&&(typeof b==="string"&&(b=document.getElementById(b)),l(b.getElementsByTagName("tr"),function(a,b){g=0;b>=c&&b<=f&&l(a.childNodes,function(a){if((a.tagName==="TD"||a.tagName==="TH")&&g>=e&&g<=j)d[g]||(d[g]=[]),d[g][b-c]=a.innerHTML,g+=1})}))},findHeaderRow:function(){l(this.columns,
|
10
|
+
function(){});this.headerRow=0},trim:function(a){return a.replace(/^\s+|\s+$/g,"")},parseTypes:function(){for(var a=this.columns,b=a.length,d,c,f,e;b--;)for(d=a[b].length;d--;)c=a[b][d],f=parseFloat(c),e=this.trim(c),e==f?(a[b][d]=f,f>31536E6?a[b].isDatetime=!0:a[b].isNumeric=!0):(c=Date.parse(c),b===0&&typeof c==="number"&&!isNaN(c)?(a[b][d]=c,a[b].isDatetime=!0):a[b][d]=e)},parsed:function(){this.options.parsed&&this.options.parsed.call(this,this.columns)},complete:function(){var a=this.columns,
|
11
|
+
b,d,c,f,e=this.options,j,g,k,h,i;if(e.complete){a.length>1&&(c=a.shift(),this.headerRow===0&&c.shift(),(b=c.isNumeric||c.isDatetime)||(d=c),c.isDatetime&&(f="datetime"));j=[];for(h=0;h<a.length;h++){this.headerRow===0&&(k=a[h].shift());g=[];for(i=0;i<a[h].length;i++)g[i]=a[h][i]!==void 0?b?[c[i],a[h][i]]:a[h][i]:null;j[h]={name:k,data:g}}e.complete({xAxis:{categories:d,type:f},series:j})}}});m.Data=n;m.data=function(a){return new n(a)}})(Highcharts);
|
@@ -0,0 +1,277 @@
|
|
1
|
+
/**
|
2
|
+
* @license Data plugin for Highcharts v0.1
|
3
|
+
*
|
4
|
+
* (c) 2012 Torstein Hønsi
|
5
|
+
*
|
6
|
+
* License: www.highcharts.com/license
|
7
|
+
*/
|
8
|
+
|
9
|
+
/*
|
10
|
+
* Demo: http://jsfiddle.net/highcharts/SnLFj/
|
11
|
+
*/
|
12
|
+
|
13
|
+
(function (Highcharts) {
|
14
|
+
|
15
|
+
// Utilities
|
16
|
+
var each = Highcharts.each;
|
17
|
+
|
18
|
+
|
19
|
+
// The Data constructor
|
20
|
+
var Data = function (options) {
|
21
|
+
this.init(options);
|
22
|
+
};
|
23
|
+
|
24
|
+
// Set the prototype properties
|
25
|
+
Highcharts.extend(Data.prototype, {
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Initialize the Data object with the given options
|
29
|
+
*/
|
30
|
+
init: function (options) {
|
31
|
+
this.options = options;
|
32
|
+
this.columns = [];
|
33
|
+
|
34
|
+
|
35
|
+
// Parse a CSV string if options.csv is given
|
36
|
+
this.parseCSV();
|
37
|
+
|
38
|
+
// Parse a HTML table if options.table is given
|
39
|
+
this.parseTable();
|
40
|
+
|
41
|
+
// Interpret the values into right types
|
42
|
+
this.parseTypes();
|
43
|
+
|
44
|
+
// Use first row for series names?
|
45
|
+
this.findHeaderRow();
|
46
|
+
|
47
|
+
// Handle columns if a handleColumns callback is given
|
48
|
+
this.parsed();
|
49
|
+
|
50
|
+
// Complete if a complete callback is given
|
51
|
+
this.complete();
|
52
|
+
|
53
|
+
},
|
54
|
+
|
55
|
+
/**
|
56
|
+
* Parse a CSV input string
|
57
|
+
*/
|
58
|
+
parseCSV: function () {
|
59
|
+
var options = this.options,
|
60
|
+
csv = options.csv,
|
61
|
+
columns = this.columns,
|
62
|
+
startRow = options.startRow || 0,
|
63
|
+
endRow = options.endRow || Number.MAX_VALUE,
|
64
|
+
startColumn = options.startColumn || 0,
|
65
|
+
endColumn = options.endColumn || Number.MAX_VALUE,
|
66
|
+
lines;
|
67
|
+
|
68
|
+
if (csv) {
|
69
|
+
lines = csv.split(options.lineDelimiter || '\n');
|
70
|
+
|
71
|
+
each(lines, function (line, rowNo) {
|
72
|
+
if (rowNo >= startRow && rowNo <= endRow) {
|
73
|
+
var items = line.split(options.itemDelimiter || ',');
|
74
|
+
each(items, function (item, colNo) {
|
75
|
+
if (colNo >= startColumn && colNo <= endColumn) {
|
76
|
+
if (!columns[colNo - startColumn]) {
|
77
|
+
columns[colNo - startColumn] = [];
|
78
|
+
}
|
79
|
+
|
80
|
+
columns[colNo - startColumn][rowNo - startRow] = item;
|
81
|
+
}
|
82
|
+
});
|
83
|
+
}
|
84
|
+
});
|
85
|
+
}
|
86
|
+
},
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Parse a HTML table
|
90
|
+
*/
|
91
|
+
parseTable: function () {
|
92
|
+
var options = this.options,
|
93
|
+
table = options.table,
|
94
|
+
columns = this.columns,
|
95
|
+
startRow = options.startRow || 0,
|
96
|
+
endRow = options.endRow || Number.MAX_VALUE,
|
97
|
+
startColumn = options.startColumn || 0,
|
98
|
+
endColumn = options.endColumn || Number.MAX_VALUE,
|
99
|
+
colNo;
|
100
|
+
|
101
|
+
if (table) {
|
102
|
+
|
103
|
+
if (typeof table === 'string') {
|
104
|
+
table = document.getElementById(table);
|
105
|
+
}
|
106
|
+
|
107
|
+
each(table.getElementsByTagName('tr'), function (tr, rowNo) {
|
108
|
+
colNo = 0;
|
109
|
+
if (rowNo >= startRow && rowNo <= endRow) {
|
110
|
+
each(tr.childNodes, function (item) {
|
111
|
+
if ((item.tagName === 'TD' || item.tagName === 'TH') && colNo >= startColumn && colNo <= endColumn) {
|
112
|
+
if (!columns[colNo]) {
|
113
|
+
columns[colNo] = [];
|
114
|
+
}
|
115
|
+
columns[colNo][rowNo - startRow] = item.innerHTML;
|
116
|
+
|
117
|
+
colNo += 1;
|
118
|
+
}
|
119
|
+
});
|
120
|
+
}
|
121
|
+
});
|
122
|
+
}
|
123
|
+
},
|
124
|
+
|
125
|
+
/**
|
126
|
+
* Find the header row. For now, we just check whether the first row contains
|
127
|
+
* numbers or strings. Later we could loop down and find the first row with
|
128
|
+
* numbers.
|
129
|
+
*/
|
130
|
+
findHeaderRow: function () {
|
131
|
+
var headerRow = 0;
|
132
|
+
each(this.columns, function (column) {
|
133
|
+
if (typeof column[0] !== 'string') {
|
134
|
+
headerRow = null;
|
135
|
+
}
|
136
|
+
});
|
137
|
+
this.headerRow = 0;
|
138
|
+
},
|
139
|
+
|
140
|
+
/**
|
141
|
+
* Trim a string from whitespace
|
142
|
+
*/
|
143
|
+
trim: function (str) {
|
144
|
+
return str.replace(/^\s+|\s+$/g, '');
|
145
|
+
},
|
146
|
+
|
147
|
+
/**
|
148
|
+
* Parse numeric cells in to number types and date types in to true dates.
|
149
|
+
* @param {Object} columns
|
150
|
+
*/
|
151
|
+
parseTypes: function () {
|
152
|
+
var columns = this.columns,
|
153
|
+
col = columns.length,
|
154
|
+
row,
|
155
|
+
val,
|
156
|
+
floatVal,
|
157
|
+
trimVal,
|
158
|
+
dateVal;
|
159
|
+
|
160
|
+
while (col--) {
|
161
|
+
row = columns[col].length;
|
162
|
+
while (row--) {
|
163
|
+
val = columns[col][row];
|
164
|
+
floatVal = parseFloat(val);
|
165
|
+
trimVal = this.trim(val);
|
166
|
+
/*jslint eqeq: true*/
|
167
|
+
if (trimVal == floatVal) { // is numeric
|
168
|
+
/*jslint eqeq: false*/
|
169
|
+
columns[col][row] = floatVal;
|
170
|
+
|
171
|
+
// If the number is greater than milliseconds in a year, assume datetime
|
172
|
+
if (floatVal > 365 * 24 * 3600 * 1000) {
|
173
|
+
columns[col].isDatetime = true;
|
174
|
+
} else {
|
175
|
+
columns[col].isNumeric = true;
|
176
|
+
}
|
177
|
+
|
178
|
+
} else { // string, continue to determine if it is a date string or really a string
|
179
|
+
dateVal = Date.parse(val);
|
180
|
+
|
181
|
+
if (col === 0 && typeof dateVal === 'number' && !isNaN(dateVal)) { // is date
|
182
|
+
columns[col][row] = dateVal;
|
183
|
+
columns[col].isDatetime = true;
|
184
|
+
|
185
|
+
} else { // string
|
186
|
+
columns[col][row] = trimVal;
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
190
|
+
}
|
191
|
+
}
|
192
|
+
},
|
193
|
+
|
194
|
+
parsed: function () {
|
195
|
+
if (this.options.parsed) {
|
196
|
+
this.options.parsed.call(this, this.columns);
|
197
|
+
}
|
198
|
+
},
|
199
|
+
|
200
|
+
/**
|
201
|
+
* If a complete callback function is provided in the options, interpret the
|
202
|
+
* columns into a Highcharts options object.
|
203
|
+
*/
|
204
|
+
complete: function () {
|
205
|
+
|
206
|
+
var columns = this.columns,
|
207
|
+
hasXData,
|
208
|
+
categories,
|
209
|
+
firstCol,
|
210
|
+
type,
|
211
|
+
options = this.options,
|
212
|
+
series,
|
213
|
+
data,
|
214
|
+
name,
|
215
|
+
i,
|
216
|
+
j;
|
217
|
+
|
218
|
+
|
219
|
+
if (options.complete) {
|
220
|
+
|
221
|
+
// Use first column for X data or categories?
|
222
|
+
if (columns.length > 1) {
|
223
|
+
firstCol = columns.shift();
|
224
|
+
if (this.headerRow === 0) {
|
225
|
+
firstCol.shift(); // remove the first cell
|
226
|
+
}
|
227
|
+
|
228
|
+
// Use the first column for categories or X values
|
229
|
+
hasXData = firstCol.isNumeric || firstCol.isDatetime;
|
230
|
+
if (!hasXData) { // means type is neither datetime nor linear
|
231
|
+
categories = firstCol;
|
232
|
+
}
|
233
|
+
|
234
|
+
if (firstCol.isDatetime) {
|
235
|
+
type = 'datetime';
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
// Use the next columns for series
|
240
|
+
series = [];
|
241
|
+
for (i = 0; i < columns.length; i++) {
|
242
|
+
if (this.headerRow === 0) {
|
243
|
+
name = columns[i].shift();
|
244
|
+
}
|
245
|
+
data = [];
|
246
|
+
for (j = 0; j < columns[i].length; j++) {
|
247
|
+
data[j] = columns[i][j] !== undefined ?
|
248
|
+
(hasXData ?
|
249
|
+
[firstCol[j], columns[i][j]] :
|
250
|
+
columns[i][j]
|
251
|
+
) :
|
252
|
+
null;
|
253
|
+
}
|
254
|
+
series[i] = {
|
255
|
+
name: name,
|
256
|
+
data: data
|
257
|
+
};
|
258
|
+
}
|
259
|
+
|
260
|
+
// Do the callback
|
261
|
+
options.complete({
|
262
|
+
xAxis: {
|
263
|
+
categories: categories,
|
264
|
+
type: type
|
265
|
+
},
|
266
|
+
series: series
|
267
|
+
});
|
268
|
+
}
|
269
|
+
}
|
270
|
+
});
|
271
|
+
|
272
|
+
// Register the Data prototype and data function on Highcharts
|
273
|
+
Highcharts.Data = Data;
|
274
|
+
Highcharts.data = function (options) {
|
275
|
+
return new Data(options);
|
276
|
+
};
|
277
|
+
}(Highcharts));
|
@@ -0,0 +1,23 @@
|
|
1
|
+
/*
|
2
|
+
Highcharts JS v2.3.3 (2012-10-04)
|
3
|
+
Exporting module
|
4
|
+
|
5
|
+
(c) 2010-2011 Torstein Hønsi
|
6
|
+
|
7
|
+
License: www.highcharts.com/license
|
8
|
+
*/
|
9
|
+
(function(){function x(a){for(var b=a.length;b--;)typeof a[b]==="number"&&(a[b]=Math.round(a[b])-0.5);return a}var g=Highcharts,y=g.Chart,z=g.addEvent,B=g.removeEvent,r=g.createElement,u=g.discardElement,t=g.css,s=g.merge,k=g.each,n=g.extend,C=Math.max,h=document,D=window,A=h.documentElement.ontouchstart!==void 0,v=g.getOptions();n(v.lang,{downloadPNG:"Download PNG image",downloadJPEG:"Download JPEG image",downloadPDF:"Download PDF document",downloadSVG:"Download SVG vector image",exportButtonTitle:"Export to raster or vector image",
|
10
|
+
printButtonTitle:"Print the chart"});v.navigation={menuStyle:{border:"1px solid #A0A0A0",background:"#FFFFFF"},menuItemStyle:{padding:"0 5px",background:"none",color:"#303030",fontSize:A?"14px":"11px"},menuItemHoverStyle:{background:"#4572A5",color:"#FFFFFF"},buttonOptions:{align:"right",backgroundColor:{linearGradient:[0,0,0,20],stops:[[0.4,"#F7F7F7"],[0.6,"#E3E3E3"]]},borderColor:"#B0B0B0",borderRadius:3,borderWidth:1,height:20,hoverBorderColor:"#909090",hoverSymbolFill:"#81A7CF",hoverSymbolStroke:"#4572A5",
|
11
|
+
symbolFill:"#E0E0E0",symbolStroke:"#A0A0A0",symbolX:11.5,symbolY:10.5,verticalAlign:"top",width:24,y:10}};v.exporting={type:"image/png",url:"http://export.highcharts.com/",width:800,buttons:{exportButton:{symbol:"exportIcon",x:-10,symbolFill:"#A8BF77",hoverSymbolFill:"#768F3E",_id:"exportButton",_titleKey:"exportButtonTitle",menuItems:[{textKey:"downloadPNG",onclick:function(){this.exportChart()}},{textKey:"downloadJPEG",onclick:function(){this.exportChart({type:"image/jpeg"})}},{textKey:"downloadPDF",
|
12
|
+
onclick:function(){this.exportChart({type:"application/pdf"})}},{textKey:"downloadSVG",onclick:function(){this.exportChart({type:"image/svg+xml"})}}]},printButton:{symbol:"printIcon",x:-36,symbolFill:"#B5C9DF",hoverSymbolFill:"#779ABF",_id:"printButton",_titleKey:"printButtonTitle",onclick:function(){this.print()}}}};n(y.prototype,{getSVG:function(a){var b=this,c,d,e,f=s(b.options,a);if(!h.createElementNS)h.createElementNS=function(a,b){return h.createElement(b)};a=r("div",null,{position:"absolute",
|
13
|
+
top:"-9999em",width:b.chartWidth+"px",height:b.chartHeight+"px"},h.body);n(f.chart,{renderTo:a,forExport:!0});f.exporting.enabled=!1;f.chart.plotBackgroundImage=null;f.series=[];k(b.series,function(a){e=s(a.options,{animation:!1,showCheckbox:!1,visible:a.visible});if(!e.isInternal){if(e&&e.marker&&/^url\(/.test(e.marker.symbol))e.marker.symbol="circle";f.series.push(e)}});c=new Highcharts.Chart(f);k(["xAxis","yAxis"],function(a){k(b[a],function(b,d){var e=c[a][d],f=b.getExtremes(),g=f.userMin,f=f.userMax;
|
14
|
+
(g!==void 0||f!==void 0)&&e.setExtremes(g,f,!0,!1)})});d=c.container.innerHTML;f=null;c.destroy();u(a);d=d.replace(/zIndex="[^"]+"/g,"").replace(/isShadow="[^"]+"/g,"").replace(/symbolName="[^"]+"/g,"").replace(/jQuery[0-9]+="[^"]+"/g,"").replace(/isTracker="[^"]+"/g,"").replace(/url\([^#]+#/g,"url(#").replace(/<svg /,'<svg xmlns:xlink="http://www.w3.org/1999/xlink" ').replace(/ href=/g," xlink:href=").replace(/\n/," ").replace(/<\/svg>.*?$/,"</svg>").replace(/ /g," ").replace(/­/g,"").replace(/<IMG /g,
|
15
|
+
"<image ").replace(/height=([^" ]+)/g,'height="$1"').replace(/width=([^" ]+)/g,'width="$1"').replace(/hc-svg-href="([^"]+)">/g,'xlink:href="$1"/>').replace(/id=([^" >]+)/g,'id="$1"').replace(/class=([^" ]+)/g,'class="$1"').replace(/ transform /g," ").replace(/:(path|rect)/g,"$1").replace(/style="([^"]+)"/g,function(a){return a.toLowerCase()});d=d.replace(/(url\(#highcharts-[0-9]+)"/g,"$1").replace(/"/g,"'");d.match(/ xmlns="/g).length===2&&(d=d.replace(/xmlns="[^"]+"/,""));return d},exportChart:function(a,
|
16
|
+
b){var c,d=this.getSVG(s(this.options.exporting.chartOptions,b)),a=s(this.options.exporting,a);c=r("form",{method:"post",action:a.url,enctype:"multipart/form-data"},{display:"none"},h.body);k(["filename","type","width","svg"],function(b){r("input",{type:"hidden",name:b,value:{filename:a.filename||"chart",type:a.type,width:a.width,svg:d}[b]},null,c)});c.submit();u(c)},print:function(){var a=this,b=a.container,c=[],d=b.parentNode,e=h.body,f=e.childNodes;if(!a.isPrinting)a.isPrinting=!0,k(f,function(a,
|
17
|
+
b){if(a.nodeType===1)c[b]=a.style.display,a.style.display="none"}),e.appendChild(b),D.print(),setTimeout(function(){d.appendChild(b);k(f,function(a,b){if(a.nodeType===1)a.style.display=c[b]});a.isPrinting=!1},1E3)},contextMenu:function(a,b,c,d,e,f){var i=this,g=i.options.navigation,h=g.menuItemStyle,o=i.chartWidth,p=i.chartHeight,q="cache-"+a,j=i[q],l=C(e,f),m,w;if(!j)i[q]=j=r("div",{className:"highcharts-"+a},{position:"absolute",zIndex:1E3,padding:l+"px"},i.container),m=r("div",null,n({MozBoxShadow:"3px 3px 10px #888",
|
18
|
+
WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},g.menuStyle),j),w=function(){t(j,{display:"none"})},z(j,"mouseleave",w),k(b,function(a){if(a){var b=r("div",{onmouseover:function(){t(this,g.menuItemHoverStyle)},onmouseout:function(){t(this,h)},innerHTML:a.text||i.options.lang[a.textKey]},n({cursor:"pointer"},h),m);b[A?"ontouchstart":"onclick"]=function(){w();a.onclick.apply(i,arguments)};i.exportDivElements.push(b)}}),i.exportDivElements.push(m,j),i.exportMenuWidth=j.offsetWidth,
|
19
|
+
i.exportMenuHeight=j.offsetHeight;a={display:"block"};c+i.exportMenuWidth>o?a.right=o-c-e-l+"px":a.left=c-l+"px";d+f+i.exportMenuHeight>p?a.bottom=p-d-l+"px":a.top=d+f-l+"px";t(j,a)},addButton:function(a){function b(){p.attr(l);o.attr(j)}var c=this,d=c.renderer,e=s(c.options.navigation.buttonOptions,a),f=e.onclick,g=e.menuItems,h=e.width,k=e.height,o,p,q,a=e.borderWidth,j={stroke:e.borderColor},l={stroke:e.symbolStroke,fill:e.symbolFill},m=e.symbolSize||12;if(!c.exportDivElements)c.exportDivElements=
|
20
|
+
[],c.exportSVGElements=[];e.enabled!==!1&&(o=d.rect(0,0,h,k,e.borderRadius,a).align(e,!0).attr(n({fill:e.backgroundColor,"stroke-width":a,zIndex:19},j)).add(),q=d.rect(0,0,h,k,0).align(e).attr({id:e._id,fill:"rgba(255, 255, 255, 0.001)",title:c.options.lang[e._titleKey],zIndex:21}).css({cursor:"pointer"}).on("mouseover",function(){p.attr({stroke:e.hoverSymbolStroke,fill:e.hoverSymbolFill});o.attr({stroke:e.hoverBorderColor})}).on("mouseout",b).on("click",b).add(),g&&(f=function(){b();var a=q.getBBox();
|
21
|
+
c.contextMenu("export-menu",g,a.x,a.y,h,k)}),q.on("click",function(){f.apply(c,arguments)}),p=d.symbol(e.symbol,e.symbolX-m/2,e.symbolY-m/2,m,m).align(e,!0).attr(n(l,{"stroke-width":e.symbolStrokeWidth||1,zIndex:20})).add(),c.exportSVGElements.push(o,q,p))},destroyExport:function(){var a,b;for(a=0;a<this.exportSVGElements.length;a++)b=this.exportSVGElements[a],b.onclick=b.ontouchstart=null,this.exportSVGElements[a]=b.destroy();for(a=0;a<this.exportDivElements.length;a++)b=this.exportDivElements[a],
|
22
|
+
B(b,"mouseleave"),this.exportDivElements[a]=b.onmouseout=b.onmouseover=b.ontouchstart=b.onclick=null,u(b)}});g.Renderer.prototype.symbols.exportIcon=function(a,b,c,d){return x(["M",a,b+c,"L",a+c,b+d,a+c,b+d*0.8,a,b+d*0.8,"Z","M",a+c*0.5,b+d*0.8,"L",a+c*0.8,b+d*0.4,a+c*0.4,b+d*0.4,a+c*0.4,b,a+c*0.6,b,a+c*0.6,b+d*0.4,a+c*0.2,b+d*0.4,"Z"])};g.Renderer.prototype.symbols.printIcon=function(a,b,c,d){return x(["M",a,b+d*0.7,"L",a+c,b+d*0.7,a+c,b+d*0.4,a,b+d*0.4,"Z","M",a+c*0.2,b+d*0.4,"L",a+c*0.2,b,a+c*
|
23
|
+
0.8,b,a+c*0.8,b+d*0.4,"Z","M",a+c*0.2,b+d*0.7,"L",a,b+d,a+c,b+d,a+c*0.8,b+d*0.7,"Z"])};y.prototype.callbacks.push(function(a){var b,c=a.options.exporting,d=c.buttons;if(c.enabled!==!1){for(b in d)a.addButton(d[b]);z(a,"destroy",a.destroyExport)}})})();
|
@@ -0,0 +1,736 @@
|
|
1
|
+
/**
|
2
|
+
* @license Highcharts JS v2.3.3 (2012-10-04)
|
3
|
+
* Exporting module
|
4
|
+
*
|
5
|
+
* (c) 2010-2011 Torstein Hønsi
|
6
|
+
*
|
7
|
+
* License: www.highcharts.com/license
|
8
|
+
*/
|
9
|
+
|
10
|
+
// JSLint options:
|
11
|
+
/*global Highcharts, document, window, Math, setTimeout */
|
12
|
+
|
13
|
+
(function () { // encapsulate
|
14
|
+
|
15
|
+
// create shortcuts
|
16
|
+
var HC = Highcharts,
|
17
|
+
Chart = HC.Chart,
|
18
|
+
addEvent = HC.addEvent,
|
19
|
+
removeEvent = HC.removeEvent,
|
20
|
+
createElement = HC.createElement,
|
21
|
+
discardElement = HC.discardElement,
|
22
|
+
css = HC.css,
|
23
|
+
merge = HC.merge,
|
24
|
+
each = HC.each,
|
25
|
+
extend = HC.extend,
|
26
|
+
math = Math,
|
27
|
+
mathMax = math.max,
|
28
|
+
doc = document,
|
29
|
+
win = window,
|
30
|
+
hasTouch = doc.documentElement.ontouchstart !== undefined,
|
31
|
+
M = 'M',
|
32
|
+
L = 'L',
|
33
|
+
DIV = 'div',
|
34
|
+
HIDDEN = 'hidden',
|
35
|
+
NONE = 'none',
|
36
|
+
PREFIX = 'highcharts-',
|
37
|
+
ABSOLUTE = 'absolute',
|
38
|
+
PX = 'px',
|
39
|
+
UNDEFINED,
|
40
|
+
defaultOptions = HC.getOptions();
|
41
|
+
|
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'
|
50
|
+
});
|
51
|
+
|
52
|
+
// Buttons and menus are collected in a separate config option set called 'navigation'.
|
53
|
+
// This can be extended later to add control buttons like zoom and pan right click menus.
|
54
|
+
defaultOptions.navigation = {
|
55
|
+
menuStyle: {
|
56
|
+
border: '1px solid #A0A0A0',
|
57
|
+
background: '#FFFFFF'
|
58
|
+
},
|
59
|
+
menuItemStyle: {
|
60
|
+
padding: '0 5px',
|
61
|
+
background: NONE,
|
62
|
+
color: '#303030',
|
63
|
+
fontSize: hasTouch ? '14px' : '11px'
|
64
|
+
},
|
65
|
+
menuItemHoverStyle: {
|
66
|
+
background: '#4572A5',
|
67
|
+
color: '#FFFFFF'
|
68
|
+
},
|
69
|
+
|
70
|
+
buttonOptions: {
|
71
|
+
align: 'right',
|
72
|
+
backgroundColor: {
|
73
|
+
linearGradient: [0, 0, 0, 20],
|
74
|
+
stops: [
|
75
|
+
[0.4, '#F7F7F7'],
|
76
|
+
[0.6, '#E3E3E3']
|
77
|
+
]
|
78
|
+
},
|
79
|
+
borderColor: '#B0B0B0',
|
80
|
+
borderRadius: 3,
|
81
|
+
borderWidth: 1,
|
82
|
+
//enabled: true,
|
83
|
+
height: 20,
|
84
|
+
hoverBorderColor: '#909090',
|
85
|
+
hoverSymbolFill: '#81A7CF',
|
86
|
+
hoverSymbolStroke: '#4572A5',
|
87
|
+
symbolFill: '#E0E0E0',
|
88
|
+
//symbolSize: 12,
|
89
|
+
symbolStroke: '#A0A0A0',
|
90
|
+
//symbolStrokeWidth: 1,
|
91
|
+
symbolX: 11.5,
|
92
|
+
symbolY: 10.5,
|
93
|
+
verticalAlign: 'top',
|
94
|
+
width: 24,
|
95
|
+
y: 10
|
96
|
+
}
|
97
|
+
};
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
// Add the export related options
|
102
|
+
defaultOptions.exporting = {
|
103
|
+
//enabled: true,
|
104
|
+
//filename: 'chart',
|
105
|
+
type: 'image/png',
|
106
|
+
url: 'http://export.highcharts.com/',
|
107
|
+
width: 800,
|
108
|
+
buttons: {
|
109
|
+
exportButton: {
|
110
|
+
//enabled: true,
|
111
|
+
symbol: 'exportIcon',
|
112
|
+
x: -10,
|
113
|
+
symbolFill: '#A8BF77',
|
114
|
+
hoverSymbolFill: '#768F3E',
|
115
|
+
_id: 'exportButton',
|
116
|
+
_titleKey: 'exportButtonTitle',
|
117
|
+
menuItems: [{
|
118
|
+
textKey: 'downloadPNG',
|
119
|
+
onclick: function () {
|
120
|
+
this.exportChart();
|
121
|
+
}
|
122
|
+
}, {
|
123
|
+
textKey: 'downloadJPEG',
|
124
|
+
onclick: function () {
|
125
|
+
this.exportChart({
|
126
|
+
type: 'image/jpeg'
|
127
|
+
});
|
128
|
+
}
|
129
|
+
}, {
|
130
|
+
textKey: 'downloadPDF',
|
131
|
+
onclick: function () {
|
132
|
+
this.exportChart({
|
133
|
+
type: 'application/pdf'
|
134
|
+
});
|
135
|
+
}
|
136
|
+
}, {
|
137
|
+
textKey: 'downloadSVG',
|
138
|
+
onclick: function () {
|
139
|
+
this.exportChart({
|
140
|
+
type: 'image/svg+xml'
|
141
|
+
});
|
142
|
+
}
|
143
|
+
}
|
144
|
+
// Enable this block to add "View SVG" to the dropdown menu
|
145
|
+
/*
|
146
|
+
,{
|
147
|
+
|
148
|
+
text: 'View SVG',
|
149
|
+
onclick: function () {
|
150
|
+
var svg = this.getSVG()
|
151
|
+
.replace(/</g, '\n<')
|
152
|
+
.replace(/>/g, '>');
|
153
|
+
|
154
|
+
doc.body.innerHTML = '<pre>' + svg + '</pre>';
|
155
|
+
}
|
156
|
+
} // */
|
157
|
+
]
|
158
|
+
|
159
|
+
},
|
160
|
+
printButton: {
|
161
|
+
//enabled: true,
|
162
|
+
symbol: 'printIcon',
|
163
|
+
x: -36,
|
164
|
+
symbolFill: '#B5C9DF',
|
165
|
+
hoverSymbolFill: '#779ABF',
|
166
|
+
_id: 'printButton',
|
167
|
+
_titleKey: 'printButtonTitle',
|
168
|
+
onclick: function () {
|
169
|
+
this.print();
|
170
|
+
}
|
171
|
+
}
|
172
|
+
}
|
173
|
+
};
|
174
|
+
|
175
|
+
|
176
|
+
|
177
|
+
extend(Chart.prototype, {
|
178
|
+
/**
|
179
|
+
* Return an SVG representation of the chart
|
180
|
+
*
|
181
|
+
* @param additionalOptions {Object} Additional chart options for the generated SVG representation
|
182
|
+
*/
|
183
|
+
getSVG: function (additionalOptions) {
|
184
|
+
var chart = this,
|
185
|
+
chartCopy,
|
186
|
+
sandbox,
|
187
|
+
svg,
|
188
|
+
seriesOptions,
|
189
|
+
options = merge(chart.options, additionalOptions); // copy the options and add extra options
|
190
|
+
|
191
|
+
// IE compatibility hack for generating SVG content that it doesn't really understand
|
192
|
+
if (!doc.createElementNS) {
|
193
|
+
/*jslint unparam: true*//* allow unused parameter ns in function below */
|
194
|
+
doc.createElementNS = function (ns, tagName) {
|
195
|
+
return doc.createElement(tagName);
|
196
|
+
};
|
197
|
+
/*jslint unparam: false*/
|
198
|
+
}
|
199
|
+
|
200
|
+
// create a sandbox where a new chart will be generated
|
201
|
+
sandbox = createElement(DIV, null, {
|
202
|
+
position: ABSOLUTE,
|
203
|
+
top: '-9999em',
|
204
|
+
width: chart.chartWidth + PX,
|
205
|
+
height: chart.chartHeight + PX
|
206
|
+
}, doc.body);
|
207
|
+
|
208
|
+
// override some options
|
209
|
+
extend(options.chart, {
|
210
|
+
renderTo: sandbox,
|
211
|
+
forExport: true
|
212
|
+
});
|
213
|
+
options.exporting.enabled = false; // hide buttons in print
|
214
|
+
options.chart.plotBackgroundImage = null; // the converter doesn't handle images
|
215
|
+
|
216
|
+
// prepare for replicating the chart
|
217
|
+
options.series = [];
|
218
|
+
each(chart.series, function (serie) {
|
219
|
+
seriesOptions = merge(serie.options, {
|
220
|
+
animation: false, // turn off animation
|
221
|
+
showCheckbox: false,
|
222
|
+
visible: serie.visible
|
223
|
+
});
|
224
|
+
|
225
|
+
if (!seriesOptions.isInternal) { // used for the navigator series that has its own option set
|
226
|
+
|
227
|
+
// remove image markers
|
228
|
+
if (seriesOptions && seriesOptions.marker && /^url\(/.test(seriesOptions.marker.symbol)) {
|
229
|
+
seriesOptions.marker.symbol = 'circle';
|
230
|
+
}
|
231
|
+
|
232
|
+
options.series.push(seriesOptions);
|
233
|
+
}
|
234
|
+
});
|
235
|
+
|
236
|
+
// generate the chart copy
|
237
|
+
chartCopy = new Highcharts.Chart(options);
|
238
|
+
|
239
|
+
// reflect axis extremes in the export
|
240
|
+
each(['xAxis', 'yAxis'], function (axisType) {
|
241
|
+
each(chart[axisType], function (axis, i) {
|
242
|
+
var axisCopy = chartCopy[axisType][i],
|
243
|
+
extremes = axis.getExtremes(),
|
244
|
+
userMin = extremes.userMin,
|
245
|
+
userMax = extremes.userMax;
|
246
|
+
|
247
|
+
if (userMin !== UNDEFINED || userMax !== UNDEFINED) {
|
248
|
+
axisCopy.setExtremes(userMin, userMax, true, false);
|
249
|
+
}
|
250
|
+
});
|
251
|
+
});
|
252
|
+
|
253
|
+
// get the SVG from the container's innerHTML
|
254
|
+
svg = chartCopy.container.innerHTML;
|
255
|
+
|
256
|
+
// free up memory
|
257
|
+
options = null;
|
258
|
+
chartCopy.destroy();
|
259
|
+
discardElement(sandbox);
|
260
|
+
|
261
|
+
// sanitize
|
262
|
+
svg = svg
|
263
|
+
.replace(/zIndex="[^"]+"/g, '')
|
264
|
+
.replace(/isShadow="[^"]+"/g, '')
|
265
|
+
.replace(/symbolName="[^"]+"/g, '')
|
266
|
+
.replace(/jQuery[0-9]+="[^"]+"/g, '')
|
267
|
+
.replace(/isTracker="[^"]+"/g, '')
|
268
|
+
.replace(/url\([^#]+#/g, 'url(#')
|
269
|
+
.replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
|
270
|
+
.replace(/ href=/g, ' xlink:href=')
|
271
|
+
.replace(/\n/, ' ')
|
272
|
+
.replace(/<\/svg>.*?$/, '</svg>') // any HTML added to the container after the SVG (#894)
|
273
|
+
/* This fails in IE < 8
|
274
|
+
.replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
|
275
|
+
return s2 +'.'+ s3[0];
|
276
|
+
})*/
|
277
|
+
|
278
|
+
// Replace HTML entities, issue #347
|
279
|
+
.replace(/ /g, '\u00A0') // no-break space
|
280
|
+
.replace(/­/g, '\u00AD') // soft hyphen
|
281
|
+
|
282
|
+
// IE specific
|
283
|
+
.replace(/<IMG /g, '<image ')
|
284
|
+
.replace(/height=([^" ]+)/g, 'height="$1"')
|
285
|
+
.replace(/width=([^" ]+)/g, 'width="$1"')
|
286
|
+
.replace(/hc-svg-href="([^"]+)">/g, 'xlink:href="$1"/>')
|
287
|
+
.replace(/id=([^" >]+)/g, 'id="$1"')
|
288
|
+
.replace(/class=([^" ]+)/g, 'class="$1"')
|
289
|
+
.replace(/ transform /g, ' ')
|
290
|
+
.replace(/:(path|rect)/g, '$1')
|
291
|
+
.replace(/style="([^"]+)"/g, function (s) {
|
292
|
+
return s.toLowerCase();
|
293
|
+
});
|
294
|
+
|
295
|
+
// IE9 beta bugs with innerHTML. Test again with final IE9.
|
296
|
+
svg = svg.replace(/(url\(#highcharts-[0-9]+)"/g, '$1')
|
297
|
+
.replace(/"/g, "'");
|
298
|
+
if (svg.match(/ xmlns="/g).length === 2) {
|
299
|
+
svg = svg.replace(/xmlns="[^"]+"/, '');
|
300
|
+
}
|
301
|
+
|
302
|
+
return svg;
|
303
|
+
},
|
304
|
+
|
305
|
+
/**
|
306
|
+
* Submit the SVG representation of the chart to the server
|
307
|
+
* @param {Object} options Exporting options. Possible members are url, type and width.
|
308
|
+
* @param {Object} chartOptions Additional chart options for the SVG representation of the chart
|
309
|
+
*/
|
310
|
+
exportChart: function (options, chartOptions) {
|
311
|
+
var form,
|
312
|
+
chart = this,
|
313
|
+
svg = chart.getSVG(merge(chart.options.exporting.chartOptions, chartOptions)); // docs
|
314
|
+
|
315
|
+
// merge the options
|
316
|
+
options = merge(chart.options.exporting, options);
|
317
|
+
|
318
|
+
// create the form
|
319
|
+
form = createElement('form', {
|
320
|
+
method: 'post',
|
321
|
+
action: options.url,
|
322
|
+
enctype: 'multipart/form-data'
|
323
|
+
}, {
|
324
|
+
display: NONE
|
325
|
+
}, doc.body);
|
326
|
+
|
327
|
+
// add the values
|
328
|
+
each(['filename', 'type', 'width', 'svg'], function (name) {
|
329
|
+
createElement('input', {
|
330
|
+
type: HIDDEN,
|
331
|
+
name: name,
|
332
|
+
value: {
|
333
|
+
filename: options.filename || 'chart',
|
334
|
+
type: options.type,
|
335
|
+
width: options.width,
|
336
|
+
svg: svg
|
337
|
+
}[name]
|
338
|
+
}, null, form);
|
339
|
+
});
|
340
|
+
|
341
|
+
// submit
|
342
|
+
form.submit();
|
343
|
+
|
344
|
+
// clean up
|
345
|
+
discardElement(form);
|
346
|
+
},
|
347
|
+
|
348
|
+
/**
|
349
|
+
* Print the chart
|
350
|
+
*/
|
351
|
+
print: function () {
|
352
|
+
|
353
|
+
var chart = this,
|
354
|
+
container = chart.container,
|
355
|
+
origDisplay = [],
|
356
|
+
origParent = container.parentNode,
|
357
|
+
body = doc.body,
|
358
|
+
childNodes = body.childNodes;
|
359
|
+
|
360
|
+
if (chart.isPrinting) { // block the button while in printing mode
|
361
|
+
return;
|
362
|
+
}
|
363
|
+
|
364
|
+
chart.isPrinting = true;
|
365
|
+
|
366
|
+
// hide all body content
|
367
|
+
each(childNodes, function (node, i) {
|
368
|
+
if (node.nodeType === 1) {
|
369
|
+
origDisplay[i] = node.style.display;
|
370
|
+
node.style.display = NONE;
|
371
|
+
}
|
372
|
+
});
|
373
|
+
|
374
|
+
// pull out the chart
|
375
|
+
body.appendChild(container);
|
376
|
+
|
377
|
+
// print
|
378
|
+
win.print();
|
379
|
+
|
380
|
+
// allow the browser to prepare before reverting
|
381
|
+
setTimeout(function () {
|
382
|
+
|
383
|
+
// put the chart back in
|
384
|
+
origParent.appendChild(container);
|
385
|
+
|
386
|
+
// restore all body content
|
387
|
+
each(childNodes, function (node, i) {
|
388
|
+
if (node.nodeType === 1) {
|
389
|
+
node.style.display = origDisplay[i];
|
390
|
+
}
|
391
|
+
});
|
392
|
+
|
393
|
+
chart.isPrinting = false;
|
394
|
+
|
395
|
+
}, 1000);
|
396
|
+
|
397
|
+
},
|
398
|
+
|
399
|
+
/**
|
400
|
+
* Display a popup menu for choosing the export type
|
401
|
+
*
|
402
|
+
* @param {String} name An identifier for the menu
|
403
|
+
* @param {Array} items A collection with text and onclicks for the items
|
404
|
+
* @param {Number} x The x position of the opener button
|
405
|
+
* @param {Number} y The y position of the opener button
|
406
|
+
* @param {Number} width The width of the opener button
|
407
|
+
* @param {Number} height The height of the opener button
|
408
|
+
*/
|
409
|
+
contextMenu: function (name, items, x, y, width, height) {
|
410
|
+
var chart = this,
|
411
|
+
navOptions = chart.options.navigation,
|
412
|
+
menuItemStyle = navOptions.menuItemStyle,
|
413
|
+
chartWidth = chart.chartWidth,
|
414
|
+
chartHeight = chart.chartHeight,
|
415
|
+
cacheName = 'cache-' + name,
|
416
|
+
menu = chart[cacheName],
|
417
|
+
menuPadding = mathMax(width, height), // for mouse leave detection
|
418
|
+
boxShadow = '3px 3px 10px #888',
|
419
|
+
innerMenu,
|
420
|
+
hide,
|
421
|
+
menuStyle;
|
422
|
+
|
423
|
+
// create the menu only the first time
|
424
|
+
if (!menu) {
|
425
|
+
|
426
|
+
// create a HTML element above the SVG
|
427
|
+
chart[cacheName] = menu = createElement(DIV, {
|
428
|
+
className: PREFIX + name
|
429
|
+
}, {
|
430
|
+
position: ABSOLUTE,
|
431
|
+
zIndex: 1000,
|
432
|
+
padding: menuPadding + PX
|
433
|
+
}, chart.container);
|
434
|
+
|
435
|
+
innerMenu = createElement(DIV, null,
|
436
|
+
extend({
|
437
|
+
MozBoxShadow: boxShadow,
|
438
|
+
WebkitBoxShadow: boxShadow,
|
439
|
+
boxShadow: boxShadow
|
440
|
+
}, navOptions.menuStyle), menu);
|
441
|
+
|
442
|
+
// hide on mouse out
|
443
|
+
hide = function () {
|
444
|
+
css(menu, { display: NONE });
|
445
|
+
};
|
446
|
+
|
447
|
+
addEvent(menu, 'mouseleave', hide);
|
448
|
+
|
449
|
+
|
450
|
+
// create the items
|
451
|
+
each(items, function (item) {
|
452
|
+
if (item) {
|
453
|
+
var div = createElement(DIV, {
|
454
|
+
onmouseover: function () {
|
455
|
+
css(this, navOptions.menuItemHoverStyle);
|
456
|
+
},
|
457
|
+
onmouseout: function () {
|
458
|
+
css(this, menuItemStyle);
|
459
|
+
},
|
460
|
+
innerHTML: item.text || chart.options.lang[item.textKey]
|
461
|
+
}, extend({
|
462
|
+
cursor: 'pointer'
|
463
|
+
}, menuItemStyle), innerMenu);
|
464
|
+
|
465
|
+
div[hasTouch ? 'ontouchstart' : 'onclick'] = function () {
|
466
|
+
hide();
|
467
|
+
item.onclick.apply(chart, arguments);
|
468
|
+
};
|
469
|
+
|
470
|
+
// Keep references to menu divs to be able to destroy them
|
471
|
+
chart.exportDivElements.push(div);
|
472
|
+
}
|
473
|
+
});
|
474
|
+
|
475
|
+
// Keep references to menu and innerMenu div to be able to destroy them
|
476
|
+
chart.exportDivElements.push(innerMenu, menu);
|
477
|
+
|
478
|
+
chart.exportMenuWidth = menu.offsetWidth;
|
479
|
+
chart.exportMenuHeight = menu.offsetHeight;
|
480
|
+
}
|
481
|
+
|
482
|
+
menuStyle = { display: 'block' };
|
483
|
+
|
484
|
+
// if outside right, right align it
|
485
|
+
if (x + chart.exportMenuWidth > chartWidth) {
|
486
|
+
menuStyle.right = (chartWidth - x - width - menuPadding) + PX;
|
487
|
+
} else {
|
488
|
+
menuStyle.left = (x - menuPadding) + PX;
|
489
|
+
}
|
490
|
+
// if outside bottom, bottom align it
|
491
|
+
if (y + height + chart.exportMenuHeight > chartHeight) {
|
492
|
+
menuStyle.bottom = (chartHeight - y - menuPadding) + PX;
|
493
|
+
} else {
|
494
|
+
menuStyle.top = (y + height - menuPadding) + PX;
|
495
|
+
}
|
496
|
+
|
497
|
+
css(menu, menuStyle);
|
498
|
+
},
|
499
|
+
|
500
|
+
/**
|
501
|
+
* Add the export button to the chart
|
502
|
+
*/
|
503
|
+
addButton: function (options) {
|
504
|
+
var chart = this,
|
505
|
+
renderer = chart.renderer,
|
506
|
+
btnOptions = merge(chart.options.navigation.buttonOptions, options),
|
507
|
+
onclick = btnOptions.onclick,
|
508
|
+
menuItems = btnOptions.menuItems,
|
509
|
+
buttonWidth = btnOptions.width,
|
510
|
+
buttonHeight = btnOptions.height,
|
511
|
+
box,
|
512
|
+
symbol,
|
513
|
+
button,
|
514
|
+
borderWidth = btnOptions.borderWidth,
|
515
|
+
boxAttr = {
|
516
|
+
stroke: btnOptions.borderColor
|
517
|
+
|
518
|
+
},
|
519
|
+
symbolAttr = {
|
520
|
+
stroke: btnOptions.symbolStroke,
|
521
|
+
fill: btnOptions.symbolFill
|
522
|
+
},
|
523
|
+
symbolSize = btnOptions.symbolSize || 12;
|
524
|
+
|
525
|
+
// Keeps references to the button elements
|
526
|
+
if (!chart.exportDivElements) {
|
527
|
+
chart.exportDivElements = [];
|
528
|
+
chart.exportSVGElements = [];
|
529
|
+
}
|
530
|
+
|
531
|
+
if (btnOptions.enabled === false) {
|
532
|
+
return;
|
533
|
+
}
|
534
|
+
|
535
|
+
// element to capture the click
|
536
|
+
function revert() {
|
537
|
+
symbol.attr(symbolAttr);
|
538
|
+
box.attr(boxAttr);
|
539
|
+
}
|
540
|
+
|
541
|
+
// the box border
|
542
|
+
box = renderer.rect(
|
543
|
+
0,
|
544
|
+
0,
|
545
|
+
buttonWidth,
|
546
|
+
buttonHeight,
|
547
|
+
btnOptions.borderRadius,
|
548
|
+
borderWidth
|
549
|
+
)
|
550
|
+
//.translate(buttonLeft, buttonTop) // to allow gradients
|
551
|
+
.align(btnOptions, true)
|
552
|
+
.attr(extend({
|
553
|
+
fill: btnOptions.backgroundColor,
|
554
|
+
'stroke-width': borderWidth,
|
555
|
+
zIndex: 19
|
556
|
+
}, boxAttr)).add();
|
557
|
+
|
558
|
+
// the invisible element to track the clicks
|
559
|
+
button = renderer.rect(
|
560
|
+
0,
|
561
|
+
0,
|
562
|
+
buttonWidth,
|
563
|
+
buttonHeight,
|
564
|
+
0
|
565
|
+
)
|
566
|
+
.align(btnOptions)
|
567
|
+
.attr({
|
568
|
+
id: btnOptions._id,
|
569
|
+
fill: 'rgba(255, 255, 255, 0.001)',
|
570
|
+
title: chart.options.lang[btnOptions._titleKey],
|
571
|
+
zIndex: 21
|
572
|
+
}).css({
|
573
|
+
cursor: 'pointer'
|
574
|
+
})
|
575
|
+
.on('mouseover', function () {
|
576
|
+
symbol.attr({
|
577
|
+
stroke: btnOptions.hoverSymbolStroke,
|
578
|
+
fill: btnOptions.hoverSymbolFill
|
579
|
+
});
|
580
|
+
box.attr({
|
581
|
+
stroke: btnOptions.hoverBorderColor
|
582
|
+
});
|
583
|
+
})
|
584
|
+
.on('mouseout', revert)
|
585
|
+
.on('click', revert)
|
586
|
+
.add();
|
587
|
+
|
588
|
+
// add the click event
|
589
|
+
if (menuItems) {
|
590
|
+
onclick = function () {
|
591
|
+
revert();
|
592
|
+
var bBox = button.getBBox();
|
593
|
+
chart.contextMenu('export-menu', menuItems, bBox.x, bBox.y, buttonWidth, buttonHeight);
|
594
|
+
};
|
595
|
+
}
|
596
|
+
/*addEvent(button.element, 'click', function() {
|
597
|
+
onclick.apply(chart, arguments);
|
598
|
+
});*/
|
599
|
+
button.on('click', function () {
|
600
|
+
onclick.apply(chart, arguments);
|
601
|
+
});
|
602
|
+
|
603
|
+
// the icon
|
604
|
+
symbol = renderer.symbol(
|
605
|
+
btnOptions.symbol,
|
606
|
+
btnOptions.symbolX - (symbolSize / 2),
|
607
|
+
btnOptions.symbolY - (symbolSize / 2),
|
608
|
+
symbolSize,
|
609
|
+
symbolSize
|
610
|
+
)
|
611
|
+
.align(btnOptions, true)
|
612
|
+
.attr(extend(symbolAttr, {
|
613
|
+
'stroke-width': btnOptions.symbolStrokeWidth || 1,
|
614
|
+
zIndex: 20
|
615
|
+
})).add();
|
616
|
+
|
617
|
+
// Keep references to the renderer element so to be able to destroy them later.
|
618
|
+
chart.exportSVGElements.push(box, button, symbol);
|
619
|
+
},
|
620
|
+
|
621
|
+
/**
|
622
|
+
* Destroy the buttons.
|
623
|
+
*/
|
624
|
+
destroyExport: function () {
|
625
|
+
var i,
|
626
|
+
chart = this,
|
627
|
+
elem;
|
628
|
+
|
629
|
+
// Destroy the extra buttons added
|
630
|
+
for (i = 0; i < chart.exportSVGElements.length; i++) {
|
631
|
+
elem = chart.exportSVGElements[i];
|
632
|
+
// Destroy and null the svg/vml elements
|
633
|
+
elem.onclick = elem.ontouchstart = null;
|
634
|
+
chart.exportSVGElements[i] = elem.destroy();
|
635
|
+
}
|
636
|
+
|
637
|
+
// Destroy the divs for the menu
|
638
|
+
for (i = 0; i < chart.exportDivElements.length; i++) {
|
639
|
+
elem = chart.exportDivElements[i];
|
640
|
+
|
641
|
+
// Remove the event handler
|
642
|
+
removeEvent(elem, 'mouseleave');
|
643
|
+
|
644
|
+
// Remove inline events
|
645
|
+
chart.exportDivElements[i] = elem.onmouseout = elem.onmouseover = elem.ontouchstart = elem.onclick = null;
|
646
|
+
|
647
|
+
// Destroy the div by moving to garbage bin
|
648
|
+
discardElement(elem);
|
649
|
+
}
|
650
|
+
}
|
651
|
+
});
|
652
|
+
|
653
|
+
/**
|
654
|
+
* Crisp for 1px stroke width, which is default. In the future, consider a smarter,
|
655
|
+
* global function.
|
656
|
+
*/
|
657
|
+
function crisp(arr) {
|
658
|
+
var i = arr.length;
|
659
|
+
while (i--) {
|
660
|
+
if (typeof arr[i] === 'number') {
|
661
|
+
arr[i] = Math.round(arr[i]) - 0.5;
|
662
|
+
}
|
663
|
+
}
|
664
|
+
return arr;
|
665
|
+
}
|
666
|
+
|
667
|
+
// Create the export icon
|
668
|
+
HC.Renderer.prototype.symbols.exportIcon = function (x, y, width, height) {
|
669
|
+
return crisp([
|
670
|
+
M, // the disk
|
671
|
+
x, y + width,
|
672
|
+
L,
|
673
|
+
x + width, y + height,
|
674
|
+
x + width, y + height * 0.8,
|
675
|
+
x, y + height * 0.8,
|
676
|
+
'Z',
|
677
|
+
M, // the arrow
|
678
|
+
x + width * 0.5, y + height * 0.8,
|
679
|
+
L,
|
680
|
+
x + width * 0.8, y + height * 0.4,
|
681
|
+
x + width * 0.4, y + height * 0.4,
|
682
|
+
x + width * 0.4, y,
|
683
|
+
x + width * 0.6, y,
|
684
|
+
x + width * 0.6, y + height * 0.4,
|
685
|
+
x + width * 0.2, y + height * 0.4,
|
686
|
+
'Z'
|
687
|
+
]);
|
688
|
+
};
|
689
|
+
// Create the print icon
|
690
|
+
HC.Renderer.prototype.symbols.printIcon = function (x, y, width, height) {
|
691
|
+
return crisp([
|
692
|
+
M, // the printer
|
693
|
+
x, y + height * 0.7,
|
694
|
+
L,
|
695
|
+
x + width, y + height * 0.7,
|
696
|
+
x + width, y + height * 0.4,
|
697
|
+
x, y + height * 0.4,
|
698
|
+
'Z',
|
699
|
+
M, // the upper sheet
|
700
|
+
x + width * 0.2, y + height * 0.4,
|
701
|
+
L,
|
702
|
+
x + width * 0.2, y,
|
703
|
+
x + width * 0.8, y,
|
704
|
+
x + width * 0.8, y + height * 0.4,
|
705
|
+
'Z',
|
706
|
+
M, // the lower sheet
|
707
|
+
x + width * 0.2, y + height * 0.7,
|
708
|
+
L,
|
709
|
+
x, y + height,
|
710
|
+
x + width, y + height,
|
711
|
+
x + width * 0.8, y + height * 0.7,
|
712
|
+
'Z'
|
713
|
+
]);
|
714
|
+
};
|
715
|
+
|
716
|
+
|
717
|
+
// Add the buttons on chart load
|
718
|
+
Chart.prototype.callbacks.push(function (chart) {
|
719
|
+
var n,
|
720
|
+
exportingOptions = chart.options.exporting,
|
721
|
+
buttons = exportingOptions.buttons;
|
722
|
+
|
723
|
+
if (exportingOptions.enabled !== false) {
|
724
|
+
|
725
|
+
for (n in buttons) {
|
726
|
+
chart.addButton(buttons[n]);
|
727
|
+
}
|
728
|
+
|
729
|
+
// Destroy the export elements at chart destroy
|
730
|
+
addEvent(chart, 'destroy', chart.destroyExport);
|
731
|
+
}
|
732
|
+
|
733
|
+
});
|
734
|
+
|
735
|
+
|
736
|
+
}());
|