~jstys-z/helioviewer.org/timeline

« back to all changes in this revision

Viewing changes to timeline/Highstock-1.3.10/exporting-server/java/highcharts-export/highcharts-export-convert/src/main/resources/phantomjs/highcharts-convert.js

  • Committer: Jeff Stys
  • Date: 2014-04-21 12:46:26 UTC
  • Revision ID: jstys@sesda3.com-20140421124626-2332pb2dyjc33jxi
Proof-of-concept version of Data Coverage Timeline using Highchart/Highstock javascript library.  Changes to getDataCoverage API in order to feed the necessary data to the Timeline

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * @license Highcharts JS v3.0.1 (2012-11-02)
 
3
 *
 
4
 * (c) 20013-2014
 
5
 *
 
6
 * Author: Gert Vaartjes
 
7
 *
 
8
 * License: www.highcharts.com/license
 
9
 *
 
10
 * version: 2.0.1
 
11
 */
 
12
 
 
13
/*jslint white: true */
 
14
/*global window, require, phantom, console, $, document, Image, Highcharts, clearTimeout, clearInterval, options, cb, globalOptions, dataOptions, customCode */
 
15
 
 
16
 
 
17
(function () {
 
18
        "use strict";
 
19
 
 
20
        var config = {
 
21
                        /* define locations of mandatory javascript files */
 
22
                        HIGHCHARTS: 'highstock.js',
 
23
                        HIGHCHARTS_MORE: 'highcharts-more.js',
 
24
                        HIGHCHARTS_DATA: 'data.js',
 
25
                        JQUERY: 'jquery.1.9.1.min.js',
 
26
                        TIMEOUT: 2000 /* 2 seconds timout for loading images */
 
27
                },
 
28
                mapCLArguments,
 
29
                render,
 
30
                startServer = false,
 
31
                args,
 
32
                pick,
 
33
                SVG_DOCTYPE = '<?xml version=\"1.0" standalone=\"no\"?><!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">',
 
34
                dpiCorrection = 1.4,
 
35
                system = require('system'),
 
36
                fs = require('fs'),
 
37
                serverMode = false;
 
38
 
 
39
        pick = function () {
 
40
                var args = arguments, i, arg, length = args.length;
 
41
                for (i = 0; i < length; i += 1) {
 
42
                        arg = args[i];
 
43
                        if (arg !== undefined && arg !== null && arg !== 'null' && arg != '0') {
 
44
                                return arg;
 
45
                        }
 
46
                }
 
47
        };
 
48
 
 
49
        mapCLArguments = function () {
 
50
                var map = {},
 
51
                        i,
 
52
                        key;
 
53
 
 
54
                if (system.args.length < 1) {
 
55
                        console.log('Commandline Usage: highcharts-convert.js -infile URL -outfile filename -scale 2.5 -width 300 -constr Chart -callback callback.js');
 
56
                        console.log(', or run PhantomJS as server: highcharts-convert.js -host 127.0.0.1 -port 1234');
 
57
                }
 
58
 
 
59
                for (i = 0; i < system.args.length; i += 1) {
 
60
                        if (system.args[i].charAt(0) === '-') {
 
61
                                key = system.args[i].substr(1, i.length);
 
62
                                if (key === 'infile' || key === 'callback' || key === 'dataoptions' || key === 'globaloptions' || key === 'customcode') {
 
63
                                        // get string from file
 
64
                                        try {
 
65
                                                map[key] = fs.read(system.args[i + 1]).replace(/^\s+/, '');
 
66
                                        } catch (e) {
 
67
                                                console.log('Error: cannot find file, ' + system.args[i + 1]);
 
68
                                                phantom.exit();
 
69
                                        }
 
70
                                } else {
 
71
                                        map[key] = system.args[i + 1];
 
72
                                }
 
73
                        }
 
74
                }
 
75
                return map;
 
76
        };
 
77
 
 
78
        render = function (params, exitCallback) {
 
79
 
 
80
                var page = require('webpage').create(),
 
81
                        messages = {},
 
82
                        scaleAndClipPage,
 
83
                        loadChart,
 
84
                        createChart,
 
85
                        input,
 
86
                        constr,
 
87
                        callback,
 
88
                        width,
 
89
                        output,
 
90
                        outType,
 
91
                        timer,
 
92
                        renderSVG,
 
93
                        convert,
 
94
                        exit,
 
95
                        interval;
 
96
 
 
97
                messages.imagesLoaded = 'Highcharts.images.loaded';
 
98
                messages.optionsParsed = 'Highcharts.options.parsed';
 
99
                messages.callbackParsed = 'Highcharts.cb.parsed';
 
100
                window.imagesLoaded = false;
 
101
                window.optionsParsed = false;
 
102
                window.callbackParsed = false;
 
103
 
 
104
                page.onConsoleMessage = function (msg) {
 
105
                        //console.log(msg);
 
106
 
 
107
                        /*
 
108
                         * Ugly hack, but only way to get messages out of the 'page.evaluate()'
 
109
                         * sandbox. If any, please contribute with improvements on this!
 
110
                         */
 
111
 
 
112
                        if (msg === messages.imagesLoaded) {
 
113
                                window.imagesLoaded = true;
 
114
                        }
 
115
                        /* more ugly hacks, to check options or callback are properly parsed */
 
116
                        if (msg === messages.optionsParsed) {
 
117
                                window.optionsParsed = true;
 
118
                        }
 
119
 
 
120
                        if (msg === messages.callbackParsed) {
 
121
                                window.callbackParsed = true;
 
122
                        }
 
123
                };
 
124
 
 
125
                page.onAlert = function (msg) {
 
126
                        console.log(msg);
 
127
                };
 
128
 
 
129
                /* scale and clip the page */
 
130
                scaleAndClipPage = function (svg) {
 
131
                        /*      param: svg: The scg configuration object
 
132
                        */
 
133
 
 
134
                        var zoom = 1,
 
135
                                pageWidth = pick(params.width, svg.width),
 
136
                                clipwidth,
 
137
                                clipheight;
 
138
 
 
139
                        if (parseInt(pageWidth, 10) == pageWidth) {
 
140
                                zoom = pageWidth / svg.width;
 
141
                        }
 
142
 
 
143
                        /* set this line when scale factor has a higher precedence
 
144
                        scale has precedence : page.zoomFactor = params.scale  ? zoom * params.scale : zoom;*/
 
145
 
 
146
                        /* params.width has a higher precedence over scaling, to not break backover compatibility */
 
147
                        page.zoomFactor = params.scale && params.width == undefined ? zoom * params.scale : zoom;
 
148
 
 
149
                        clipwidth = svg.width * page.zoomFactor;
 
150
                        clipheight = svg.height * page.zoomFactor;
 
151
 
 
152
                        /* define the clip-rectangle */
 
153
                        /* ignored for PDF, see https://github.com/ariya/phantomjs/issues/10465 */
 
154
                        page.clipRect = {
 
155
                                top: 0,
 
156
                                left: 0,
 
157
                                width: clipwidth,
 
158
                                height: clipheight
 
159
                        };
 
160
 
 
161
                        /* for pdf we need a bit more paperspace in some cases for example (w:600,h:400), I don't know why.*/
 
162
                        if (outType === 'pdf') {
 
163
                                // changed to a multiplication with 1.333 to correct systems dpi setting
 
164
                                clipwidth = clipwidth * dpiCorrection;
 
165
                                clipheight = clipheight * dpiCorrection;
 
166
                                // redefine the viewport
 
167
                                page.viewportSize = { width: clipwidth, height: clipheight};
 
168
                                // make the paper a bit larger than the viewport
 
169
                                page.paperSize = { width: clipwidth + 2 , height: clipheight + 2 };
 
170
                        }
 
171
                };
 
172
 
 
173
                exit = function (result) {
 
174
                        if (serverMode) {
 
175
                                //Calling page.close(), may stop the increasing heap allocation
 
176
                                page.close();
 
177
                        }
 
178
                        exitCallback(result);
 
179
                };
 
180
 
 
181
                convert = function (svg) {
 
182
                        var base64;
 
183
                        scaleAndClipPage(svg);
 
184
                        if (outType === 'pdf' || output !== undefined || !serverMode) {
 
185
                                if (output === undefined) {
 
186
                                        // in case of pdf files
 
187
                                        output = config.tmpDir + '/chart.' + outType;
 
188
                                }
 
189
                                page.render(output);
 
190
                                exit(output);
 
191
                        } else {
 
192
                                base64 = page.renderBase64(outType);
 
193
                                exit(base64);
 
194
                        }
 
195
                };
 
196
 
 
197
                renderSVG = function (svg) {
 
198
                        var svgFile;
 
199
                        // From this point we have loaded/or created a SVG
 
200
                        try {
 
201
                                if (outType.toLowerCase() === 'svg') {
 
202
                                        // output svg
 
203
                                        svg = svg.html.replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ').replace(/ href=/g, ' xlink:href=').replace(/<\/svg>.*?$/, '</svg>');
 
204
                                        // add xml doc type
 
205
                                        svg = SVG_DOCTYPE + svg;
 
206
 
 
207
                                        if (output !== undefined) {
 
208
                                                // write the file
 
209
                                                svgFile = fs.open(output, "w");
 
210
                                                svgFile.write(svg);
 
211
                                                svgFile.close();
 
212
                                                exit(output);
 
213
                                        } else {
 
214
                                                // return the svg as a string
 
215
                                                exit(svg);
 
216
                                        }
 
217
 
 
218
                                } else {
 
219
                                        // output binary images or pdf
 
220
                                        if (!window.imagesLoaded) {
 
221
                                                // render with interval, waiting for all images loaded
 
222
                                                interval = window.setInterval(function () {
 
223
                                                        console.log('waiting');
 
224
                                                        if (window.imagesLoaded) {
 
225
                                                                clearTimeout(timer);
 
226
                                                                clearInterval(interval);
 
227
                                                                convert(svg);
 
228
                                                        }
 
229
                                                }, 50);
 
230
 
 
231
                                                // we have a 3 second timeframe..
 
232
                                                timer = window.setTimeout(function () {
 
233
                                                        clearInterval(interval);
 
234
                                                        exitCallback('ERROR: While rendering, there\'s is a timeout reached');
 
235
                                                }, config.TIMEOUT);
 
236
                                        } else {
 
237
                                                // images are loaded, render rightaway
 
238
                                                convert(svg);
 
239
                                        }
 
240
                                }
 
241
                        } catch (e) {
 
242
                                console.log('ERROR: While rendering, ' + e);
 
243
                        }
 
244
                };
 
245
 
 
246
                loadChart = function (input, outputType, messages) {
 
247
                        var nodeIter, nodes, elem, opacity, counter, svgElem;
 
248
 
 
249
                        document.body.style.margin = '0px';
 
250
                        document.body.innerHTML = input;
 
251
 
 
252
                        function decrementImgCounter() {
 
253
                                counter -= 1;
 
254
                                if (counter < 1) {
 
255
                                        console.log(messages.imagesLoaded);
 
256
                                }
 
257
                        }
 
258
 
 
259
                        function loadImages() {
 
260
                                var images = document.getElementsByTagName('image'), i, img;
 
261
 
 
262
                                if (images.length > 0) {
 
263
 
 
264
                                        counter = images.length;
 
265
 
 
266
                                        for (i = 0; i < images.length; i += 1) {
 
267
                                                img = new Image();
 
268
                                                /* onload decremnts the counter, also when error (perhaps 404), then we wont wait for this image to be loaded */
 
269
                                                img.onload = img.onerror = decrementImgCounter;
 
270
                                                /* force loading of images by setting the src attr.*/
 
271
                                                img.src = images[i].href.baseVal;
 
272
                                        }
 
273
                                } else {
 
274
                                        // no images set property to:imagesLoaded = true
 
275
                                        console.log(messages.imagesLoaded);
 
276
                                }
 
277
                        }
 
278
 
 
279
                        if (outputType === 'jpeg') {
 
280
                                document.body.style.backgroundColor = 'white';
 
281
                        }
 
282
 
 
283
 
 
284
                        nodes = document.querySelectorAll('*[stroke-opacity]');
 
285
 
 
286
                        for (nodeIter = 0; nodeIter < nodes.length; nodeIter += 1) {
 
287
                                elem = nodes[nodeIter];
 
288
                                opacity = elem.getAttribute('stroke-opacity');
 
289
                                elem.removeAttribute('stroke-opacity');
 
290
                                elem.setAttribute('opacity', opacity);
 
291
                        }
 
292
 
 
293
                        // ensure all image are loaded
 
294
                        loadImages();
 
295
 
 
296
                        svgElem = document.getElementsByTagName('svg')[0];
 
297
 
 
298
                        return {
 
299
                            html: document.body.innerHTML,
 
300
                            width: svgElem.getAttribute("width"),
 
301
                            height: svgElem.getAttribute("height")
 
302
                        };
 
303
                };
 
304
 
 
305
                createChart = function (width, constr, input, globalOptionsArg, dataOptionsArg, customCodeArg, outputType, callback, messages) {
 
306
 
 
307
                        var $container, chart, nodes, nodeIter, elem, opacity, counter;
 
308
 
 
309
                        // dynamic script insertion
 
310
                        function loadScript(varStr, codeStr) {
 
311
                                var $script = $('<script>').attr('type', 'text/javascript');
 
312
                                $script.html('var ' + varStr + ' = ' + codeStr);
 
313
                                document.getElementsByTagName("head")[0].appendChild($script[0]);
 
314
                                if (window[varStr] !== undefined) {
 
315
                                        console.log('Highcharts.' + varStr + '.parsed');
 
316
                                }
 
317
                        }
 
318
 
 
319
                        function decrementImgCounter() {
 
320
                                counter -= 1;
 
321
                                if (counter < 1) {
 
322
                                        console.log(messages.imagesLoaded);
 
323
                                }
 
324
                        }
 
325
 
 
326
                        function loadImages() {
 
327
                                var $images = $('svg image'), i, img;
 
328
 
 
329
                                if ($images.length > 0) {
 
330
 
 
331
                                        counter = $images.length;
 
332
 
 
333
                                        for (i = 0; i < $images.length; i += 1) {
 
334
                                                img = new Image();
 
335
                                                /* onload decremnts the counter, also when error (perhaps 404), then we wont wait for this image to be loaded */
 
336
                                                img.onload = img.onerror = decrementImgCounter;
 
337
                                                /* force loading of images by setting the src attr.*/
 
338
                                                img.src = $images[i].getAttribute('href');
 
339
                                        }
 
340
                                } else {
 
341
                                        // no images set property to:imagesLoaded = true
 
342
                                        console.log(messages.imagesLoaded);
 
343
                                }
 
344
                        }
 
345
 
 
346
                        function parseData(completeHandler, chartOptions, dataConfig) {
 
347
                                try {
 
348
                                        dataConfig.complete = completeHandler;
 
349
                                        Highcharts.data(dataConfig, chartOptions);
 
350
                                } catch (error) {
 
351
                                        completeHandler(undefined);
 
352
                                }
 
353
                        }
 
354
 
 
355
                        if (input !== 'undefined') {
 
356
                                loadScript('options', input);
 
357
                        }
 
358
 
 
359
                        if (callback !== 'undefined') {
 
360
                                loadScript('cb', callback);
 
361
                        }
 
362
 
 
363
                        if (globalOptionsArg !== 'undefined') {
 
364
                                loadScript('globalOptions', globalOptionsArg);
 
365
                        }
 
366
 
 
367
                        if (dataOptionsArg !== 'undefined') {
 
368
                                loadScript('dataOptions', dataOptionsArg);
 
369
                        }
 
370
 
 
371
                        if (customCodeArg !== 'undefined') {
 
372
                                loadScript('customCode', customCodeArg);
 
373
                        }
 
374
 
 
375
                        $(document.body).css('margin', '0px');
 
376
 
 
377
                        if (outputType === 'jpeg') {
 
378
                                $(document.body).css('backgroundColor', 'white');
 
379
                        }
 
380
 
 
381
                        $container = $('<div>').appendTo(document.body);
 
382
                        $container.attr('id', 'container');
 
383
 
 
384
                        // disable animations
 
385
                        Highcharts.SVGRenderer.prototype.Element.prototype.animate = Highcharts.SVGRenderer.prototype.Element.prototype.attr;
 
386
 
 
387
                        if (!options.chart) {
 
388
                                options.chart = {};
 
389
                        }
 
390
 
 
391
                        options.chart.renderTo = $container[0];
 
392
 
 
393
                        // check if witdh is set. Order of precedence:
 
394
                        // args.width, options.chart.width and 600px
 
395
 
 
396
                        // OLD. options.chart.width = width || options.chart.width || 600;
 
397
                        // Notice we don't use commandline parameter width here. Commandline parameter width is used for scaling.
 
398
 
 
399
                        options.chart.width = (options.exporting && options.exporting.sourceWidth) || options.chart.width || 600;
 
400
                        options.chart.height = (options.exporting && options.exporting.sourceHeight) || options.chart.height || 400;
 
401
 
 
402
                        // Load globalOptions
 
403
                        if (globalOptions) {
 
404
                                Highcharts.setOptions(globalOptions);
 
405
                        }
 
406
 
 
407
                        // Load data
 
408
                        if (dataOptions) {
 
409
                                parseData(function completeHandler(opts) {
 
410
                                        // Merge series configs
 
411
                                        if (options.series) {
 
412
                                                Highcharts.each(options.series, function (series, i) {
 
413
                                                        options.series[i] = Highcharts.merge(series, opts.series[i]);
 
414
                                                });
 
415
                                        }
 
416
 
 
417
                                        var mergedOptions = Highcharts.merge(opts, options);
 
418
 
 
419
                                        // Run customCode
 
420
                                        if (customCode) {
 
421
                                                customCode(mergedOptions);
 
422
                                        }
 
423
 
 
424
                                        chart = new Highcharts[constr](mergedOptions, cb);
 
425
 
 
426
                                        // ensure images are all loaded
 
427
                                        loadImages();
 
428
                                }, options, dataOptions);
 
429
                        } else {
 
430
                                chart = new Highcharts[constr](options, cb);
 
431
 
 
432
                                // ensure images are all loaded
 
433
                                loadImages();
 
434
                        }
 
435
 
 
436
                        /* remove stroke-opacity paths, used by mouse-trackers, they turn up as
 
437
                        *  as fully opaque in the PDF
 
438
                        */
 
439
                        nodes = document.querySelectorAll('*[stroke-opacity]');
 
440
 
 
441
                        for (nodeIter = 0; nodeIter < nodes.length; nodeIter += 1) {
 
442
                                elem = nodes[nodeIter];
 
443
                                opacity = elem.getAttribute('stroke-opacity');
 
444
                                elem.removeAttribute('stroke-opacity');
 
445
                                elem.setAttribute('opacity', opacity);
 
446
                        }
 
447
 
 
448
                        return {
 
449
                                //html: $container[0].firstChild.innerHTML,
 
450
                                html: $('div.highcharts-container')[0].innerHTML,
 
451
                                width: chart.chartWidth,
 
452
                                height: chart.chartHeight
 
453
                        };
 
454
                };
 
455
 
 
456
                if (params.length < 1) {
 
457
                        exit("Error: Insuficient parameters");
 
458
                } else {
 
459
                        input = params.infile;
 
460
                        output = params.outfile;
 
461
 
 
462
                        if (output !== undefined) {
 
463
                                outType = pick(output.split('.').pop(),'png');
 
464
                        } else {
 
465
                                outType = pick(params.type,'png');
 
466
                        }
 
467
 
 
468
                        constr = pick(params.constr, 'Chart');
 
469
                        callback = params.callback;
 
470
                        width = params.width;
 
471
 
 
472
                        if (input === undefined || input.length === 0) {
 
473
                                exit('Error: Insuficient or wrong parameters for rendering');
 
474
                        }
 
475
 
 
476
                        page.open('about:blank', function (status) {
 
477
                                var svg,
 
478
                                        globalOptions = params.globaloptions,
 
479
                                        dataOptions = params.dataoptions,
 
480
                                        customCode = 'function customCode(options) {\n' + params.customcode + '}\n';
 
481
 
 
482
                                /* Decide if we have to generate a svg first before rendering */
 
483
                                if (input.substring(0, 4).toLowerCase() === "<svg" || input.substring(0, 5).toLowerCase() === "<?xml"
 
484
                                        || input.substring(0, 9).toLowerCase() === "<!doctype") {
 
485
                                        //render page directly from svg file
 
486
                                        svg = page.evaluate(loadChart, input, outType, messages);
 
487
                                        page.viewportSize = { width: svg.width, height: svg.height };
 
488
                                        renderSVG(svg);
 
489
                                } else {
 
490
                                        // We have a js file, let highcharts create the chart first and grab the svg
 
491
 
 
492
                                        // load necessary libraries
 
493
                                        page.injectJs(config.JQUERY);
 
494
                                        page.injectJs(config.HIGHCHARTS);
 
495
                                        page.injectJs(config.HIGHCHARTS_MORE);
 
496
                                        page.injectJs(config.HIGHCHARTS_DATA);
 
497
 
 
498
                                        // load chart in page and return svg height and width
 
499
                                        svg = page.evaluate(createChart, width, constr, input, globalOptions, dataOptions, customCode, outType, callback, messages);
 
500
 
 
501
                                        if (!window.optionsParsed) {
 
502
                                                exit('ERROR: the options variable was not available, contains the infile an syntax error? see' + input);
 
503
                                        }
 
504
 
 
505
                                        if (callback !== undefined && !window.callbackParsed) {
 
506
                                                exit('ERROR: the callback variable was not available, contains the callbackfile an syntax error? see' + callback);
 
507
                                        }
 
508
                                        renderSVG(svg);
 
509
                                }
 
510
                        });
 
511
                }
 
512
        };
 
513
 
 
514
        startServer = function (host, port) {
 
515
                var server = require('webserver').create();
 
516
 
 
517
                server.listen(host + ':' + port,
 
518
                        function (request, response) {
 
519
                                var jsonStr = request.post,
 
520
                                        params,
 
521
                                        msg;
 
522
                                try {
 
523
                                        params = JSON.parse(jsonStr);
 
524
                                        if (params.status) {
 
525
                                                // for server health validation
 
526
                                                response.statusCode = 200;
 
527
                                                response.write('OK');
 
528
                                                response.close();
 
529
                                        } else {
 
530
                                                render(params, function (result) {
 
531
                                                        response.statusCode = 200;
 
532
                                                        response.write(result);
 
533
                                                        response.close();
 
534
                                                });
 
535
                                        }
 
536
                                } catch (e) {
 
537
                                        msg = "Failed rendering: \n" + e;
 
538
                                        response.statusCode = 500;
 
539
                                        response.setHeader('Content-Type', 'text/plain');
 
540
                                        response.setHeader('Content-Length', msg.length);
 
541
                                        response.write(msg);
 
542
                                        response.close();
 
543
                                }
 
544
                        }); // end server.listen
 
545
 
 
546
                // switch to serverMode
 
547
                serverMode = true;
 
548
 
 
549
                console.log("OK, PhantomJS is ready.");
 
550
        };
 
551
 
 
552
        args = mapCLArguments();
 
553
 
 
554
        // set tmpDir, for output temporary files.
 
555
        if (args.tmpdir === undefined) {
 
556
                config.tmpDir = fs.workingDirectory + '/tmp';
 
557
        } else {
 
558
                config.tmpDir = args.tmpdir;
 
559
        }
 
560
 
 
561
        // exists tmpDir and is it writable?
 
562
        if (!fs.exists(config.tmpDir)) {
 
563
                try{
 
564
                        fs.makeDirectory(config.tmpDir);
 
565
                } catch (e) {
 
566
                        console.log('ERROR: Cannot make temp directory');
 
567
                }
 
568
        }
 
569
 
 
570
 
 
571
        if (args.host !== undefined && args.port !== undefined) {
 
572
                startServer(args.host, args.port);
 
573
        } else {
 
574
                // presume commandline usage
 
575
                render(args, function (msg) {
 
576
                        console.log(msg);
 
577
                        phantom.exit();
 
578
                });
 
579
        }
 
580
}());