      1 /*
      2     Copyright 2008-2018
      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 dual licensed under the GNU LGPL or MIT License.
     13
     14     You can redistribute it and/or modify it under the terms of the
     15
     16       * GNU Lesser General Public License as published by
     17         the Free Software Foundation, either version 3 of the License, or
     18         (at your option) any later version
     19       OR
     20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
     21
     22     JSXGraph is distributed in the hope that it will be useful,
     23     but WITHOUT ANY WARRANTY; without even the implied warranty of
     24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     25     GNU Lesser General Public License for more details.
     26
     27     You should have received a copy of the GNU Lesser General Public License and
     28     the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/>
     29     and <http://opensource.org/licenses/MIT/>.
     30  */
     31
     32
     33 /*global JXG: true, define: true, html_sanitize: true*/
     34 /*jslint nomen: true, plusplus: true*/
     35
     36 /* depends:
     37  jxg
     38  base/constants
     39  */
     40
     41 /**
     42  * @fileoverview type.js contains several functions to help deal with javascript's weak types. This file mainly consists
     43  * of detector functions which verify if a variable is or is not of a specific type and converter functions that convert
     44  * variables to another type or normalize the type of a variable.
     45  */
     46
     47 define([
     48     'jxg', 'base/constants'
     49 ], function (JXG, Const) {
     50
     51     "use strict";
     52
     53     JXG.extend(JXG, /** @lends JXG */ {
     54         /**
     55          * Checks if the given string is an id within the given board.
     56          * @param {JXG.Board} board
     57          * @param {String} s
     58          * @returns {Boolean}
     59          */
     60         isId: function (board, s) {
     61             return (typeof s === 'string') && !!board.objects[s];
     62         },
     63
     64         /**
     65          * Checks if the given string is a name within the given board.
     66          * @param {JXG.Board} board
     67          * @param {String} s
     68          * @returns {Boolean}
     69          */
     70         isName: function (board, s) {
     71             return typeof s === 'string' && !!board.elementsByName[s];
     72         },
     73
     74         /**
     75          * Checks if the given string is a group id within the given board.
     76          * @param {JXG.Board} board
     77          * @param {String} s
     78          * @returns {Boolean}
     79          */
     80         isGroup: function (board, s) {
     81             return typeof s === 'string' && !!board.groups[s];
     82         },
     83
     84         /**
     85          * Checks if the value of a given variable is of type string.
     86          * @param v A variable of any type.
     87          * @returns {Boolean} True, if v is of type string.
     88          */
     89         isString: function (v) {
     90             return typeof v === "string";
     91         },
     92
     93         /**
     94          * Checks if the value of a given variable is of type number.
     95          * @param v A variable of any type.
     96          * @returns {Boolean} True, if v is of type number.
     97          */
     98         isNumber: function (v) {
     99             return typeof v === "number" || Object.prototype.toString.call(v) === '[Object Number]';
    100         },
    101
    102         /**
    103          * Checks if a given variable references a function.
    104          * @param v A variable of any type.
    105          * @returns {Boolean} True, if v is a function.
    106          */
    107         isFunction: function (v) {
    108             return typeof v === "function";
    109         },
    110
    111         /**
    112          * Checks if a given variable references an array.
    113          * @param v A variable of any type.
    114          * @returns {Boolean} True, if v is of type array.
    115          */
    116         isArray: function (v) {
    117             var r;
    118
    119             // use the ES5 isArray() method and if that doesn't exist use a fallback.
    120             if (Array.isArray) {
    121                 r = Array.isArray(v);
    122             } else {
    123                 r = (v !== null && typeof v === "object" && typeof v.splice === 'function' && typeof v.join === 'function');
    124             }
    125
    126             return r;
    127         },
    128
    129         /**
    130          * Tests if the input variable is an Object
    131          * @param v
    132          */
    133         isObject: function (v) {
    134             return typeof v === 'object' && !this.isArray(v);
    135         },
    136
    137         /**
    138          * Checks if a given variable is a reference of a JSXGraph Point element.
    139          * @param v A variable of any type.
    140          * @returns {Boolean} True, if v is of type JXG.Point.
    141          */
    142         isPoint: function (v) {
    143             if (v !== null && typeof v === 'object') {
    144                 return (v.elementClass === Const.OBJECT_CLASS_POINT);
    145             }
    146
    147             return false;
    148         },
    149
    150         /**
    151          * Checks if a given variable is a reference of a JSXGraph Point element or an array of length at least two or
    152          * a function returning an array of length two or three.
    153          * @param {JXG.Board} board
    154          * @param v A variable of any type.
    155          * @returns {Boolean} True, if v is of type JXG.Point.
    156          */
    157         isPointType: function (board, v) {
    158             var val;
    159
    160             if (this.isArray(v)) {
    161                 return true;
    162             }
    163             if (this.isFunction(v)) {
    164                 val = v();
    165                 if (this.isArray(val) && val.length > 1) {
    166                     return true;
    167                 }
    168             }
    169             v = board.select(v);
    170             return this.isPoint(v);
    171         },
    172
    173         /**
    174          * Checks if a given variable is a reference of a JSXGraph transformation element or an array
    175          * of JSXGraph transformation elements.
    176          * @param v A variable of any type.
    177          * @returns {Boolean} True, if v is of type JXG.Transformation.
    178          */
    179         isTransformationOrArray: function(v) {
    180             if (v !== null) {
    181                 if (this.isArray(v) && v.length > 0) {
    182                     return this.isTransformationOrArray(v[0]);
    183                 } else if (typeof v === 'object') {
    184                     return (v.type === Const.OBJECT_TYPE_TRANSFORMATION);
    185                 }
    186             }
    187             return false;
    188         },
    189
    190         /**
    191          * Checks if a given variable is neither undefined nor null. You should not use this together with global
    192          * variables!
    193          * @param v A variable of any type.
    194          * @returns {Boolean} True, if v is neither undefined nor null.
    195          */
    196         exists: (function (undef) {
    197             return function (v) {
    198                 return !(v === undef || v === null);
    199             };
    200         }()),
    201
    202         /**
    203          * Handle default parameters.
    204          * @param v Given value
    205          * @param d Default value
    206          * @returns <tt>d</tt>, if <tt>v</tt> is undefined or null.
    207          */
    208         def: function (v, d) {
    209             if (this.exists(v)) {
    210                 return v;
    211             }
    212
    213             return d;
    214         },
    215
    216         /**
    217          * Converts a string containing either <strong>true</strong> or <strong>false</strong> into a boolean value.
    218          * @param {String} s String containing either <strong>true</strong> or <strong>false</strong>.
    219          * @returns {Boolean} String typed boolean value converted to boolean.
    220          */
    221         str2Bool: function (s) {
    222             if (!this.exists(s)) {
    223                 return true;
    224             }
    225
    226             if (typeof s === 'boolean') {
    227                 return s;
    228             }
    229
    230             if (this.isString(s)) {
    231                 return (s.toLowerCase() === 'true');
    232             }
    233
    234             return false;
    235         },
    236
    237         /**
    238          * Convert a String, a number or a function into a function. This method is used in Transformation.js
    239          * @param {JXG.Board} board Reference to a JSXGraph board. It is required to resolve dependencies given
    240          * by a GEONE<sub>X</sub>T string, thus it must be a valid reference only in case one of the param
    241          * values is of type string.
    242          * @param {Array} param An array containing strings, numbers, or functions.
    243          * @param {Number} n Length of <tt>param</tt>.
    244          * @returns {Function} A function taking one parameter k which specifies the index of the param element
    245          * to evaluate.
    246          */
    247         createEvalFunction: function (board, param, n) {
    248             var f = [], i;
    249
    250             for (i = 0; i < n; i++) {
    251                 f[i] = JXG.createFunction(param[i], board, '', true);
    252             }
    253
    254             return function (k) {
    255                 return f[k]();
    256             };
    257         },
    258
    259         /**
    260          * Convert a String, number or function into a function.
    261          * @param {String|Number|Function} term A variable of type string, function or number.
    262          * @param {JXG.Board} board Reference to a JSXGraph board. It is required to resolve dependencies given
    263          * by a GEONE<sub>X</sub>T string, thus it must be a valid reference only in case one of the param
    264          * values is of type string.
    265          * @param {String} variableName Only required if evalGeonext is set to true. Describes the variable name
    266          * of the variable in a GEONE<sub>X</sub>T string given as term.
    267          * @param {Boolean} [evalGeonext=true] Set this true, if term should be treated as a GEONE<sub>X</sub>T string.
    268          * @returns {Function} A function evaluation the value given by term or null if term is not of type string,
    269          * function or number.
    270          */
    271         createFunction: function (term, board, variableName, evalGeonext) {
    272             var f = null;
    273
    274             if ((!this.exists(evalGeonext) || evalGeonext) && this.isString(term)) {
    275                 // Convert GEONExT syntax into  JavaScript syntax
    276                 //newTerm = JXG.GeonextParser.geonext2JS(term, board);
    277                 //return new Function(variableName,'return ' + newTerm + ';');
    278
    279                 //term = JXG.GeonextParser.replaceNameById(term, board);
    280                 //term = JXG.GeonextParser.geonext2JS(term, board);
    281                 f = board.jc.snippet(term, true, variableName, true);
    282             } else if (this.isFunction(term)) {
    283                 f = term;
    284             } else if (this.isNumber(term)) {
    285                 /** @ignore */
    286                 f = function () {
    287                     return term;
    288                 };
    289             } else if (this.isString(term)) {
    290                 // In case of string function like fontsize
    291                 /** @ignore */
    292                 f = function () {
    293                     return term;
    294                 };
    295             }
    296
    297             if (f !== null) {
    298                 f.origin = term;
    299             }
    300
    301             return f;
    302         },
    303
    304         /**
    305          *  Test if the parents array contains existing points. If instead parents contains coordinate arrays or function returning coordinate arrays
    306          *  free points with these coordinates are created.
    307          *
    308          * @param {JXG.Board} board Board object
    309          * @param {Array} parents Array containing parent elements for a new object. This array may contain
    310          *    <ul>
    311          *      <li> {@link JXG.Point} objects
    312          *      <li> {@link JXG.Element#name} of {@link JXG.Point} objects
    313          *      <li> {@link JXG.Element#id} of {@link JXG.Point} objects
    314          *      <li> Coordinates of points given as array of numbers of length two or three, e.g. [2, 3].
    315          *      <li> Coordinates of points given as array of functions of length two or three. Each function returns one coordinate, e.g.
    316          *           [function(){ return 2; }, function(){ return 3; }]
    317          *      <li> Function returning coordinates, e.g. function() { return [2, 3]; }
    318          *    </ul>
    319          *  In the last three cases a new point will be created.
    320          * @param {String} attrClass Main attribute class of newly created points, see {@link JXG@copyAttributes}
    321          * @param {Array} attrArray List of subtype attributes for the newly created points. The list of subtypes is mapped to the list of new points.
    322          * @returns {Array} List of newly created {@link JXG.Point} elements or false if not all returned elements are points.
    323          */
    324         providePoints: function (board, parents, attributes, attrClass, attrArray) {
    325             var i, j,
    326                 len,
    327                 lenAttr = 0,
    328                 points = [], attr, val;
    329
    330             if (!this.isArray(parents)) {
    331                 parents = [parents];
    332             }
    333             len = parents.length;
    334             if (this.exists(attrArray)) {
    335                 lenAttr = attrArray.length;
    336             }
    337             if (lenAttr === 0) {
    338                 attr = this.copyAttributes(attributes, board.options, attrClass);
    339             }
    340
    341             for (i = 0; i < len; ++i) {
    342                 if (lenAttr > 0) {
    343                     j = Math.min(i, lenAttr - 1);
    344                     attr = this.copyAttributes(attributes, board.options, attrClass, attrArray[j]);
    345                 }
    346                 if (this.isArray(parents[i]) && parents[i].length > 1) {
    347                     points.push(board.create('point', parents[i], attr));
    348                 } else if (this.isFunction(parents[i])) {
    349                     val = parents[i]();
    350                     if (this.isArray(val) && (val.length > 1)) {
    351                         points.push(board.create('point', [parents[i]], attr));
    352                     }
    353                 } else {
    354                     points.push(board.select(parents[i]));
    355                 }
    356
    357                 if (!this.isPoint(points[i])) {
    358                     return false;
    359                 }
    360             }
    361
    362             return points;
    363         },
    364
    365         /**
    366          * Generates a function which calls the function fn in the scope of owner.
    367          * @param {Function} fn Function to call.
    368          * @param {Object} owner Scope in which fn is executed.
    369          * @returns {Function} A function with the same signature as fn.
    370          */
    371         bind: function (fn, owner) {
    372             return function () {
    373                 return fn.apply(owner, arguments);
    374             };
    375         },
    376
    377         /**
    378          * If <tt>val</tt> is a function, it will be evaluated without giving any parameters, else the input value
    379          * is just returned.
    380          * @param val Could be anything. Preferably a number or a function.
    381          * @returns If <tt>val</tt> is a function, it is evaluated and the result is returned. Otherwise <tt>val</tt> is returned.
    382          */
    383         evaluate: function (val) {
    384             if (this.isFunction(val)) {
    385                 return val();
    386             }
    387
    388             return val;
    389         },
    390
    391         /**
    392          * Search an array for a given value.
    393          * @param {Array} array
    394          * @param value
    395          * @param {String} [sub] Use this property if the elements of the array are objects.
    396          * @returns {Number} The index of the first appearance of the given value, or
    397          * <tt>-1</tt> if the value was not found.
    398          */
    399         indexOf: function (array, value, sub) {
    400             var i, s = this.exists(sub);
    401
    402             if (Array.indexOf && !s) {
    403                 return array.indexOf(value);
    404             }
    405
    406             for (i = 0; i < array.length; i++) {
    407                 if ((s && array[i][sub] === value) || (!s && array[i] === value)) {
    408                     return i;
    409                 }
    410             }
    411
    412             return -1;
    413         },
    414
    415         /**
    416          * Eliminates duplicate entries in an array consisting of numbers and strings.
    417          * @param {Array} a An array of numbers and/or strings.
    418          * @returns {Array} The array with duplicate entries eliminated.
    419          */
    420         eliminateDuplicates: function (a) {
    421             var i,
    422                 len = a.length,
    423                 result = [],
    424                 obj = {};
    425
    426             for (i = 0; i < len; i++) {
    427                 obj[a[i]] = 0;
    428             }
    429
    430             for (i in obj) {
    431                 if (obj.hasOwnProperty(i)) {
    432                     result.push(i);
    433                 }
    434             }
    435
    436             return result;
    437         },
    438
    439         /**
    440          * Swaps to array elements.
    441          * @param {Array} arr
    442          * @param {Number} i
    443          * @param {Number} j
    444          * @returns {Array} Reference to the given array.
    445          */
    446         swap: function (arr, i, j) {
    447             var tmp;
    448
    449             tmp = arr[i];
    450             arr[i] = arr[j];
    451             arr[j] = tmp;
    452
    453             return arr;
    454         },
    455
    456         /**
    457          * Generates a copy of an array and removes the duplicate entries. The original
    458          * Array will be altered.
    459          * @param {Array} arr
    460          * @returns {Array}
    461          */
    462         uniqueArray: function (arr) {
    463             var i, j, isArray, ret = [];
    464
    465             if (arr.length === 0) {
    466                 return [];
    467             }
    468
    469             for (i = 0; i < arr.length; i++) {
    470                 isArray = this.isArray(arr[i]);
    471
    472                 if (!this.exists(arr[i])) {
    473                     arr[i] = '';
    474                     continue;
    475                 }
    476                 for (j = i + 1; j < arr.length; j++) {
    477                     if (isArray && JXG.cmpArrays(arr[i], arr[j])) {
    478                         arr[i] = [];
    479                     } else if (!isArray && arr[i] === arr[j]) {
    480                         arr[i] = '';
    481                     }
    482                 }
    483             }
    484
    485             j = 0;
    486
    487             for (i = 0; i < arr.length; i++) {
    488                 isArray = this.isArray(arr[i]);
    489
    490                 if (!isArray && arr[i] !== '') {
    491                     ret[j] = arr[i];
    492                     j++;
    493                 } else if (isArray && arr[i].length !== 0) {
    494                     ret[j] = (arr[i].slice(0));
    495                     j++;
    496                 }
    497             }
    498
    499             arr = ret;
    500             return ret;
    501         },
    502
    503         /**
    504          * Checks if an array contains an element equal to <tt>val</tt> but does not check the type!
    505          * @param {Array} arr
    506          * @param val
    507          * @returns {Boolean}
    508          */
    509         isInArray: function (arr, val) {
    510             return JXG.indexOf(arr, val) > -1;
    511         },
    512
    513         /**
    514          * Converts an array of {@link JXG.Coords} objects into a coordinate matrix.
    515          * @param {Array} coords
    516          * @param {Boolean} split
    517          * @returns {Array}
    518          */
    519         coordsArrayToMatrix: function (coords, split) {
    520             var i,
    521                 x = [],
    522                 m = [];
    523
    524             for (i = 0; i < coords.length; i++) {
    525                 if (split) {
    526                     x.push(coords[i].usrCoords[1]);
    527                     m.push(coords[i].usrCoords[2]);
    528                 } else {
    529                     m.push([coords[i].usrCoords[1], coords[i].usrCoords[2]]);
    530                 }
    531             }
    532
    533             if (split) {
    534                 m = [x, m];
    535             }
    536
    537             return m;
    538         },
    539
    540         /**
    541          * Compare two arrays.
    542          * @param {Array} a1
    543          * @param {Array} a2
    544          * @returns {Boolean} <tt>true</tt>, if the arrays coefficients are of same type and value.
    545          */
    546         cmpArrays: function (a1, a2) {
    547             var i;
    548
    549             // trivial cases
    550             if (a1 === a2) {
    551                 return true;
    552             }
    553
    554             if (a1.length !== a2.length) {
    555                 return false;
    556             }
    557
    558             for (i = 0; i < a1.length; i++) {
    559                 if (this.isArray(a1[i]) && this.isArray(a2[i])) {
    560                     if (!this.cmpArrays(a1[i], a2[i])) {
    561                         return false;
    562                     }
    563                 }
    564                 else if (a1[i] !== a2[i]) {
    565                     return false;
    566                 }
    567             }
    568
    569             return true;
    570         },
    571
    572         /**
    573          * Removes an element from the given array
    574          * @param {Array} ar
    575          * @param el
    576          * @returns {Array}
    577          */
    578         removeElementFromArray: function (ar, el) {
    579             var i;
    580
    581             for (i = 0; i < ar.length; i++) {
    582                 if (ar[i] === el) {
    583                     ar.splice(i, 1);
    584                     return ar;
    585                 }
    586             }
    587
    588             return ar;
    589         },
    590
    591         /**
    592          * Truncate a number <tt>n</tt> after <tt>p</tt> decimals.
    593          * @param {Number} n
    594          * @param {Number} p
    595          * @returns {Number}
    596          */
    597         trunc: function (n, p) {
    598             p = JXG.def(p, 0);
    599
    600             return this.toFixed(n, p);
    601         },
    602
    603         /**
    604     	 * Decimal adjustment of a number.
    605     	 * From https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Math/round
    606     	 *
    607     	 * @param	{String}	type	The type of adjustment.
    608     	 * @param	{Number}	value	The number.
    609     	 * @param	{Number}	exp		The exponent (the 10 logarithm of the adjustment base).
    610     	 * @returns	{Number}			The adjusted value.
    611     	 *
    612     	 * @private
    613     	 */
    614     	_decimalAdjust: function(type, value, exp) {
    615     		// If the exp is undefined or zero...
    616     		if (exp === undefined || +exp === 0) {
    617     			return Math[type](value);
    618     		}
    619
    620     		value = +value;
    621     		exp = +exp;
    622     		// If the value is not a number or the exp is not an integer...
    623     		if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
    624     			return NaN;
    625     		}
    626
    627             // Shift
    628     		value = value.toString().split('e');
    629     		value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
    630
    631     		// Shift back
    632     		value = value.toString().split('e');
    633     		return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
    634     	},
    635
    636         /**
    637          * Round a number to given number of decimal digits.
    638          *
    639          * Example: JXG._toFixed(3.14159, -2) gives 3.14
    640          * @param  {Number} value Number to be rounded
    641          * @param  {Number} exp   Number of decimal digits given as negative exponent
    642          * @return {Number}       Rounded number.
    643          *
    644          * @private
    645          */
    646     	_round10: function(value, exp) {
    647     		return this._decimalAdjust('round', value, exp);
    648 		},
    649
    650         /**
    651          * "Floor" a number to given number of decimal digits.
    652          *
    653          * Example: JXG._toFixed(3.14159, -2) gives 3.14
    654          * @param  {Number} value Number to be floored
    655          * @param  {Number} exp   Number of decimal digits given as negative exponent
    656          * @return {Number}       "Floored" number.
    657          *
    658          * @private
    659          */
    660         _floor10: function(value, exp) {
    661     		return this._decimalAdjust('floor', value, exp);
    662     	},
    663
    664         /**
    665          * "Ceil" a number to given number of decimal digits.
    666          *
    667          * Example: JXG._toFixed(3.14159, -2) gives 3.15
    668          * @param  {Number} value Number to be ceiled
    669          * @param  {Number} exp   Number of decimal digits given as negative exponent
    670          * @return {Number}       "Ceiled" number.
    671          *
    672          * @private
    673          */
    674         _ceil10: function(value, exp) {
    675     		return this._decimalAdjust('ceil', value, exp);
    676     	},
    677
    678         /**
    679          * Replacement of the default toFixed() method.
    680          * It does a correct rounding (independent of the browser) and
    681          * returns "0.00" for toFixed(-0.000001, 2) instead of "-0.00" which
    682          * is returned by JavaScript's toFixed()
    683          *
    684          * @param  {Number} num       Number tp be rounded
    685          * @param  {Number} precision Decimal digits
    686          * @return {String}           Rounded number is returned as string
    687          */
    688         toFixed: function(num, precision) {
    689             return this._round10(num, -precision).toFixed(precision);
    690         },
    691
    692         /**
    693          * Truncate a number <tt>val</tt> automatically.
    694          * @param val
    695          * @returns {Number}
    696          */
    697         autoDigits: function (val) {
    698             var x = Math.abs(val),
    699                 str;
    700
    701             if (x > 0.1) {
    702                 str = this.toFixed(val, 2);
    703             } else if (x >= 0.01) {
    704                 str = this.toFixed(val, 4);
    705             } else if (x >= 0.0001) {
    706                 str = this.toFixed(val, 6);
    707             } else {
    708                 str = val;
    709             }
    710             return str;
    711         },
    712
    713         /**
    714          * Extracts the keys of a given object.
    715          * @param object The object the keys are to be extracted
    716          * @param onlyOwn If true, hasOwnProperty() is used to verify that only keys are collected
    717          * the object owns itself and not some other object in the prototype chain.
    718          * @returns {Array} All keys of the given object.
    719          */
    720         keys: function (object, onlyOwn) {
    721             var keys = [], property;
    722
    723             // the caller decides if we use hasOwnProperty
    724             /*jslint forin:true*/
    725             for (property in object) {
    726                 if (onlyOwn) {
    727                     if (object.hasOwnProperty(property)) {
    728                         keys.push(property);
    729                     }
    730                 } else {
    731                     keys.push(property);
    732                 }
    733             }
    734             /*jslint forin:false*/
    735
    736             return keys;
    737         },
    738
    739         /**
    740          * This outputs an object with a base class reference to the given object. This is useful if
    741          * you need a copy of an e.g. attributes object and want to overwrite some of the attributes
    742          * without changing the original object.
    743          * @param {Object} obj Object to be embedded.
    744          * @returns {Object} An object with a base class reference to <tt>obj</tt>.
    745          */
    746         clone: function (obj) {
    747             var cObj = {};
    748
    749             cObj.prototype = obj;
    750
    751             return cObj;
    752         },
    753
    754         /**
    755          * Embeds an existing object into another one just like {@link #clone} and copies the contents of the second object
    756          * to the new one. Warning: The copied properties of obj2 are just flat copies.
    757          * @param {Object} obj Object to be copied.
    758          * @param {Object} obj2 Object with data that is to be copied to the new one as well.
    759          * @returns {Object} Copy of given object including some new/overwritten data from obj2.
    760          */
    761         cloneAndCopy: function (obj, obj2) {
    762             var r,
    763                 cObj = function () {};
    764
    765             cObj.prototype = obj;
    766
    767             // no hasOwnProperty on purpose
    768             /*jslint forin:true*/
    769             /*jshint forin:true*/
    770
    771             for (r in obj2) {
    772                 cObj[r] = obj2[r];
    773             }
    774
    775             /*jslint forin:false*/
    776             /*jshint forin:false*/
    777
    778             return cObj;
    779         },
    780
    781         /**
    782          * Recursively merges obj2 into obj1. Contrary to {@link JXG#deepCopy} this won't create a new object
    783          * but instead will overwrite obj1.
    784          * @param {Object} obj1
    785          * @param {Object} obj2
    786          * @returns {Object}
    787          */
    788         merge: function (obj1, obj2) {
    789             var i, j;
    790
    791             for (i in obj2) {
    792                 if (obj2.hasOwnProperty(i)) {
    793                     if (this.isArray(obj2[i])) {
    794                         if (!obj1[i]) {
    795                             obj1[i] = [];
    796                         }
    797
    798                         for (j = 0; j < obj2[i].length; j++) {
    799                             if (typeof obj2[i][j] === 'object') {
    800                                 obj1[i][j] = this.merge(obj1[i][j], obj2[i][j]);
    801                             } else {
    802                                 obj1[i][j] = obj2[i][j];
    803                             }
    804                         }
    805                     } else if (typeof obj2[i] === 'object') {
    806                         if (!obj1[i]) {
    807                             obj1[i] = {};
    808                         }
    809
    810                         obj1[i] = this.merge(obj1[i], obj2[i]);
    811                     } else {
    812                         obj1[i] = obj2[i];
    813                     }
    814                 }
    815             }
    816
    817             return obj1;
    818         },
    819
    820         /**
    821          * Creates a deep copy of an existing object, i.e. arrays or sub-objects are copied component resp.
    822          * element-wise instead of just copying the reference. If a second object is supplied, the two objects
    823          * are merged into one object. The properties of the second object have priority.
    824          * @param {Object} obj This object will be copied.
    825          * @param {Object} obj2 This object will merged into the newly created object
    826          * @param {Boolean} [toLower=false] If true the keys are convert to lower case. This is needed for visProp, see JXG#copyAttributes
    827          * @returns {Object} copy of obj or merge of obj and obj2.
    828          */
    829         deepCopy: function (obj, obj2, toLower) {
    830             var c, i, prop, i2;
    831
    832             toLower = toLower || false;
    833
    834             if (typeof obj !== 'object' || obj === null) {
    835                 return obj;
    836             }
    837
    838             // missing hasOwnProperty is on purpose in this function
    839             if (this.isArray(obj)) {
    840                 c = [];
    841                 for (i = 0; i < obj.length; i++) {
    842                     prop = obj[i];
    843                     if (typeof prop === 'object') {
    844                         // We certainly do not want to recurse into a JSXGraph object.
    845                         // This would for sure result in an infinite recursion.
    846                         // As alternative we copy the id of the object.
    847                         if (this.exists(prop.board)) {
    848                             c[i] = prop.id;
    849                         } else {
    850                             c[i] = this.deepCopy(prop);
    851                         }
    852                     } else {
    853                         c[i] = prop;
    854                     }
    855                 }
    856             } else {
    857                 c = {};
    858                 for (i in obj) {
    859                     if (obj.hasOwnProperty(i)) {
    860                         i2 = toLower ? i.toLowerCase() : i;
    861                         prop = obj[i];
    862                         if (prop !== null && typeof prop === 'object') {
    863                             if (this.exists(prop.board)) {
    864                                 c[i2] = prop.id;
    865                             } else {
    866                                 c[i2] = this.deepCopy(prop);
    867                             }
    868                         } else {
    869                             c[i2] = prop;
    870                         }
    871                     }
    872                 }
    873
    874                 for (i in obj2) {
    875                     if (obj2.hasOwnProperty(i)) {
    876                         i2 = toLower ? i.toLowerCase() : i;
    877
    878                         prop = obj2[i];
    879                         if (typeof prop === 'object') {
    880                             if (this.isArray(prop) || !this.exists(c[i2])) {
    881                                 c[i2] = this.deepCopy(prop);
    882                             } else {
    883                                 c[i2] = this.deepCopy(c[i2], prop, toLower);
    884                             }
    885                         } else {
    886                             c[i2] = prop;
    887                         }
    888                     }
    889                 }
    890             }
    891
    892             return c;
    893         },
    894
    895         /**
    896          * Generates an attributes object that is filled with default values from the Options object
    897          * and overwritten by the user specified attributes.
    898          * @param {Object} attributes user specified attributes
    899          * @param {Object} options defaults options
    900          * @param {String} s variable number of strings, e.g. 'slider', subtype 'point1'.
    901          * @returns {Object} The resulting attributes object
    902          */
    903         copyAttributes: function (attributes, options, s) {
    904             var a, i, len, o, isAvail,
    905                 primitives = {
    906                     'circle': 1,
    907                     'curve': 1,
    908                     'image': 1,
    909                     'line': 1,
    910                     'point': 1,
    911                     'polygon': 1,
    912                     'text': 1,
    913                     'ticks': 1,
    914                     'integral': 1
    915                 };
    916
    917
    918             len = arguments.length;
    919             if (len < 3 || primitives[s]) {
    920                 // default options from Options.elements
    921                 a = JXG.deepCopy(options.elements, null, true);
    922             } else {
    923                 a = {};
    924             }
    925
    926             // Only the layer of the main element is set.
    927             if (len < 4 && this.exists(s) && this.exists(options.layer[s])) {
    928                 a.layer = options.layer[s];
    929             }
    930
    931             // default options from specific elements
    932             o = options;
    933             isAvail = true;
    934             for (i = 2; i < len; i++) {
    935                 if (this.exists(o[arguments[i]])) {
    936                     o = o[arguments[i]];
    937                 } else {
    938                     isAvail = false;
    939                     break;
    940                 }
    941             }
    942             if (isAvail) {
    943                 a = JXG.deepCopy(a, o, true);
    944             }
    945
    946             // options from attributes
    947             o = attributes;
    948             isAvail = true;
    949             for (i = 3; i < len; i++) {
    950                 if (this.exists(o[arguments[i]])) {
    951                     o = o[arguments[i]];
    952                 } else {
    953                     isAvail = false;
    954                     break;
    955                 }
    956             }
    957             if (isAvail) {
    958                 this.extend(a, o, null, true);
    959             }
    960
    961             // Special treatment of labels
    962             o = options;
    963             isAvail = true;
    964             for (i = 2; i < len; i++) {
    965                 if (this.exists(o[arguments[i]])) {
    966                     o = o[arguments[i]];
    967                 } else {
    968                     isAvail = false;
    969                     break;
    970                 }
    971             }
    972             if (isAvail && this.exists(o.label)) {
    973                 a.label =  JXG.deepCopy(o.label, a.label);
    974             }
    975             a.label = JXG.deepCopy(options.label, a.label);
    976
    977             return a;
    978         },
    979
    980         /**
    981          * Copy all prototype methods from object "superObject" to object
    982          * "subObject". The constructor of superObject will be available
    983          * in subObject as subObject.constructor[constructorName].
    984          * @param {Object} subObj A JavaScript object which receives new methods.
    985          * @param {Object} superObj A JavaScript object which lends its prototype methods to subObject
    986          * @returns {String} constructorName Under this name the constructor of superObj will be available
    987          * in subObject.
    988          * @private
    989          */
    990         copyPrototypeMethods: function (subObject, superObject, constructorName) {
    991             var key;
    992
    993             subObject.prototype[constructorName] = superObject.prototype.constructor;
    994             for (key in superObject.prototype) {
    995                 if (superObject.prototype.hasOwnProperty(key)) {
    996                     subObject.prototype[key] = superObject.prototype[key];
    997                 }
    998             }
    999         },
    1000
    1001         /**
    1002          * Converts a JavaScript object into a JSON string.
    1003          * @param {Object} obj A JavaScript object, functions will be ignored.
    1004          * @param {Boolean} [noquote=false] No quotes around the name of a property.
    1005          * @returns {String} The given object stored in a JSON string.
    1006          */
    1007         toJSON: function (obj, noquote) {
    1008             var list, prop, i, s, val;
    1009
    1010             noquote = JXG.def(noquote, false);
    1011
    1012             // check for native JSON support:
    1013             if (typeof JSON && JSON.stringify && !noquote) {
    1014                 try {
    1015                     s = JSON.stringify(obj);
    1016                     return s;
    1017                 } catch (e) {
    1018                     // if something goes wrong, e.g. if obj contains functions we won't return
    1019                     // and use our own implementation as a fallback
    1020                 }
    1021             }
    1022
    1023             switch (typeof obj) {
    1024             case 'object':
    1025                 if (obj) {
    1026                     list = [];
    1027
    1028                     if (this.isArray(obj)) {
    1029                         for (i = 0; i < obj.length; i++) {
    1030                             list.push(JXG.toJSON(obj[i], noquote));
    1031                         }
    1032
    1033                         return '[' + list.join(',') + ']';
    1034                     }
    1035
    1036                     for (prop in obj) {
    1037                         if (obj.hasOwnProperty(prop)) {
    1038                             try {
    1039                                 val = JXG.toJSON(obj[prop], noquote);
    1040                             } catch (e2) {
    1041                                 val = '';
    1042                             }
    1043
    1044                             if (noquote) {
    1045                                 list.push(prop + ':' + val);
    1046                             } else {
    1047                                 list.push('"' + prop + '":' + val);
    1048                             }
    1049                         }
    1050                     }
    1051
    1052                     return '{' + list.join(',') + '} ';
    1053                 }
    1054                 return 'null';
    1055             case 'string':
    1056                 return '\'' + obj.replace(/(["'])/g, '\\$1') + '\'';
    1057             case 'number':
    1058             case 'boolean':
    1059                 return obj.toString();
    1060             }
    1061
    1062             return '0';
    1063         },
    1064
    1065         /**
    1066          * Resets visPropOld.
    1067          * @param {JXG.GeometryElement} el
    1068          * @returns {GeometryElement}
    1069          */
    1070         clearVisPropOld: function (el) {
    1071             el.visPropOld = {
    1072                 cssclass: '',
    1073                 cssdefaultstyle: '',
    1074                 cssstyle: '',
    1075                 fillcolor: '',
    1076                 fillopacity: '',
    1077                 firstarrow: false,
    1078                 fontsize: -1,
    1079                 lastarrow: false,
    1080                 left: -100000,
    1081                 linecap: '',
    1082                 shadow: false,
    1083                 strokecolor: '',
    1084                 strokeopacity: '',
    1085                 strokewidth: '',
    1086                 transitionduration: 0,
    1087                 top: -100000,
    1088                 visible: null
    1089             };
    1090
    1091             return el;
    1092         },
    1093
    1094         /**
    1095          * Checks if an object contains a key, whose value equals to val.
    1096          * @param {Object} obj
    1097          * @param val
    1098          * @returns {Boolean}
    1099          */
    1100         isInObject: function (obj, val) {
    1101             var el;
    1102
    1103             for (el in obj) {
    1104                 if (obj.hasOwnProperty(el)) {
    1105                     if (obj[el] === val) {
    1106                         return true;
    1107                     }
    1108                 }
    1109             }
    1110
    1111             return false;
    1112         },
    1113
    1114         /**
    1115          * Replaces all occurences of & by &amp;, > by &gt;, and < by &lt;.
    1116          * @param {String} str
    1117          * @returns {String}
    1118          */
    1119         escapeHTML: function (str) {
    1120             return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
    1121         },
    1122
    1123         /**
    1124          * Eliminates all substrings enclosed by < and > and replaces all occurences of
    1125          * &amp; by &, &gt; by >, and &lt; by <.
    1126          * @param {String} str
    1127          * @returns {String}
    1128          */
    1129         unescapeHTML: function (str) {
    1130             // this regex is NOT insecure. We are replacing everything found with ''
    1131             /*jslint regexp:true*/
    1132             return str.replace(/<\/?[^>]+>/gi, '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
    1133         },
    1134
    1135         /**
    1136          * Makes a string lower case except for the first character which will be upper case.
    1137          * @param {String} str Arbitrary string
    1138          * @returns {String} The capitalized string.
    1139          */
    1140         capitalize: function (str) {
    1141             return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
    1142         },
    1143
    1144         /**
    1145          * Make numbers given as strings nicer by removing all unnecessary leading and trailing zeroes.
    1146          * @param {String} str
    1147          * @returns {String}
    1148          */
    1149         trimNumber: function (str) {
    1150             str = str.replace(/^0+/, '');
    1151             str = str.replace(/0+$/, '');
    1152
    1153             if (str[str.length - 1] === '.' || str[str.length - 1] === ',') {
    1154                 str = str.slice(0, -1);
    1155             }
    1156
    1157             if (str[0] === '.' || str[0] === ',') {
    1158                 str = "0" + str;
    1159             }
    1160
    1161             return str;
    1162         },
    1163
    1164         /**
    1165          * Filter an array of elements.
    1166          * @param {Array} list
    1167          * @param {Object|function} filter
    1168          * @returns {Array}
    1169          */
    1170         filterElements: function (list, filter) {
    1171             var i, f, item, flower, value, visPropValue, pass,
    1172                 l = list.length,
    1173                 result = [];
    1174
    1175             if (typeof filter !== 'function' && typeof filter !== 'object') {
    1176                 return result;
    1177             }
    1178
    1179             for (i = 0; i < l; i++) {
    1180                 pass = true;
    1181                 item = list[i];
    1182
    1183                 if (typeof filter === 'object') {
    1184                     for (f in filter) {
    1185                         if (filter.hasOwnProperty(f)) {
    1186                             flower = f.toLowerCase();
    1187
    1188                             if (typeof item[f] === 'function') {
    1189                                 value = item[f]();
    1190                             } else {
    1191                                 value = item[f];
    1192                             }
    1193
    1194                             if (item.visProp && typeof item.visProp[flower] === 'function') {
    1195                                 visPropValue = item.visProp[flower]();
    1196                             } else {
    1197                                 visPropValue = item.visProp && item.visProp[flower];
    1198                             }
    1199
    1200                             if (typeof filter[f] === 'function') {
    1201                                 pass = filter[f](value) || filter[f](visPropValue);
    1202                             } else {
    1203                                 pass = (value === filter[f] || visPropValue === filter[f]);
    1204                             }
    1205
    1206                             if (!pass) {
    1207                                 break;
    1208                             }
    1209                         }
    1210                     }
    1211                 } else if (typeof filter === 'function') {
    1212                     pass = filter(item);
    1213                 }
    1214
    1215                 if (pass) {
    1216                     result.push(item);
    1217                 }
    1218             }
    1219
    1220             return result;
    1221         },
    1222
    1223         /**
    1224          * Remove all leading and trailing whitespaces from a given string.
    1225          * @param {String} str
    1226          * @returns {String}
    1227          */
    1228         trim: function (str) {
    1229             // str = str.replace(/^\s+/, '');
    1230             // str = str.replace(/\s+$/, '');
    1231             //
    1232             // return str;
    1233             return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
    1234         },
    1235
    1236         /**

    1238          * @param {String} str
    1239          * @param {Boolean} caja
    1240          * @returns {String} Sanitized string
    1241          */
    1242         sanitizeHTML: function (str, caja) {
    1243             if (typeof html_sanitize === 'function' && caja) {
    1244                 return html_sanitize(str, function () {}, function (id) { return id; });
    1245             }
    1246
    1247             if (str) {
    1248                 str = str.replace(/</g, '<').replace(/>/g, '>');
    1249             }
    1250
    1251             return str;
    1252         },
    1253
    1254         /**
    1255          * If <tt>s</tt> is a slider, it returns the sliders value, otherwise it just returns the given value.
    1256          * @param {*} s
    1257          * @returns {*} s.Value() if s is an element of type slider, s otherwise
    1258          */
    1259         evalSlider: function (s) {
    1260             if (s && s.type === Const.OBJECT_TYPE_GLIDER && typeof s.Value === 'function') {
    1261                 return s.Value();
    1262             }
    1263
    1264             return s;
    1265         }
    1266     });
    1267
    1268     return JXG;
    1269 });
    1270