/* **********************************************
mjs_plot
CC MATTHEW SARSBY, 2014,Some Rights Reserved.
You are free to use, copy, and modify this
software in non-profit applications.
Modifications must retain this header, and a file
name in the format mjs_plot_*.js
mjs_plot is distributed with hope it will be useful
but WITHOUT ANY WARRANTY.
TODO:
add more functions:
+ annotations system (big!)
- arrows
- lines
- text
- annotation storage inside graphics_style
- annotation selector, move, edit.
- kernals graph
- improve the interpolation algorythem
- add fitting errors
- improve the smooth function
+ extend graph.data type to include x and y error options
+ include x,y,x_error_low,x_error_high,y_error_low,y_error_high
- then expand all the function to correctally manage the propogation of errors. :/
- start using a Series object to hold each series. this might make things cleaner?
- add support for a series to use left or right axies top/bottom too
- put arrows in the lines menu to show what axis that line is on
- expand units_to_pixels and pixles_to_units to include which one (left/right, top/bottom)
- add axies menu
- which axis is active (left, right, both; top, bottom, both)
- put log-lin mode changes here
- put changable time modes here
- expand line menu to have per-line pickable line modes /colour.
- have functions work an any series, individually. not shore how this will work.
- add remove_backgound function that seaks the longest straightline removeing the bad points.
- fix the large year spacing issure to quantise correctally.
Hold ups:
None
Known Bugs:
- time in 10s of years doesn't align correctlly to decade boundaries.
- on mobile devices if the viewport has a zoom != 1 the position infomaition is wrong.
- the swap axis button changes the axis modes, but this isn't reset when the reset button is pushed.
Done:
2_2_6 - added y-c and x-c functions
- history began
2_2_7 - fixed transforms making nans (dam logs) by implementing transforms.clean() that cleans data.
- added a curved line to log/lin and log/log graph that shows what a line in linear space looks like.
- hooked onto mouseout event so that when the mouse leaves the graph the buttons on the top/bottom don't still show.
- sum function now works in a basic way.
- added graph.errors = [] list of strings that prints before getting the scale.
- added error messages throughout.
- imporved handling of manuly picked negative scales when chanig to log mode
- fixed errors trigering other errors. so now only the more useful errors are shown
- added not_drawn_something and no_data flags
- added font checks.
- added version number and me to the extended info page.
2_2_8 - changed minwidth and maxwidth to guideWidth.
- implemented polynomial fits and a fits menu.
- added data reader that sticks to the points
- implemented smooth function. uses weighting so that it works on unevenly spaced data. Still not great.
- implemented 'mid' line mode. Like zig and zag but does the vertical at the mid x-point
- improved the find_scale function. much simpler!
- fixed the major/minor ticks regression.
- refactored this.graphics_style inside graph.plot() to gs.
- refactored context to ctx
- imporved line mode changing code (smaller, faster, eayser to read)
- imporved 'hist' line mode
- added 'mid' and 'hist' to the caption sketch box
- moved title and subtitle to draw ontop of the data.
- added transforms text list so you can see the transforms stack.
- fixed an extra label being drawn at the top of the scale when using narrow log mode.
- added fittings menu and fittings!
2_2_9 - split guideWidth into guideWidthx and guideWidthy
- changed over to load_gs and save_gs function. - much smaller cookies!
- added options pane. user can now edit everything in graphics style, even though they ofern shouldn't
- implemented graph.default_graphics_style as a way to change the defaults but still allow the user to make there own changes. uses graphics style peroperty called .modified that is false on fresh graphics styles.
- zoom out by right click in zoom mode.
- overridden the right click context menu on the canvas (required for the zooming out)
- unified label drawing code. (about 100lines less!)
- added basic ability to draw grids under the data
- fixed the end of the mouse zoom rectangle ending on a button and activating the button.
- added graph.ui object. and ui.size as the size of the ui in pixels.
- graph.ui is set by what will fit on screen, no more buttons going away!
- unified find_limits. also reducing number of lines.
- can use local storage if it is avaliable. defaults back to cookie storage.
- export menu: low res png, high 3x png, data tabbed, data code style, data cvs, png for figure is a 1000px*750px image scaled up by 2.5x.
- fixed inconvienence with the y axis scale labels covering up the y axis label
2_2_10 - exported graph now use transparent backgrounds - better for posters
- text uses the text_center method.
2_2_10b-removed cookie based storage, reverted back to saving full JSON'd graphics_style object.
- added basic touch support detections and forwarding touch events to act like mouse.
- basic touch screen support
- imporved options menu layout to fit the screen better and simpler code.
- imporved the visibility of the caption text by using strokeText() this only works if the background isn't transparent.
- fixed graph.graphics_style.hidden_lines not being populated after a graphics_style reset.
- imporved visability of fit text by using strokeText
- fixed issue with the scale stepping changing if 0 was on the edge. (change between -0.0 and 0.0) causing the stepping flag to jump randomly depending on floating point rounding.
- added mouse mode 'mouse' for moving objects.
- added captions_position object to graphics_style
- added fit_text_position object to graphics_style
- can use mouse mode to move the position of the fit text and the captions
- captions have correct alignment depending on which quadrant they are in.
2_2_10c- changed ctx.lineJoin to rounded from miter. looks better.
- imporved touch support. including zoom out button. (no right click on a touch screen!)
- view under finger on touch screens.
- added mobile support as well as touch support.
- added flag DEBUGG_FORCE_TOUCH to help testing.
- fixed regression in placement of transforms list.
2_2_11 - fixed bug where the user pics an manual man/min that is off the range of the data and then picks an autoscale direction that is away from the data causing max to be lower than min (or viser versa).
- moved fitting function into fits object, unified fitting code.
- fixed bug with fittings on log scale drawing odd lines between -ve line points
- added y=c fitting (trivial)
- added option for fits to extrapolate rather than interpolate
- added graphics_style.fit_color boolean, no ui avaliable yet.
- overhaul of the infomation button. now shows other graph infomation
2_2_11b- added ax^b, a*blnx and abx^c fits.
- added remove-fit
- added remove-outliers
- added keep only outliers
- added fit option 'old' to show the old fit and not make a new one (usefull for understanding the new functions)
- added graph.old_fit to hold the old fit.
- added gauss dirstobution
2_2_11b2 - added subtitle2 for a second line of subtitles
- added jitter funciton
- the transforms list drawn position now depends on if the subtitles are drawn or not. (looks better)
- improved the drawing of histograms. the bars now only go to zero.
- fixed bug with intergrate transform.
- added spacing function. 1/spacing is usefull for finding the data dencity
- added touch only on screen zoom out button, and auto zoom button.
- Fixed problum with the menu text now being drawn on mobile devices.
- Changed default fit-text posision.
- fixed bug with number_quote causing crashes when given NaNs
2_2_11b3 - quickfix to change MJSprecision
2_2_12 - added JSON export [[][]]
- added matlab style data export. [] with spaces between the numbers.
2_2_13 - changed -mx+c button to be a user selectable line by drawing. was an automatic line, but that is now acheviable by picking a linear fit and then subtract fit.
2_2_14 - added time axis
- added functions to print a time at different precisions
- added methods to find correct time axis
- added transofrms to convert time to numbers
- added time menu in functions menu.
- modified code to use get_axis_string so that time can be provided in the measure, x-c,y-c tools.
- added website to info button
2_2_15 - changed default apperence, font to "sans-serif", white->#111111, dark->#eeeeee.
- added markers on mouse-zoom while dragging to make it easyer to see where the zoom is going.
- pinch to zoom on mobile works in drag mode.
- ping zoom scale has min 0.1x and max of 10x
- reading off graphs is now easyer as the labels have stroked text
- data reader mode has stroked text for easyer reading.
2_2_16 - vastly improved the axis labels stepping charastic using font-metrics on the left
and right sides. meaning slim fonts jump to stepping less.
- improved performace after dragging
- improved performace in mjs_plot() if not dragging, no need to save a drag image.
- added code to check for visable sequences of data, and then to only draw the data that appears
on screen. Huge improvements in performance when zoomed in.
- then only do that if the view is zoomed in, as finding sequences takes time.
- fixed regression in the options menu where the background wasn't transparent
- added show_grid option to graphic_style, grid button in lower left, grid drawing
- added option for middle ground colour. but this is not used anywhere yet.
- measure mode on mobile devices saves the gradient and measurments after the user lifted one finger.
- modified date printing behavour.
- added the 5day interval for timemode axies. the jump from 2d to 7d was too much.
- added fullscreen button in lower right corner. This kills the rest of the page to put the graph full size and requensts
that the browser goes full screen too.
2_2_17 - added shim function between mouse_move_event and the event hander
so that if the function hasn't ended it won't try to call it again
- added fps counter and DEBUGG_FPS global flag
- reverted graph position overlay to be near the pointer, with
position caped to be on screen. This fixes a performance regression.
- added custom function to functions.
- function parser can now use the "now" variable. useful if working with time data.
- line menu has add function line button and keeps track of fuction lines
- cleaned up code when drawing simple line on the graph.
- modified the code for datetime printing. still trying to get this right.
0_2_18 - added mode menu in top right
- removed dot_size and circle_size
- added symbol_size as replacement
- removed feature: draw label over the graph.
- added symbols cross and x
- added captions menu
- added captions_display and show_captions to graphics style.
BREAKING CHANGES MADE rename 0_2_13 to 0_3_1
0_3_1 - fixed fonts changing size in on_mouse_over
- added normalisation for fits when using a time axis. Huge improvement (now usable)
- preliminary support for poltting error bars.
new allowed formats:
[x,y] // existing
[x,y,yerror] //using yerrors
[x,y,yerror,xerror] //using x and y errors.
//note errors are not accounted for in transformations. yet.
support for errors should be considered unstable.
- changed namespace. now using a javascirpt module pattern.
use mjs_plot.new_graph(name,canvas)
mjs_plot.convert_time_strings(strings)
mjs_plot.get_graph_style()
all the helper funtioncs are hidden away from the global name space.
added "use strict"; to many functions to avoid pollution.
0_3_2 - added area consideration to gauss fitting. much more usefull now.
- fixed gauss fit area being upside down for right-to-left data
- fixed the mid variable not beging declaired.
- added mean_normalise function y' = (y-mean(y))/sigma(y)
- changed the size of the buttons to scale with the size of the tick text rather than the size of the ticks.
- the mouse move event text on mobile devices is at the edge rather than near the finger.
- removed limitation of 1e-13 being the smallest plottable number. now it is 1e-203 ish.
- modified/fixed time scale behavour when working in different time zones
- time does scaling to the nearest start of the unit. i.e. going up in every 2h is 0,2,4,6,8 etc, not 1,3,5,7.
- improved tick labels for time. check if the highprecision is needed. Results in shorter strings with no losses.
0_3_3svg - added svg output!
- large and small figure sizes
- drawing improvements (things spotted when drawing svgs
0_3_4 - changed website to github.io
- improved right-click zoom out method. Now uses the guidewidth method.
- removed the "there is no x/y data to plot" error from popping up, as users draw funciton lines without any data on screen.
- fixed svg opacity.
- added svg font handling in fillText
- fixed bug in find_limits where there are is no data and the user picks a negative low point for a log scale.
- removed the + and - buttons from the top row, this was confusing new users.
- replaced it with a 'graph' menu. from which users can set the title and other strings.
- added new options to hide the transform text.
this means users can clean up the axis titles to what they want before printing.
- improved HTML embedding export.
- improved text to screen. Rather than dumping all over the
it now places a textarea with the contents
and a button to go back.
- added robustness to users zooming way out trying to break things. caps at +-1e250 and 1e-250 on log mode.
- time now can have a scale in 1000s of years.
0_3_5 - imporved the mouse_out functanalitty.
- improved the options overlay mouse mode.
- added x_axis_title y_axis_title x_axis_prefix x_axis_postfix y_axis_prefix y_axis_postfix to graphics style
- added x_label_mode y_label_mode to graphics style
- added x and y menues.
- added functanality for pre and postfixes on axies numbers.
- added functanality for different number writing methods, infix postfix and scientific form
- fixed bug in drawEclips. This was introduced in the namespace cleanup.
- changed html embed to html sharing. Gives a single link to a full screen graph.
this requires a know domain to hold a loader program.
- embed LZstring.min.js for compression of the strings.
- added splash screen to be draw when a new graph is made.
- apperentally my "use strict" was in the wrong palce and wasn't checking globals. This is fixed and
then a bunch of global fixes followed.
0_3_6 - imporved axis lable layout.
- imporved string rendering of linear fits
- removed options menu. replaced with style menu
- added font menu. Finds fonts avaliable on the system using MJSFontList.
- fixed bug with minor ticks being overdrawn. Only visable on SVG output.
- added groups to the SVG context. This means each line is grouped for easyer editing.
- added clipPath to SVG output. this prevents the lines going outside the box.
- added maths number display mode, it uses unicode characters for 10x^3 etc..
- put in method to find precision on log axis.
- fixed minor typos
- imporved mjs_precision output
- added and using getFloat. a helper function to get a number from the user. Does better checking.
0_3_7 - touch screens now use a small block in the center in the under-the-finger view
- the grid uses color_mg rather than transparancy.
- added per-line colours
- added per-line symbol and line modes.
- gs.symbol_modes gs.line_modes and gs.line_colours
0_3_8 - improved speed of the clean transform by up to 22x. this is used in trim, cut and filter, and many other transforms to remove non-finite values
- added view last n transform to look at only the last however-many points. usefull for rolling data.
- added custom filter transform that will evaluate an expression and filter from that. Very useful for looking at only that last hour (rolling) of data from logs.
- added extra-precision option to the captions menu, so that the values of the captions can be expressed more precisly if the user wants it.
0_3_9 - fixed bug with graph reset. the colours now are reset correctlly.
- fixed bug with data display not 'bouncing' off the right side of the screen.
- fixed bug if mouseMove is called befor mjs_plot trying to draw an image that isn't rendered yet.
- fixed UI bug where user trys to set an empty string ( a falsy type) and then it is ignored. Added getString function which garrentees a string to return.
0_3_10 - added svg+png export type, the background data is rendered as a high resolution PNG, while the text and ticks are SVG
- added us for svg+png in the export menu, this does need an overhaul.
- fixed bug with error bars not being handled corrently while being cleaned.
0_3_11 - added a basic annotation system/marker system.
users can now go to the marker menu at the top to add/remove markers - lines, text and boxes.
for each of lines, boxes and arrows, the user can pick to add text to it,
the text when added with a shape has an offset defined by pixels on the screen,
rather than by graph units, this means better scaling when zooming in.
to get text drawn on the other side of the object, draw the ojects the other way around.
0_3_12 - added accumulate funciton, this creates an acumulate count from y.
- speed improvements by reducing the number of calls to mjs_plot(). this should help working with (large > 100k) data.
*********************************************** */
mjs_plot = (function () {
var MJS_PLOT_VERSION = '0_3_12';
var MJS_PLOT_AUTOR = 'MJS';
var MJS_PLOT_DATE = '2016';
var MJS_PLOT_WEBSITE = 'http://generalsarsby.github.io/mjs_plot/';
var MJS_PLOT_LINK_LOADER = "http://generalsarsby.github.io/mjs_plot/load.html#";
var DEBUGG_FORCE_TOUCH = false;
var DEBUGG_FPS = false;
var svgNS = "http://www.w3.org/2000/svg";
var vals = [1,2,5]; // only go up in steps of 1s 2s or 5s.
var few_minor_ticks = [2,2,5];
var many_minor_ticks = [5,4,5];
//initial color table. hand picked for up to 7 colours.
//after 7 they are generated automatically.
var color_table = [['#aa3939'],
['#aa3939','#383276'],
['#aa3939','#236467','#7b9f35'],
['#aa3939','#aa6d39','#236467','#2d882d'],
['#aa3939','#aa6d39','#236467','#2d882d','#304473'],
['#aa3939','#aa6d39','#236467','#2d882d','#304473','#ac873c']];
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
} else {
function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return 'rgb(' + Math.round(r * 255) +','+ Math.round(g * 255) +','+ Math.round(b * 255) + ')';
}
function get_color(i,j){
//j is number of colors
//i is current color(0 starting). i.e get color 1 of 7
//i 0-->(j-1)
if (j 0)
|| (navigator.msMaxTouchPoints > 0));
return ((can_touch && isMobile()) || DEBUGG_FORCE_TOUCH );
}
var mouse_down = false;
var start_x = 0;
var end_x = 0;
var full_screen_graph;
//fix f****ng google chrome and ie.
try {
//might not even have log10.
Math.log10(100);
}
catch(e) {
//have to add it!
Math.log10 = function(x){
return Math.log(x) / Math.LN10;
};
}
var log_vals = [[1],
[1,3],
[1,2,4],
[1,2,3,6],
[1,2,3,4,6],
[1,2,3,4,6,8],
[1,1.5,2,3,4,6,8],
[1,1.5,2,2.5,3,4,6,8],
[1,1.5,2,2.5,3,4,6,7,8],
[1,1.2,1.5,2,2.5,3,4,5,6,7,9],
[1,1.2,1.5,2,2.5,3,4,5,6,7,8,9],
[1,1.2,1.4,1.6,1.8,2,2.5,3,4,5,6,7,8,9],
[1,1.1,1.2,1.3,1.4,1.6,1.8,2,2.2,2.4,2.6,2.8,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,8.5,9,9.5],
[1,1.1,1.2,1.3,1.4,1.6,1.8,2,2.2,2.4,2.6,2.8,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,8.5,9,9.5],
[1,1.05,1.1,1.15,1.2,1.25,1.3,1.4,1.6,1.8,2,2.2,2.4,2.6,2.8,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,8.5,9,9.5],
[1,1.05,1.1,1.15,1.2,1.25,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,2.2,2.4,2.6,2.8,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,8.5,9,9.5],
[1,1.05,1.1,1.15,1.2,1.25,1.3,1.35,1.4,1.45,1.5,1.55,1.6,1.65,1.7,1.75,1.8,1.85,1.9,1.95,2,2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8,2.9,3,3.2,3.4,3.6,3.8,4,4.2,4.4,4.6,4.8,5,5.5,6,6.5,7,7.5,8,8.5,9,9.5],
];
var log_vals_ticks = [[1,3],
[1,2,3,4,5,6,7,8,9],
[1,2,3,4,5,6,7,8,9],
[1,2,3,4,5,6,7,8,9],
[1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,3,4,5,6,7,8,9],
[1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,2.5,3,4,5,6,7,8,9],
[1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7,8,9],
[1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,2.25,2.75,3,3.5,4,4.5,5,5.5,6,6.5,7,8,9],
[1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,2.25,2.75,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,9],
[1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,2.2,2.4,2.6,2.8,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,9],
[1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,2.2,2.4,2.6,2.8,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,9]
]; //minor tick mode changes after this many.
//There is no ruleset to generate the allowed time intervals. so I've just written them out. all in milliseconds.
// all the miliseeconds up to the seconds - a base 60 system minuilts - a base 60 system hours - a base 12/24system days
// 500 ms 1s 2s 5s 10s 20s 30s 1m 2m 5m 10m 15m 20m 30m 1h 2h 3h 6h 12h 1d 2d 5d 7d 14d 30d 60d 180d 1y 2y 5y 10y 20y 50y 100y 200y 500y 1000y
var allowed_intervals = [1,2,5,10,20,50,100,200,500,1000,2000,5000,10000,20000,30000,60000,120000,300000,600000,900000,1200000,1800000,3600000,7200000,10800000,21600000,43200000,86400000,172800000,432000000,604800000,1209600000,2592000000,5184000000,15552000000,31536000000,63072000000,157680000000,315360000000,630720000000,1576800000000,3153600000000,6307200000000,15768000000000,31536000000000 ];
var time_ticks=[0.2,0.5,1,2 ,5 ,10,20 ,50 ,100,200 ,500 ,1000,2000 ,5000 ,10000,20000,30000 ,60000 ,120000,300000,300000 ,600000 ,1200000,1800000,3600000 ,7200000 ,10800000,21600000,43200000 ,86400000 , 86400000 ,172800000 ,864000000 ,1728000000,2592000000 ,7884000000 ,31536000000,31536000000 ,63072000000 ,157680000000,315360000000 ,630720000000 ,1576800000000,3153600000000,6307200000000 ];
// 2d 10d 20d 30d 1/4y 1/y
var string_precisions = [8,8,8,7, 7 ,7 ,6 ,6 ,6 ,5 ,5 ,5 ,5 ,5 ,5 ,4 ,4 ,4 ,4 ,4 ,4 ,4 ,3 ,3 ,3 ,3 ,3 ,2 ,2 ,2 ,2 ,2 ,1 ,1 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0];
//181 is the character code for the micro - mu symbol.
si_prefixes = ['y','z','a','f','p','n',String.fromCharCode( 181 ),'m','.','k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
function eng_form_infix(number,precision){
if (!isFinite(precision)){precision = 5;}
if (precision<1){precision =1;}
if (precision>21){precision =21;}
if (Math.abs(number) < 1e-24 || Math.abs(number) >= 1e27){
return mjs_precision(number,precision);
}
//return an infix engeneering form number with at least precision significant figures.
var neg_sign = '';
if (number<0){neg_sign = '-';}
number = Math.abs(number);
var degree = Math.floor(Math.log10(number) / 3);
var scaled = number * Math.pow(1000, -degree);
var first_parts = ( scaled.toFixed(Math.max((precision-3),0)) +'.').split('.');
var last_parts = ( scaled.toFixed(Math.max(0,precision - first_parts[0].length)) +'.').split('.');
var infix_form = last_parts[0] + si_prefixes[degree+8] + last_parts[1];
if (last_parts[1].length==0 && degree ==0){
infix_form = last_parts[0]
}
return neg_sign+infix_form;
}
function eng_form_postfix(number,precision){
//return an postfix engeneering form number with at least precision significant figures.
if (!isFinite(precision)){precision = 5;}
if (precision<1){precision =1;}
if (precision>21){precision =21;}
if (Math.abs(number) < 1e-24 || Math.abs(number) >= 1e27){
return mjs_precision(number,precision);
}
var neg_sign = '';
if (number<0){neg_sign = '-';}
number = Math.abs(number);
var degree = Math.floor(Math.log10(number) / 3);
var scaled = number * Math.pow(1000, -degree);
var first_parts = ( scaled.toFixed(Math.max((precision-3),0)) +'.').split('.');
var last_parts = ( scaled.toFixed(Math.max(0,precision - first_parts[0].length)) +'.').split('.');
var postfix_form = last_parts[0]+'.'+last_parts[1] + si_prefixes[degree+8];
if (last_parts[1].length!=0 && degree ==0){
postfix_form = last_parts[0]+'.'+last_parts[1];
}
if (last_parts[1].length==0 && degree !=0){
postfix_form = last_parts[0] + si_prefixes[degree+8];
}
if (last_parts[1].length==0 && degree ==0){
postfix_form = last_parts[0];
}
return neg_sign+postfix_form;
}
var superscriptCharacters = [8304,185,178,179,8308,8309,8310,8311,8312,8313]; //zero to 9 in unicode superscripts.
var superscriptMinus = String.fromCharCode(8315);
var superscriptPlus = String.fromCharCode(8314);
var unicodeTimes = String.fromCharCode(215);
function toSuperscript(n){
var s
if (n<0){s = superscriptMinus; n*=-1;}
else{s=''}
n=parseInt(n).toString();
for (var i=0;ih
n = Math.max(n,l);
n = Math.min(n,h);
return n;
}
function getFloat(promptString,fallback){
var r = parseFloat(prompt(promptString,fallback));
if (isFinite(r)){
return r;
} else {
return fallback;
}
}
function getInt(promptString,fallback){
var r = parseInt(prompt(promptString,fallback));
if (isFinite(r)){
return r;
} else {
return fallback;
}
}
function getString(promptString,fallback){
var r = prompt(promptString,fallback);
if (typeof r ==='string'){
return r;
} else {
return fallback;
}
}
function getDrawFunctionRotateable(s){
var t = s.split(' '); //split on spaces
var commands = [],d = [];
for (var i =0;i0){s= seconds+'seconds ' +milliseconds.toFixed(0) +'ms' }
if (min>0){s= min+'min ' + seconds+'seconds'}
if (hours>0){s= hours+'hours '+ min+'min ' + seconds+'seconds' ;}
if (days>0){s = days+'days ' + hours+'hours ' + min+'min'; }
return s;
}
function mjs_date_print(milliseconds,hp,lp){
//milliseconds is the date
//hp and lp in the range 0-8 inclusive
// yyyy-mm-dd HH:MM:ss.###
// 0 1 2 3 4 5 678
//eg hp -> lp
// mm-dd HH:mm
//
hp = isFinite(hp) ? hp : 0;
lp = isFinite(lp) ? lp : 8;
//there is usully enough space for two even if it's not asked for.
//if (hp == lp){hp--;}
hp = trim(hp,0,8);
//if (hp == lp){lp--;}
lp = trim(lp,0,8);
hp = Math.min(hp,lp);
var d = new Date(milliseconds);
var s = '';
//if the day is first hp==2 put the day mon tue wed...
var days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
if (hp == 2 && lp == 3){
s+=days[d.getDay()];
s+= ' ' + padLeft(d.getHours(), 2, '0')+'h';
return s;
}
//if (hp == 2){s+=months[d.getMonth()];}
if ( hp == 2 && lp == 2 ){
s+=(d.getMonth()+1)+'-'//months[d.getMonth()]
}
if (hp == 1 && lp>1){s+=months[d.getMonth()]+'-';hp++;}
if (hp == 3 && (lp == 4 || lp == 3)){
s+=padLeft(d.getHours(),2,'0');
s+=':'+padLeft(d.getMinutes(),2,'0');
return s;
}
if (hp == 4 && lp == 4){
s+=padLeft(d.getHours(),2,'0');
s+=':'+padLeft(d.getMinutes(),2,'0');
return s;
}
if (hp == 4 && lp == 5){
s+=padLeft(d.getMinutes(),2,'0');
s+='m'+padLeft(d.getSeconds(),2,'0') +'s';
return s;
}
if (hp == 1 && lp == 1){
s=months[d.getMonth()];
return s;
}
if (hp <= 0){s+=d.getFullYear();}
if (hp <= 0 && lp >= 1){s+='-';}
if (hp <= 1 && lp >= 1){s+=(d.getMonth()+1);}
if (hp <= 1 && lp >= 2){s+='-';}
if (hp <= 2 && lp >= 2){s+=(d.getDate());}
if (hp <= 2 && lp >= 3){s+=' ';}
if (hp <= 3 && lp >= 3){s+=padLeft(d.getHours(),2,'0');}
if (hp <= 3 && lp >= 4){s+=':';}
if (hp <= 4 && lp >= 4){s+=padLeft(d.getMinutes(),2,'0');}
if (hp <= 4 && lp >= 5){s+=':';}
if (hp <= 5 && lp >= 5){s+=padLeft(d.getSeconds(),2,'0');}
if (hp <= 5 && lp >= 6){s+='.';}
if (hp <= 6 && lp >= 6){s+=padLeft(d.getMilliseconds(),3,'0')[0];}
if (hp <= 7 && lp >= 7){s+=padLeft(d.getMilliseconds(),3,'0')[1];}
if (hp <= 8 && lp >= 8){s+=padLeft(d.getMilliseconds(),3,'0')[2];}
//touches to make things easyer to read
//hours are last without seconds
if (lp == 3 && hp <=3){s+='h';}
if (lp == 4 && hp == 4){s+='m';}
if (lp == 5 && (hp ==5 || hp == 4)){s+='s';}
return s;
}
function round_to_month(milliseconds){
//takes a milliseconds date and returns milliseconds rounded to the nearest month
var d = new Date(milliseconds);
var down = d.getDate();
var u = new Date(d.getFullYear(),d.getMonth()+1,0);
var up = u.getDate() - down;
if (down < up){
return milliseconds - (down-1)* 24*60*60*1000;
} else {
return milliseconds + (up+1)* 24*60*60*1000;
}
}
function round_to_year(milliseconds){
var d = new Date(milliseconds);
var down = new Date(d.getFullYear(),0,1);
var up = new Date(d.getFullYear()+1,0,1);
if (d.getTime()-down.getTime() < up.getTime() - d.getTime() ){
return down.getTime();
} else {
return up.getTime();
}
}
var fits = {
//each fit has some_fit and some_fun
//some_fun takes an array of x and the params for the fit returning an array of y
//some_fit takes an array of x and and array of y
//returning parameters - arbitary length array of numbers from the result of the fit
//returning strings - an array length 3 of string to describe to the user the results (ofern printed)
//returning fun - a function that will realise this fit.
exponential_fun : function(x,params){
var y = [];
for (var i=0;i0){
x.push(ox[i]);
y.push(oy[i]);
}
}
var sum = [0, 0, 0, 0, 0, 0];
var results = [];
for (var n=0; n < x.length; n++) {
sum[0] += x[n];
sum[1] += y[n];
sum[2] += x[n] * x[n] * y[n];
sum[3] += y[n] * Math.log(y[n]);
sum[4] += x[n] * y[n] * Math.log(y[n]);
sum[5] += x[n] * y[n];
}
var denominator = (sum[1] * sum[2] - sum[5] * sum[5]);
var A = Math.pow(Math.E, (sum[2] * sum[3] - sum[5] * sum[4]) / denominator);
var B = (sum[1] * sum[4] - sum[5] * sum[3]) / denominator;
var string = 'y = ' +mjs_precision(A,5) + 'e^(' + mjs_precision(B,5) + 'x)';
return { parameters:[A,B], strings : [string,'',''], fun : fits.exponential_fun}
},
exponential_plus_c_fun : function(x,params){
var y = [];
for (var i=0;i= number.toPrecision(precision+1).length){
label = number.toPrecision(precision+1);
}
if (label.length >= number.toPrecision(precision+2).length){
label = number.toPrecision(precision+2);
}
if (label.length >= number.toPrecision(precision+3).length){
label = number.toPrecision(precision+3);
}
return sign+label
}
function number_quote(number,error){
//returns a string which would be correct to quote to the given error.
// assuming that error is given to 2 sig fig.
if (! isFinite(error)){error = 1e-20;}
if (! isFinite(number)){number = 0;}
return mjs_precision(number, Math.max(2,Math.floor(Math.log10(Math.abs(number))) - Math.floor(Math.log10(Math.abs(error))) + 2) );
}
function drawEllipse(graph,ctx, centerX, centerY, radiusX, radiusY) {
ctx.beginPath();
var rotationAngle = 0.5;
for (var i = 0 * Math.PI; i < 2 * Math.PI; i += 0.01 ) {
var xPos = centerX - (radiusX * Math.sin(i)) * Math.sin(rotationAngle * Math.PI) + (radiusY * Math.cos(i)) * Math.cos(rotationAngle * Math.PI);
var yPos = centerY + (radiusY * Math.cos(i)) * Math.sin(rotationAngle * Math.PI) + (radiusX * Math.sin(i)) * Math.cos(rotationAngle * Math.PI);
if (i == 0) {
ctx.moveTo(graph.units_to_pixels(xPos,'x'),graph.units_to_pixels(yPos,'y') );
} else {
ctx.lineTo(graph.units_to_pixels(xPos,'x'),graph.units_to_pixels(yPos,'y'));
}
}
ctx.stroke();
}
function mouse_move_event(event,graph){
graph.ui.latestEvent = event;
if(!graph.ui.ticking) {
graph.ui.ticking = true;
requestAnimationFrame( function(){mouse_move_event_shim(graph.ui.latestEvent,graph);} );
//setTimeout(function(){mouse_move_event_shim(graph.ui.latestEvent,graph);}, 10);
}
}
function mouse_move_event_shim(event,graph){
var start_time = new Date();
mouse_move_event_actual(event,graph);
var end_time = new Date();
graph.ui.copy_time=end_time.getTime() - start_time.getTime();
graph.ui.ticking=false;
}
function mouse_move_event_actual(event,graph){
var canvas = graph.canvas;
var ctx = canvas.getContext('2d');
var gs = graph.graphics_style;
//ctx.stroke();
var rect = canvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
var px = graph.pixels_to_units(x,'x');
var py = graph.pixels_to_units(y,'y');
if (graph.graph_image){
ctx.putImageData(graph.graph_image,0,0);
} else {
return;
}
if (graph.ui.touch){
var edge = Math.min(Math.min(canvas.width / 22, canvas.height/15));
} else {
var edge = Math.min(gs.tick_labels_font_size/0.55 * gs.scaling_factor,Math.min(canvas.width / 22, canvas.height/15));
}
graph.ui.size = edge;
var cs = edge/4;
ctx.font = (0.55*edge) + 'px ' + gs.font_name;//"24px Courier New";
if (DEBUGG_FPS){
ctx.fillText('fps: '+ (1/graph.ui.copy_time*1000),30,30);
}
if (mouse_down == false){
ctx.textAlign="left";
//not at the very edges draw interactive stuff
ctx.fillStyle = gs.color_fg;
ctx.strokeStyle = gs.color_fg;
//do the mouse modes
if (x>edge && y>edge && canvas.height-y > edge && canvas.width-x > edge ){
if (gs.mouse_mode === 'zoom'){
ctx.beginPath();
ctx.arc(x, y, cs , 0 ,Math.PI*2, true);
ctx.stroke();
}
if (gs.mouse_mode === 'zoom' || gs.mouse_mode === 'drag' || gs.mouse_mode === 'measure' ){
var labelx = graph.get_axis_string(px,'x');
var labelxWidth = ctx.measureText(labelx).width;
var labely = graph.get_axis_string(py,'y');
lw = labely.length * gs.scaling_factor*gs.axis_labels_font_size;
if (graph.ui.touch && graph.ui.is_touching ){
ctx.fillText(labelx,Math.min(x+cs, canvas.width-edge-labelxWidth), canvas.height-2*edge );
ctx.fillText(labely,2*edge,y-cs);
} else {
ctx.fillText(labelx,Math.min(x+cs, canvas.width-edge-labelxWidth), Math.min(y+2*cs+2*edge,canvas.height-2*edge) );
ctx.fillText(labely,Math.max(x-2*cs-lw-2*edge,2*edge),Math.max(y-cs,gs.scaling_factor*gs.axis_labels_font_size+edge));
}
ctx.beginPath();
ctx.moveTo(x,y+edge);
ctx.lineTo(x,canvas.height );
ctx.moveTo(x,y-edge);
ctx.lineTo(x,0);
ctx.moveTo(x-edge,y);
ctx.lineTo(0,y);
ctx.moveTo(x+edge,y);
ctx.lineTo(canvas.width,y);
ctx.stroke();
ctx.beginPath();
ctx.stroke();
}
if (gs.mouse_mode === 'trim'){
ctx.fillText('trim',x+cs*2,y-cs*2);
ctx.beginPath();
ctx.moveTo(x,0);
ctx.lineTo(x,canvas.height);
ctx.moveTo(0,y);
ctx.lineTo(canvas.width,y);
ctx.stroke();
}
if (gs.mouse_mode === 'cut'){
ctx.fillText('cut',x+cs*2,y-cs*2);
ctx.beginPath();
ctx.moveTo(x,0);
ctx.lineTo(x,canvas.height);
ctx.moveTo(0,y);
ctx.lineTo(canvas.width,y);
ctx.stroke();
}
if (gs.mouse_mode === 'sublin'){
ctx.fillText('draw a line to subtract',x+cs*2,y-cs*2);
}
if (gs.mouse_mode === 'x-c'){
label = graph.get_axis_string(px,'x');
ctx.fillText('x-'+label,x+cs*2,y-cs*2);
ctx.beginPath();
ctx.moveTo(x,0);
ctx.lineTo(x,canvas.height);
ctx.stroke();
}
if (gs.mouse_mode === 'y-c'){
label = graph.get_axis_string(py,'y');
ctx.fillText('y-'+label,x+cs*2,y-cs*2);
ctx.beginPath();
ctx.moveTo(0,y);
ctx.lineTo(canvas.width,y);
ctx.stroke();
}
if (gs.mouse_mode === 'addmarkertext'){
label = graph.markerText;
ctx.textAlign="center";
ctx.fillText(label,x,y);
}
if (gs.mouse_mode === 'addmarkerline' ||
gs.mouse_mode === 'addmarkerlinentext' ||
gs.mouse_mode === 'addmarkerarrowntext'||
gs.mouse_mode === 'addmarkerbox'){
label = ' click and drag to draw' ;
ctx.fillText(label,x+cs,y+cs);
}
if (gs.mouse_mode === 'removeMarker' || gs.mouse_mode === 'markeredit'){
for (var i=0;i0){
//there are captions visable
//caption position handle
var cap_x = gs.scaling_factor*gs.caption_position.x;
if (gs.caption_position.x>0){
cap_x += gs.scaling_factor*gs.tick_len;
} else {
cap_x = canvas.width + cap_x;
cap_x -= gs.scaling_factor*gs.tick_len;
}
cap_y = gs.scaling_factor*gs.caption_position.y;
if (gs.caption_position.y>0){
//going down from top
cap_y += gs.scaling_factor*gs.tick_len;
} else {
//up from botton
cap_y = canvas.height + cap_y;
cap_y -= gs.scaling_factor*gs.tick_len;
}
ctx.beginPath();
ctx.arc(cap_x, cap_y, cs , 0 ,Math.PI*2, true);
ctx.stroke();
}
}
ctx.beginPath();
ctx.stroke();
if (gs.mouse_mode === 'measure'){
//measure mode
ctx.beginPath();
ctx.moveTo(x-cs,y-cs);
ctx.lineTo(x+cs,y+cs);
ctx.moveTo(x-cs,y+cs);
ctx.lineTo(x+cs,y-cs);
ctx.stroke();
ctx.beginPath();
ctx.stroke();
}
if (gs.mouse_mode === 'drag'){
//center lines
drag_x = x-0.5*edge;
drag_y = y-0.5*edge;
ctx.beginPath();
ctx.moveTo(drag_x+edge*0.2,drag_y+edge*0.5);
ctx.lineTo(drag_x+edge*0.8,drag_y+edge*0.5);
ctx.moveTo(drag_x+edge*0.5,drag_y+edge*0.2);
ctx.lineTo(drag_x+edge*0.5,drag_y+edge*0.8);
//diagonals
ctx.moveTo(drag_x+edge*0.2,drag_y+edge*0.5);
ctx.lineTo(drag_x+edge*0.3,drag_y+edge*0.6);
ctx.moveTo(drag_x+edge*0.2,drag_y+edge*0.5);
ctx.lineTo(drag_x+edge*0.3,drag_y+edge*0.4);
ctx.moveTo(drag_x+edge*0.8,drag_y+edge*0.5);
ctx.lineTo(drag_x+edge*0.7,drag_y+edge*0.6);
ctx.moveTo(drag_x+edge*0.8,drag_y+edge*0.5);
ctx.lineTo(drag_x+edge*0.7,drag_y+edge*0.4);
ctx.moveTo(drag_x+edge*0.5,drag_y+edge*0.2);
ctx.lineTo(drag_x+edge*0.4,drag_y+edge*0.3);
ctx.moveTo(drag_x+edge*0.5,drag_y+edge*0.2);
ctx.lineTo(drag_x+edge*0.6,drag_y+edge*0.3);
ctx.moveTo(drag_x+edge*0.5,drag_y+edge*0.8);
ctx.lineTo(drag_x+edge*0.4,drag_y+edge*0.7);
ctx.moveTo(drag_x+edge*0.5,drag_y+edge*0.8);
ctx.lineTo(drag_x+edge*0.6,drag_y+edge*0.7);
ctx.stroke();
ctx.beginPath();
ctx.stroke();
}
if (gs.mouse_mode === 'reader'){
var pi = graph.reader_index[0];
var pj = graph.reader_index[1];
var xi = graph.units_to_pixels(graph.data[pi][0][pj],'x');
var yi = graph.units_to_pixels(graph.data[pi][1][pj],'y');
var old_line_width = ctx.lineWidth;
ctx.strokeStyle = gs.color_bg;
ctx.lineWidth = gs.scaling_factor*gs.axis_labels_font_size/3;
if (gs.x_scale_mode ==='lin' || gs.x_scale_mode ==='log'){
label= mjs_precision(graph.data[pi][0][pj],gs.x_precision+5);
}
if (gs.x_scale_mode ==='time'){
label= mjs_date_print(graph.data[pi][0][pj],0,8);
}
ctx.strokeText(label,x+2*cs,y+gs.axis_labels_font_size*1.6);
ctx.fillText(label,x+2*cs,y+gs.axis_labels_font_size*1.6);
if (gs.y_scale_mode ==='lin' || gs.y_scale_mode ==='log'){
label= mjs_precision(graph.data[pi][1][pj],gs.y_precision+5);
}
if (gs.y_scale_mode ==='time'){
label= mjs_date_print(graph.data[pi][1][pj],0,8);
}
ctx.strokeText(label,x+2*cs,y);
ctx.fillText(label,x+2*cs,y);
label = graph.captions[pi];
ctx.strokeText(label,x+2*cs,y-gs.axis_labels_font_size*1.6);
ctx.fillText(label,x+2*cs,y-gs.axis_labels_font_size*1.6);
label = 'i='+pj;//the index of the data point in the series.
//called j internally, as i if for which series.
//label = 'y='+graph.data[pi][1][pj];
ctx.strokeText(label,x+2*cs,y+gs.axis_labels_font_size*1.6*2);
ctx.fillText(label,x+2*cs,y+gs.axis_labels_font_size*1.6*2);
ctx.lineWidth = old_line_width;
ctx.strokeStyle = gs.color_fg;
ctx.beginPath();
ctx.moveTo(x,y);
ctx.lineTo(xi,yi);
ctx.stroke()
ctx.beginPath();
ctx.arc(x,y, edge*0.3*0.8 , 0 ,Math.PI*2, true);
ctx.moveTo(x,y-edge*.3);
ctx.lineTo(x,y+edge*.3);
ctx.moveTo(x+edge*0.3,y);
ctx.lineTo(x-edge*0.3,y);
ctx.stroke();
}
}
// draw some buttons.... top edge
if (ycanvas.height-edge){
//draw bottom buttons
graph.menushown = true;
ctx.fillStyle = gs.color_bg;
ctx.beginPath();
ctx.rect(0,canvas.height-edge,edge*2,edge);
ctx.rect(2*edge,canvas.height-edge,edge*2,edge);
ctx.fill();
ctx.stroke();
ctx.fillStyle = gs.color_fg;
ctx.beginPath();
ctx.fillText('x',0.2*edge, canvas.height - edge*0.3);
ctx.fillText('y',2.2*edge, canvas.height - edge*0.3);
//grid button
ctx.fillStyle = gs.color_bg;
ctx.rect(4*edge,canvas.height-edge,edge,edge);
ctx.fill();
ctx.stroke();
ctx.beginPath();
var lx = 4*edge;
var ly = canvas.height-edge;
ctx.moveTo(lx+0.2*edge,ly+0.4*edge);
ctx.lineTo(lx+0.8*edge,ly+0.4*edge);
ctx.moveTo(lx+0.2*edge,ly+0.6*edge);
ctx.lineTo(lx+0.8*edge,ly+0.6*edge);
ctx.moveTo(lx+0.4*edge,ly+0.2*edge);
ctx.lineTo(lx+0.4*edge,ly+0.8*edge);
ctx.moveTo(lx+0.6*edge,ly+0.2*edge);
ctx.lineTo(lx+0.6*edge,ly+0.8*edge);
ctx.stroke();
//lin log buttons
ctx.rect(5*edge,canvas.height-edge,edge,edge);
ctx.fill();
ctx.stroke();
ctx.rect(6*edge,canvas.height-edge,edge,edge);
ctx.fill();
ctx.stroke();
ctx.rect(7*edge,canvas.height-edge,edge,edge);
ctx.fill();
ctx.stroke();
if (gs.y_scale_mode == 'log'){
//draw lines showing linear
ctx.moveTo(5*edge,canvas.height-0.2*edge);
ctx.lineTo(6*edge,canvas.height-0.2*edge);
ctx.moveTo(5*edge,canvas.height-0.4*edge);
ctx.lineTo(6*edge,canvas.height-0.4*edge);
ctx.moveTo(5*edge,canvas.height-0.6*edge);
ctx.lineTo(6*edge,canvas.height-0.6*edge);
ctx.moveTo(5*edge,canvas.height-0.8*edge);
ctx.lineTo(6*edge,canvas.height-0.8*edge);
} else {
//draw log like lines
ctx.moveTo(5*edge,canvas.height-0.4*edge);
ctx.lineTo(6*edge,canvas.height-0.4*edge);
ctx.moveTo(5*edge,canvas.height-0.65*edge);
ctx.lineTo(6*edge,canvas.height-0.65*edge);
ctx.moveTo(5*edge,canvas.height-0.8*edge);
ctx.lineTo(6*edge,canvas.height-0.8*edge);
ctx.moveTo(5*edge,canvas.height-0.9*edge);
ctx.lineTo(6*edge,canvas.height-0.9*edge);
}
if (gs.x_scale_mode === 'log'){
ctx.moveTo(6.2*edge,canvas.height);
ctx.lineTo(6.2*edge,canvas.height-edge);
ctx.moveTo(6.4*edge,canvas.height);
ctx.lineTo(6.4*edge,canvas.height-edge);
ctx.moveTo(6.6*edge,canvas.height);
ctx.lineTo(6.6*edge,canvas.height-edge);
ctx.moveTo(6.8*edge,canvas.height);
ctx.lineTo(6.8*edge,canvas.height-edge);
} else {
ctx.moveTo(6.4*edge,canvas.height);
ctx.lineTo(6.4*edge,canvas.height-edge);
ctx.moveTo(6.65*edge,canvas.height);
ctx.lineTo(6.65*edge,canvas.height-edge);
ctx.moveTo(6.8*edge,canvas.height);
ctx.lineTo(6.8*edge,canvas.height-edge);
ctx.moveTo(6.9*edge,canvas.height);
ctx.lineTo(6.9*edge,canvas.height-edge);
}
ctx.stroke();
//time axis button
var lx = 7*edge;
var ly = canvas.height-edge;
ctx.beginPath();
ctx.moveTo(lx+0.1*edge,ly+0.6*edge);
ctx.lineTo(lx+0.1*edge,ly+0.9*edge);
ctx.lineTo(lx+0.9*edge,ly+0.9*edge);
ctx.lineTo(lx+0.9*edge,ly+0.6*edge);
ctx.moveTo(lx+0.5*edge,ly+0.9*edge);
ctx.lineTo(lx+0.5*edge,ly+0.8*edge);
ctx.stroke();
ctx.beginPath();
ctx.arc(lx+0.5*edge,ly+0.4*edge, edge*0.3 , 0 ,Math.PI*2, true);
ctx.moveTo(lx+0.5*edge,ly+0.2*edge);
ctx.lineTo(lx+0.5*edge,ly+0.4*edge);
ctx.lineTo(lx+0.6*edge,ly+0.4*edge);
ctx.stroke();
//f() button
ctx.fillStyle = gs.color_bg;
ctx.rect(8*edge,canvas.height-edge,edge*3,edge);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.fillStyle = gs.color_fg;
//ctx.font= (0.7*edge) + 'px ' + gs.font_name;//"24px Courier New";
ctx.fillText('f()',8.2*edge, canvas.height - edge*0.3);
ctx.stroke();
//line menu button
ctx.fillStyle = gs.color_bg;
ctx.rect(11*edge,canvas.height-edge,edge*4,edge);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.fillStyle = gs.color_fg;
ctx.fillText('lines',11.2*edge, canvas.height - edge*0.3);
ctx.stroke();
//fits menu button
ctx.fillStyle = gs.color_bg;
ctx.rect(15*edge,canvas.height-edge,edge*4,edge);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.fillStyle = gs.color_fg;
ctx.fillText('fits',15.2*edge, canvas.height - edge*0.3);
ctx.stroke();
//draw data out button
ctx.fillStyle = gs.color_bg;
ctx.rect(canvas.width-edge,canvas.height-edge,edge,edge);
//the fullscreen button
ctx.rect(canvas.width-edge*3,canvas.height-edge,edge,edge);
ctx.rect(canvas.width-edge*2.8,canvas.height-edge*.8,edge*0.6,edge*0.6);
ctx.fill();
ctx.stroke();
ctx.fillStyle = gs.color_fg;
ctx.fillText('e',canvas.width-0.7*edge, canvas.height - edge*0.3);
ctx.stroke();
//draw day/nightmode button
ctx.fillStyle = gs.color_fg;
ctx.beginPath();
ctx.rect(canvas.width-2*edge,canvas.height-edge,edge,edge);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.fillStyle = gs.color_bg;
ctx.moveTo(canvas.width-2*edge,canvas.height);
ctx.lineTo(canvas.width-edge-1,canvas.height-edge+1);
ctx.lineTo(canvas.width-edge-1,canvas.height);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.fill();
ctx.stroke();
} else {
graph.menushown = false;
}
if (graph.drawStylemenu){
if (x < canvas.width-2*edge && x > canvas.width-10*edge && y < 14*edge){
ctx.fillStyle = gs.color_bg;
ctx.rect(canvas.width-10*edge,0,edge*8,edge*14);
ctx.fill();
ctx.stroke();
ctx.fillStyle = gs.color_fg;
ctx.beginPath();
var ly = edge*0.8;
var dy = -1*edge;
var lx = canvas.width-10*edge+0.2*edge;
ctx.textAlign="left";
ctx.fillText('Style',lx+5*edge, ly);ly-=dy;
ctx.fillText('Pick Font',lx, ly);
if (gs.font_name.length <10){
ctx.fillText(gs.font_name,lx+4*edge, ly);
} else {
ctx.fillText(gs.font_name.slice(0,9)+'...',lx+4*edge, ly);
}
ly-=dy;
ctx.fillText('Font Size:',lx, ly);
ctx.fillText('Title:',lx+4*edge, ly);ly-=dy;
ctx.fillText('Ticks:',lx+4*edge, ly);ly-=dy;
ctx.fillText('Axis:',lx+4*edge, ly);ly-=dy;
ctx.fillText('Spacing:',lx, ly);
ctx.fillText('Title:',lx+4*edge, ly);ly-=dy;
ctx.fillText('Ticks:',lx+4*edge, ly);ly-=dy;
ctx.fillText('Axis:',lx+4*edge, ly);ly-=dy;
ctx.fillText('Colors:',lx, ly);
ctx.fillText('FG:',lx+4*edge, ly);ly-=dy;
ctx.fillText('BG:',lx+4*edge, ly);ly-=dy;
ctx.fillText('MG:',lx+4*edge, ly);ly-=dy;
ctx.fillText('Line Width:',lx, ly);ly-=dy;
ctx.fillText('Tick Len -major:',lx, ly);ly-=dy;
ctx.fillText('Tick Len -minor:',lx, ly);ly-=dy;
ly = edge*2.8;
ctx.textAlign="center";
lx = canvas.width-3.5*edge;
ctx.fillText('[-]',lx, ly);ctx.fillText('[+]',lx+edge, ly);ly-=dy;
ctx.fillText('[-]',lx, ly);ctx.fillText('[+]',lx+edge, ly);ly-=dy;
ctx.fillText('[-]',lx, ly);ctx.fillText('[+]',lx+edge, ly);ly-=dy;
ctx.fillText('[-]',lx, ly);ctx.fillText('[+]',lx+edge, ly);ly-=dy;
ctx.fillText('[-]',lx, ly);ctx.fillText('[+]',lx+edge, ly);ly-=dy;
ctx.fillText('[-]',lx, ly);ctx.fillText('[+]',lx+edge, ly);ly-=dy;
ctx.fillText('[edit]',lx+0.5*edge, ly);ly-=dy;
ctx.fillText('[edit]',lx+0.5*edge, ly);ly-=dy;
ctx.fillText('[edit]',lx+0.5*edge, ly);ly-=dy;
ctx.fillText('[-]',lx, ly);ctx.fillText('[+]',lx+edge, ly);ly-=dy;
ctx.fillText('[-]',lx, ly);ctx.fillText('[+]',lx+edge, ly);ly-=dy;
ctx.fillText('[-]',lx, ly);ctx.fillText('[+]',lx+edge, ly);ly-=dy;
} else {
graph.drawStylemenu = false;
}
}
if (graph.drawxmenu){
if (x < 12*edge && y > canvas.height - 9*edge){
ctx.fillStyle = gs.color_bg;
ctx.rect(0,canvas.height-edge*9,edge*12,edge*9);
ctx.fill();
ctx.stroke();
ctx.fillStyle = gs.color_fg;
ctx.beginPath();
var ly = canvas.height - edge*0.2;
var dy = edge;
var lx = 0.2*edge;
ctx.textAlign="left";
ctx.fillText('x:',lx, ly);ly-=dy;
ctx.fillText('Min:' + graph.get_axis_string(gs.x_manual_min,'x'),lx, ly);ly-=dy;
ctx.fillText('Max:' + graph.get_axis_string(gs.x_manual_max,'x'),lx, ly);ly-=dy;
ctx.fillText('Mode:',lx, ly);ly-=dy;
ctx.fillText('Spacing: ' + mjs_precision(gs.guideWidthx,2) ,lx, ly);ly-=dy;
ctx.fillText('Style:',lx, ly);ly-=dy;
ctx.fillText('Set Prefix:',lx, ly);ly-=dy;
ctx.fillText('Set Postfix:',lx, ly);ly-=dy;
ctx.fillText('Set Label:',lx, ly);ly-=dy;
ly = canvas.height - edge*1.2;
ctx.textAlign="center";
ctx.fillText('[auto]',6*edge, ly);
ctx.fillText('[manual]',10*edge, ly);ly-=dy;
ctx.fillText('[auto]',6*edge, ly);
ctx.fillText('[manual]',10*edge, ly);ly-=dy;
ctx.fillText('[lin]',4.5*edge, ly);
ctx.fillText('[log]',7.5*edge, ly);
ctx.fillText('[time]',10.5*edge, ly);ly-=dy;
ctx.fillText('[-]',6*edge, ly);
ctx.fillText('[+]',10*edge, ly);ly-=dy;
ctx.fillText('[1200]',5*edge, ly);
ctx.fillText('[1k2]',7*edge, ly);
ctx.fillText('[1.2k]',9*edge, ly);
ctx.fillText('[1.2e3]',11*edge, ly);ly-=dy;
ctx.fillText('"'+gs.x_axis_prefix+'"',8*edge, ly);ly-=dy;
ctx.fillText('"'+gs.x_axis_postfix+'"',8*edge, ly);ly-=dy;
ctx.fillText('"'+gs.x_axis_title+'"',8*edge, ly);
ctx.textAlign="left";
}else {
graph.drawxmenu = false;
}
}
if (graph.drawymenu){
if (x < 14*edge && x>2*edge && y > canvas.height - 9*edge){
ctx.fillStyle = gs.color_bg;
ctx.rect(2*edge,canvas.height-edge*9,edge*12,edge*9);
ctx.fill();
ctx.stroke();
ctx.fillStyle = gs.color_fg;
ctx.beginPath();
var ly = canvas.height - edge*0.2;
var dy = edge;
var lx = 2.2*edge;
ctx.textAlign="left";
ctx.fillText('y:',lx, ly);ly-=dy;
ctx.fillText('Min:' + graph.get_axis_string(gs.y_manual_min,'y'),lx, ly);ly-=dy;
ctx.fillText('Max:' + graph.get_axis_string(gs.y_manual_max,'y'),lx, ly);ly-=dy;
ctx.fillText('Mode:',lx, ly);ly-=dy;
ctx.fillText('Spacing: ' + mjs_precision(gs.guideWidthy,2) ,lx, ly);ly-=dy;
ctx.fillText('Style:',lx, ly);ly-=dy;
ctx.fillText('Set Prefix:',lx, ly);ly-=dy;
ctx.fillText('Set Postfix:',lx, ly);ly-=dy;
ctx.fillText('Set Label:',lx, ly);ly-=dy;
ly = canvas.height - edge*1.2;
ctx.textAlign="center";
var lx = 2*edge;
ctx.fillText('[auto]',lx+6*edge, ly);
ctx.fillText('[manual]',lx+10*edge, ly);ly-=dy;
ctx.fillText('[auto]',lx+6*edge, ly);
ctx.fillText('[manual]',lx+10*edge, ly);ly-=dy;
ctx.fillText('[lin]',lx+4.5*edge, ly);
ctx.fillText('[log]',lx+7.5*edge, ly);
ctx.fillText('[time]',lx+10.5*edge, ly);ly-=dy;
ctx.fillText('[-]',lx+6*edge, ly);
ctx.fillText('[+]',lx+10*edge, ly);ly-=dy;
//ctx.fillText('[1.2'+unicodeTimes+'10]'+String.fromCharCode(178),lx+3*edge, ly);
ctx.fillText('[1200]',lx+5*edge, ly);
ctx.fillText('[1k2]',lx+7*edge, ly);
ctx.fillText('[1.2k]',lx+9*edge, ly);
ctx.fillText('[1.2e3]',lx+11*edge, ly);ly-=dy;
ctx.fillText('"'+gs.y_axis_prefix+'"',lx+8*edge, ly);ly-=dy;
ctx.fillText('"'+gs.y_axis_postfix+'"',lx+8*edge, ly);ly-=dy;
ctx.fillText('"'+gs.y_axis_title+'"',lx+8*edge, ly);
ctx.textAlign="left";
}else {
graph.drawymenu = false;
}
}
if (graph.drawmodemenu){
if (x < 10*edge && y < edge*3){
//ctx.font= (0.55*edge) + 'px ' + gs.font_name;//"24px Courier New";
//box
ctx.fillStyle = gs.color_bg;
ctx.beginPath();
ctx.rect(0,0,edge*10,edge*3);
ctx.fill();
ctx.stroke();
ctx.fillStyle = gs.color_fg;
ctx.beginPath();
ctx.fillText('symbol/line',0.2*edge, 0.8*edge);
//+ and - buttons
ctx.beginPath();
ctx.moveTo(0.2*edge,1.5*edge);
ctx.lineTo(0.8*edge,1.5*edge);
ctx.moveTo(0.5*edge,1.2*edge);
ctx.lineTo(0.5*edge,1.8*edge);
ctx.moveTo(1.2*edge,1.5*edge);
ctx.lineTo(1.8*edge,1.5*edge);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0.2*edge,2.5*edge);
ctx.lineTo(0.8*edge,2.5*edge);
ctx.moveTo(0.5*edge,2.2*edge);
ctx.lineTo(0.5*edge,2.8*edge);
ctx.moveTo(1.2*edge,2.5*edge);
ctx.lineTo(1.8*edge,2.5*edge);
ctx.stroke();
//structure lines
ctx.beginPath();
ctx.moveTo(0,edge);
ctx.lineTo(10*edge,edge);
ctx.moveTo(0,2*edge);
ctx.lineTo(10*edge,2*edge);
ctx.moveTo(2*edge,1*edge)
ctx.lineTo(2*edge,3*edge);
ctx.stroke();
ctx.beginPath();
ctx.arc(2.5*edge,1.5*edge,edge*0.35,0 ,Math.PI*2, true);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(2.2*edge,1.2*edge)
ctx.lineTo(2.8*edge,1.8*edge);
ctx.stroke();
ctx.beginPath();
ctx.arc(2.5*edge,2.5*edge,edge*0.35,0 ,Math.PI*2, true);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(2.2*edge,2.2*edge)
ctx.lineTo(2.8*edge,2.8*edge);
ctx.stroke();
//symbol buttons
var lx = 3*edge;
var ly = 1*edge;//top left corner of a 1x1edge square
//dot
ctx.fillStyle = gs.color_fg;
ctx.rect(lx + 0.3*edge,ly + 0.3*edge,edge*0.4,edge*0.4);
ctx.fill();
//filled circle
lx += edge;
ctx.arc(edge*0.5+lx, ly+ edge*0.5, edge*0.3 , 0 ,Math.PI*2, true);
ctx.fill();
//box
lx += edge;
ctx.fillStyle = gs.color_bg;
ctx.beginPath();
ctx.rect(lx + 0.3*edge,ly + 0.3*edge,edge*0.4,edge*0.4);
ctx.stroke();
//stroked circle
lx += edge;
ctx.beginPath();
ctx.arc(edge*0.5+lx, ly+ edge*0.5, edge*0.3 , 0 ,Math.PI*2, true);
ctx.stroke();
//x
lx += edge;
ctx.beginPath();
ctx.moveTo(lx+0.3*edge,ly+0.3*edge);
ctx.lineTo(lx+0.7*edge,ly+0.7*edge);
ctx.moveTo(lx+0.7*edge,ly+0.3*edge);
ctx.lineTo(lx+0.3*edge,ly+0.7*edge);
ctx.stroke();
//cross
lx += edge;
ctx.beginPath();
ctx.moveTo(lx+0.5*edge,ly+0.3*edge);
ctx.lineTo(lx+0.5*edge,ly+0.7*edge);
ctx.moveTo(lx+0.7*edge,ly+0.5*edge);
ctx.lineTo(lx+0.3*edge,ly+0.5*edge);
ctx.stroke();
//line modes
ly += 1.5*edge;
lx = 3.5*edge;
var ll = 0.3;
//line
ctx.beginPath();
ctx.moveTo(lx-ll*edge,ly-ll*edge);
ctx.lineTo(lx+ll*edge,ly+ll*edge);
//approx
lx += edge;
ctx.moveTo(lx-ll*edge,ly+ll*edge);
ctx.quadraticCurveTo(lx-ll*edge,ly-ll*edge,lx+ll*edge,ly-ll*edge);
//interp
lx += edge;
ctx.moveTo(lx-ll*edge,ly+ll*edge);
ctx.bezierCurveTo(lx+ll*edge,ly+ll*edge,lx-ll*edge,ly-ll*edge,lx+ll*edge,ly-ll*edge);
//zig
lx += edge;
ctx.moveTo(lx-ll*edge,ly-ll*edge);
ctx.lineTo(lx-ll*edge,ly+ll*edge);
ctx.lineTo(lx+ll*edge,ly+ll*edge);
//zag
lx += edge;
ctx.moveTo(lx-ll*edge,ly-ll*edge);
ctx.lineTo(lx+ll*edge,ly-ll*edge);
ctx.lineTo(lx+ll*edge,ly+ll*edge);
//mid
lx += edge;
ctx.moveTo(lx-ll*edge,ly-ll*edge);
ctx.lineTo(lx,ly-ll*edge);
ctx.lineTo(lx,ly+ll*edge);
ctx.lineTo(lx+ll*edge,ly+ll*edge);
//hist
lx += edge;
ctx.moveTo(lx-ll*edge,ly-ll*edge);
ctx.lineTo(lx-ll*edge,ly+ll*edge);
ctx.moveTo(lx-ll*edge,ly);
ctx.lineTo(lx+ll*edge,ly);
ctx.lineTo(lx+ll*edge,ly+ll*edge);
ctx.stroke();
} else {
console.log('leaving menu');
graph.drawmodemenu = false;
}
}
if (graph.drawmarkermenu){
if (x < 21*edge && x > 14*edge && y < edge*10){
ctx.fillStyle = gs.color_bg;
ctx.rect(14*edge,0,edge*7,edge*10);
ctx.fill();
ctx.stroke();
ctx.fillStyle = gs.color_fg;
ctx.beginPath();
var ly = edge*0.8;
var dy = -edge;
var lx = 14.2*edge;
var lx2 = 17.2*edge;
ctx.fillText('Marker',lx, ly);ly-=dy;
ctx.fillText('Clear All',lx, ly); ctx.fillText('Hide/Show',lx2, ly); ly-=dy;
ctx.fillText('Remove',lx, ly); ctx.fillText('edit',lx2, ly);ly-=dy;
ctx.fillText('Add Text',lx, ly);ly-=dy;
ctx.fillText('Add line',lx, ly); ctx.fillText('+text',lx2, ly);ly-=dy;
ctx.fillText('Add box',lx, ly); ctx.fillText('+text',lx2, ly);ly-=dy;
ctx.fillText('Add arrow',lx, ly); ctx.fillText('+text',lx2, ly);ly-=dy;
} else {
graph.drawmarkermenu = false;
}
}
if (graph.drawgraphmenu){
if (x < 12*edge && x > 4*edge && y < edge*10){
ctx.fillStyle = gs.color_bg;
ctx.rect(4*edge,0,edge*8,edge*10);
ctx.fill();
ctx.stroke();
ctx.fillStyle = gs.color_fg;
ctx.beginPath();
var ly = edge*0.8;
var dy = -edge;
var lx = 4.2*edge;
ctx.fillText('Graph',lx, ly);ly-=dy;
ctx.fillText('Set title',lx, ly);ly-=dy;
ctx.fillText('Set subtitle',lx, ly);ly-=dy;
ctx.fillText('Set subtitle2',lx, ly);ly-=dy;
ctx.fillText('Toggle show transforms',lx, ly);ly-=dy;
ctx.fillText('set x-axis title',lx, ly);ly-=dy;
ctx.fillText('set y-axis title',lx, ly);ly-=dy;
ctx.fillText('Scaling Factor down',lx, ly);ly-=dy;
ctx.fillText('Scaling Factor up',lx, ly);ly-=dy;
ctx.fillText('Reset Graph',lx, ly);ly-=dy;
} else {
graph.drawgraphmenu = false;
}
}
if (graph.drawFontMenu){
var items = MJSFontList.fontList.length+1;
if (x < canvas.width-2*edge && x > canvas.width-10*edge && y < edge*items){
ctx.fillStyle = gs.color_bg;
ctx.rect(canvas.width-10*edge,0,edge*8,edge*items);
ctx.fill();
ctx.stroke();
ctx.fillStyle = gs.color_fg;
ctx.beginPath();
var ly = edge*0.8;
var dy = -1*edge;
var lx = canvas.width-10*edge+0.2*edge;
ctx.textAlign="left";
ctx.fillText('Other ...',lx, ly);ly-=dy;
for (var i =0;i canvas.width - 16*edge && y > canvas.height-edge*14){
//ctx.font= (0.55*edge) + 'px ' + gs.font_name;//"24px Courier New";
//box
ctx.fillStyle = gs.color_bg;
ctx.rect(canvas.width - 16*edge,canvas.height-edge*14,edge*16,edge*14);
ctx.fill();
ctx.stroke();
ctx.fillStyle = gs.color_fg;
ctx.beginPath();
var ly = canvas.height - edge*0.1;
var dy = edge;
var lx = canvas.width - 7.8*edge;
ctx.fillText('export',lx, ly);ly-=dy;
ctx.fillText('data (csv)',lx, ly);ly-=dy;
ctx.fillText('data (code)',lx, ly);ly-=dy;
ctx.fillText('data (code [matlab])',lx, ly);ly-=dy;
ctx.fillText('data (code [JSON])',lx, ly);ly-=dy;
ctx.fillText('data (tabbed)',lx, ly);ly-=dy;
ctx.fillText('png (hi res)',lx, ly);ly-=dy;
ctx.fillText('png (low res)',lx, ly);ly-=dy;
ctx.fillText('png (small figure)',lx, ly);ly-=dy;
ctx.fillText('png (large figure)',lx, ly);ly-=dy;
ctx.fillText('svg',lx, ly);ly-=dy;
ctx.fillText('svg (small figure)',lx, ly);ly-=dy;
ctx.fillText('svg (large figure)',lx, ly);ly-=dy;
ctx.fillText('HTML [link]',lx, ly);ly-=dy;
lx = canvas.width - 15.8*edge;
ly = canvas.height - edge*10.1;
ctx.fillText('svg+png',lx, ly);ly-=dy;
ctx.fillText('svg+png(small fig)',lx, ly);ly-=dy;
ctx.fillText('svg+png(large fig)',lx, ly);ly-=dy;
} else {
graph.drawexportmenu = false;
}
}
if (graph.drawcaptionmenu){
if (x>canvas.width-4*edge && y < edge*10){
ctx.fillStyle = gs.color_bg;
ctx.beginPath();
ctx.rect(canvas.width-4*edge,0,edge*4,edge*10);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.fillStyle = gs.color_fg;
//ctx.font= (0.55*edge) + 'px ' + gs.font_name;//"24px Courier New";
ctx.fillText('captions',canvas.width-3.8*edge, 0.8*edge);
ctx.fillText('Show/Hide',canvas.width-3.8*edge, 1.8*edge);
ctx.fillText('x',canvas.width-3.8*edge, 2.8*edge);
ctx.fillText('y',canvas.width-1.8*edge, 2.8*edge);
ctx.fillText('First',canvas.width-3.8*edge, 3.8*edge);
ctx.fillText('Last',canvas.width-3.8*edge, 4.8*edge);
ctx.fillText('Min',canvas.width-3.8*edge, 5.8*edge);
ctx.fillText('Max',canvas.width-3.8*edge, 6.8*edge);
ctx.fillText('First',canvas.width-1.8*edge, 3.8*edge);
ctx.fillText('Last',canvas.width-1.8*edge, 4.8*edge);
ctx.fillText('Min',canvas.width-1.8*edge, 5.8*edge);
ctx.fillText('Max',canvas.width-1.8*edge, 6.8*edge);
ctx.fillText('None',canvas.width-2.8*edge, 7.8*edge);
ctx.fillText('Extra Precision:',canvas.width-3.8*edge, 8.8*edge);
ctx.fillText(gs.captions_extra_precision,canvas.width-3.8*edge, 9.8*edge);
ctx.fillText('[-]',canvas.width-1.8*edge, 9.8*edge);
ctx.fillText('[+]',canvas.width-0.8*edge, 9.8*edge);
ctx.beginPath()
ctx.moveTo(canvas.width-4*edge,edge);
ctx.lineTo(canvas.width,edge);
ctx.moveTo(canvas.width-4*edge,2*edge);
ctx.lineTo(canvas.width,2*edge);
ctx.moveTo(canvas.width-4*edge,3*edge);
ctx.lineTo(canvas.width,3*edge);
ctx.moveTo(canvas.width-2*edge,2*edge);
ctx.lineTo(canvas.width-2*edge,7*edge);
ctx.moveTo(canvas.width-4*edge,8*edge);
ctx.lineTo(canvas.width,8*edge);
ctx.stroke();
} else {
graph.drawcaptionmenu = false;
}
}
//draw fx menue
if (graph.drawfxmenu){
if (x>8*edge && x<22*edge && y > canvas.height-edge*14){
//f() button
ctx.fillStyle = gs.color_bg;
ctx.rect(8*edge,canvas.height-edge*14,edge*14,edge*14);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.fillStyle = gs.color_fg;
//ctx.font= (0.55*edge) + 'px ' + gs.font_name;//"24px Courier New";
ctx.fillText('functions',8.2*edge, canvas.height - edge*0.3);
ctx.stroke();
//the lines seperating buttons
ctx.beginPath();
for (var i = 1;i<14;i++){
ctx.moveTo(edge*8,canvas.height - edge*i);
ctx.lineTo(edge*22,canvas.height - edge*i);
}
ctx.moveTo(edge*12,canvas.height);
ctx.lineTo(edge*12,canvas.height - edge*14);
ctx.moveTo(edge*16,canvas.height);
ctx.lineTo(edge*16,canvas.height - edge*14);
ctx.moveTo(edge*10,canvas.height - edge*9);
ctx.lineTo(edge*10,canvas.height - edge*13);
ctx.moveTo(edge*10,canvas.height - edge*4);
ctx.lineTo(edge*10,canvas.height - edge*5);
ctx.moveTo(edge*14,canvas.height - edge*9);
ctx.lineTo(edge*14,canvas.height - edge*13);
ctx.moveTo(edge*14,canvas.height - edge*3);
ctx.lineTo(edge*14,canvas.height - edge*5);
ctx.stroke();
ctx.fillText('reset',8.2*edge, canvas.height - edge*1.3);
ctx.fillText('cut',8.2*edge, canvas.height - edge*2.3);
ctx.fillText('norm(y)',8.2*edge, canvas.height - edge*3.3);
ctx.fillText('y-c',8.2*edge, canvas.height - edge*4.3);
ctx.fillText('x-c',10.2*edge, canvas.height - edge*4.3);
ctx.fillText('smooth',8.2*edge, canvas.height - edge*5.3);
ctx.fillText('interpolate',8.2*edge, canvas.height - edge*6.3);
ctx.fillText('-(mx+c)',8.2*edge, canvas.height - edge*7.3);
ctx.fillText('dy/dx',8.2*edge, canvas.height - edge*8.3);
ctx.fillText('ln x',8.2*edge, canvas.height - edge*9.3);
ctx.fillText('e^x',10.2*edge, canvas.height - edge*9.3);
ctx.fillText('log x',8.2*edge, canvas.height - edge*10.3);
ctx.fillText('10^x',10.2*edge, canvas.height - edge*10.3);
ctx.fillText('10x',8.2*edge, canvas.height - edge*11.3);
ctx.fillText('x/10',10.2*edge, canvas.height - edge*11.3);
ctx.fillText('-x',8.2*edge, canvas.height - edge*12.3);
ctx.fillText('1/x',10.2*edge, canvas.height - edge*12.3);
ctx.fillText('x<->y',12.2*edge, canvas.height - edge*1.3);
ctx.fillText('trim',12.2*edge, canvas.height - edge*2.3);
ctx.fillText('x^n',12.2*edge, canvas.height - edge*3.3);
ctx.fillText('y^n',14.2*edge, canvas.height - edge*3.3);
ctx.fillText('rt x',12.2*edge, canvas.height - edge*4.3);
ctx.fillText('rt y',14.2*edge, canvas.height - edge*4.3);
ctx.fillText('sum',12.2*edge, canvas.height - edge*5.3);
//ctx.fillText('B-A',14.2*edge, canvas.height - edge*5.3);
ctx.fillText('jitter(y)',12.2*edge, canvas.height - edge*6.3);
ctx.fillText('Hist',12.2*edge, canvas.height - edge*7.3);
ctx.fillText('Intergrate',12.2*edge, canvas.height - edge*8.3);
ctx.fillText('ln y',12.2*edge, canvas.height - edge*9.3);
ctx.fillText('e^y',14.2*edge, canvas.height - edge*9.3);
ctx.fillText('log y',12.2*edge, canvas.height - edge*10.3);
ctx.fillText('10^y',14.2*edge, canvas.height - edge*10.3);
ctx.fillText('10y',12.2*edge, canvas.height - edge*11.3);
ctx.fillText('y/10',14.2*edge, canvas.height - edge*11.3);
ctx.fillText('-y',12.2*edge, canvas.height - edge*12.3);
ctx.fillText('1/y',14.2*edge, canvas.height - edge*12.3);
ctx.fillText('pop',12.2*edge, canvas.height - edge*0.3);
ctx.fillText('y vs y',8.2*edge, canvas.height - edge*13.3);
ctx.fillText('sub fit',16.2*edge, canvas.height - edge*13.3);
ctx.fillText('rmv outliers',16.2*edge, canvas.height - edge*12.3);
ctx.fillText('keep outliers',16.2*edge, canvas.height - edge*11.3);
ctx.fillText('custom y=f(x,y)',16.2*edge, canvas.height - edge*10.3);
ctx.fillText('custom filter',16.2*edge, canvas.height - edge*9.3);
ctx.fillText('accumulate(y)',16.2*edge, canvas.height - edge*4.3);
ctx.fillText('view last n',16.2*edge, canvas.height - edge*3.3);
ctx.fillText('(y - mean)/sigma',16.2*edge, canvas.height - edge*2.3);
ctx.fillText('x spacing',16.2*edge, canvas.height - edge*1.3);
ctx.fillText('time->number',16.2*edge, canvas.height - edge*0.3);
} else {
graph.drawfxmenu = false;
}
}
if (graph.drawtimemenu){
graph.drawfxmenu = false;
if (x>10*edge && x<20*edge && y > canvas.height-edge*6){
ctx.fillStyle = gs.color_bg;
ctx.beginPath();
ctx.rect(10*edge,canvas.height-edge*6,edge*10,edge*6);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.fillStyle = gs.color_fg;
//ctx.font= (0.55*edge) + 'px ' + gs.font_name;//"24px Courier New";
ctx.fillText('time->number',10.2*edge, canvas.height - edge*0.3);
var timeoptionsstrings = [['days','hours','mins','sec'],['now','ago','start','end']];
var lx = 10.2*edge;
var ldx = 2*edge;
var ly = canvas.height - edge*3.3;
var ldy = edge;
for (var i = 0;i<2;i++){
for (var j = 0;j<4;j++){
var label = timeoptionsstrings[i][j];
if (graph.timemenuoptions[i] == j ){
label = '['+label+']';
} else
{
label = ' '+label+' ';
}
ctx.fillText(label,lx+j*ldx, ly -i*ldy);
}
}
ctx.fillText('Pick reference and unit',lx, ly -2*ldy);
ctx.fillText('apply on x',lx, ly +2*ldy);
ctx.fillText('apply on y',lx+3*ldx, ly +2*ldy);
} else {
graph.drawtimemenu = false;
}
}
//draw line menu buttons
if (graph.drawlinemenu){
//get number of lines
var no_of_lines = graph.data_backup.length;
for (var i = 0;i11*edge && x<21*edge && y > canvas.height-edge*(no_of_lines+no_of_function_lines+2)){
//ctx.font= (0.55*edge) + 'px ' + gs.font_name;//"24px Courier New";
//box
ctx.fillStyle = gs.color_bg;
ctx.rect(11*edge,canvas.height-edge*(no_of_lines+no_of_function_lines+2),edge*10,edge*(no_of_lines+no_of_function_lines+2));
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(edge*11,canvas.height - edge);
ctx.lineTo(edge*21,canvas.height - edge);
ctx.moveTo(edge*11,canvas.height - edge*(no_of_lines+1));
ctx.lineTo(edge*21,canvas.height - edge*(no_of_lines+1));
ctx.stroke();
ctx.fillStyle = gs.color_fg;
ctx.fillText('line menu',11.2*edge+0.2, canvas.height -edge*0.2);
var ly = canvas.height - no_of_lines*edge - 0.2*edge;
var lx = 13.2*edge+0.2;
for (var i = 0;i0;i--){
var label = graph.captions_backup[i] || 'label' ;
ctx.fillStyle = graph.colors_backup[i];
ctx.fillText(label,13.2*edge+0.2, canvas.height -no_of_lines*edge + edge*(i-0.2));
//show or hide button
if (gs.hidden_lines[i]){
ctx.fillText('( )',11.2*edge+0.2, canvas.height - no_of_lines*edge + edge*(i-0.2));
} else {
ctx.fillText('(+)',11.2*edge+0.2, canvas.height - no_of_lines*edge + edge*(i-0.2));
}
ctx.beginPath();
ctx.rect(20.2*edge,canvas.height - no_of_lines*edge + edge*(i-0.8),0.6*edge,0.6*edge);
ctx.fill();
ctx.beginPath();
ctx.rect(19.2*edge,canvas.height - no_of_lines*edge + edge*(i-0.8),0.6*edge,0.6*edge);
ctx.stroke();
}
ctx.fillStyle = gs.color_fg;
//all button
//none button
ctx.fillText('all',15.2*edge+0.2, canvas.height -edge*0.2);
ctx.fillText('none',17.2*edge+0.2, canvas.height -edge*0.2);
}else {
graph.drawlinemenu = false;
}
}
//draw fit's menu buttons
if (graph.drawfitsmenu){
if (x>15*edge && x<21*edge && y > canvas.height-edge*10){
//f() button
ctx.fillStyle = gs.color_bg;
ctx.rect(15*edge,canvas.height-edge*10,edge*6,edge*10);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.stroke();
ctx.fillStyle = gs.color_fg;
//ctx.font= (0.55*edge) + 'px ' + gs.font_name;//"24px Courier New";
var label = '';
if (gs.extrapolate){
label = '(+) extrapolate';
} else {
label = '( ) extrapolate';
}
var lx = 18.2*edge;
var ly= canvas.height - edge*0.3;
var dly = edge;
ctx.fillText('none',lx, ly);ly-=dly;
ly-=dly;//extrapolate takes up two.
ctx.fillText('stats',lx, ly);ly-=dly;
ctx.fillText('ae^bx',lx, ly);ly-=dly;
ctx.fillText('a+be^cx',lx, ly);ly-=dly;
ctx.fillText('a+blnx',lx, ly);ly-=dly;
ctx.fillText('ax^b',lx, ly);ly-=dly;
ctx.fillText('a+bx^c',lx, ly);ly-=dly;
ctx.fillText('gauss',lx, ly);ly-=dly;
lx = 15.2*edge;
ly = canvas.height - edge*0.3;
ctx.fillText('fits',lx, ly);ly-=dly;
ctx.fillText(label,lx, ly);ly-=dly;
ctx.fillText('y=c',lx, ly);ly-=dly;
ctx.fillText('linear',lx, ly);ly-=dly;
ctx.fillText('quad',lx,ly);ly-=dly;
ctx.fillText('cubic',lx, ly);ly-=dly;
ctx.fillText('poly 4',lx, ly);ly-=dly;
ctx.fillText('poly 5',lx,ly);ly-=dly;
ctx.fillText('poly 6',lx, ly);ly-=dly;
ctx.fillText('poly 7',lx,ly);ly-=dly;
} else {
graph.drawfitsmenu = false;
}
}
//the x and y menu should not be under the button. Don't draw if the menus are open.
if (graph.ui.touch && graph.ui.is_touching && (gs.mouse_mode === 'zoom' || gs.mouse_mode === 'drag') && !( graph.drawxmenu || graph.drawymenu) ){
//big zoom out button
ctx.fillStyle = gs.color_bg;
ctx.rect(0,canvas.height-2*edge,edge*4,edge);
ctx.fill();
ctx.stroke();
if (gs.mouse_mode === 'zoom'){
ctx.rect(0,canvas.height-3*edge,edge*4,edge);
ctx.fill();
ctx.stroke();
}
ctx.fillStyle = gs.color_fg;
//ctx.font= (0.55*edge) + 'px ' + gs.font_name;//"24px Courier New";
ctx.fillText('Auto Zoom',0.2*edge, canvas.height - edge*1.3);
if (gs.mouse_mode === 'zoom'){
ctx.fillText('Zoom out',0.2*edge, canvas.height - edge*2.3);
}
}
if (graph.ui.touch && graph.ui.is_touching ){
//draw the bit under the finger. in the middle
var image = ctx.getImageData(x-2*edge,y-2*edge,edge*4,4*edge);
ctx.beginPath();
ctx.moveTo(x,y);
ctx.lineTo(canvas.width/2,canvas.height/2);
ctx.stroke();
//the bit is 4edge by 4edge. the boarder has 2px on each side as well.
ctx.rect(canvas.width/2-2*edge-2,canvas.height/2-2*edge-2,4*edge+4,4*edge+4);
ctx.stroke();
ctx.putImageData(image,canvas.width/2-2*edge,canvas.height/2-2*edge);
ctx.beginPath();
var cd = Math.max(0.1*edge,2); //center dot size
ctx.rect(canvas.width/2-cd,canvas.height/2-cd,2*cd,2*cd);
ctx.fill();
}
}
ctx.beginPath();
ctx.stroke();
if (mouse_down && gs.mouse_mode === 'zoom'){
//is dragging for zoom
end_x = x;
end_y = y;
//draw bounding box
ctx.beginPath();
ctx.rect(start_x+1,start_y+1,end_x-start_x-1,end_y-start_y-1);
ctx.moveTo(start_x,0);
ctx.lineTo(start_x,2*edge);
ctx.moveTo(start_x,canvas.height);
ctx.lineTo(start_x,canvas.height-2*edge);
ctx.moveTo(end_x,canvas.height);
ctx.lineTo(end_x,canvas.height-2*edge);
ctx.moveTo(end_x,0);
ctx.lineTo(end_x,2*edge);
ctx.moveTo(0,start_y);
ctx.lineTo(2*edge,start_y);
ctx.moveTo(canvas.width-2*edge,start_y);
ctx.lineTo(canvas.width,start_y);
ctx.moveTo(0,end_y);
ctx.lineTo(2*edge,end_y);
ctx.moveTo(canvas.width-2*edge,end_y);
ctx.lineTo(canvas.width,end_y);
ctx.stroke();
//this is a hack to get the screen to redraw correctly.
ctx.beginPath();
ctx.stroke();
}
if (mouse_down && gs.mouse_mode === 'drag'){
//is dragging
if (graph.ui.is_touching){
is_touch_dragging = true;
start_x = 0.5*(touch_start_x[0]+touch_start_x[1]);
start_y = 0.5*(touch_start_y[0]+touch_start_y[1]);
end_x = 0.5*(touch_x[0]+touch_x[1]);
end_y = 0.5*(touch_y[0]+touch_y[1]);
scale_x = (touch_x[0]-touch_x[1]) / (touch_start_x[0]-touch_start_x[1]);
scale_y = (touch_y[0]-touch_y[1]) / (touch_start_y[0]-touch_start_y[1]);
scale_x = trim(Math.abs(scale_x),0.1,10);
scale_y = trim(Math.abs(scale_y),0.1,10);
ctx.putImageData(graph.graph_image_for_drag,0,0);
ctx.save();
ctx.translate((start_x) ,(start_y) );
ctx.scale(scale_x,scale_y);
ctx.translate((end_x-start_x)/scale_x ,(end_y-start_y)/scale_y );
//draw the canvas on its self. Crazy.
ctx.drawImage(canvas, -start_x, -start_y);// this dam line too me ages to figure out.
ctx.restore();
} else {
end_x = x;
end_y = y;
ctx.fillStyle = gs.color_bg;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.putImageData(graph.graph_image_for_drag,end_x-start_x,end_y-start_y);
}
}
if (mouse_down && gs.mouse_mode === 'measure'){
ctx.beginPath();
ctx.moveTo(start_x,start_y);
ctx.lineTo(x,y);
ctx.stroke();
dx = x - start_x;
dy = y - start_y;
px = graph.pixels_to_units(x,'x');
py = graph.pixels_to_units(y,'y');
px_start = graph.pixels_to_units(start_x,'x');
py_start = graph.pixels_to_units(start_y,'y');
label = graph.get_axis_string(px,'x');
ctx.fillText(label,x+cs,canvas.height-1.5*edge);
label = graph.get_axis_string(py,'y');
ctx.fillText(label,2*edge,y-cs*2);
ctx.beginPath();
ctx.moveTo(x,canvas.height);
ctx.lineTo(x,canvas.height - 1.5*edge );
ctx.moveTo(x,0);
ctx.lineTo(x,2*edge);
ctx.moveTo(0,y);
ctx.lineTo(2*edge,y);
ctx.moveTo(canvas.width,y);
ctx.lineTo(canvas.width-2*edge,y);
ctx.stroke();
ctx.beginPath();
ctx.stroke();
label = graph.get_axis_string(px_start,'x');
ctx.fillText(label,start_x+cs,canvas.height-2*edge);
label = graph.get_axis_string(py_start,'y');
ctx.fillText(label,2*edge,start_y-cs*2);
ctx.beginPath();
ctx.moveTo(start_x,canvas.height);
ctx.lineTo(start_x,canvas.height - 2*edge );
ctx.moveTo(start_x,0);
ctx.lineTo(start_x,2*edge);
ctx.moveTo(0,start_y);
ctx.lineTo(2*edge,start_y);
ctx.moveTo(canvas.width,start_y);
ctx.lineTo(canvas.width-2*edge,start_y);
ctx.stroke();
ctx.beginPath();
ctx.stroke();
//show dx infomation
if (dx > edge/2 || dx < -edge/2){
x_meas = Math.min(y,start_y)-edge;
ctx.beginPath();
ctx.moveTo(x,x_meas-0.3*edge);
ctx.lineTo(x,x_meas+0.3*edge);
ctx.moveTo(start_x,x_meas-0.3*edge);
ctx.lineTo(start_x,x_meas+0.3*edge);
ctx.moveTo(x,x_meas);
ctx.lineTo(start_x,x_meas);
ctx.stroke();
if (gs.x_scale_mode === 'time'){
label = mjs_time_difference_print(px-px_start);
} else {
label = graph.get_axis_string(px-px_start,'x');
}
ctx.fillText(label,(x+start_x)/2-.3*gs.scaling_factor*gs.axis_labels_font_size*label.length,x_meas-.4*gs.axis_labels_font_size);
}
//show dy infomation
if (dy > edge/2 || dy < -edge/2){
y_meas = Math.min(x,start_x)-edge;
//label = mjs_precision(py-py_start, gs.y_precision );
if (gs.y_scale_mode === 'time'){
label = mjs_time_difference_print(py-py_start);
} else {
label = graph.get_axis_string(py-py_start,'y');
}
label_pos = y_meas - .7*gs.axis_labels_font_size*gs.scaling_factor*label.length;
ctx.beginPath();
ctx.moveTo(y_meas-0.3*edge,y);
ctx.lineTo(y_meas+0.3*edge,y);
ctx.moveTo(y_meas-0.3*edge,start_y);
ctx.lineTo(y_meas+0.3*edge,start_y);
ctx.moveTo(y_meas,y);
ctx.lineTo(y_meas,start_y);
ctx.stroke();
ctx.fillText(label,label_pos,(y+start_y)/2);
}
//show grad
if ((dx > edge/2 || dx < -edge/2) && (dy > edge/2 || dy < -edge/2)){
grad = (py-py_start) / (px-px_start);
var unit = '';
if ( (gs.y_scale_mode === 'lin' || gs.y_scale_mode === 'log' ) && gs.x_scale_mode === 'time'){
unit = '/ms';
if (Math.abs(grad)<1){grad*=1000; unit='/s';}
if (Math.abs(grad)<1){grad*=60; unit='/m';}
if (Math.abs(grad)<1){grad*=60; unit='/h';}
if (Math.abs(grad)<1){grad*=24; unit='/d';}
grad = mjs_precision(grad,5);
}
else {
grad = mjs_precision(grad, Math.min(gs.y_precision,gs.x_precision));
}
ctx.fillText('g = ' + grad+unit,x+cs,y-cs);
//show tau if semi log y graph.
if ( gs.y_scale_mode === 'log' && (gs.x_scale_mode === 'lin' || gs.x_scale_mode === 'time' )){
var tau = (Math.log10(py)-Math.log10(py_start)) / (px-px_start);
if ( gs.x_scale_mode === 'time'){
unit = '/ms';
if (Math.abs(tau)<1){tau*=1000; unit='/s';}
if (Math.abs(tau)<1){tau*=60; unit='/m';}
if (Math.abs(tau)<1){tau*=60; unit='/h';}
if (Math.abs(tau)<1){tau*=24; unit='/d';}
tau = mjs_precision(tau,5);
} else {
unit = '';
tau = mjs_precision(tau, Math.min(gs.y_precision,gs.x_precision));
}
ctx.fillText('tau =' + tau+unit,x+cs,y+cs);
var n = Math.ceil(Math.max(dx,dy)/4);
ctx.beginPath();
for (var k = 0;k0){
cap_x += gs.scaling_factor*gs.tick_len;
} else {
cap_x = canvas.width + cap_x;
cap_x -= gs.scaling_factor*gs.tick_len;
}
cap_y = gs.scaling_factor*gs.caption_position.y;
if (gs.caption_position.y>0){
//going down from top
cap_y += gs.scaling_factor*gs.tick_len;
} else {
//up from botton
cap_y = canvas.height + cap_y;
cap_y -= gs.scaling_factor*gs.tick_len;
}
d = Math.sqrt(Math.pow(start_x-cap_x,2) + Math.pow(start_y-cap_y,2));
if (d<2*cs){
ctx.beginPath();
ctx.moveTo(start_x,start_y);
ctx.lineTo(x,y);
ctx.stroke();
var l = 2*cs;
ctx.beginPath();
if (x>canvas.width/2 && y>canvas.height/2){
ctx.moveTo(x-l,y);ctx.lineTo(x,y);ctx.lineTo(x,y-l);
}
if (x>canvas.width/2 && ycanvas.height/2){
ctx.moveTo(x+l,y);ctx.lineTo(x,y);ctx.lineTo(x,y-l);
}
if (x no_drag_size && Math.abs(start_y - end_y) > no_drag_size){
console.log('mouse zoom');
start_x = graph.pixels_to_units(start_x,'x');
start_y = graph.pixels_to_units(start_y,'y');
end_x = graph.pixels_to_units(end_x,'x');
end_y = graph.pixels_to_units(end_y,'y');
gs.x_manual_min = Math.min(start_x,end_x);
gs.x_manual_max = Math.max(start_x,end_x);
gs.y_manual_min = Math.min(start_y,end_y);
gs.y_manual_max = Math.max(start_y,end_y);
gs.x_scale_auto_min = false;
gs.y_scale_auto_min = false;
gs.x_scale_auto_max = false;
gs.y_scale_auto_max = false;
gs.y_scale_tight = true;
gs.x_scale_tight = true;
graph.mjs_plot();
return;
}
}
//touch zoom out button
if (graph.ui.touch && (gs.mouse_mode === 'zoom' || gs.mouse_mode === 'drag') && end_x < 4*edge && end_y >canvas.height-2*edge && end_y canvas.height-3*edge && end_y 2 && ly<7){
gs.show_captions=true;
}
if (ly > 7){
graph.drawcaptionmenu=true;
if (end_x > canvas.width-edge){
gs.captions_extra_precision = Math.min(gs.captions_extra_precision + 1, 10);
} else if (end_x > canvas.width - 2*edge){
gs.captions_extra_precision = Math.max(0,gs.captions_extra_precision - 1);
}
}
graph.mjs_plot();
setTimeout(function(){ mouse_move_event(event,graph); }, 0);
return;
}
if (graph.drawxmenu){
var lx = Math.floor(end_x/edge);
var ly = Math.floor((canvas.height-end_y)/edge);
switch(ly){
case 0:
graph.drawxmenu = false
mouse_move_event(event,graph);
return;
case 1:
if (lx < 8){
gs.x_scale_auto_min=true;
gs.x_scale_tight=false;
} else {
gs.x_scale_auto_min=false;
if (gs.x_scale_mode ==='time'){
var d = new Date(gs.x_manual_min);
var r = Date.parse( prompt("new low limit",d.toISOString() ) );
gs.x_manual_min = r || gs.x_manual_min;
} else {
gs.x_manual_min = getFloat("new low limit",gs.x_manual_min );
}
}
break;
case 2:
if (lx < 8){
gs.x_scale_auto_max=true;
gs.x_scale_tight=false;
} else {
gs.x_scale_auto_max=false;
if (gs.x_scale_mode ==='time'){
var d = new Date(gs.x_manual_max);
var r = Date.parse( prompt("new high limit",d.toISOString() ) );
gs.x_manual_max = r || gs.x_manual_max;
} else {
gs.x_manual_max = getFloat("new high limit",gs.x_manual_max );
}
}
break;
case 3:
if (lx < 6){
gs.x_scale_mode='lin';
} else if ( lx > 9) {
gs.x_scale_mode='time';
} else {
gs.x_scale_mode='log';
}
break;
case 4:
if (lx < 8){
gs.guideWidthx /= 1.5;
} else {
gs.guideWidthx *= 1.5;
}
break;
case 5:
if (lx < 6){
gs.x_label_mode='norm';
} else if ( lx >= 10) {
gs.x_label_mode='sci';
} else if ( lx >= 8) {
gs.x_label_mode='postfix';
} else {
gs.x_label_mode='infix';
}
console.log(gs.x_label_mode);
break;
case 6:
gs.x_axis_prefix = getString("new prefix",gs.x_axis_prefix);
break;
case 7:
gs.x_axis_postfix = getString("new postfix",gs.x_axis_postfix );
break;
case 8:
gs.x_axis_title = getString("new title",gs.x_axis_title );
graph.transform_index--;
break;
}
graph.mjs_plot();
setTimeout(function(){ mouse_move_event(event,graph); }, 0);
return;
}
if (graph.drawymenu){
var lx = Math.floor(end_x/edge)-2;
var ly = Math.floor((canvas.height-end_y)/edge);
switch(ly){
case 0:
graph.drawymenu = false
mouse_move_event(event,graph);
return;
case 1:
if (lx < 8){
gs.y_scale_auto_min=true;
gs.y_scale_tight=false;
} else {
gs.y_scale_auto_min=false;
if (gs.y_scale_mode ==='time'){
var d = new Date(gs.y_manual_min);
var r = Date.parse( prompt("new low limit",d.toISOString() ) );
gs.y_manual_min = r || gs.y_manual_min;
} else {
gs.y_manual_min = getFloat("new low limit",gs.y_manual_min );
}
}
break;
case 2:
if (lx < 8){
gs.y_scale_auto_max=true;
gs.y_scale_tight=false;
} else {
gs.y_scale_auto_max=false;
if (gs.y_scale_mode ==='time'){
var d = new Date(gs.y_manual_max);
var r = Date.parse( prompt("new high limit",d.toISOString() ) );
gs.y_manual_max = r || gs.y_manual_max;
} else {
gs.y_manual_max =getFloat("new high limit",gs.y_manual_max )
}
}
break;
case 3:
if (lx < 6){
gs.y_scale_mode='lin';
} else if ( lx > 9) {
gs.y_scale_mode='time';
} else {
gs.y_scale_mode='log';
}
break;
case 4:
if (lx < 8){
gs.guideWidthy /= 1.5;
} else {
gs.guideWidthy *= 1.5;
}
break;
case 5:
if (lx < 6){
gs.y_label_mode='norm';
} else if ( lx >= 10) {
gs.y_label_mode='sci';
} else if ( lx >= 8) {
gs.y_label_mode='postfix';
} else {
gs.y_label_mode='infix';
}
console.log(gs.y_label_mode);
break;
case 6:
gs.y_axis_prefix = getString("new prefix",gs.y_axis_prefix );
break;
case 7:
gs.y_axis_postfix = getString("new postfix",gs.y_axis_postfix );
break;
case 8:
gs.y_axis_title = getString("new title",gs.y_axis_title );
graph.transform_index--;
break;
}
graph.mjs_plot();
setTimeout(function(){ mouse_move_event(event,graph); }, 0);
return;
}
if(graph.drawmodemenu){
var lx = Math.floor(end_x/edge);
var ly = Math.floor(end_y/edge);
if (ly==0){
graph.drawmodemenu=false;
setTimeout(function(){ mouse_move_event(event,graph); }, 0);
return;
}
if (ly==1){
//symbols
switch(lx){
case 0:
gs.symbol_size*=1.2;
break;
case 1:
gs.symbol_size/=1.2;
break;
case 2:
gs.symbol_mode = "none";
break;
case 3:
gs.symbol_mode = "dot";
break;
case 4:
gs.symbol_mode = "cdot";
break;
case 5:
gs.symbol_mode = "box";
break;
case 6:
gs.symbol_mode = "circ";
break;
case 7:
gs.symbol_mode = "x";
break;
case 8:
gs.symbol_mode = "cross";
break;
}
if (lx > 1){
gs.symbol_modes = [];
}
}
if (ly==2){
//lines
switch(lx){
case 0:
gs.line_thickness*=1.2;
break;
case 1:
gs.line_thickness/=1.2;
break;
case 2:
gs.line_mode = "none";
break;
case 3:
gs.line_mode = "line";
break;
case 4:
gs.line_mode = "approx";
break;
case 5:
gs.line_mode = "interp";
break;
case 6:
gs.line_mode = "zig";
break;
case 7:
gs.line_mode = "zag";
break;
case 8:
gs.line_mode = "mid";
break;
case 9:
gs.line_mode = "hist";
break;
}
if (lx > 1){
gs.line_modes = [];
}
}
graph.do_line_modes();
graph.mjs_plot();
setTimeout(function(){ mouse_move_event(event,graph); }, 0);
return;
}
if(graph.drawgraphmenu){
var ly = Math.floor(end_y/edge);
switch(ly){
case 0:
graph.drawgraphmenu=false;
mouse_move_event(event,graph);
return;
case 1:
gs.title = getString("new title",gs.title );
break;
case 2:
gs.subtitle = getString("new subtitle",gs.subtitle );
break;
case 3:
gs.subtitle2 = getString("new subtitle",gs.subtitle2 );
break;
case 4:
gs.showTransformTexts = !gs.showTransformTexts;
break;
case 5:
gs.x_axis_title = getString("new x axis title",gs.x_axis_title )
graph.transform_index--;
break;
case 6:
gs.y_axis_title = getString("new y axis title",gs.y_axis_title )
graph.transform_index--;
break;
case 7:
gs.scaling_factor/=1.1;
break;
case 8:
gs.scaling_factor*=1.1;
break;
case 9:
graph.reset();
break;
}
graph.mjs_plot();
setTimeout(function(){ mouse_move_event(event,graph); }, 0);
return;
}
if(graph.drawmarkermenu){
var ly = Math.floor(end_y/edge);
if (ly>=2 && ! gs.show_markers){
gs.show_markers = true;
setTimeout(function(){ graph.mjs_plot(); }, 0);
}
if (end_x <17*edge){
switch(ly){
case 0:
break;
case 1:
gs.markers = [];
graph.mjs_plot();
break
case 2:
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'removeMarker';
break
case 3:
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'addmarkertext';
graph.markerText = getString("Text:",'' );
break;
case 4:
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'addmarkerline';
break;
case 5:
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'addmarkerbox';
break;
case 6:
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'addmarkerarrow';
break;
}
} else {
switch(ly){
case 0:
break;
case 1:
//hide and show all markers
gs.show_markers = ! gs.show_markers;
graph.mjs_plot();
break
case 2:
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'markeredit';
break
case 4:
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'addmarkerlinentext';
graph.markerText = getString("Text:",'' );
break
case 5:
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'addmarkerboxntext';
graph.markerText = getString("Text:",'' );
break
case 6:
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'addmarkerarrowntext';
graph.markerText = getString("Text:",'' );
break
}
}
graph.drawmarkermenu=false;
//graph.mjs_plot();
setTimeout(function(){ mouse_move_event(event,graph); }, 0);
return;
}
if(graph.drawStylemenu){
var ly = Math.floor(end_y/edge);
var lx = Math.floor((canvas.width-end_x)/edge)-2; //true for right side false for left
switch(ly){
case 0:
graph.drawStylemenu=false;
mouse_move_event(event,graph);
return;
case 1:
//pick font
graph.drawStylemenu=false;
graph.drawFontMenu=true;
mouse_move_event(event,graph);
return;
case 2:
if (lx){gs.title_font_size--;} else {gs.title_font_size++;}
break;
case 3:
if (lx){gs.tick_labels_font_size--;} else {gs.tick_labels_font_size++;}
break;
case 4:
if (lx){gs.axis_labels_font_size--;} else {gs.axis_labels_font_size++;}
break;
case 5:
if (lx){gs.title_spacing--;} else {gs.title_spacing++;}
break;
case 6:
if (lx){gs.tick_lables_font_padding--;} else {gs.tick_lables_font_padding++;}
break;
case 7:
if (lx){gs.lable_spacing--;} else {gs.lable_spacing++;}
break;
case 8:
gs.color_fg = getString("new forground color",gs.color_fg );
break;
case 9:
gs.color_bg = getString("new background color",gs.color_bg );
break;
case 10:
gs.color_mg = getString("new midground color",gs.color_mg );
break;
case 11:
if (lx){gs.graph_line_thickness = Math.max(0.1,gs.graph_line_thickness-0.5);} else {gs.graph_line_thickness+=0.5;}
break;
case 12:
if (lx){gs.tick_len = Math.max(0.1,gs.tick_len-1);} else {gs.tick_len+=1;}
break;
case 13:
if (lx){gs.minor_tick_len = Math.max(0.1,gs.minor_tick_len-1);} else {gs.minor_tick_len+=1;}
break;
}
graph.mjs_plot();
mouse_move_event(event,graph);
return;
}
if(graph.drawFontMenu){
var ly = Math.floor(end_y/edge)-1;
if (ly == -1){
gs.font_name = getString("font",gs.font_name );
} else {
gs.font_name = MJSFontList.fontList[ly];
}
graph.mjs_plot();
mouse_move_event(event,graph);
return;
}
//top buttons
if (end_y < edge){
if ( end_x < 4*edge){
graph.drawmodemenu = true;
mouse_move_event(event,graph);
return;
}
if ( end_x >4*edge && end_x <8 *edge){
graph.drawgraphmenu = true;
mouse_move_event(event,graph);
return;
}
if ( end_x < 10*edge && end_x > 9*edge){
//drag button
gs.mouse_mode = 'drag';
graph.needs_drag_image = true;
graph.mjs_plot();
mouse_move_event(event,graph);
return;
}
if ( end_x < 11*edge && end_x > 10*edge){
//zoom button
gs.mouse_mode = 'zoom';
mouse_move_event(event,graph);
return;
}
if ( end_x < 12*edge && end_x > 11*edge){
//measure button
gs.mouse_mode = 'measure';
mouse_move_event(event,graph);
return;
}
if ( end_x < 13*edge && end_x > 12*edge){
//data reader button
gs.mouse_mode = 'reader';
mouse_move_event(event,graph);
return;
}
if ( end_x < 14*edge && end_x > 13*edge){
//mouse button
gs.mouse_mode = 'mouse';
mouse_move_event(event,graph);
return;
}
if ( end_x > 14*edge && end_x < 17*edge){
//mouse button
graph.drawmarkermenu = true;
mouse_move_event(event,graph);
return;
}
if ( end_x < canvas.width && end_x > canvas.width - edge){
//show/hide captions
graph.drawcaptionmenu = true;
mouse_move_event(event,graph);
return;
}
if ( end_x < canvas.width - edge && end_x > canvas.width - 2*edge){
//infomation button
gs.i = (gs.i + 1)%2;//a two state button.
}
if ( end_x < canvas.width - 2*edge && end_x > canvas.width - 5*edge){
graph.drawStylemenu = true;
mouse_move_event(event,graph);
return;
}
graph.mjs_plot();
mouse_move_event(event,graph);
return;
}
if(graph.drawtimemenu){
var lx = 10*edge;
var ldx = 2*edge;
var ly = canvas.height - edge*3;
var ldy = edge;
for (var i = 0;i<2;i++){
if (end_y > ly-(i+1)*ldy && end_y < ly-i*ldy && end_x>lx && end_x < lx+4*ldx){
for (var j = 0;j<4;j++){
var selected = end_x>lx+j*ldx && end_x < lx+(j+1)*ldx;
if (selected){graph.timemenuoptions[i] = j;}
}
}
}
if (end_y < ly +2*ldy && end_y > ly +1*ldy && end_x > lx && end_x < lx+3*ldx){
//apply to x button
console.log('apply button');
var n = gs.data_transforms.length;
gs.data_transforms[n] = "time_to_num";
gs.x_scale_mode ='lin';
var now = new Date();
gs.data_transforms_args[n] = [graph.timemenuoptions[1],graph.timemenuoptions[0],'x',now.getTime()];
graph.drawtimemenu = false;
graph.mjs_plot();
}
if (end_y < ly +2*ldy && end_y > ly +1*ldy && end_x > lx+3*ldx && end_x < lx+6*ldx){
//apply to ybutton
console.log('apply button');
var n = gs.data_transforms.length;
gs.data_transforms[n] = "time_to_num";
gs.y_scale_mode ='lin';
var now = new Date();
gs.data_transforms_args[n] = [graph.timemenuoptions[1],graph.timemenuoptions[0],'y',now.getTime()];
graph.drawtimemenu = false;
graph.mjs_plot();
}
mouse_move_event(event,graph);
return;
}
//function buttons
if (graph.drawfxmenu){
var n = gs.data_transforms.length;
if ( end_x < 12*edge && end_x > 8*edge && end_y > canvas.height-edge){
//pressed the fit menu button again
graph.drawfxmenu = false;
mouse_move_event(event,graph);
return;
}
if ( end_x < 12*edge && end_x > 8*edge && end_y > canvas.height - edge*2 && end_y < canvas.height - edge*1){
// reset button
console.log('resetbutton');
graph.graphics_style.data_transforms = [];
graph.graphics_style.data_transforms_args = [];
if (gs.mode === 'hist dot'){
gs.mode = 'line dot';
}
}
if ( end_x < 16*edge && end_x > 12*edge && end_y > canvas.height - edge*1){
// pop button
console.log('pop');
gs.data_transforms.pop();
gs.data_transforms_args.pop();
}
if ( end_x < 12*edge && end_x >8*edge && end_y > canvas.height - edge*14 && end_y < canvas.height - edge*13){
// y vs y button
console.log('y vs y');
if (gs.data_transforms[n-1] == "y_vs_y"){
gs.data_transforms_args[n-1][0] += 1;
graph.transform_index--;
} else {
gs.data_transforms[n] = "y_vs_y";
gs.data_transforms_args[n] = [1];
}
}
if ( end_x < 12*edge && end_x > 8*edge && end_y > canvas.height - edge*3 && end_y < canvas.height - edge*2){
// cut button button
console.log('cut button');
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'cut';
graph.drawfxmenu = false;
}
if ( end_x < 12*edge && end_x > 8*edge && end_y > canvas.height - edge*4 && end_y < canvas.height - edge*3){
// normalise button
console.log('normalise');
if (gs.data_transforms[n-1] == "normalise"){
gs.data_transforms.pop();
gs.data_transforms_args.pop();
} else {
gs.data_transforms[n] = "normalise";
gs.data_transforms_args[n] = [0];
}
gs.y_scale_auto_min = true;
gs.y_scale_auto_max = true;
}
if ( end_x < 10*edge && end_x > 8*edge && end_y > canvas.height - edge*5 && end_y < canvas.height - edge*4){
// y-c button
console.log('y-c');
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'y-c';
graph.drawfxmenu = false;
}
if ( end_x < 12*edge && end_x > 10*edge && end_y > canvas.height - edge*5 && end_y < canvas.height - edge*4){
// x-c button
console.log('x-c');
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'x-c';
graph.drawfxmenu = false;
}
if ( end_x < 12*edge && end_x > 8*edge && end_y > canvas.height - edge*6 && end_y < canvas.height - edge*5){
// smooth button
console.log('smooth');
if (gs.data_transforms[n-1] == "smooth"){
gs.data_transforms_args[n-1][0] = gs.data_transforms_args[n-1][0]*1.467799;
graph.transform_index--;
} else {
gs.data_transforms[n] = "smooth";
gs.data_transforms_args[n] = [2.1544339];
}
}
if ( end_x < 12*edge && end_x > 8*edge && end_y > canvas.height - edge*7 && end_y < canvas.height - edge*6){
// interpolate button
console.log('interpolate');
if (gs.data_transforms[n-1] == "interpolate"){
gs.data_transforms_args[n-1][0] *= 2;
graph.transform_index--;
} else {
gs.data_transforms[n] = "interpolate";
gs.data_transforms_args[n] = [20];
}
}
if ( end_x < 12*edge && end_x > 8*edge && end_y > canvas.height - edge*8 && end_y < canvas.height - edge*7){
// subtract user defined line
console.log('-baseline');
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'sublin';
graph.drawfxmenu = false;
//gs.data_transforms[n] = "remove_linear";
//gs.data_transforms_args[n] = [];
}
if ( end_x < 12*edge && end_x > 8*edge && end_y > canvas.height - edge*9 && end_y < canvas.height - edge*8){
// differentiate button
console.log('dy/dx');
gs.data_transforms[n] = "differentiate";
gs.data_transforms_args[n] = [0];
gs.y_scale_auto_min = true;
gs.y_scale_auto_max = true;
}
if ( end_x < 12*edge && end_x > 10*edge && end_y > canvas.height - edge*10 && end_y < canvas.height - edge*9){
// e to the power x button
console.log('ex');
if (gs.data_transforms[n-1] == "ln_x"){
gs.data_transforms.pop();
gs.data_transforms_args.pop();
} else {
gs.data_transforms[n] = "e_to_x";
gs.data_transforms_args[n] = [0];
}
}
if ( end_x < 10*edge && end_x > 8*edge && end_y > canvas.height - edge*10 && end_y < canvas.height - edge*9){
// ln x button
console.log('lnx');
if (gs.data_transforms[n-1] == "e_to_x"){
gs.data_transforms.pop();
gs.data_transforms_args.pop();
} else {
gs.data_transforms[n] = "ln_x";
gs.data_transforms_args[n] = [0];
}
}
if ( end_x < 12*edge && end_x > 10*edge && end_y > canvas.height - edge*11 && end_y < canvas.height - edge*10){
// ten to the x button
console.log('10^x');
if (gs.data_transforms[n-1] == "log_x"){
gs.data_transforms.pop();
gs.data_transforms_args.pop();
} else {
gs.data_transforms[n] = "ten_to_x";
gs.data_transforms_args[n] = [0];
}
}
if ( end_x < 10*edge && end_x > 8*edge && end_y > canvas.height - edge*11 && end_y < canvas.height - edge*10){
// log x button
console.log('logx');
if (gs.data_transforms[n-1] == "ten_to_x"){
gs.data_transforms.pop();
gs.data_transforms_args.pop();
} else {
gs.data_transforms[n] = "log_x";
gs.data_transforms_args[n] = [0];
}
}
if ( end_x < 12*edge && end_x > 10*edge && end_y > canvas.height - edge*12 && end_y < canvas.height - edge*11){
// x over 10 button
console.log('x/10');
if (gs.data_transforms[n-1] == "scale_x"){
console.log('incresing scaling');
gs.data_transforms_args[n-1][0] *= 0.1;
graph.transform_index--;
} else {
gs.data_transforms[n] = "scale_x";
gs.data_transforms_args[n] = [0.1];
}
gs.x_manual_min *=0.10;
gs.x_manual_max *=0.10;
}
if ( end_x < 10*edge && end_x > 8*edge && end_y > canvas.height - edge*12 && end_y < canvas.height - edge*11){
// ten x button
console.log('10x');
if (gs.data_transforms[n-1] == "scale_x"){
console.log('incresing scaling');
gs.data_transforms_args[n-1][0] *= 10;
graph.transform_index--;
} else {
gs.data_transforms[n] = "scale_x";
gs.data_transforms_args[n] = [10];
}
gs.x_manual_min *=10;
gs.x_manual_max *=10;
}
if ( end_x < 12*edge && end_x > 10*edge && end_y > canvas.height - edge*13 && end_y < canvas.height - edge*12){
// one over x button
console.log('1/x');
if (gs.data_transforms[n-1] == "one_over_x"){
gs.data_transforms.pop();
gs.data_transforms_args.pop();
} else {
gs.data_transforms[n] = "one_over_x";
gs.data_transforms_args[n] = [];
}
}
if ( end_x < 10*edge && end_x > 8*edge && end_y > canvas.height - edge*13 && end_y < canvas.height - edge*12){
// neg x button
console.log('-x');
if (gs.data_transforms[n-1] == "scale_x"){
console.log('incresing scaling');
gs.data_transforms_args[n-1][0] *= -1;
graph.transform_index--;
} else {
gs.data_transforms[n] = "scale_x";
gs.data_transforms_args[n] = [-1];
}
var oldmin = gs.x_manual_min*-1;
var oldmax = gs.x_manual_max*-1;
gs.x_manual_min = Math.min(oldmin,oldmax);
gs.x_manual_max = Math.max(oldmin,oldmax);
}
if ( end_x < 16*edge && end_x > 12*edge && end_y > canvas.height - edge*2 && end_y < canvas.height - edge*1){
// swap button
console.log('x<->y');
if (gs.data_transforms[n-1] == "swap_x_y"){
console.log('undoing swap');
gs.data_transforms.pop();
gs.data_transforms_args.pop();
} else {
gs.data_transforms[n] = "swap_x_y";
gs.data_transforms_args[n] = [];
}
var temp = gs.x_manual_min;
gs.x_manual_min =gs.y_manual_min;
gs.y_manual_min =temp;
temp =gs.x_manual_max;
gs.x_manual_max =gs.y_manual_max;
gs.y_manual_max =temp;
temp = gs.x_scale_mode;
gs.x_scale_mode = gs.y_scale_mode;
gs.y_scale_mode = temp;
//swap the axis prefixs postfixes
temp = gs.x_axis_prefix;
gs.x_axis_prefix = gs.y_axis_prefix;
gs.y_axis_prefix = temp;
temp = gs.x_axis_postfix
gs.x_axis_postfix = gs.y_axis_postfix;
gs.y_axis_postfix = temp;
temp = gs.x_label_mode;
gs.x_label_mode = gs.y_label_mode;
gs.y_label_mode = temp;
}
if ( end_x < 16*edge && end_x > 12*edge && end_y > canvas.height - edge*3 && end_y < canvas.height - edge*2){
// trim button
console.log('trim');
graph.pre_mouse_mode = gs.mouse_mode;
gs.mouse_mode = 'trim';
graph.drawfxmenu = false;
}
if ( end_x < 16*edge && end_x > 14*edge && end_y > canvas.height - edge*4 && end_y < canvas.height - edge*3){
// y^(n) button
console.log('y^(n)');
if (gs.data_transforms[n-1] == "y_pow_n"){
console.log('incresing power');
gs.data_transforms_args[n-1][0] += 1;
var pwr = gs.data_transforms_args[n-1][0];
gs.y_manual_min = Math.pow(gs.y_manual_min,pwr/(pwr-1));
gs.y_manual_max = Math.pow(gs.y_manual_max,pwr/(pwr-1));
graph.transform_index--;
} else {
gs.data_transforms[n] = "y_pow_n";
gs.data_transforms_args[n] = [2];
gs.y_manual_min = Math.pow(gs.y_manual_min,2);
gs.y_manual_max = Math.pow(gs.y_manual_max,2);
}
}
if ( end_x < 14*edge && end_x > 12*edge && end_y > canvas.height - edge*4 && end_y < canvas.height - edge*3){
// some button
console.log('x^(n)');
if (gs.data_transforms[n-1] == "x_pow_n"){
console.log('incresing power');
gs.data_transforms_args[n-1][0] += 1;
var pwr = gs.data_transforms_args[n-1][0];
gs.x_manual_min = Math.pow(gs.x_manual_min,pwr/(pwr-1));
gs.x_manual_max = Math.pow(gs.x_manual_max,pwr/(pwr-1));
graph.transform_index--;
} else {
gs.data_transforms[n] = "x_pow_n";
gs.data_transforms_args[n] = [2];
gs.x_manual_min = Math.pow(gs.x_manual_min,2);
gs.x_manual_max = Math.pow(gs.x_manual_max,2);
}
}
if ( end_x < 16*edge && end_x > 14*edge && end_y > canvas.height - edge*5 && end_y < canvas.height - edge*4){
// some button
console.log('sqrty');
if (gs.data_transforms[n-1] == "y_nth_root"){
console.log('incresing power');
gs.data_transforms_args[n-1][0] += 1;
var pwr = gs.data_transforms_args[n-1][0];
graph.transform_index--;
} else {
gs.data_transforms[n] = "y_nth_root";
gs.data_transforms_args[n] = [2];
var pwr = 2;
}
var rtmin = Math.pow(Math.abs(gs.y_manual_min),1/2);
var rtmax = Math.pow(Math.abs(gs.y_manual_max),1/2)
if (gs.y_manual_min < 0 && gs.y_manual_max > 0){
//if graph is accross zero then make the minimum zero. max could be ether still.
var make_zero = 0;
} else {make_zero = 1;}
gs.y_manual_min = Math.min( rtmin,rtmax)*make_zero;
gs.y_manual_max = Math.max( rtmin,rtmax);
}
if ( end_x < 14*edge && end_x > 12*edge && end_y > canvas.height - edge*5 && end_y < canvas.height - edge*4){
// some button
console.log('sqrtx');
if (gs.data_transforms[n-1] == "x_nth_root"){
console.log('incresing power');
gs.data_transforms_args[n-1][0] += 1;
var pwr = gs.data_transforms_args[n-1][0];
graph.transform_index--;
} else {
gs.data_transforms[n] = "x_nth_root";
gs.data_transforms_args[n] = [2];
var pwr = 2;
}
var rtmin = Math.pow(Math.abs(gs.x_manual_min),1/2);
var rtmax = Math.pow(Math.abs(gs.x_manual_max),1/2)
if (gs.x_manual_min < 0 && gs.x_manual_max > 0){
//if graph is accross zero then make the minimum zero. max could be ether still.
var make_zero = 0;
} else {make_zero = 1;}
gs.x_manual_min = Math.min( rtmin,rtmax)*make_zero;
gs.x_manual_max = Math.max( rtmin,rtmax);
}
if ( end_x < 16*edge && end_x > 14*edge && end_y > canvas.height - edge*6 && end_y < canvas.height - edge*5){
// some button
console.log('B-A');
}
if ( end_x < 14*edge && end_x > 12*edge && end_y > canvas.height - edge*6 && end_y < canvas.height - edge*5){
// some button
console.log('sum');
gs.data_transforms[n] = "sum";
gs.data_transforms_args[n] = [];
}
if ( end_x < 16*edge && end_x > 12*edge && end_y > canvas.height - edge*7 && end_y < canvas.height - edge*6){
// some button
console.log('Jitter');
if (gs.data_transforms[n-1] == "jitter"){
gs.data_transforms_args[n-1][0] += 1;
graph.transform_index--;
} else {
gs.data_transforms[n] = "jitter";
gs.data_transforms_args[n] = [1];
}
}
if ( end_x < 16*edge && end_x > 12*edge && end_y > canvas.height - edge*8 && end_y < canvas.height - edge*7){
// some button
console.log('Hist');
gs.data_transforms[n] = "hist";
gs.data_transforms_args[n] = [0];
gs.x_scale_auto_max = true;
gs.x_scale_auto_min = true;
gs.y_scale_auto_max = true;
gs.y_scale_auto_min = true;
gs.x_scale_mode = 'lin';
gs.y_scale_mode = 'lin';
gs.mode = 'hist dot';
}
if ( end_x < 16*edge && end_x > 12*edge && end_y > canvas.height - edge*9 && end_y < canvas.height - edge*8){
// some button
console.log('Intergrate');
gs.data_transforms[n] = "intergrate";
gs.data_transforms_args[n] = [0];
gs.y_scale_auto_min = true;
gs.y_scale_auto_max = true;
}
if ( end_x < 16*edge && end_x > 14*edge && end_y > canvas.height - edge*10 && end_y < canvas.height - edge*9){
// some button
console.log('ey');
gs.data_transforms[n] = "e_to_y";
gs.data_transforms_args[n] = [0];
}
if ( end_x < 14*edge && end_x > 12*edge && end_y > canvas.height - edge*10 && end_y < canvas.height - edge*9){
// some button
console.log('lny');
gs.data_transforms[n] = "ln_y";
gs.data_transforms_args[n] = [0];
}
if ( end_x < 16*edge && end_x > 14*edge && end_y > canvas.height - edge*11 && end_y < canvas.height - edge*10){
// some button
console.log('10^y');
gs.data_transforms[n] = "ten_to_y";
gs.data_transforms_args[n] = [0];
}
if ( end_x < 14*edge && end_x > 12*edge && end_y > canvas.height - edge*11 && end_y < canvas.height - edge*10){
// some button
console.log('logy');
gs.data_transforms[n] = "log_y";
gs.data_transforms_args[n] = [0];
}
if ( end_x < 16*edge && end_x > 14*edge && end_y > canvas.height - edge*12 && end_y < canvas.height - edge*11){
// some button
console.log('y/10');
if (gs.data_transforms[n-1] == "scale_y"){
console.log('incresing scaling');
gs.data_transforms_args[n-1][0] *= 0.1;
graph.transform_index--;
} else {
gs.data_transforms[n] = "scale_y";
gs.data_transforms_args[n] = [0.1];
}
}
if ( end_x < 14*edge && end_x > 12*edge && end_y > canvas.height - edge*12 && end_y < canvas.height - edge*11){
// some button
console.log('10y');
if (gs.data_transforms[n-1] == "scale_y"){
console.log('incresing scaling');
gs.data_transforms_args[n-1][0] *= 10;
graph.transform_index--;
} else {
gs.data_transforms[n] = "scale_y";
gs.data_transforms_args[n] = [10];
}
}
if ( end_x < 16*edge && end_x > 14*edge && end_y > canvas.height - edge*13 && end_y < canvas.height - edge*12){
// some button
console.log('1/y');
if (gs.data_transforms[n-1] == "one_over_y"){
gs.data_transforms.pop();
gs.data_transforms_args.pop();
} else {
gs.data_transforms[n] = "one_over_y";
gs.data_transforms_args[n] = [];
}
}
if ( end_x < 14*edge && end_x > 12*edge && end_y > canvas.height - edge*13 && end_y < canvas.height - edge*12){
// some button
console.log('-y');
if (gs.data_transforms[n-1] == "scale_y"){
console.log('incresing scaling');
gs.data_transforms_args[n-1][0] *= -1;
graph.transform_index--;
} else {
gs.data_transforms[n] = "scale_y";
gs.data_transforms_args[n] = [-1];
}
var oldmin = gs.y_manual_min*-1;
var oldmax = gs.y_manual_max*-1;
gs.y_manual_min = Math.min(oldmin,oldmax);
gs.y_manual_max = Math.max(oldmin,oldmax);
}
if ( end_x < 22*edge && end_x >16*edge && end_y > canvas.height - edge*14 && end_y < canvas.height - edge*13){
// subtract trend button
console.log('subtract trend');
if (fits.fit_strings.indexOf(gs.fits)>-1){
gs.data_transforms[n] = "subtract_fit";
gs.data_transforms_args[n] = [gs.fits];
gs.fits = 'none';
} else {
graph.errors.push('No fit chosen');
}
}
if ( end_x < 22*edge && end_x >16*edge && end_y > canvas.height - edge*13 && end_y < canvas.height - edge*12){
// remove outliers button
console.log('subtract trend');
if (gs.data_transforms[n-1] == "remove_outliers"){
gs.data_transforms_args[n-1][1] -= 1;
graph.transform_index--;
} else {
if (fits.fit_strings.indexOf(gs.fits)>-1){
//only add if there is a valid fit
gs.data_transforms[n] = "remove_outliers";
gs.data_transforms_args[n] = [gs.fits,5];
gs.fits = 'old';
} else {
graph.errors.push('No fit chosen');
}
}
}
if ( end_x < 22*edge && end_x >16*edge && end_y > canvas.height - edge*12 && end_y < canvas.height - edge*11){
// remove outliers button
console.log('subtract trend');
if (gs.data_transforms[n-1] == "keep_outliers"){
gs.data_transforms_args[n-1][1] += 1;
graph.transform_index--;
} else {
if (fits.fit_strings.indexOf(gs.fits)>-1){
gs.data_transforms[n] = "keep_outliers";
gs.data_transforms_args[n] = [gs.fits,0];
gs.fits = 'old';
} else {
graph.errors.push('No fit chosen');
}
}
}
if ( end_x < 22*edge && end_x >16*edge && end_y > canvas.height - edge*11 && end_y < canvas.height - edge*10){
console.log('custom f(x) button');
var f = prompt('y = f(x,y) =','y');
if (f){
gs.data_transforms[n] = "custom_y";
gs.data_transforms_args[n] = [f];
}
}
if ( end_x < 22*edge && end_x >16*edge && end_y > canvas.height - edge*10 && end_y < canvas.height - edge*9){
// remove outliers button
console.log('custom filter button');
var f;
if (gs.x_scale_mode ==='time' || gs.y_scale_mode ==='time' ){
f = prompt('use, >, <, >=, <=, ==, x, y, now (time) and maths','x > now - 1000*60*60');
} else {
f = prompt('use, >, <, >=, <=, ==, x, y and maths','x*2+y*2 < 16');
}
if (f){
gs.data_transforms[n] = "custom_filter";
gs.data_transforms_args[n] = [f];
}
}
if ( end_x < 22*edge && end_x >16*edge && end_y > canvas.height - edge*2 && end_y < canvas.height - edge*1){
// spacing button
console.log('x spacing');
gs.data_transforms[n] = "spacing";
gs.data_transforms_args[n] = [];
}
if ( end_x < 22*edge && end_x >16*edge && end_y > canvas.height - edge*5 && end_y < canvas.height - edge*4){
gs.data_transforms[n] = "accumulate";
gs.data_transforms_args[n] = [ ];
}
if ( end_x < 22*edge && end_x >16*edge && end_y > canvas.height - edge*4 && end_y < canvas.height - edge*3){
// view last n button
console.log('view_last_n');
gs.data_transforms[n] = "view_last_n";
gs.data_transforms_args[n] = [ getInt("How Many Points",512) ];
}
if ( end_x < 22*edge && end_x >16*edge && end_y > canvas.height - edge*3 && end_y < canvas.height - edge*2){
// mean_normalise button
console.log('mean_normalise');
gs.data_transforms[n] = "mean_normalise";
gs.data_transforms_args[n] = [];
}
if ( end_x < 22*edge && end_x >16*edge && end_y > canvas.height - edge*1 && end_y < canvas.height){
// time menu button
console.log('time menu');
graph.drawtimemenu=true;
graph.drawfxmenu = false;
}
graph.mjs_plot();
mouse_move_event(event,graph);
return;
}
//line menu buttons
if (graph.drawlinemenu){
if ( end_x < 15*edge && end_x > 11*edge && end_y > canvas.height-edge){
//pressed the fit menu button again
graph.drawlinemenu = false;
mouse_move_event(event,graph);
return;
}
//pressed hide/show
if ( end_x < 21*edge && end_x > 11*edge && end_y < canvas.height-edge){
var no_of_lines = graph.data_backup.length;
var no_of_function_lines = gs.function_lines.length;
var item = Math.floor((no_of_lines+no_of_function_lines+1)-((canvas.height-end_y) / edge -1));
if (item == 0){
console.log('new function');
var f;
if (gs.x_scale_mode === 'time'){
f = prompt('y = f(x)','cos(2*pi*x/1000/60/60/24)');
} else {
f = prompt('y = f(x)','x*x/2');
}
if (f){
gs.function_lines.push(f);
}
}
item -=1;
if (item < no_of_function_lines && item >= 0 && end_x < 13*edge){
console.log('remove item '+ item);
gs.function_lines.splice(no_of_function_lines-item-1,1);
}
if (item < no_of_function_lines && item >= 0 && end_x > 13*edge){
console.log('edit item '+ item);
var f = prompt('y = f(x)',gs.function_lines[no_of_function_lines-item-1]);
if (f){
gs.function_lines[no_of_function_lines-item-1] = f;
}
//gs.function_lines.splice(no_of_function_lines-item-1,1);
}
item -= no_of_function_lines;
if (item >= 0){
console.log('hideshow'+ item);
if (end_x > 20*edge){
//line colour stuff
gs.line_colours[item] = getString("new color name or hex",graph.colors_backup[item] );
} else if (end_x > 19*edge){
gs.symbol_modes[item] = getString("new symbol mode ( dot cdot circ box x cross)",graph.symbol_modes_backup[item] );
gs.line_modes[item] = getString("new line mode ( line zig zag mid arrpox interp hist)",graph.line_modes_backup[item] );
graph.do_line_modes()
} else {
gs.hidden_lines[item] = !gs.hidden_lines[item];
}
}
}
//all button
if ( end_x < 17*edge && end_x > 15*edge && end_y > canvas.height-edge){
console.log('all');
for (var k = 0;k 17*edge && end_y > canvas.height-edge){
console.log('non');
for (var k = 0;k my - dy && end_y < my){
return;
}
my-=dy;
if ( end_y > my - dy && end_y < my){
console.log('csv');
//find longest data set
len = 0;
for (i = 0;i my - dy && end_y < my){
console.log('code');
var theBody = document.getElementsByTagName('body')[0];
var text = '';
for (i = 0;i my - dy && end_y < my){
console.log('code matlab');
var theBody = document.getElementsByTagName('body')[0];
var text = '';
for (i = 0;i my - dy && end_y < my){
console.log('code JSON');
var text = '';
text = JSON.stringify(graph.data);
download_text(text,'mjsplot_graph.json');
}
my-=dy;
if ( end_y > my - dy && end_y < my){
console.log('tabbed');
//find longest data set
var len = 0;
for (i = 0;i my - dy && end_y < my){
console.log('png hi');
graph.export_png(graph.canvas.width*3,graph.canvas.height*3,3);
}
my-=dy;
if ( end_y > my - dy && end_y < my){
console.log('png low');
graph.export_png(graph.canvas.width,graph.canvas.height,1);
}
my-=dy;
if ( end_y > my - dy && end_y < my){
console.log('png (small figure)');
graph.export_png(1000,750,2.5);
}
my-=dy;
if ( end_y > my - dy && end_y < my){
console.log('png (large figure)');
graph.export_png(1000*2,750*2,2.5);
}
my-=dy;
if (end_x > canvas.width - 8*edge){
if ( end_y > my - dy && end_y < my){
console.log('svg');
graph.export_svg(graph.canvas.width,graph.canvas.height);
}my-=dy;
if ( end_y > my - dy && end_y < my){
console.log('svg (small fig)');
graph.export_svg(1000/2.5,750/2.5);
}my-=dy;
if ( end_y > my - dy && end_y < my){
console.log('svg (large fig)');
graph.export_svg(2*1000/2.5,2*750/2.5);
}my-=dy;
} else {
if ( end_y > my - dy && end_y < my){
console.log('svgpng');
graph.export_svg_png(graph.canvas.width,graph.canvas.height,3);
}my-=dy;
if ( end_y > my - dy && end_y < my){
console.log('svgpng (small fig)');
graph.export_svg_png(1000/2.5,750/2.5,3);
}my-=dy;
if ( end_y > my - dy && end_y < my){
console.log('svgpng (large fig)');
graph.export_svg_png(2*1000/2.5,2*750/2.5,3);
}my-=dy;
}
if ( end_y > my - dy && end_y < my){
console.log('html (link)');
var text = '';
var nl= ' \n';
var graph_name = "egraph";//graph.graph_name;
//get a random 4 character name.
var graph_name = window.btoa(Math.random().toString()).slice(6,10);
var canvas_name = graph.canvas_name ;
var mjsplot_embed = ''
var canvas_embed = '';
var data_code = JSON.stringify(graph.data_backup);
var gs_code = JSON.stringify(graph.graphics_style);
var captions_code = JSON.stringify(graph.captions_backup);
text+= graph_name+'.set_data( '+data_code+' );'+nl;
text+= graph_name+'.set_captions( '+captions_code+' ); '+nl;
text+= graph_name+'.default_graphics_style = '+gs_code+'; '+nl;
var shower = MJS_PLOT_LINK_LOADER;
var url = shower+graph_name+LZString.compressToEncodedURIComponent(text);
window.open(url, graph.graphics_style.title, "width="+graph.canvas.width+", height="+graph.canvas.height);
}my-=dy;
return;
}
//fit menu buttons
if (graph.drawfitsmenu){
if ( end_x < 21*edge && end_x > 18*edge && end_y > canvas.height-edge){
//pressed none fits button
gs.fits = 'none';
}
if ( end_y > canvas.height-2*edge && end_y < canvas.height-1*edge){
gs.extrapolate = !gs.extrapolate;
}
if ( end_y > canvas.height-3*edge && end_y < canvas.height-2*edge && end_x > 18*edge){
gs.fits = 'stats';
}
if ( end_y > canvas.height-4*edge && end_y < canvas.height-3*edge && end_x > 18*edge){
gs.fits = 'exp';
}
if ( end_y > canvas.height-5*edge && end_y < canvas.height-4*edge && end_x > 18*edge){
gs.fits = 'exp_c';
}
if ( end_y > canvas.height-6*edge && end_y < canvas.height-5*edge && end_x > 18*edge){
gs.fits = 'log';
}
if ( end_y > canvas.height-7*edge && end_y < canvas.height-6*edge && end_x > 18*edge){
gs.fits = 'power';
}
if ( end_y > canvas.height-8*edge && end_y < canvas.height-7*edge && end_x > 18*edge){
gs.fits = 'power_c';
}
if ( end_y > canvas.height-9*edge && end_y < canvas.height-8*edge && end_x > 18*edge){
gs.fits = 'gauss';
}
if ( end_y > canvas.height-3*edge && end_y < canvas.height-2*edge && end_x < 18*edge){
gs.fits = 'const';
}
if ( end_y > canvas.height-4*edge && end_y < canvas.height-3*edge && end_x < 18*edge){
gs.fits = 'linear';
}
if ( end_y > canvas.height-5*edge && end_y < canvas.height-4*edge && end_x < 18*edge){
gs.fits = 'quad';
}
if ( end_y > canvas.height-6*edge && end_y < canvas.height-5*edge && end_x < 18*edge){
gs.fits = 'cubic';
}
if ( end_y > canvas.height-7*edge && end_y < canvas.height-6*edge && end_x < 18*edge){
gs.fits = 'poly4';
}
if ( end_y > canvas.height-8*edge && end_y < canvas.height-7*edge && end_x < 18*edge){
gs.fits = 'poly5';
}
if ( end_y > canvas.height-9*edge && end_y < canvas.height-8*edge && end_x < 18*edge){
gs.fits = 'poly6';
}
if ( end_y > canvas.height-10*edge && end_y < canvas.height-9*edge && end_x < 18*edge){
gs.fits = 'poly5';
}
if ( end_x < 18*edge && end_x > 15*edge && end_y > canvas.height-edge){
//pressed the fit menu button again
graph.drawfitsmenu = false;
mouse_move_event(event,graph);
return;
}
if ( end_x < 21*edge && end_x > 18*edge && end_y > canvas.height-edge){
//pressed none fits button
gs.fits = 'none';
graph.drawfitsmenu = false;
}
graph.mjs_plot();
console.log(gs.fits);
mouse_move_event(event,graph);
return;
}
//bottom buttons
if (end_y>canvas.height-edge){
if ( end_x < 2*edge && end_x > 0*edge){
graph.drawxmenu = true;
mouse_move_event(event,graph);
return;
}
if ( end_x < 4*edge && end_x > 2*edge){
graph.drawymenu = true;
mouse_move_event(event,graph);
return;
}
if ( end_x < 5*edge && end_x > 4*edge){
gs.show_grid = !gs.show_grid;
}
if ( end_x < 6*edge && end_x > 5*edge){
//y scalemode button
if (gs.y_scale_mode === 'log' ){
gs.y_scale_mode = 'lin';
} else {
gs.y_scale_mode = 'log';
}
}
if ( end_x < 7*edge && end_x > 6*edge){
//x scalemode button
if (gs.x_scale_mode === 'log' ){
gs.x_scale_mode = 'lin';
} else {
gs.x_scale_mode = 'log';
}
}
if ( end_x < 8*edge && end_x > 7*edge){
//x scalemode button
gs.x_scale_mode = 'time';
}
if ( end_x < 11*edge && end_x > 8*edge){
//f() menu button
graph.drawfxmenu = true;
mouse_move_event(event,graph);
return;
}
if ( end_x < 15*edge && end_x > 11*edge){
//line menu button
console.log('line menu button');
graph.drawlinemenu = true;
mouse_move_event(event,graph);
return;
}
if ( end_x < 19*edge && end_x > 15*edge){
//fits menu button
console.log('fits menu button');
graph.drawfitsmenu = true;
mouse_move_event(event,graph);
return;
}
if ( end_x < canvas.width && end_x > canvas.width - edge){
graph.drawexportmenu = true;
mouse_move_event(event,graph);
return;
}
if ( end_x < canvas.width-edge && end_x > canvas.width - 2*edge){
//day/night data button
var temp = gs.color_fg
gs.color_fg = gs.color_bg;
gs.color_bg = temp;
}
if ( end_x < canvas.width-edge*2 && end_x > canvas.width - edge*3){
//fullscreen button
if (graph.isFullscreen){
graph.exit_full_screen();
} else {
graph.make_full_screen(graph);
}
}
graph.mjs_plot();
mouse_move_event(event,graph);
return;
}
//zoom without drag
if (gs.mouse_mode === 'zoom'){
ctx.rect(start_x-no_drag_size*0.5,start_y-no_drag_size*0.5,no_drag_size,no_drag_size);
ctx.stroke();
if( event.button=== 0){
gs.x_scale_auto_min = true;
gs.y_scale_auto_min = true;
gs.x_scale_auto_max = true;
gs.y_scale_auto_max = true;
gs.y_scale_tight = false;
gs.x_scale_tight = false;
}
else if(event.button === 2){
//console.log('was right click');
gs.x_scale_auto_min = false;
gs.y_scale_auto_min = false;
gs.x_scale_auto_max = false;
gs.y_scale_auto_max = false;
gs.y_scale_tight = false;
gs.x_scale_tight = false;
var f = 0.8;
gs.x_manual_min = graph.pixels_to_units(-f*gs.guideWidthx,'x');
gs.x_manual_max = graph.pixels_to_units(canvas.width+f*gs.guideWidthx,'x');
gs.y_manual_min = graph.pixels_to_units(canvas.height+f*gs.guideWidthy,'y');
gs.y_manual_max = graph.pixels_to_units(-f*gs.guideWidthy,'y');
}
}
//on mobile, don't replot the graph if using the measure mode.
if (graph.ui.touch && gs.mouse_mode === 'measure' && graph.ui.is_touching){
graph.graph_image = ctx.getImageData(0,0,canvas.width,canvas.height);
return;
}
//on mobile, don't replot the graph if using the measure mode.
if (graph.ui.touch && gs.mouse_mode === 'measure'){
graph.mjs_plot();return;
}
//dragging
if (gs.mouse_mode === 'drag'){
if (graph.ui.touch && graph.ui.is_touching){
start_x = 0.5*(touch_start_x[0]+touch_start_x[1]);
start_y = 0.5*(touch_start_y[0]+touch_start_y[1]);
end_x = 0.5*(touch_x[0]+touch_x[1]);
end_y = 0.5*(touch_y[0]+touch_y[1]);
var scale_x = (touch_x[0]-touch_x[1]) / (touch_start_x[0]-touch_start_x[1]);
var scale_y = (touch_y[0]-touch_y[1]) / (touch_start_y[0]-touch_start_y[1]);
scale_x = trim(Math.abs(scale_x),0.1,10);
scale_y = trim(Math.abs(scale_y),0.1,10);
//p + (1-1/scale)*( start-p )+ end - start
//this set of equations took two days of trial and fuck error. Lots of error.
var new_max_y = 0 + (1-1/scale_y)*( start_y-0 ) +(- end_y + start_y)/scale_y;
var new_min_y = canvas.height + (1-1/scale_y)*( start_y-canvas.height )+(- end_y + start_y)/scale_y
var new_max_x = canvas.width + (1-1/scale_x)*( start_x-canvas.width ) +(- end_x + start_x)/scale_x;
var new_min_x = 0 + (1-1/scale_x)*( start_x-0 ) +(- end_x + start_x)/scale_x
gs.y_manual_max = Math.min(1e250,graph.pixels_to_units(new_max_y,'y'));
gs.y_manual_min = Math.max(-1e250,graph.pixels_to_units(new_min_y,'y'));
gs.x_manual_min = Math.max(-1e250,graph.pixels_to_units(new_min_x,'x'));
gs.x_manual_max = Math.min(1e250,graph.pixels_to_units(new_max_x,'x'));
//the 1e250 is to protect the
gs.x_scale_auto_min = false;
gs.y_scale_auto_min = false;
gs.x_scale_auto_max = false;
gs.y_scale_auto_max = false;
gs.y_scale_tight = true;
gs.x_scale_tight = true;
graph.mjs_plot();return;
}
if (!graph.ui.touch){
gs.y_manual_max = graph.pixels_to_units(start_y-end_y+gs.guideWidthy+1,'y');
gs.y_manual_min = graph.pixels_to_units(canvas.height+start_y-end_y-gs.guideWidthy-1,'y');
gs.x_manual_min = graph.pixels_to_units(start_x-end_x+gs.guideWidthx-1,'x');
gs.x_manual_max = graph.pixels_to_units(canvas.width+start_x-end_x-gs.guideWidthx+1,'x');
gs.x_scale_auto_min = false;
gs.y_scale_auto_min = false;
gs.x_scale_auto_max = false;
gs.y_scale_auto_max = false;
gs.y_scale_tight = true;
gs.x_scale_tight = true;
graph.mjs_plot();return;
}
}
if (gs.mouse_mode === 'reader'){
//the the point that is closed to whene the mouse ended the click
//end_x = graph.pixels_to_units(end_x,'x');
//end_y = graph.pixels_to_units(end_y,'y');
var pi = 0;//index of the series it is in
var pj = 0;// index in the series.
var best_dist = 1e200;
var dist = 1e200;
for (i = 0;i canvas.width/2){
//on right side
gs.caption_position.x = (canvas.width-end_x-gs.tick_len*gs.scaling_factor)*-1/gs.scaling_factor;
} else {
gs.caption_position.x = (end_x-gs.tick_len*gs.scaling_factor)/gs.scaling_factor;
}
if (end_y > canvas.height/2){
//on bottom half
gs.caption_position.y = (canvas.height-end_y-gs.tick_len*gs.scaling_factor)*-1/gs.scaling_factor;
} else {
gs.caption_position.y = (end_y-gs.tick_len*gs.scaling_factor)/gs.scaling_factor;
}
}
graph.mjs_plot();
mouse_move_event(event,graph);
graph.ui.mouse_is_dragging = false;
return;
}
// if the mouse mode is 'i' then the covering rectangle hides everything
// clicking in this mode changes the mouse to zoom and remove the info panel.
//this should stop users feeling stuck.
if (gs.mouse_mode === 'i'){
gs.mouse_mode = graph.pre_mouse_mode;
gs.i = 0;
//if the last mouse mode was laso 'i' then just switch to zoom. a psudo default.
if (gs.mouse_mode === 'i'){
gs.mouse_mode = 'zoom';
}
}
graph.mjs_plot();
}
var touch_start_x = [];
var touch_start_y = [];
var touch_x = [];
var touch_y = [];
function touch_start_event(event,graph){
//console.log('touch start');
event.preventDefault();
graph.ui.is_touching = true;
var rect = graph.canvas.getBoundingClientRect();
touch_start_x[0] = event.targetTouches[0].clientX-rect.left;
touch_start_y[0] = event.targetTouches[0].clientY-rect.top;
if (event.targetTouches.length >= 2){
touch_start_x[1] = event.targetTouches[1].clientX-rect.left;
touch_start_y[1] = event.targetTouches[1].clientY-rect.top;
}
touch_move_event(event,graph);
}
function touch_move_event(event,graph){
//console.log('touch move');
//document.getElementById('errors').innerHTML="touch move event";
my_touches = event.targetTouches;
event.preventDefault();
graph.ui.is_touching = true;
mouse_down = my_touches.length >=2;
var rect = graph.canvas.getBoundingClientRect();
start_x = event.targetTouches[0].clientX-rect.left;
start_y = event.targetTouches[0].clientY-rect.top;
touch_x[0] = event.targetTouches[0].clientX-rect.left;
touch_y[0] = event.targetTouches[0].clientY-rect.top;
if (event.targetTouches.length > 1){
touch_x[1] = event.targetTouches[1].clientX-rect.left;
touch_y[1] = event.targetTouches[1].clientY-rect.top;
}
event.clientX = my_touches[my_touches.length-1].clientX;//+rect.left;
event.clientY = my_touches[my_touches.length-1].clientY;//+rect.top;
mouse_move_event(event,graph);
}
function touch_cancel_event(event,graph){
graph.ui.is_touching = event.targetTouches.length != 0;
event.preventDefault();
touch_end_event(event,graph);
}
function touch_leave_event(event,graph){
touch_end_event(event,graph);
}
function touch_click_event(event,graph){
rect = graph.canvas.getBoundingClientRect();
start_x = event.clientX - rect.left;
start_y = event.clientY - rect.top;
mouse_up_event(event,graph);
}
function touch_end_event(event,graph){
//event.preventDefault();
graph.ui.is_touching = event.targetTouches.length != 0;
rect = graph.canvas.getBoundingClientRect();
start_x = my_touches[0].clientX-rect.left;;//+rect.left;
start_y = my_touches[0].clientY-rect.top;;//+rect.top;
event.clientX = my_touches[my_touches.length-1].clientX;//+rect.left;
event.clientY = my_touches[my_touches.length-1].clientY;//+rect.top;
mouse_up_event(event,graph);
}
function keep_parent_sized (e,graph){
// Make it visually fill the positioned parent
graph.canvas.style.width ='100%';
graph.canvas.style.height='100%';
// ...then set the internal size to match
graph.canvas.width = canvas.offsetWidth;
graph.canvas.height = canvas.offsetHeight;
//graph.canvas.width = some_graph.canvas.parentNode.clientWidth;
//graph.canvas.height = some_graph.canvas.parentNode.clientHeight;
graph.mjs_plot();
}
function make_interactive(graph){
if (graph.ui.touch){
graph.canvas.addEventListener("touchstart", function(event){touch_start_event(event,graph);}, false);
graph.canvas.addEventListener("touchend", function(event){touch_end_event(event,graph);}, false);
graph.canvas.addEventListener("touchmove", function(event){touch_move_event(event,graph);}, false);
graph.canvas.addEventListener("touchcancel", function(event){touch_cancel_event(event,graph);}, false);
graph.canvas.addEventListener("touchleave", function(event){touch_leave_event(event,graph);}, false);
graph.canvas.addEventListener("click", function(event){touch_click_event(event,graph);}, false);
} else {
graph.canvas.addEventListener('mousemove', function(event){mouse_move_event(event,graph);}, false);
graph.canvas.addEventListener('mousedown', function(event){mouse_down_event(event,graph);}, false);
graph.canvas.addEventListener('mouseup', function(event){mouse_up_event(event,graph);} , false);
graph.canvas.addEventListener('mouseout', function(event){mouse_out_event(event,graph);} , false);
}
graph.canvas.oncontextmenu = function (e) {e.preventDefault();};
// TODO get keyboard to work. z for zoom, m for measure, c for cut etc....
//graph.canvas.addEventListener('keydown', function(event){keypress_event(event,graph);} , false);
}
function SVGContext(ctx){
this.target = document.getElementById("_mjsplotSVG");
this.targetStack = [];
this._ctx = ctx;//borrow from the 2dcanvas context
this.x=0;
this.y=0;
this.strokeStyle = '#000';
this.lineWidth = 2;
this.lineCap;
this.lineJoin;
this.miterLimit=10;
//.getLineDash()
//.setLineDash()
//.lineDashOffset
this.font;
this.textAlign = 'left';// or center or right
this.textBaseline = 'alphabetic';//or top hanging middle ideographic bottom.
this.direction = 'inherit';//or ltr rtl
//.createLinearGradient()
//.createRadialGradient()
this._pixel_precision = 2;
//.createPattern()
//.shadowBlur = 0;
//.shadowColor = ctx.shadowColor;
//.shadowOffsetX
//.shadowOffsetY
/*.bezierCurveTo()
.arc()
.arcTo()
.rect()
.drawFocusIfNeeded()
.scrollPathIntoView()
.clip()
.isPointInPath()
.isPointInStroke()
*/
this._shape_type = 'path';
this.fillStyle = '#000';
this._path = '';
this._isdrawn = false;
this.clearAll = function(){
while (this.target.lastChild) {
this.target.removeChild(this.target.lastChild);
}
}
this.arc = function(x,y,r,a1,a2,direction){
this._shape_type = 'arc';
this._shape = document.createElementNS(svgNS, "circle");
this._shape.setAttributeNS(null, "stroke-width", this.lineWidth.toFixed(this._pixel_precision));
this._shape.setAttributeNS(null, "stroke", this.strokeStyle);
this._shape.setAttributeNS(null, "r", r.toFixed(this._pixel_precision));
this._shape.setAttributeNS(null, "cx", x.toFixed(this._pixel_precision));
this._shape.setAttributeNS(null, "cy", y.toFixed(this._pixel_precision));
}
this.moveTo = function (x,y){
this._path+= ' M' + x.toFixed(this._pixel_precision) + ' ' + y.toFixed(this._pixel_precision);
}
this.lineTo = function (x,y){
this._path += ' L' + x.toFixed(this._pixel_precision) + ' ' + y.toFixed(this._pixel_precision);
}
this.beginPath = function(){
this._shape_type = 'path';
this._shape = document.createElementNS(svgNS, "path");
this._path = '';
this._isdrawn = false;
}
this.closePath = function(){
this._path += 'Z';
}
this.quadraticCurveTo = function(x1,y1,x2,y2){
this._path += ' Q' + x1.toFixed(this._pixel_precision) +' '+ y1.toFixed(this._pixel_precision) +' '+ x2.toFixed(this._pixel_precision) +' '+ y2.toFixed(this._pixel_precision);
}
this.bezierCurveTo = function(x1,y1,x2,y2,x3,y3){
console.log('bezierCurveTo is not implemented yet');
this._path += ' Q' + (x1+x2)/02 +' '+ (y1+y2)/2 +' '+ x3.toFixed(this._pixel_precision) +' '+ y3.toFixed(this._pixel_precision);
}
this.stroke = function (){
this._shape.setAttributeNS(null, "fill", 'none');
this._shape.setAttributeNS(null, "stroke-width", this.lineWidth.toFixed(this._pixel_precision));
this._shape.setAttributeNS(null, "stroke", this.strokeStyle);
if (!this._isdrawn){
if (this._shape_type === 'path'){ this._shape.setAttributeNS(null, "d", this._path); }
if (this.globalAlpha <1 ){
this._shape.setAttributeNS(null, "opacity", this.globalAlpha.toFixed(2));
}
this.target.appendChild(this._shape);
this._isdrawn = true;
}
}
this.fill = function(){
this._shape.setAttributeNS(null, "fill", this.fillStyle);
if (!this._isdrawn){
if (this._shape_type === 'path'){ this._shape.setAttributeNS(null, "d", this._path); }
this.target.appendChild(this._shape);
this._isdrawn = true;
}
}
this.fillText = function (l,x,y){
var t = document.createElementNS(svgNS, "text");
var anchor = 'start';
switch (this.textAlign){
case 'left':anchor = 'start';break
case 'right':anchor = 'end';break
case 'center':anchor = 'middle';break
case 'middle':anchor = 'middle';break
}
t.setAttributeNS(null, "text-anchor", anchor);
t.setAttributeNS(null, "fill", this.fillStyle);
t.setAttributeNS(null, "x", x.toFixed(this._pixel_precision));
t.setAttributeNS(null, "x", x.toFixed(this._pixel_precision));
t.setAttributeNS(null, "font-family", this.font_name );
t.setAttributeNS(null, "y", y.toFixed(this._pixel_precision));
t.setAttributeNS(null, "font-size", parseFloat(this.font.split(' ')[0].slice(0,-2)).toFixed(this._pixel_precision) );
t.textContent=l;
this.target.appendChild(t);
}
this.rect = function(x,y,w,h){
this.moveTo(x,y);
this.lineTo(x+w,y);
this.lineTo(x+w,y+h);
this.lineTo(x,y+h);
this.lineTo(x,y);
}
this.fillRect = function(x,y,w,h){
this.beginPath();
this.rect(x,y,w,h);
this.fill();
}
this.strokeText = function (l,x,y){ }
this.startGroup = function(){
this.targetStack.push(this.target);
var g = document.createElementNS(svgNS, "g");
this.target.appendChild(g);
this.target = g;
}
this.endGroup = function(){
this.target = this.targetStack.pop();
}
this.startClipBox = function(x,y,w,h){
var defs = document.createElementNS(svgNS, "defs");
var clippath = document.createElementNS(svgNS, "clipPath");
clippath.setAttributeNS(null, "id", "graphClipPath");
var rect = document.createElementNS(svgNS, "rect");
rect.setAttributeNS(null, 'x', x.toFixed());
rect.setAttributeNS(null, 'y', y.toFixed());
rect.setAttributeNS(null, 'width', w.toFixed());
rect.setAttributeNS(null, 'height', h.toFixed());
clippath.appendChild(rect);
defs.appendChild(clippath);
this.target.appendChild(defs);
this.targetStack.push(this.target);
var g = document.createElementNS(svgNS, "g");
g.setAttributeNS(null, 'clip-path', 'url(#graphClipPath)' );
this.target.appendChild(g);
this.target = g;
}
this.getImageData = this._ctx.getImageData;
this.measureText = function(s){return {width:this._ctx.measureText(s).width}} ;
this.image = function (x,y,w,h,data){
this._shape = document.createElementNS(svgNS, "image");
this._shape.setAttributeNS(null, "x", x.toFixed(this._pixel_precision));
this._shape.setAttributeNS(null, "y", y.toFixed(this._pixel_precision));
this._shape.setAttributeNS(null, "width", w.toFixed(this._pixel_precision));
this._shape.setAttributeNS(null, "height", h.toFixed(this._pixel_precision));
this._shape.setAttributeNS('http://www.w3.org/1999/xlink', "xlink:href", data );
this.target.appendChild(this._shape);
this._isdrawn = true;
}
}
function drawSplash(canvas,ctx){
ctx.textAlign="center";
ctx.beginPath();
ctx.font='20 px "Monaco","Menlo","Ubuntu Mono","Consolas","source-code-pro",monospace"';
ctx.fillText('MJS Plot',canvas.width/2,canvas.height/2-20);
ctx.font='10 px "Monaco","Menlo","Ubuntu Mono","Consolas","source-code-pro",monospace"';
ctx.fillText('Setting Up',canvas.width/2,canvas.height/2+20);
ctx.rect(10,10,canvas.width-20,canvas.height-20);
ctx.stroke();
}
var transforms = {
scale_x: function(data,args,graph){
for (i = 0;i= 0; ii--){
if (args[ii]){
data.splice(ii,1);
graph.captions.splice(ii,1);
graph.colors.splice(ii,1);
graph.line_modes.splice(ii,1);
graph.symbol_modes.splice(ii,1);
}
}
},
y_vs_y: function(data,args,graph){
//args[0] is the series ment to be the x-axis.
var x_axis = args[0] % data.length; //might be bigger than the data, so roll around
console.log(x_axis);
var points = data[x_axis][0].length //Math.min(data[0][1].length,data[1][1].length);
for (var i = 0;i= 0; ii--) {
if( data[i][0][ii] < xlow ||
data[i][0][ii] > xhigh ||
data[i][1][ii] < ylow ||
data[i][1][ii] > yhigh ) {
data[i][0][ii] = NaN;
}
}
}
transforms.clean(data,graph);
graph.transform_text_x= 'trim('+graph.transform_text_x +')';
graph.transform_text_y= 'trim('+graph.transform_text_y +')';
},
cut:function(data,args,graph){
//like trim, but removes any data in the box
var xlow = args[0];
var xhigh = args[1];
var ylow = args[2];
var yhigh = args[3];
for (i = 0;i= 0; ii--) {
if( !(data[i][0][ii] < xlow ||
data[i][0][ii] > xhigh ||
data[i][1][ii] < ylow ||
data[i][1][ii] > yhigh)) {
data[i][0][ii] = NaN;
}
}
}
transforms.clean(data,graph);
graph.transform_text_x= 'cut('+graph.transform_text_x +')';
graph.transform_text_y= 'cut('+graph.transform_text_y +')';
},
remove_linear: function(data,args,graph){
for (i = 0;i= 0; ii--){
if (data[ii][0].length == 0){
data.splice(ii,1);
graph.captions.splice(ii,1);
graph.colors.splice(ii,1);
graph.symbol_modes.splice(ii,1);
graph.line_modes.splice(ii,1);
}
}
},
intergrate: function(data,args,graph){
var tally = 0;
for (i = 0;i best_sections ){
val_i+=1;
if (val_i == 3){
val_i = 0;
power +=1;
}
scale = vals[val_i]* Math.pow(10,power);
sections = Math.ceil(ymax/scale)-Math.floor(ymin/scale)+extra_sections;
}
var lowpoint = Math.floor(ymin/scale-1)*scale; // bring low one section lower.
var highpoint = Math.ceil(ymax/scale+1)*scale; //one section higher
//starting from lowpoint to highpoint with bin widths of scale
var bin_centers = [];
var bin_counts = [];
var bin_i = 0;
for (bin_start = lowpoint;bin_start= 0 && diff < closest_left){
closest_left = diff;
cli = k;
}
if (diff < 0 && -1*diff < closest_right){
closest_right = -1*diff;
cri =k;
}
}
newys[j] = data[i][1][cli] + (data[i][1][cri]-data[i][1][cli])/(data[i][0][cri]-data[i][0][cli])*(newxs[j]-data[i][0][cli]);
}
data[i][0] = newxs;
data[i][1]= newys;
}
transforms.clean(data,graph);
graph.transform_text_y= 'interpolate('+graph.transform_text_y +','+args[0]+')';
graph.transform_text_x= 'interpolate('+graph.transform_text_x +','+args[0]+')';
},
differentiate: function(data,args,graph){
for (i = 0;i threshold * sigma_r){
data[i][1][j] = NaN;
}
}
} else {
for (j = 0;j 1e-250){
var power = Math.floor(Math.log10( max_point - min_point ))-10 ;
} else {
power = -250;
}
var notgood = 1;
var scale = 0;
var width;
var extra_sections = 0;
if (tight){
extra_sections = 0;
} else {
extra_sections = 2;
}
var best_sections = Math.floor( size / guide_width ) +extra_sections;
var scale = vals[val_i]* Math.pow(10,power);
var sections = Math.ceil(max_point/scale)-Math.floor(min_point/scale)+extra_sections;
while (sections > best_sections ){
val_i+=1;
if (val_i == 3){
val_i = 0;
power +=1;
}
scale = vals[val_i]* Math.pow(10,power);
sections = Math.ceil(max_point/scale)-Math.floor(min_point/scale)+extra_sections;
}
if (sections == 0){ width = size;}
else{width = size/sections; }
if (tight){
var lowpoint = Math.floor(min_point/scale)*scale;
var highpoint = Math.ceil(max_point/scale)*scale;
} else {
var lowpoint = Math.floor(min_point/scale-1)*scale; // bring low one section lower.
var highpoint = Math.ceil(max_point/scale+1)*scale; //one section higher
}
return the_scale = {
lowpoint : lowpoint,
highpoint : highpoint,
scale : scale,
width : width,
val_i : val_i,
val_i_low : 1, //not used
power_low : 1, //not used
val_i_high : 1, //not used
power_high : 1, //not used
scalemode : scalemode
}
}
if (scalemode === 'log'){
//log scale
var val_i = 0;
var power = 100;
var log_vals_i = -1;
var do_not_use_linear = false;
var required_secions = Math.floor( size / guide_width/2 );
sections = 0;
while (sections < required_secions){
log_vals_i +=1;
power = Math.ceil(Math.log10(min_point))+1;
val_i = 0;
while (log_vals[log_vals_i][val_i] * Math.pow(10,power) >= min_point){
//roll down to find the low
val_i -=1;
if (val_i < 0){
power -= 1;
val_i = log_vals[log_vals_i].length-1;
}
}
//roll down one more, this marks the low point
if (tight === false){
val_i -=1;
if (val_i < 0){
power -= 1;
val_i = log_vals[log_vals_i].length-1;
}
}
var val_i_low = val_i;
var power_low = power;
//roll up to find the high point and index keeping track on the number of sections
sections = 0;
while (log_vals[log_vals_i][val_i] * Math.pow(10,power) <= max_point){
val_i +=1;
sections +=1;
if (val_i >= log_vals[log_vals_i].length){
power += 1;
val_i = 0;
}
}
if (tight === false){
//roll up one more
val_i +=1;
sections +=1;
if (val_i >= log_vals[log_vals_i].length){
power += 1;
val_i = 0;
}
}
var val_i_high = val_i;
var power_high = power;
if ( (log_vals_i >= log_vals.length || Math.log10(max_point)-Math.log10(min_point) < 0.6) && do_not_use_linear == false ){
sections = required_secions;
the_scale = this.find_scale(min_point,max_point,size,guide_width,'lin',tight);
if (the_scale.lowpoint <= 0){do_not_use_linear = true; sections = 0;}
//the lowpoint from the linear scale can be negative or zero !
lowpoint = Math.log10(the_scale.lowpoint);
highpoint = Math.log10(the_scale.highpoint);
scale = the_scale.scale;
width = the_scale.width;
log_vals_i = the_scale.val_i;
} else {
lowpoint = Math.log10( log_vals[log_vals_i][val_i_low] * Math.pow(10,power_low) ) ;//far left point
highpoint = Math.log10( log_vals[log_vals_i][val_i_high] * Math.pow(10,power_high) ); //far right point
scale = -1; //not used! // the width of a section on the graph.
width = -1; //also not used! //is the width of one section in pixels
log_vals_i = log_vals_i; //which log scale to use. . .
}
}
if (scale < 0){
//this corrects for the log finding scale loop.
//it addes one at the end which is not needed if
//the scale is ok.
//log_vals_i -=1;
}
if (log_vals_i == 0 && sections > 2*required_secions){
//do the far zoomed out log scale.
//if using si numbers change the way liner scales are found.
var c = (axis === 'y' && ( this.graphics_style.y_label_mode === 'infix' || this.graphics_style.y_label_mode === 'postfix' ));
c = c || (axis === 'x' && ( this.graphics_style.x_label_mode === 'infix' || this.graphics_style.x_label_mode === 'postfix' ));
if (c){ vals = [1,3,6,9];}
the_scale = this.find_scale( Math.max(-249,Math.log10(min_point)),Math.min(249,Math.log10(max_point)),size,guide_width,'lin',tight);
if (c){ vals = [1,2,5];}
lowpoint = the_scale.lowpoint; //308 is the javascript float maxamum and minimum
highpoint = the_scale.highpoint;
scale = the_scale.scale;
scalemode = 'log';
width = the_scale.width;
log_vals_i = -1; // ! this is the flag to use the zoomed out view.
}
return the_scale = {
lowpoint : lowpoint,
highpoint : highpoint,
scale : scale,
width : width,
val_i : log_vals_i,
val_i_low : val_i_low,
power_low : power_low,
val_i_high : val_i_high,
power_high : power_high,
scalemode : scalemode
}
}
if (scalemode ==='time'){
//min_point,max_point,size,guide_width,scalemode,tight
if (tight){
var extra_sections = 0;
} else {
var extra_sections = 2;
}
var required_sections = Math.floor( size / guide_width ) +extra_sections;
var target_time_diff = (max_point-min_point)/required_sections;
var i = 0;
while (allowed_intervals[i] < target_time_diff){i++;}
//i++;
//can only go up to intervals of 500years.
//so cap i at 43 :allowed_intervals.length-1;
i = Math.min(i,allowed_intervals.length-1);
extra_sections/=2;
var start_time = min_point-(min_point%allowed_intervals[i]) - extra_sections*allowed_intervals[i];
var high_time = max_point-(max_point%allowed_intervals[i]) + extra_sections*allowed_intervals[i];// +allowed_intervals[i] ;
if (allowed_intervals[i]>=31536000000){
//using years
var d = new Date(start_time);
d = new Date(d.getFullYear(),0,0);
start_time = d.getTime();
var d = new Date(high_time);
d = new Date(d.getFullYear() + Math.round(allowed_intervals[i]/1000/60/60/24/365) + Math.round( d.getMonth()/12 ),0,0);
high_time = d.getTime();
}
if (allowed_intervals[i]>=2592000000 && allowed_intervals[i]< 31536000000){
//using months
var d = new Date(start_time);
d = new Date(d.getFullYear(),d.getMonth(),1);
start_time = d.getTime();
var d = new Date(high_time);
if (d.getDate() < 15){
d = new Date(d.getFullYear(),d.getMonth(),1);
} else {
d = new Date(d.getFullYear(),d.getMonth()+1,1);
}
high_time = d.getTime()+allowed_intervals[i];
}
if (allowed_intervals[i]>=43200000 && allowed_intervals[i]< 2592000000){
//using days
var d = new Date(start_time);
var c = new Date(d.getFullYear(),d.getMonth(),d.getDate(),0,0,0,0);
start_time = c.getTime();
var d = new Date(high_time);
d = new Date(d.getFullYear(),d.getMonth(),d.getDate(),0,0,0,0);
high_time = d.getTime()+allowed_intervals[i];
}
if (allowed_intervals[i]>=1800000 && allowed_intervals[i]<= 21600000){
//using hours
var d = new Date(start_time);
var c = new Date(d.getFullYear(),d.getMonth(),d.getDate(),d.getHours(),0,0,0);
start_time = c.getTime();
var d = new Date(high_time);
d = new Date(d.getFullYear(),d.getMonth(),d.getDate(),d.getHours()+1,0,0,0);
high_time = d.getTime();
}
if (allowed_intervals[i]==7200000 ){
//every two hours
var d = new Date(start_time);
var c = new Date(d.getFullYear(),d.getMonth(),d.getDate(),Math.floor(d.getHours()/2)*2,0,0,0);
start_time = c.getTime();
var d = new Date(high_time);
d = new Date(d.getFullYear(),d.getMonth(),d.getDate(),Math.floor(1+d.getHours()/2)*2,0,0,0);
high_time = d.getTime();
}
if (allowed_intervals[i]==10800000 ){
//every three hours
console.log('every three horus');
var d = new Date(start_time);
var c = new Date(d.getFullYear(),d.getMonth(),d.getDate(),Math.floor(d.getHours()/3)*3,0,0,0);
start_time = c.getTime();
var d = new Date(high_time);
d = new Date(d.getFullYear(),d.getMonth(),d.getDate(),Math.floor(1+d.getHours()/3)*3,0,0,0);
high_time = d.getTime();
}
if (allowed_intervals[i]==21600000 ){
//every six hours
console.log('every six horus');
var d = new Date(start_time);
var c = new Date(d.getFullYear(),d.getMonth(),d.getDate(),Math.floor(d.getHours()/6)*6,0,0,0);
start_time = c.getTime();
var d = new Date(high_time);
d = new Date(d.getFullYear(),d.getMonth(),d.getDate(),Math.floor(1+d.getHours()/6)*6,0,0,0);
high_time = d.getTime();
}
if (allowed_intervals[i]<1800000 ){
high_time += allowed_intervals[i];
}
var number_of_sections = Math.floor((high_time-start_time)/allowed_intervals[i]);
var scale = size/number_of_sections;
var number_of_minor_ticks = Math.floor((high_time-start_time)/time_ticks[i]);
var tick_scale = size/number_of_minor_ticks;
var the_scale;
return the_scale = {
lowpoint : start_time,
highpoint : high_time,
scale : scale,
width : tick_scale, //abuse here
val_i : i,
val_i_low : 0,
power_low : 0,
val_i_high : 0,
power_high : 0,
scalemode : 'time'
}
}
},
find_limits : function(data,automin,automax,manualmin,manualmax,scale,name){
"use strict";
//data is [ [],[],...]
// automax and automin are bools
//manualmax and manual min are values for the min and max
//scale is a string, e.g. 'log','lin'.
//return object has:
// limits.low limits.high
// limits.scale //it can change to log or lin by looking at the data.
//limits.automax
//limits.automin //can change if a scale uses the autos or not.
//name is a string so that errors are more useful to the user.
//protect agains +- infinity.
manualmax = Math.min(manualmax,1e250);
manualmin = Math.max(manualmin,-1e250);
//protect against numbers smaller than 1e-250
var t = manualmax;
manualmax = Math.max(manualmax,manualmin);
manualmin = Math.min(t,manualmin);
//protect against times really far away. this could be improved.
if (scale === 'time'){
manualmax = Math.min(manualmax,+100000000000000);
manualmin = Math.max(manualmin,-100000000000000);
}
//protect against empty data series
this.no_data = false;
if (data.length == 0){
if (!automin && !automax){
low = manualmin;
high = manualmax;
} else {
low = 1;
high = 10;
automin = true;
automax = true;
}
if (scale === 'log'){
if (low<=0){low = 1;}
if (high<=0 || low > high){high = 10;}
}
this.no_data = true;
return {low:low, high:high, automax:automax,automin:automin,scale:scale};
}
//catch negatives for the log scale
if (scale === 'log'){
if (manualmin <= 0 && automin == false){
automin = true;
this.errors.push(name + " minimum was negative - using auto");
}
if (manualmax <= 0 && automax == false){
automin = true;
automax = true;
this.errors.push(name + " maximum was negative - using auto");
}
}
if ((automin == true ||
automax == true ||
low>=high )){
var amin = 1e+307;
var amax = -1e+307;
var low = 1e+307; //very high. easy to shrink
var high = -1e+307; //very low. easy to grow
for (i = 0;i high && automax && !automin){
automin = true;
automax = true;
this.errors.push(name + " can't have inverted scale - using auto");
}
if (manualmax < low && automin && !automax){
automin = true;
automax = true;
this.errors.push(name + " can't have inverted scale - using auto");
}
if (automin == false){
low = manualmin;
}
if (automax == false){
high = manualmax;
}
if (scale === 'log' && high <= 0){
scale = 'lin';
this.errors.push("all "+name+" data is negative, cant plot that log"+name);
}
if (scale === 'log' && low<=2e-250){
low = 2e250;
for (var i = 0;i 0 && data[i][ii] < low){
low = data[i][ii];
}
}
}
}
if (scale === 'log'){
manualmax = Math.max(manualmax,2e-250);
manualmin = Math.max(manualmin,2e-250);
}
return {low:low, high:high, automax:automax,automin:automin,scale:scale};
},
use_scale : function (the_scale,target_size,guideWidth,axis){
"use strict";
var lowpoint = the_scale.lowpoint;
var highpoint = the_scale.highpoint;
var scale = the_scale.scale;
var width = the_scale.width;
var val_i = the_scale.val_i;
var val_i_low =the_scale.val_i_low;
var power_low = the_scale.power_low;
var val_i_high = the_scale.val_i_high;
var power_high = the_scale.power_high;
var scalemode = the_scale.scalemode;
//var target_size = 300;//this is going to be canvas.width or canvas.height depending which one is being used. . .
var many_tick_limit = 1;
var pos = [];
var strings = [];
var minor_pos = [];
var val_is;
var power;
var old_i;
if (scalemode === 'lin'){
var tick = 0;// for the dummy run start at the far left, even though there
//won't be an actual label there.
var prev_label = ' ';
var precision = 1;
var label = '';
var zero_position = (0-lowpoint)*target_size/(highpoint - lowpoint)
//dummy run to get the precision
while (tick <= target_size) {
i = tick/width*scale+lowpoint;
//if (Math.abs(i)< not_zero) {i=0;}//javasript sucks.
label = i.toPrecision(precision);
if (label === prev_label){precision += 1;label = i.toPrecision(precision);}
prev_label = label;
tick += width;
}
//draw it this time
tick = width;
var j = 1;
//j here is just the same as tick/width.
//however repeated additions of width lead to annoying floating point errors.
//using j reduces the number of floating point problums
while (tick < target_size-1) {
i = j*scale+lowpoint;
pos.push(tick);
//if (Math.abs(i)< not_zero) {i=0;}//javasript sucks.
//within 2 pixels of zero. call it zero
if (Math.abs(tick - zero_position) <2){
label = (0).toFixed(Math.max(1,precision-1));
} else {
//label = mjs_precision(i,precision);
label = this.get_axis_string(i,axis,precision);
}
strings.push(label);
tick += width;
j +=1;
}
var stepping = label.length*.7*this.graphics_style.tick_labels_font_size*this.graphics_style.scaling_factor > width;
tick = 0;
var minor_ticks = 1;
if ( width > guideWidth*many_tick_limit) {
minor_ticks = many_minor_ticks[val_i];
//use many small ticks
} else {
//use few small ticks
minor_ticks = few_minor_ticks[val_i];
}
while (tick < target_size-2) {
tick += width/minor_ticks;
minor_pos.push(tick);
}
minor_pos.pop();
}
//the far zoomed out view.
if (scalemode === 'log' && val_i == -1){
var tick = 0;// for the dummy run start at the far left, even though there
//won't be an actual label there.
var prev_label = ' ';
var precision = 1;
var label = '';
//dummy run to get the precision
while (tick <= target_size) {
i = Math.pow(10,tick/width*scale+lowpoint);
label = i.toPrecision(precision);
if (label === prev_label){precision += 1;label = i.toPrecision(precision);}
prev_label = label;
tick += width;
}
//draw it this time
tick = width;
var j = 1;
//j here is just the same as tick/width.
//however repeated additions of width lead to annoying floating point errors.
//using j reduces the number of floating point problums
while (tick < target_size-1) {
i = Math.pow(10,j*scale+lowpoint);
pos.push(tick);
//label = mjs_precision(i,precision);
label = this.get_axis_string(i,axis,precision);
strings.push(label);
tick += width;
j +=1;
}
var stepping = label.length*.7*this.graphics_style.tick_labels_font_size*this.graphics_style.scaling_factor > width;
tick = 0;
var minor_ticks = 1;
if ( width > guideWidth*many_tick_limit) {
minor_ticks = many_minor_ticks[val_i];
//use many small ticks
} else {
//use few small ticks
minor_ticks = few_minor_ticks[val_i];
}
while (tick <= target_size) {
minor_pos.push(tick);
tick += width/minor_ticks;
}
minor_pos.pop();
}
if (scalemode === 'log' && val_i != -1){
//the pure log mode
if (scale < 0) {
val_is = val_i_low;
power = power_low;
i = log_vals[val_i][val_i_low] * Math.pow(10,power_low);
old_i = i;
precision = 1;
var lable = '';
var old_lable = ''
//dummy run for precision
while (i < log_vals[val_i][val_i_high] * Math.pow(10,power_high) ){
val_is +=1;
if (val_is >= log_vals[val_i].length){
power += 1;
val_is = 0;
}
i = log_vals[val_i][val_is] * Math.pow(10,power);
tick = (Math.log10(i)-lowpoint)*target_size/(highpoint - lowpoint);
lable = this.get_axis_string(i,axis,precision);
if (lable === old_lable){
precision++;
lable = this.get_axis_string(i,axis,precision);
}
old_lable = lable;
}
//actual run
i = log_vals[val_i][val_i_low] * Math.pow(10,power_low);
val_is = val_i_low;
power = power_low;
while (i < log_vals[val_i][val_i_high] * Math.pow(10,power_high) ){
val_is +=1;
if (val_is >= log_vals[val_i].length){
power += 1;
val_is = 0;
}
i = log_vals[val_i][val_is] * Math.pow(10,power);
tick = (Math.log10(i)-lowpoint)*target_size/(highpoint - lowpoint);
if (tick< target_size-10){
//strings.push(mjs_precision(i,2));
strings.push(this.get_axis_string(i,axis,precision));
pos.push(tick);
}
//use the mid point rule for minor ticks
if (val_i > 8){
var mid = (Math.log10( (old_i + i)/2 )-lowpoint)*target_size/(highpoint - lowpoint);
minor_pos.push(mid);
}
old_i = i;
}
//use the minor ticks array
if (val_i <= 8){
i = log_vals[val_i][val_i_low] * Math.pow(10,power_low);
val_is = val_i_low;
power = power_low;
while (i < log_vals[val_i][val_i_high] * Math.pow(10,power_high)){
val_is +=1;
if (val_is >= log_vals_ticks[val_i].length){
power += 1;
val_is = 0;
}
i = log_vals_ticks[val_i][val_is] * Math.pow(10,power);
tick = (Math.log10(i)-lowpoint)*target_size/(highpoint - lowpoint);
minor_pos.push(tick);
}
minor_pos.pop();
}
} else {
//log mode with a linear labels and ticks( really zoomed in).
var tick = 0;
var prev_label = ' ';
var precision = 1;
var label = '';
//dummy run to get the precision
i = Math.pow(10,lowpoint);
while (i <= Math.pow(10,highpoint) ) {
label = i.toPrecision(precision);
if (label === prev_label){precision += 1;label = i.toPrecision(precision);}
prev_label = label;
i += scale;
}
//sepping if the labels are really long. this stops them running into each other.
//the other way of doing this is to not show every other label. or re-analyse the
//data with a larger min_width. = label.lendth*text_size/2
var stepping = label.length*.7*this.graphics_style.tick_labels_font_size*this.graphics_style.scaling_factor >= width;
//draw it this time
i = Math.pow(10,lowpoint)+scale;
while (i < Math.pow(10,highpoint)-scale/2 ) {
//label = mjs_precision(i,precision);
label = this.get_axis_string(i,axis,precision);
tick = (Math.log10(i)-lowpoint)*target_size/(highpoint - lowpoint);
pos.push(tick);
strings.push(label);
i += scale;
}
//draw minor ticks
var minor_ticks = 1;
if ( width >guideWidth*many_tick_limit) {
minor_ticks = many_minor_ticks[val_i];
//use many small ticks
} else {
//use few small ticks
minor_ticks = few_minor_ticks[val_i];
}
i = Math.pow(10,lowpoint);
while (i < Math.pow(10,highpoint)) {
i += scale/minor_ticks;
tick = (Math.log10(i)-lowpoint)*target_size/(highpoint - lowpoint);
minor_pos.push(tick);
}
minor_pos.pop();
}
}
if (scalemode ==='time'){
var i = the_scale.val_i;
//find the high point precision
var total_diff = (highpoint-lowpoint);
//find which interval to use.
j = 0;
while (allowed_intervals[j] < total_diff){j++;}
j--;
var low_precision = string_precisions[i];
var high_precision = string_precisions[j];
//check that the low-precision is enough.
//dummy run to get precision
var section_time = lowpoint;
var first_lable = mjs_date_print(section_time,high_precision,high_precision);
var tick = 0;
var old_label = '';
var label = '';
while (section_time < highpoint) {
section_time+=allowed_intervals[i];
if (allowed_intervals[i]>=2592000000){
section_time = round_to_month(section_time);
}
tick = (section_time-lowpoint)/total_diff * target_size;
label = mjs_date_print(section_time,high_precision,low_precision)
if (label === old_label){
//i--;
low_precision++;
//low_precision = string_precisions[i];
}
old_label = label;
}
var last_lable = mjs_date_print(section_time,high_precision,high_precision);
if (last_lable === first_lable){
high_precision++;
}
var section_time = lowpoint;
var tick = 0;
var mtick = 0;
//for ( var section_time = lowpoint;section_time= 2592000000){
section_time = round_to_month(section_time);
}
tick = (section_time-lowpoint)/total_diff * target_size;
pos.push(tick);
label = mjs_date_print(section_time,high_precision,low_precision);
strings.push(label);
while (mtick < tick) {
mtick += width;
minor_pos.push(mtick);
}
var mtick = tick;
minor_pos.pop();
}
while (mtick <= target_size-2) {
mtick += width;
minor_pos.push(mtick);
}
minor_pos.pop();
stepping = false;
var stepping = label.length*.7*this.graphics_style.tick_labels_font_size*this.graphics_style.scaling_factor > scale;
precision = low_precision;
}
return {pos:pos, strings:strings, minor_pos:minor_pos, stepping:stepping, precision:precision}
},
fit_points_drawn : 0,
drawSimpleLine : function(ctx,x,y){
var xi = this.units_to_pixels(x[0],'x');
var yi = this.units_to_pixels(y[0],'y');
var oxi = xi;
var oyi = yi;
ctx.beginPath();
ctx.moveTo(xi,yi);
var ongraph = true;
var wasOngraph = true;
for (var k = 0;k 0 && yi < this.canvas.height);
if (ongraph && wasOngraph){
ctx.lineTo(xi,yi);
}
if (!ongraph && wasOngraph){
ctx.lineTo(xi,yi);
ctx.stroke();
}
if (ongraph && !wasOngraph){
ctx.beginPath();
ctx.moveTo(oxi,oyi);
ctx.lineTo(xi,yi);
}
wasOngraph = ongraph;
oxi = xi;
oyi=yi;
}
ctx.stroke();
},
mjs_plot : function () {
"use strict";
var start_time = new Date();
//catch errors in plotting
if (this.plot_failed){
this.errors.push('failed for unknown reason');
this.reset();
}
this.plot_failed = true; //is set to false at the end.
// gs is the graph style.
//get things out of gs.
//if gs.modified is false use the default style
if (this.graphics_style.modified == false){
console.log('getting defautts');
this.reset();
}
var gs = this.graphics_style;
var ctx = this.canvas.getContext('2d');
if (this.isPNGSVG){
this.isSVG=true;
}
if (this.isSVG){
var oldctx = ctx;
var ctx = new SVGContext(ctx);
}
ctx.lineJoin="round";
var canvas = this.canvas;
var scaling_factor =gs.scaling_factor;
var graph_line_thickness =gs.graph_line_thickness*scaling_factor;
var symbol_size = gs.symbol_size*scaling_factor;
var line_thickness= gs.line_thickness*scaling_factor;
var tick_len = gs.tick_len*scaling_factor;
var mode = gs.mode;
var font_name = gs.font_name;
var title_font_size = gs.title_font_size*scaling_factor;
var title = gs.title;
var subtitle = gs.subtitle;
var title_spacing = gs.title_spacing*scaling_factor;
var tick_labels_font_size = gs.tick_labels_font_size*scaling_factor;
var tick_lables_font_padding = gs.tick_lables_font_padding*scaling_factor;
var axis_labels_font_size=gs.axis_labels_font_size*scaling_factor;
var lable_spacing =gs.lable_spacing*scaling_factor;
var x_axis_title =gs.x_axis_title;
var y_axis_title = gs.y_axis_title;
var minor_tick_len = gs.minor_tick_len*scaling_factor;
var guideWidthx = gs.guideWidthx*scaling_factor;
var guideWidthy = gs.guideWidthy*scaling_factor;
//befor clearing the scrren try to draw some text to check the font is ok
try{
ctx.font=tick_labels_font_size + 'px ' + font_name;
ctx.fillText('font test',10,10);
} catch(e) {
this.errors.push("font faliure");
font_name = "Courier New";
gs.font_name = "Courier New";
}
// clear prevous drawing
ctx.fillStyle = gs.color_bg;
ctx.strokeStyle = gs.color_fg;
if (this.transparent ){
ctx.clearRect(0, 0, canvas.width, canvas.height);
} else {
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
if (this.isSVG){
ctx.clearAll();
ctx.font_name = font_name;
ctx.startClipBox(0,0,canvas.width,canvas.height)
}
//run data transforms
//back up data so the transforms can mess it up
if (this.transform_index != gs.data_transforms.length ){
this.data = clone(this.data_backup);
this.captions = clone(this.captions_backup);
this.colors_backup = clone(gs.line_colours);
this.line_modes_backup = clone(gs.line_modes);
this.symbol_modes_backup = clone(gs.symbol_modes);
this.colors = clone(this.colors_backup);
this.line_modes = clone(this.line_modes_backup);
this.symbol_modes = clone(this.symbol_modes_backup);
this.transform_index = 0;
this.transform_text_x = x_axis_title;//'x';
this.transform_text_y = y_axis_title;//'y';
//hide/show lines from the lines menu
transforms.remove_lines(this.data,gs.hidden_lines,this);
}
//run the transforms
while(this.transform_index graph_left && this.data[i][0][k] < graph_right &&
this.data[i][1][k] > graph_bottom && this.data[i][1][k] < graph_top );
if (!ongraph && wasOngraph){
seq_end.push(Math.min(k+2,this.data[i][0].length));
}
if (ongraph && !wasOngraph){
seq_start.push(Math.max(k-2,0));
}
wasOngraph = ongraph;
}
//finish of the last sequence if it is open.
if (seq_start.length > seq_end.length){
seq_end.push(k);
}
//do sequence merging
//merge sequences if they are very close to each other or overlapping
for (k=seq_start.length-1;k>=0;k--){
//if they are within 1 just merge the sequences together.
if (seq_start[k] <= seq_end[k-1] +2){
seq_end[k-1] = seq_end[k];
//remove
seq_end.splice(k,1);
seq_start.splice(k,1);
}
}
} else {
seq_start= [0];
seq_end = [this.data[i][0].length];
}
drawn_something = drawn_something || seq_start.length > 0;
//keeps the prevous 3 data points for the curves
// order goes 1,2,3,i
//itinilisitation:
// j = 0 [123i, , , ]
// j = 1 [123,i, , ]
// j = 2 [12,3,i, , ]
// j = 3 [1,2,3,i, , ]
// . . .
// j = n [ , , , 1,2,3,i, , ,]
//draw the sequences.
if (this.isSVG){ctx.startGroup();}
for (k=0;k-1 ){
this.drawing_methods.draw_line(this,ctx,i,seq_start[k],seq_end[k]);
}
if ( symbol_mode === 'dot' ){
this.drawing_methods.draw_dot(this,ctx,i,seq_start[k],seq_end[k],symbol_size);
}
if ( symbol_mode === 'cdot' ){
this.drawing_methods.draw_cdot(this,ctx,i,seq_start[k],seq_end[k],symbol_size);
}
if ( symbol_mode === 'circ' ){
this.drawing_methods.draw_circ(this,ctx,i,seq_start[k],seq_end[k],symbol_size);
}
if ( symbol_mode === 'box' ){
this.drawing_methods.draw_box(this,ctx,i,seq_start[k],seq_end[k],symbol_size);
}
if ( symbol_mode === 'x' ){
this.drawing_methods.draw_x(this,ctx,i,seq_start[k],seq_end[k],symbol_size);
}
if ( symbol_mode === 'cross' ){
this.drawing_methods.draw_cross(this,ctx,i,seq_start[k],seq_end[k],symbol_size);
}
if ( line_mode.indexOf('zig') >-1 ){
this.drawing_methods.draw_zig(this,ctx,i,seq_start[k],seq_end[k]);
}
if ( line_mode.indexOf('zag') >-1 ){
this.drawing_methods.draw_zag(this,ctx,i,seq_start[k],seq_end[k]);
}
if ( line_mode.indexOf('mid') >-1 ){
this.drawing_methods.draw_mid(this,ctx,i,seq_start[k],seq_end[k]);
}
if ( line_mode.indexOf('approx') >-1 ){
this.drawing_methods.draw_approx(this,ctx,i,seq_start[k],seq_end[k]);
}
if ( line_mode.indexOf('interp') >-1 ){
this.drawing_methods.draw_interp(this,ctx,i,seq_start[k],seq_end[k]);
}
if ( line_mode.indexOf('scribble') >-1 ){
this.drawing_methods.draw_scribble(this,ctx,i,seq_start[k],seq_end[k]);
}
if ( line_mode.indexOf('hist') >-1 ){
this.drawing_methods.draw_hist(this,ctx,i,seq_start[k],seq_end[k]);
}
if (this.data[i].length >=3){
//has y error data
this.drawing_methods.draw_y_errors(this,ctx,i,seq_start[k],seq_end[k],symbol_size);
}
if (this.data[i].length >=4){
//has x error data
this.drawing_methods.draw_x_errors(this,ctx,i,seq_start[k],seq_end[k],symbol_size);
}
}
if (this.isSVG){ctx.endGroup();}
}
if (this.isPNGSVG){
ctx = svgctx;
this.isSVG=true;
ctx.image(0,0,canvas.width,canvas.height,this.pngbackground );
}
if (!this.isSVG){
if (this.needs_drag_image || gs.mouse_mode === 'drag'){
this.graph_image_for_drag = ctx.getImageData(0,0,canvas.width,canvas.height);
this.needs_drag_image = false;
}
}
ctx.lineWidth = graph_line_thickness;
ctx.strokeStyle = gs.color_fg;//'#000000';
ctx.fillStyle = gs.color_fg;;//'#000000';
if (!drawn_something && this.no_data == false && gs.function_lines.length ==0){
this.errors.push("there might be data around, just not where you're looking");
}
//draw the ticks and the points.
//for x
var l = positionsx.strings.length;
var stepping = ctx.measureText(positionsx.strings[1]+' ').width > positionsx.pos[1] - positionsx.pos[0] || ctx.measureText(positionsx.strings[l-1] + ' ').width > positionsx.pos[l-1] - positionsx.pos[l-2] ;
if (stepping ){
var j = -1*tick_labels_font_size/2;
var o = tick_labels_font_size/2;
} else {
var j = 0;
var o = 0;
}
ctx.textAlign="center";
ctx.beginPath();
for (var i = 0;i graph_line_thickness && x < canvas.width - graph_line_thickness){
var label = positionsx.strings[i];
ctx.fillText(label,x,canvas.height-tick_len*1.2-tick_lables_font_padding - o - j);
j *= -1;
/*MZ
ctx.moveTo(x,graph_line_thickness/2);
ctx.lineTo(x,tick_len);
*/
ctx.moveTo(x,canvas.height-graph_line_thickness/2);
ctx.lineTo(x,canvas.height-tick_len);
}
}
ctx.stroke();
ctx.beginPath();
for (var i = 0;i graph_line_thickness && x < canvas.width - graph_line_thickness){
/*MZ
ctx.moveTo(x,graph_line_thickness/2);
ctx.lineTo(x,minor_tick_len);
*/
ctx.moveTo(x,canvas.height-graph_line_thickness/2);
ctx.lineTo(x,canvas.height-minor_tick_len);
}
}
ctx.stroke();
gs.x_precision = positionsx.precision;
ctx.textAlign="left";
ctx.beginPath();
for (var i = 0;i graph_line_thickness && y < canvas.height - graph_line_thickness){
var label = positionsy.strings[i];
//if it is near the top, don't put a label, as this is where the axis label is.
if (y > tick_len+lable_spacing+axis_labels_font_size+tick_labels_font_size/2 && y < canvas.height - tick_labels_font_size - tick_lables_font_padding-tick_len-tick_labels_font_size/2){
ctx.fillText(label,tick_len*1.2+tick_lables_font_padding+2,y+3);
}
ctx.moveTo(graph_line_thickness/2,y);
ctx.lineTo(tick_len,y);
/*MZ
ctx.moveTo(canvas.width-graph_line_thickness/2,y);
ctx.lineTo(canvas.width-tick_len,y);
*/
}
}
ctx.stroke();
ctx.beginPath();
for (var i = 0;i graph_line_thickness && y < canvas.height - graph_line_thickness){
ctx.moveTo(graph_line_thickness/2,y);
ctx.lineTo(minor_tick_len,y);
/*MZ
ctx.moveTo(canvas.width-graph_line_thickness/2,y);
ctx.lineTo(canvas.width-minor_tick_len,y);
*/
}
}
ctx.stroke();
gs.y_precision = positionsy.precision;
if (gs.show_captions) {
ctx.font=tick_labels_font_size + 'px ' + font_name//"14px Courier New";
x = gs.scaling_factor*gs.caption_position.x;
y = gs.scaling_factor*gs.caption_position.y;
if (gs.caption_position.y>0){
//going down from top
y += tick_labels_font_size;
dy = tick_labels_font_size;
y += tick_len;
} else {
//up from botton
dy = tick_labels_font_size;
y = canvas.height + y;
y += tick_labels_font_size - this.data.length*dy;
y -= tick_len;
}
if (gs.caption_position.x>0){
ctx.textAlign="left";
x += tick_len;
} else {
ctx.textAlign="right";
x = canvas.width + x;
x -= tick_len;
}
for (i = 0;i 1 || this.transform_text_y.length > 1)&& gs.showTransformTexts ) {
var x_label = this.transform_text_x;
var y_label = this.transform_text_y;
} else {
var x_label = x_axis_title;
var y_label = y_axis_title;
}
//draw x axis label
ctx.font=axis_labels_font_size + 'px ' + font_name//"14px Courier New";
ctx.textAlign="right";
var label = x_label;
if (gs.x_scale_mode ==='time' && this.graphics_style.x_precision > 1 ){
label += ' ' + mjs_date_print( this.pixels_to_units(canvas.width,'x') ,0,Math.max(this.graphics_style.x_precision-1,0));
}
if (stepping) {
ctx.fillText(label,canvas.width - lable_spacing - tick_len,
canvas.height - tick_len-lable_spacing- 2*tick_labels_font_size-tick_lables_font_padding);
} else {
ctx.fillText(label,canvas.width - lable_spacing - tick_len,
canvas.height - tick_len-lable_spacing-tick_labels_font_size-tick_lables_font_padding);
}
ctx.textAlign="left";
//draw y axis lable
var label = y_label;
if (gs.y_scale_mode ==='time' && this.graphics_style.y_precision > 1){
label += ' ' + mjs_date_print( this.pixels_to_units(0,'y') ,0,Math.max(this.graphics_style.y_precision-1,0));
}
//var yoffset = ctx.measureText(positionsy.strings[positionsy.strings.length-1]).width;
ctx.fillText(label,tick_len+lable_spacing+tick_lables_font_padding,tick_len+lable_spacing+axis_labels_font_size);
//draw transforms stack
// draw infomation overlay if the info button is pushed.
ctx.beginPath();
ctx.stroke();
var sigma = String.fromCharCode( 963 ); //σ²
var squared = String.fromCharCode( 178 ); //σ²
var bar = String.fromCharCode( 773 ); //put befor what you want bared e.g. bar + 'y'
var edge = tick_len*2.6;
ctx.font=axis_labels_font_size + 'px ' + font_name//"14px Courier New";
var fit_points_drawn = 0;
if (gs.fits === 'none'){
//dont' do anything :(
} else {
//do some fits or stats!!!! wooo stats.
ctx.textAlign="left";
var x = gs.fit_text_position.x * canvas.width;
var y = gs.fit_text_position.y * canvas.height;
var dy = 1.2*axis_labels_font_size;
for (i = 0;i-1){
//if time axis, normalise befor fit.
var fit;
var string1;
var string2;
var string3;
if (gs.x_scale_mode ==='time'){
var normed = fits.pre_fit_normalizing(this.data[i][0]);
fit = fits.fit_funs[fits.fit_strings.indexOf(gs.fits)]( normed.x ,this.data[i][1]);
string1=fit.strings[0] + " "+normed.s;
console.log('normalised: x = -' + normed.x_mean +' / '+ normed.sigma_x);
} else {
fit = fits.fit_funs[fits.fit_strings.indexOf(gs.fits)](this.data[i][0],this.data[i][1]);
string1=fit.strings[0];
}
string2=fit.strings[1];
string3=fit.strings[2];
//draw the line
if (gs.extrapolate){
var fit_start =0;
var fit_end = canvas.width;
} else {
var fit_start = Math.max( 0, this.units_to_pixels(Math.min.apply(null, this.data[i][0]),'x') );
var fit_end = Math.min( canvas.width, this.units_to_pixels(Math.max.apply(null, this.data[i][0]),'x') );
}
//make array of fit_x points to get y-points from
var fit_x = [];
for (var j=fit_start; jx2){t+=Math.PI}
var ox = mag * Math.cos(t);
var oy = mag*Math.sin(t);
ctx.fillText(marker.text,xm+ox,ym+oy+h/2);
/* //debugg visuals
ctx.beginPath();
ctx.moveTo(xm+ox-w/2,ym+oy);
ctx.lineTo(xm+ox+w/2,ym+oy);
ctx.moveTo(xm,ym);
ctx.lineTo(xm+3*ox,ym+3*oy);
ctx.stroke();
ctx.beginPath();
ctx.arc(xm,ym,5,0,Math.PI * 2,false);
ctx.stroke();
ctx.fillText('p1',x1,y1);
ctx.fillText('p2',x2,y2);
*/
break;
case 'arrow':
ctx.beginPath();
x = this.units_to_pixels(handles[0][0],'x');
y = this.units_to_pixels(handles[0][1],'y');
ctx.moveTo(x,y);
dx = this.units_to_pixels(handles[1][0],'x');
dy = this.units_to_pixels(handles[1][1],'y');
ctx.lineTo(dx,dy);
ctx.stroke();
drawArrow(ctx,x,y,h/6,Math.atan((dy-y)/(dx-x)) + ((dx>x)?Math.PI:0) );
break;
case 'box':
ctx.beginPath();
x = this.units_to_pixels(handles[0][0],'x');
y = this.units_to_pixels(handles[0][1],'y');
dx = this.units_to_pixels(handles[1][0],'x');
dy = this.units_to_pixels(handles[1][1],'y');
ctx.rect(x,y,dx-x,dy-y);
ctx.stroke();
break;
case 'boxntext':
ctx.beginPath();
x = this.units_to_pixels(handles[0][0],'x');
y = this.units_to_pixels(handles[0][1],'y');
dx = this.units_to_pixels(handles[1][0],'x');
dy = this.units_to_pixels(handles[1][1],'y');
ctx.rect(x,y,dx-x,dy-y);
ctx.stroke();
if (y>dy){
ctx.fillText(marker.text,(x+dx)/2, y+h);
} else {
ctx.fillText(marker.text,(x+dx)/2, y-h/3);
}
break;
case 'arrowntext':
var x1 = this.units_to_pixels(handles[0][0],'x');
var y1 = this.units_to_pixels(handles[0][1],'y');
var x2 = this.units_to_pixels(handles[1][0],'x');
var y2 = this.units_to_pixels(handles[1][1],'y');
ctx.fillText(marker.text,x2,y2);
w = ctx.measureText(marker.text).width;
var minX = x2-w/2-p;
var maxX = x2+w/2+p;
var minY = y2-h-p;
var maxY= y2+h/2+p;
if ((minX <= x1 && x1 <= maxX) && (minY <= y1 && y1 <= maxY)){
break;
}
ctx.beginPath();
ctx.moveTo(x1,y1);
var midX = (minX + maxX) / 2;
var midY = (minY + maxY) / 2;
var m = (midY - y1) / (midX - x1);
if (x1 < midX) { // check "left" side
var minXy = m * (minX - x1) + y1;
if (minY < minXy && minXy < maxY)
ctx.lineTo(minX,minXy);
} if (x1 > midX) { // check "right" side
var maxXy = m * (maxX - x1) + y1;
if (minY < maxXy && maxXy < maxY)
ctx.lineTo(maxX,maxXy);
} if (y1 < midY) { // check "top" side
var minYx = (minY - y1) / m + x1;
if (minX < minYx && minYx < maxX)
ctx.lineTo(minYx,minY);
} if (y1 > midY) { // check "bottom" side
var maxYx = (maxY - y1) / m + x1;
if (minX < maxYx && maxYx < maxX)
ctx.lineTo(maxYx,maxY);
}
ctx.stroke();
drawArrow(ctx,x1,y1,h/6,Math.atan((y2-y1)/(x2-x1)) + ((x2>x1)?Math.PI:0) );
break;
}
}
ctx.lineWidth = graph_line_thickness;
ctx.textAlign="center";
ctx.strokeStyle = gs.color_fg;
//draw title and subtitle
ctx.fillStyle = gs.color_fg;
ctx.font=title_font_size + 'px ' + font_name;//"24px Courier New";
ctx.fillText(title,canvas.width/2,tick_len+title_spacing+title_font_size);
ctx.font=axis_labels_font_size + 'px ' + font_name;//"24px Courier New";
ctx.fillText(gs.subtitle,canvas.width/2,tick_len+title_spacing+axis_labels_font_size+title_font_size);
ctx.fillText(gs.subtitle2,canvas.width/2,tick_len+title_spacing+2*axis_labels_font_size+title_font_size);
if (gs.data_transforms.length > 0 && gs.showTransformTexts){
ctx.font=axis_labels_font_size + 'px ' + font_name;//"24px Courier New";
var label = '['+gs.data_transforms.join('|')+']';
var labely = title_spacing+title_font_size+tick_len+axis_labels_font_size;
if (gs.subtitle.length>0){labely += axis_labels_font_size;}
if (gs.subtitle2.length>0){labely += axis_labels_font_size;}
//top center
ctx.fillText(label,canvas.width/2,labely);
//bottom left
//ctx.fillText(label,lable_spacing + tick_len, canvas.height - tick_len-lable_spacing-tick_labels_font_size);
}
ctx.beginPath();
ctx.stroke();
if (gs.i >= 1){
ctx.strokeStyle = gs.color_fg;
//white out the bg
ctx.lineWidth = graph_line_thickness;
ctx.fillStyle = gs.color_bg;
ctx.rect(2*edge,2*edge,canvas.width - 4*edge,canvas.height - 4*edge);
ctx.fill();
ctx.stroke();
//graph.pre_mouse_mode = gs.mouse_mode;
//gs.mouse_mode = 'i';
ctx.fillStyle = gs.color_fg;
ctx.textAlign="left";
var y = 3*edge;
var dy = axis_labels_font_size*1.5;
ctx.fillText('canvas width : ' + canvas.width,3*edge,y); y +=dy;
ctx.fillText('canvas height : ' + canvas.height,3*edge,y);y +=dy;
ctx.fillText('canvas ID : ' + this.canvas_name,3*edge,y);y +=dy;
ctx.fillText('graph name : ' + this.graph_name,3*edge,y);y +=dy;
ctx.fillText('No. series : ' + this.data.length + ' of ' + this.data_backup.length,3*edge,y);y +=dy;
ctx.fillText('Points drawn : ' + this.points_drawn,3*edge,y);y +=dy;
ctx.fillText('Drawn points for fits : ' + fit_points_drawn,3*edge,y);y +=dy;
ctx.fillText('Touch : ' + is_touch_device(),3*edge,y);y +=dy;
var end_time = new Date();
graph.ui.draw_time = end_time.getTime() - start_time.getTime();
ctx.fillText('Draw Time : ' + graph.ui.draw_time+'ms',3*edge,y);y +=dy;
ctx.fillText('Copy Time : ' + graph.ui.copy_time+'ms',3*edge,y);y +=dy;
ctx.textAlign="right";
var label = "MJS"+"2015";
ctx.fillText(label,canvas.width-tick_len-2*edge,canvas.height - axis_labels_font_size-2*edge);
var label = 'version: ' + MJS_PLOT_VERSION;
ctx.fillText(label,canvas.width-tick_len-2*edge,canvas.height - axis_labels_font_size*2.5-2*edge);
var label = 'web:' + MJS_PLOT_WEBSITE;
ctx.fillText(label,canvas.width-tick_len-2*edge,canvas.height - axis_labels_font_size*3.5-2*edge);
}
this.points_drawn =0;
//print any errors to the screen
//graph.errors = ['some error']
if (this.errors.length >0){
ctx.font=axis_labels_font_size + 'px ' + font_name;//"24px Courier New";
//make the text red
var oldfill = ctx.fillStyle;
ctx.fillStyle = "#FF0000";
ctx.textAlign="center";
for (var i = 0;i';
document.body.appendChild(svgDiv);
this.isSVG=true;
this.mjs_plot();
var svg = document.getElementById("_mjsplotSVG").outerHTML;
var xmlheadder = ' ';
download_text(xmlheadder+svg,'mjsplot_graph.svg','data:image/svg+xml;charset=utf-8');
document.body.removeChild(svgDiv);
this.isSVG=false;
this.canvas.width = temp_width;
this.canvas.height = temp_height;
this.mjs_plot();
},
export_svg_png : function (width,height,sf){
var temp_width = this.canvas.width;
var temp_height = this.canvas.height;
this.canvas.width = width*sf;
this.canvas.height = height*sf;
this.graphics_style.scaling_factor *= sf;
this.transparent = true;
this.needs_drag_image = true;
this.mjs_plot();
var ctx = this.canvas.getContext('2d');
ctx.putImageData(this.graph_image_for_drag,0,0);
this.pngbackground = this.canvas.toDataURL();
this.transparent = false;
this.graphics_style.scaling_factor /= sf;
this.canvas.width = width;
this.canvas.height = height;
var svgDiv = document.createElement('div');
svgDiv.innerHTML = '';
document.body.appendChild(svgDiv);
this.isSVG=false;
this.isPNGSVG=true;
this.mjs_plot();
var svg = document.getElementById("_mjsplotSVG").outerHTML;
var xmlheadder = ' ';
download_text(xmlheadder+svg,'mjsplot_graph.svg','data:image/svg+xml;charset=utf-8');
document.body.removeChild(svgDiv);
this.isSVG=false;
this.isPNGSVG=false;
this.canvas.width = temp_width;
this.canvas.height = temp_height;
this.mjs_plot();
},
export_png :function(width,height,sf){
var temp_width = this.canvas.width;
var temp_height = this.canvas.height;
this.canvas.width = width;
this.canvas.height = height;
this.graphics_style.scaling_factor *= sf;
this.transparent = true;
this.mjs_plot();
var dataUrl = this.canvas.toDataURL();
window.open(dataUrl, "toDataURL() image", "width="+width+", height="+height);
this.transparent = false;
this.graphics_style.scaling_factor /= sf;
this.canvas.width = temp_width;
this.canvas.height = temp_height;
this.mjs_plot();
},
busy: function () {
if (mouse_down || this.menushown) {
this.paused = true;
return true;
}
mouse_move_event(this.ui.latestEvent,this);
if (this.paused) {
this.paused = false;
}
return false;
}
};//end of graph object
graph.canvas = canvas;
make_interactive(graph);
return graph;
}//end of new_graph()
,
get_graph_style : function (){
// set 'drag' to default mode on mobile, 'zoom' on desktops.
if (is_touch_device()){
var mouse_mode = 'drag'
} else {
var mouse_mode = 'zoom'
}
return {
graph_line_thickness : 1, // for the graph generally.
symbol_size : 5,
line_thickness: 2, // for plotted lines
tick_len : 7,
mode : 'dot line', //'circles', 'dot', or 'line'
symbol_mode : 'dot', // dot block circle croxx
line_mode : 'line', // line approx interp zig zag mid hist
symbol_modes : [], //per line symbol modes
line_modes : [], //per line line modes.
font_name : "serif",
title_font_size : 20,// in px (24)
title : "Title",
subtitle : "",
subtitle2 : "",
title_spacing : 2, // extra space from the top, in px.
tick_labels_font_size : 10,//10
tick_lables_font_padding : 2, // px extra space up from the ticks
axis_labels_font_size: 10,//12
lable_spacing : 4,//pixels away from the tick lables
x_axis_title : 'x axis (units)',
y_axis_title : 'y axis (units)',
x_axis_prefix : "",
x_axis_postfix : "",
y_axis_prefix : "",
y_axis_postfix : "",
x_label_mode : 'norm', // or infix,postfix,sci
y_label_mode : 'norm', // or infix,postfix,sci
minor_tick_len : 5,
guideWidthx : 35, //pixels accross
guideWidthy : 20, //pixels a across
scaling_factor : 1,//scales up everything together...
mouse_mode : mouse_mode, //changes the function of the mouse. between 'zoom' and 'drag' and 'measure'
fits : 'none', //what fitting is applied
extrapolate : false, //if the fit should extrapolate beyond the data
color_fits : false, //if the fit lines should be black or with the color of the series
x_scale_auto_max : true,
x_scale_auto_min : true,
y_scale_auto_max : true,
y_scale_auto_min : true,
x_manual_min : 0.0,
x_manual_max : 1.0,
y_manual_min : 0.0,
y_manual_max : 1.0,
x_scale_mode : 'lin', //linear or log
y_scale_mode : 'lin',
x_scale_tight : false, //data goes right to the edges.
y_scale_tight : false, // otherwise there will be a nice 1 section space on all sides.
show_captions : true,
showTransformTexts : true, //show the stack of transforms and modify the axis titles.
captions_display : "none",//xmin xmax ymin ymax yfirst ylast xfirst xlast or non
captions_extra_precision : 1, //extra precision to display on the captions has a minimum of 0. don't go negative.
color_fg : '#111', //foreground color
color_bg : '#eee', //background color
color_mg : '#aaa', //midground color, for the grid.
show_grid: false,
v : MJS_PLOT_VERSION, //version of mjsplot
caption_colors : false, // generate colors from the captions
hidden_lines : [], // array of booleans if each line should be hidden.
line_colours :[], // array of colours to use for the lines.
data_transforms : [], //a list of the transform functions
data_transforms_args : [], // the arguments for each transform
i : 0, //state of the infomation button
o : 0, //state of the options button
modified : false, //changes to true as soon as the user does anything to customise the style
x_precision :2, //the number of sf to use when printing numbers on the x axis
y_precision :2, //as above for y
fit_text_position:{x:0.1,y:0.2}, //fraction accross and fraction down.
caption_position:{x:-10,y:10}, //raw pixels down and accross (is then scaled by scaling_factor)
markers : [], //user added annotations
show_markers : true,
function_lines :[] // userdefined lines that are drawn over the graph. each item in the array is a string of the function
}
}
,
convert_time_strings : function (array_of_strings){
//converts an array of time strings to milliseconds from epoc
//example time strings:
/*
ISO-8601 and other strings (browser dependent)
March 2 2015
March 2 2015 12:45
2014/11/02 12:54
12:53 2014/11/02
2014
2/3/2014 ( MM/DD/YYYY)
Mon, 25 Dec 1995 13:30:00 GMT
2011-11-12
This does not include
dd/mm/yyyy hh:mm:ss
or
yyy-mm-dd hh:mm:ss
as that is some crazy non-standard old time brittish thing and you will end up with a mess.
*/
var r = [];
for (var i = 0 ; i < array_of_strings.length;i++){
r.push( Date.parse( array_of_strings[i] ) );
}
return r;
}
};
return mjs_plot;
}());
//embedd LZstring for compression
var LZString=function(){function o(o,r){if(!t[o]){t[o]={};for(var n=0;ne;e++){var s=r.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(o){return null==o?"":i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return"";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;ie;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u)}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==r-1){d.push(n(m));break}v++}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(t=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return"";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else{if(l!==d)return null;v=s+s.charAt(0)}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++)}}};return i}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module&&(module.exports=LZString);
//embedd MJSFont list for the font menu with added computer modern and cmr10
MJSFontList=function(){function a(a){var n,e,o,t=0;if(0==a.length)return t;for(n=0,o=a.length;o>n;n++)e=a.charCodeAt(n),t=(t<<5)-t+e,t|=0;return t}function n(n){d.clearRect(0,0,u,c),d.font=l+n,d.fillText(r,0,c/2);var e=i.toDataURL("image/png");return a(e)}function e(a){return-1==s.indexOf(n(a))}var o=["sans-serif","serif","monospace","cursive"],t=["Arial","Arial Black","Baskerville","Batang","Big Caslon","Book Antiqua","Brush Script MT","Calibri","Candara","Century Gothic","Charcoal","Comic Sans MS","Computer Modern","Copperplate","Courier","DejaVu Sans","DejaVu Sans Mono","DejaVu Serif","Didot","Droid Sans","Droid Sans Mono","Droid Serif","Franklin Gothic Medium","Futura","Gadget","Garamond","Geneva","Georgia","Goudy Old Style","Helvetica","Impact","Latin Modern Roman","Latin Modern Mono","Latin Modern Sans","Lato","Lora","Lucida Console","Lucida Grande","Lucida Sans Unicode","Monaco","Open Sans Condensed","Oswald","PT Sans","Palatino","Palatino Linotype","Roboto","Rockwell","Rockwell Extra Bold","Segoe UI","Source Sans Pro","Tahoma","Times New Roman","Trebuchet MS","Ubuntu","Ubuntu-Title","Ubuntu Mono","Verdana","cursive"],r=(document.getElementsByTagName("body")[0],["agAmMqQwWlLi1"]),i=document.createElement("canvas"),u=100,c=15;i.width=u,i.height=c;for(var l="10px ",d=i.getContext("2d"),s=[],S=0;S