1 /*
  2     Copyright 2008,2009
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Bianca Valentin,
  7         Alfred Wassermann,
  8         Peter Wilfahrt
  9 
 10     This file is part of JSXGraph.
 11 
 12     JSXGraph is free software: you can redistribute it and/or modify
 13     it under the terms of the GNU Lesser General Public License as published by
 14     the Free Software Foundation, either version 3 of the License, or
 15     (at your option) any later version.
 16 
 17     JSXGraph is distributed in the hope that it will be useful,
 18     but WITHOUT ANY WARRANTY; without even the implied warranty of
 19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 20     GNU Lesser General Public License for more details.
 21 
 22     You should have received a copy of the GNU Lesser General Public License
 23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
 24 */
 25 /**
 26  * Options object.
 27  * @class These are the default options of the board and
 28  * of all geometry elements.
 29  * @constructor
 30  */
 31 JXG.Options = {
 32     /* Options that are used directly within the board class */
 33     showCopyright : true,
 34     showNavigation : true,
 35     takeSizeFromFile : false, // If true, the construction - when read from a file or string - the size of the div can be changed.
 36     renderer: 'svg',
 37     takeFirst : false, // if true the first element with hasPoint==true is taken.
 38 
 39     /* grid options */
 40     grid : {
 41         /* grid styles */
 42         hasGrid : false,
 43         gridX : 1,
 44         gridY : 1,
 45         gridColor : '#C0C0C0',
 46         gridOpacity : '0.5',
 47         gridDash : true,
 48         /* snap to grid options */
 49         snapToGrid : false,
 50         snapSizeX : 2,
 51         snapSizeY : 2
 52     },
 53     /* zoom options */
 54     zoom : {
 55         factor : 1.25
 56     },
 57 
 58     /* geometry element options */
 59     elements : {
 60         /* color options */
 61         strokeColor: '#0000ff',
 62         highlightStrokeColor: '#C3D9FF',
 63         fillColor: 'none',
 64         highlightFillColor: 'none',
 65 
 66         strokeOpacity: 1,
 67         highlightStrokeOpacity: 1,
 68         fillOpacity: 1,
 69         highlightFillOpacity: 1,
 70         strokeWidth: '2px',
 71 	    withLabel: false,
 72 
 73         /*draft options */
 74         draft : {
 75             draft : false,
 76             color : '#565656',
 77             opacity : 0.8,
 78             strokeWidth : '1px'
 79         }
 80     },
 81 
 82     /* special point options */
 83     point : {
 84     	withLabel: true,
 85         style : 5, // deprecated
 86         face : 'o',
 87         size : 3,
 88         fillColor : '#ff0000',
 89         highlightFillColor : '#EEEEEE',
 90         strokeWidth: '2px',
 91         strokeColor : '#ff0000', //'#0000ff',
 92         highlightStrokeColor : '#C3D9FF',
 93         zoom: false,             // Change the point size on zoom
 94         showInfobox: true
 95     },
 96 
 97     /* special line options */
 98     line : {
 99         firstArrow : false,
100         lastArrow : false,
101         straightFirst : true,
102         straightLast : true,
103         fillColor : '#000000',
104         highlightFillColor : 'none',
105         strokeColor : '#0000ff',
106         highlightStrokeColor : '#888888',
107         /* line ticks options */
108         ticks : {
109             drawLabels : true,
110             drawZero : false,
111             insertTicks : false,
112             minTicksDistance : 50,
113             maxTicksDistance : 300,
114             minorHeight : 4,
115             majorHeight : 10,
116             minorTicks : 4,
117             defaultDistance : 1
118         },
119         /* absolute label offset from anchor */
120         labelOffsets: [10,10]
121     },
122 
123     /* special axis options */
124     axis : {
125         strokeColor : '#666666',
126         highlightStrokeColor : '#888888'
127     },
128 
129     /*special circle options */
130     circle : {
131         fillColor : 'none',
132         highlightFillColor : 'none',
133         strokeColor : '#0000ff',
134         highlightStrokeColor : '#C3D9FF'
135     },
136 
137     /* special conic options */
138     conic : {
139         fillColor : 'none',
140         highlightFillColor : 'none',
141         strokeColor : '#0000ff',
142         highlightStrokeColor : '#C3D9FF'
143     },
144 
145     /* special angle options */
146     angle : {
147 	    withLabel:true,
148         radius : 1.0,
149         fillColor : '#FF7F00',
150         highlightFillColor : '#FF7F00',
151         strokeColor : '#FF7F00',
152         textColor : '#0000FF',
153         fillOpacity : 0.3,
154         highlightFillOpacity : 0.3
155     },
156 
157     /* special arc options */
158     arc : {
159         firstArrow : false,
160         lastArrow : false,
161         fillColor : 'none',
162         highlightFillColor : 'none',
163         strokeColor : '#0000ff',
164         highlightStrokeColor : '#C3D9FF'
165     },
166 
167     /* special polygon options */
168     polygon : {
169         fillColor : '#00FF00',
170         highlightFillColor : '#00FF00',
171         fillOpacity : 0.3,
172         highlightFillOpacity : 0.3
173     },
174 
175     /* special sector options */
176     sector : {
177         fillColor: '#00FF00',
178         highlightFillColor: '#00FF00',
179         fillOpacity: 0.3,
180         highlightFillOpacity: 0.3
181     },
182 
183     /* special text options */
184     text : {
185         fontSize : 12,
186         strokeColor : '#000000',
187         useASCIIMathML : false,
188         useMathJax : false,
189         defaultDisplay : 'html' //'html' or 'internal'
190     },
191 
192     /* special curve options */
193     curve : {
194         strokeWidth : '1px',
195         strokeColor : '#0000ff',
196         RDPsmoothing : false,    // Apply the Ramen-Douglas-Peuker algorithm
197         numberPointsHigh : 1600, // Number of points on curves after mouseUp
198         numberPointsLow : 400,   // Number of points on curves after mousemove
199         doAdvancedPlot : true    // Use the algorithm by Gillam and Hohenwarter
200                                  // It is much slower, but the result is better
201     },
202 
203     /* precision options */
204     precision : {
205         touch    : 30,
206         mouse    : 4,
207         epsilon  : 0.0001,
208         hasPoint : 4
209     },
210 
211     // Default ordering of the layers
212     layer : {
213         numlayers:20, // only important in SVG
214         text  : 9,
215         point : 9,
216         arc   : 8,
217         line  : 7,
218         circle: 6,
219         curve : 5,
220         polygon: 4,
221         sector: 3,
222         angle : 3,
223         grid  : 1,
224         image : 0
225     },
226 
227     locus : {
228     	translateToOrigin: false,
229     	translateTo10: false,
230     	stretch: false,
231     	toOrigin: null,
232     	to10: null
233     }
234 };
235 
236 /**
237  * Apply the options stored in this object to all objects on the given board.
238  * @param {JXG.Board} board The board to which objects the options will be applied.
239  */
240 JXG.useStandardOptions = function(board) {
241     var o = JXG.Options,
242         boardHadGrid = board.hasGrid,
243         el, t, p;
244 
245     board.options.grid.hasGrid = o.grid.hasGrid;
246     board.options.grid.gridX = o.grid.gridX;
247     board.options.grid.gridY = o.grid.gridY;
248     board.options.grid.gridColor = o.grid.gridColor;
249     board.options.grid.gridOpacity = o.grid.gridOpacity;
250     board.options.grid.gridDash = o.grid.gridDash;
251     board.options.grid.snapToGrid = o.grid.snapToGrid;
252     board.options.grid.snapSizeX = o.grid.SnapSizeX;
253     board.options.grid.snapSizeY = o.grid.SnapSizeY;
254     board.takeSizeFromFile = o.takeSizeFromFile;
255 
256     for(el in board.objects) {
257         p = board.objects[el];
258         if(p.elementClass == JXG.OBJECT_CLASS_POINT) {
259             p.visProp['fillColor'] = o.point.fillColor;
260             p.visProp['highlightFillColor'] = o.point.highlightFillColor;
261             p.visProp['strokeColor'] = o.point.strokeColor;
262             p.visProp['highlightStrokeColor'] = o.point.highlightStrokeColor;
263         }
264         else if(p.elementClass == JXG.OBJECT_CLASS_LINE) {
265             p.visProp['fillColor'] = o.line.fillColor;
266             p.visProp['highlightFillColor'] = o.line.highlightFillColor;
267             p.visProp['strokeColor'] = o.line.strokeColor;
268             p.visProp['highlightStrokeColor'] = o.line.highlightStrokeColor;
269             for(t in p.ticks) {
270                 t.majorTicks = o.line.ticks.majorTicks;
271                 t.minTicksDistance = o.line.ticks.minTicksDistance;
272                 t.minorHeight = o.line.ticks.minorHeight;
273                 t.majorHeight = o.line.ticks.majorHeight;
274             }
275         }
276         else if(p.elementClass == JXG.OBJECT_CLASS_CIRCLE) {
277             p.visProp['fillColor'] = o.circle.fillColor;
278             p.visProp['highlightFillColor'] = o.circle.highlightFillColor;
279             p.visProp['strokeColor'] = o.circle.strokeColor;
280             p.visProp['highlightStrokeColor'] = o.circle.highlightStrokeColor;
281         }
282         else if(p.type == JXG.OBJECT_TYPE_ANGLE) {
283             p.visProp['fillColor'] = o.angle.fillColor;
284             p.visProp['highlightFillColor'] = o.angle.highlightFillColor;
285             p.visProp['strokeColor'] = o.angle.strokeColor;
286         }
287         else if(p.type == JXG.OBJECT_TYPE_ARC) {
288             p.visProp['fillColor'] = o.arc.fillColor;
289             p.visProp['highlightFillColor'] = o.arc.highlightFillColor;
290             p.visProp['strokeColor'] = o.arc.strokeColor;
291             p.visProp['highlightStrokeColor'] = o.arc.highlightStrokeColor;
292         }
293         else if(p.type == JXG.OBJECT_TYPE_POLYGON) {
294             p.visProp['fillColor'] = o.polygon.fillColor;
295             p.visProp['highlightFillColor'] = o.polygon.highlightFillColor;
296             p.visProp['fillOpacity'] = o.polygon.fillOpacity;
297             p.visProp['highlightFillOpacity'] = o.polygon.highlightFillOpacity;
298         }
299         else if(p.type == JXG.OBJECT_TYPE_CONIC) {
300             p.visProp['fillColor'] = o.conic.fillColor;
301             p.visProp['highlightFillColor'] = o.conic.highlightFillColor;
302             p.visProp['strokeColor'] = o.conic.strokeColor;
303             p.visProp['highlightStrokeColor'] = o.conic.highlightStrokeColor;
304         }
305         else if(p.type == JXG.OBJECT_TYPE_CURVE) {
306             p.visProp['strokeColor'] = o.curve.strokeColor;
307         }
308     }
309     for(el in board.objects) {
310         p = board.objects[el];
311         if(p.type == JXG.OBJECT_TYPE_SECTOR) {
312             p.arc.visProp['fillColor'] = o.sector.fillColor;
313             p.arc.visProp['highlightFillColor'] = o.sector.highlightFillColor;
314             p.arc.visProp['fillOpacity'] = o.sector.fillOpacity;
315             p.arc.visProp['highlightFillOpacity'] = o.sector.highlightFillOpacity;
316         }
317     }
318 
319     board.fullUpdate();
320     if(boardHadGrid && board.hasGrid) {
321         board.renderer.removeGrid(board);
322         board.renderer.drawGrid(board);
323     } else if(boardHadGrid && !board.hasGrid) {
324         board.renderer.removeGrid(board);
325     } else if(!boardHadGrid && board.hasGrid) {
326         board.renderer.drawGrid(board);
327     }
328 };
329 
330 /**
331  * Converts all color values to greyscale and calls useStandardOption to put them onto the board.
332  * @param {JXG.Board} board The board to which objects the options will be applied.
333  * @see #useStandardOptions
334  */
335 JXG.useBlackWhiteOptions = function(board) {
336     var o = JXG.Options;
337     o.point.fillColor = JXG.rgb2bw(o.point.fillColor);
338     o.point.highlightFillColor = JXG.rgb2bw(o.point.highlightFillColor);
339     o.point.strokeColor = JXG.rgb2bw(o.point.strokeColor);
340     o.point.highlightStrokeColor = JXG.rgb2bw(o.point.highlightStrokeColor);
341 
342     o.line.fillColor = JXG.rgb2bw(o.line.fillColor);
343     o.line.highlightFillColor = JXG.rgb2bw(o.line.highlightFillColor);
344     o.line.strokeColor = JXG.rgb2bw(o.line.strokeColor);
345     o.line.highlightStrokeColor = JXG.rgb2bw(o.line.highlightStrokeColor);
346 
347     o.circle.fillColor = JXG.rgb2bw(o.circle.fillColor);
348     o.circle.highlightFillColor = JXG.rgb2bw(o.circle.highlightFillColor);
349     o.circle.strokeColor = JXG.rgb2bw(o.circle.strokeColor);
350     o.circle.highlightStrokeColor = JXG.rgb2bw(o.circle.highlightStrokeColor);
351 
352     o.arc.fillColor = JXG.rgb2bw(o.arc.fillColor);
353     o.arc.highlightFillColor = JXG.rgb2bw(o.arc.highlightFillColor);
354     o.arc.strokeColor = JXG.rgb2bw(o.arc.strokeColor);
355     o.arc.highlightStrokeColor = JXG.rgb2bw(o.arc.highlightStrokeColor);
356 
357     o.polygon.fillColor = JXG.rgb2bw(o.polygon.fillColor);
358     o.polygon.highlightFillColor  = JXG.rgb2bw(o.polygon.highlightFillColor);
359 
360     o.sector.fillColor = JXG.rgb2bw(o.sector.fillColor);
361     o.sector.highlightFillColor  = JXG.rgb2bw(o.sector.highlightFillColor);
362 
363     o.curve.strokeColor = JXG.rgb2bw(o.curve.strokeColor);
364     o.grid.gridColor = JXG.rgb2bw(o.grid.gridColor);
365 
366     JXG.useStandardOptions(board);
367 };
368 
369 /**
370  * Decolorizes the given color.
371  * @param {String} color HTML string containing the HTML color code.
372  * @type String
373  * @return Returns a HTML color string
374  */
375 JXG.rgb2bw = function(color) {
376     if(color == 'none') {
377         return color;
378     }
379     var x, HexChars="0123456789ABCDEF", tmp, arr;
380     arr = JXG.rgbParser(color);
381     x = 0.3*arr[0] + 0.59*arr[1] + 0.11*arr[2];
382     tmp = HexChars.charAt((x>>4)&0xf)+HexChars.charAt(x&0xf);
383     color = "#" + tmp + "" + tmp + "" + tmp;
384     return color;
385 };
386 
387 /**
388  * Converts the colors of the elements to how a color blind person would approximately see it. Possible
389  * options are <i>protanopia</i>, <i>deuteranopia</i>, and <i>tritanopia</i>.
390  * @param {JXG.Board} board The board to which objects the options will be applied.
391  * @param {string} deficiency The type of deficiency which will be simulated.
392  * @see #useStandardOptions
393  */
394 JXG.simulateColorBlindness = function(board, deficiency) {
395     o = JXG.Options;
396     o.point.fillColor = JXG.rgb2cb(o.point.fillColor, deficiency);
397     o.point.highlightFillColor = JXG.rgb2cb(o.point.highlightFillColor, deficiency);
398     o.point.strokeColor = JXG.rgb2cb(o.point.strokeColor, deficiency);
399     o.point.highlightStrokeColor = JXG.rgb2cb(o.point.highlightStrokeColor, deficiency);
400 
401     o.line.fillColor = JXG.rgb2cb(o.line.fillColor, deficiency);
402     o.line.highlightFillColor = JXG.rgb2cb(o.line.highlightFillColor, deficiency);
403     o.line.strokeColor = JXG.rgb2cb(o.line.strokeColor, deficiency);
404     o.line.highlightStrokeColor = JXG.rgb2cb(o.line.highlightStrokeColor, deficiency);
405 
406     o.circle.fillColor = JXG.rgb2cb(o.circle.fillColor, deficiency);
407     o.circle.highlightFillColor = JXG.rgb2cb(o.circle.highlightFillColor, deficiency);
408     o.circle.strokeColor = JXG.rgb2cb(o.circle.strokeColor, deficiency);
409     o.circle.highlightStrokeColor = JXG.rgb2cb(o.circle.highlightStrokeColor, deficiency);
410 
411     o.arc.fillColor = JXG.rgb2cb(o.arc.fillColor, deficiency);
412     o.arc.highlightFillColor = JXG.rgb2cb(o.arc.highlightFillColor, deficiency);
413     o.arc.strokeColor = JXG.rgb2cb(o.arc.strokeColor, deficiency);
414     o.arc.highlightStrokeColor = JXG.rgb2cb(o.arc.highlightStrokeColor, deficiency);
415 
416     o.polygon.fillColor = JXG.rgb2cb(o.polygon.fillColor, deficiency);
417     o.polygon.highlightFillColor  = JXG.rgb2cb(o.polygon.highlightFillColor, deficiency);
418 
419     o.sector.fillColor = JXG.rgb2cb(o.sector.fillColor, deficiency);
420     o.sector.highlightFillColor  = JXG.rgb2cb(o.sector.highlightFillColor, deficiency);
421 
422     o.curve.strokeColor = JXG.rgb2cb(o.curve.strokeColor, deficiency);
423     o.grid.gridColor = JXG.rgb2cb(o.grid.gridColor, deficiency);
424 
425     JXG.useStandardOptions(board);
426 };
427 
428 /**
429  * Decolorizes the given color.
430  * @param {String} color HTML string containing the HTML color code.
431  * @param {String} deficiency The type of color blindness. Possible
432  * options are <i>protanopia</i>, <i>deuteranopia</i>, and <i>tritanopia</i>.
433  * @type String
434  * @return Returns a HTML color string
435  */
436 JXG.rgb2cb = function(color, deficiency) {
437     if(color == 'none') {
438         return color;
439     }
440 
441     var rgb, l, m, s, lms, tmp,
442         a1, b1, c1, a2, b2, c2,
443         inflection;
444 //        anchor = new Array(12), anchor_e = new Array(3);
445 /*
446  has been required to calculate the constants for a1, ..., c2, and inflection.
447 */
448 /* old stuff. just here for debugging purposes
449     anchor[0] = 0.08008;  anchor[1]  = 0.1579;    anchor[2]  = 0.5897;
450     anchor[3] = 0.1284;   anchor[4]  = 0.2237;    anchor[5]  = 0.3636;
451     anchor[6] = 0.9856;   anchor[7]  = 0.7325;    anchor[8]  = 0.001079;
452     anchor[9] = 0.0914;   anchor[10] = 0.007009;  anchor[11] = 0.0;
453 
454     anchor_e[0] = 0.14597772;
455     anchor_e[1] = 0.12188395;
456     anchor_e[2] = 0.08413913;
457 
458 
459     document.getElementById('debug').innerHTML += 'color: ' + color;
460 
461 //    document.getElementById('debug').innerHTML += 'deuteranopia<br/><br/>';
462       // find a,b,c for lam=575nm and lam=475
463       a1 = anchor_e[1] * anchor[8] - anchor_e[2] * anchor[7];
464       b1 = anchor_e[2] * anchor[6] - anchor_e[0] * anchor[8];
465       c1 = anchor_e[0] * anchor[7] - anchor_e[1] * anchor[6];
466       a2 = anchor_e[1] * anchor[2] - anchor_e[2] * anchor[1];
467       b2 = anchor_e[2] * anchor[0] - anchor_e[0] * anchor[2];
468       c2 = anchor_e[0] * anchor[1] - anchor_e[1] * anchor[0];
469       inflection = (anchor_e[2] / anchor_e[0]);
470 
471 //    document.getElementById('debug').innerHTML += 'a1 = ' + a1 + '<br/>' + 'b1 = ' + b1 + '<br/>' + 'c1 = ' + c1 + '<br/>' + 'a2 = ' + a2 + '<br/>' + 'b2 = ' + b2 + '<br/>' + 'c2 = ' + c2 + '<br/>' + 'inflection = ' + inflection + '<br/><br/>protanopia<br/><br/>';
472       // find a,b,c for lam=575nm and lam=475
473       a1 = anchor_e[1] * anchor[8] - anchor_e[2] * anchor[7];
474       b1 = anchor_e[2] * anchor[6] - anchor_e[0] * anchor[8];
475       c1 = anchor_e[0] * anchor[7] - anchor_e[1] * anchor[6];
476       a2 = anchor_e[1] * anchor[2] - anchor_e[2] * anchor[1];
477       b2 = anchor_e[2] * anchor[0] - anchor_e[0] * anchor[2];
478       c2 = anchor_e[0] * anchor[1] - anchor_e[1] * anchor[0];
479       inflection = (anchor_e[2] / anchor_e[1]);
480 
481 //    document.getElementById('debug').innerHTML += 'a1 = ' + a1 + '<br/>' + 'b1 = ' + b1 + '<br/>' + 'c1 = ' + c1 + '<br/>' + 'a2 = ' + a2 + '<br/>' + 'b2 = ' + b2 + '<br/>' + 'c2 = ' + c2 + '<br/>' + 'inflection = ' + inflection + '<br/><br/>tritanopia<br/><br/>';
482       // Set 1: regions where lambda_a=575, set 2: lambda_a=475
483       a1 = anchor_e[1] * anchor[11] - anchor_e[2] * anchor[10];
484       b1 = anchor_e[2] * anchor[9]  - anchor_e[0] * anchor[11];
485       c1 = anchor_e[0] * anchor[10] - anchor_e[1] * anchor[9];
486       a2 = anchor_e[1] * anchor[5]  - anchor_e[2] * anchor[4];
487       b2 = anchor_e[2] * anchor[3]  - anchor_e[0] * anchor[5];
488       c2 = anchor_e[0] * anchor[4]  - anchor_e[1] * anchor[3];
489       inflection = (anchor_e[1] / anchor_e[0]);
490 
491 
492 //    document.getElementById('debug').innerHTML += 'a1 = ' + a1 + '<br/>' + 'b1 = ' + b1 + '<br/>' + 'c1 = ' + c1 + '<br/>' + 'a2 = ' + a2 + '<br/>' + 'b2 = ' + b2 + '<br/>' + 'c2 = ' + c2 + '<br/>' + 'inflection = ' + inflection;
493 */
494     lms = JXG.rgb2LMS(color);
495     l = lms.l; m = lms.m; s = lms.s;
496 
497     deficiency = deficiency.toLowerCase();
498 
499     switch(deficiency) {
500         case "protanopia":
501             a1 = -0.06150039994295001;
502             b1 = 0.08277001656812001;
503             c1 = -0.013200141220000003;
504             a2 = 0.05858939668799999;
505             b2 = -0.07934519995360001;
506             c2 = 0.013289415272000003;
507             inflection = 0.6903216543277437;
508 
509             tmp = s/m;
510             if (tmp < inflection)
511                 l = -(b1 * m + c1 * s) / a1;
512             else
513                 l = -(b2 * m + c2 * s) / a2;
514             break;
515         case "tritanopia":
516             a1 = -0.00058973116217;
517             b1 = 0.007690316482;
518             c1 = -0.01011703519052;
519             a2 = 0.025495080838999994;
520             b2 = -0.0422740347;
521             c2 = 0.017005316784;
522             inflection = 0.8349489908460004;
523 
524             tmp = m / l;
525             if (tmp < inflection)
526               s = -(a1 * l + b1 * m) / c1;
527             else
528               s = -(a2 * l + b2 * m) / c2;
529             break;
530         default:
531             a1 = -0.06150039994295001;
532             b1 = 0.08277001656812001;
533             c1 = -0.013200141220000003;
534             a2 = 0.05858939668799999;
535             b2 = -0.07934519995360001;
536             c2 = 0.013289415272000003;
537             inflection = 0.5763833686400911;
538 
539             tmp = s/l;
540             if(tmp < inflection)
541                 m = -(a1 * l + c1 * s) / b1;
542             else
543                 m = -(a2 * l + c2 * s) / b2;
544             break;
545     }
546 
547     rgb = JXG.LMS2rgb(l, m, s);
548 
549     var HexChars="0123456789ABCDEF";
550     tmp = HexChars.charAt((rgb.r>>4)&0xf)+HexChars.charAt(rgb.r&0xf);
551     color = "#" + tmp;
552     tmp = HexChars.charAt((rgb.g>>4)&0xf)+HexChars.charAt(rgb.g&0xf);
553     color += tmp;
554     tmp = HexChars.charAt((rgb.b>>4)&0xf)+HexChars.charAt(rgb.b&0xf);
555     color += tmp;
556 
557     return color;
558 };
559 
560 /**
561  * Load options from a file using FileReader
562  * @param fileurl {String} URL to .json-file containing style information
563  * @param apply {bool} <tt>true</tt> when options in file should be applied to board after being loaded.
564  * @param board {JXG.Board} The board the options should be applied to.
565  */
566 JXG.loadOptionsFromFile = function(fileurl, applyTo, board) {
567    this.cbp = function(t) {
568       this.parseString(t, applyTo, board);
569    };
570    this.cb = JXG.bind(this.cbp,this);
571 
572    JXG.FileReader.parseFileContent(fileurl, this.cb, 'raw');
573 };
574 
575 /**
576  * Apply options given as a string to a board.
577  * @param text {String} Options given as a string in .json-Format
578  * @param apply {bool} <tt>true</tt> if the options should be applied to all objects on the board.
579  * @param board {JXG.Board} The board the options should be applied to.
580  */
581 JXG.parseOptionsString = function(text, applyTo, board) {
582    var newOptions = '';
583 
584    if(text != '') {
585       newOptions = eval("(" + text + ")");
586    }
587    else
588       return;
589 
590    var maxDepth = 10;
591    var applyOption = function (base, option, depth) {
592       if(depth==10)
593          return;
594       depth++;
595 
596       for(var key in option) {
597          if((JXG.isNumber(option[key])) || (JXG.isArray(option[key])) || (JXG.isString(option[key])) || (option[key]==true) || (option[key]==false)) {
598             base[key] = option[key];
599          }
600          else {
601             applyOption(base[key], option[key], depth);
602          }
603       }
604    };
605 
606    applyOption(this, newOptions, 0);
607 
608    if(applyTo && typeof board != 'undefined') {
609        JXG.useStandardOptions(board);
610    }
611 };
612 // vim: et ts=4
613