1 /* 2 Copyright 2008-2013 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"; 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' && !JXG.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 (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 neither undefined nor null. You should not use this together with global 152 * variables! 153 * @param v A variable of any type. 154 * @returns {Boolean} True, if v is neither undefined nor null. 155 */ 156 exists: (function (undef) { 157 return function (v) { 158 return !(v === undef || v === null); 159 }; 160 }()), 161 162 /** 163 * Handle default parameters. 164 * @param v Given value 165 * @param d Default value 166 * @returns <tt>d</tt>, if <tt>v</tt> is undefined or null. 167 */ 168 def: function (v, d) { 169 if (JXG.exists(v)) { 170 return v; 171 } 172 173 return d; 174 }, 175 176 /** 177 * Converts a string containing either <strong>true</strong> or <strong>false</strong> into a boolean value. 178 * @param {String} s String containing either <strong>true</strong> or <strong>false</strong>. 179 * @returns {Boolean} String typed boolean value converted to boolean. 180 */ 181 str2Bool: function (s) { 182 if (!JXG.exists(s)) { 183 return true; 184 } 185 186 if (typeof s === 'boolean') { 187 return s; 188 } 189 190 if (JXG.isString(s)) { 191 return (s.toLowerCase() === 'true'); 192 } 193 194 return false; 195 }, 196 197 /** 198 * Convert a String, a number or a function into a function. This method is used in Transformation.js 199 * @param {JXG.Board} board Reference to a JSXGraph board. It is required to resolve dependencies given 200 * by a GEONE<sub>X</sub>T string, thus it must be a valid reference only in case one of the param 201 * values is of type string. 202 * @param {Array} param An array containing strings, numbers, or functions. 203 * @param {Number} n Length of <tt>param</tt>. 204 * @returns {Function} A function taking one parameter k which specifies the index of the param element 205 * to evaluate. 206 */ 207 createEvalFunction: function (board, param, n) { 208 var f = [], i, str; 209 210 for (i = 0; i < n; i++) { 211 f[i] = JXG.createFunction(param[i], board, '', true); 212 } 213 214 return function (k) { 215 return f[k](); 216 }; 217 }, 218 219 /** 220 * Convert a String, number or function into a function. 221 * @param term A variable of type string, function or number. 222 * @param {JXG.Board} board Reference to a JSXGraph board. It is required to resolve dependencies given 223 * by a GEONE<sub>X</sub>T string, thus it must be a valid reference only in case one of the param 224 * values is of type string. 225 * @param {String} variableName Only required if evalGeonext is set to true. Describes the variable name 226 * of the variable in a GEONE<sub>X</sub>T string given as term. 227 * @param {Boolean} [evalGeonext=true] Set this true, if term should be treated as a GEONE<sub>X</sub>T string. 228 * @returns {Function} A function evaluation the value given by term or null if term is not of type string, 229 * function or number. 230 */ 231 createFunction: function (term, board, variableName, evalGeonext) { 232 var f = null; 233 234 if ((!JXG.exists(evalGeonext) || evalGeonext) && JXG.isString(term)) { 235 // Convert GEONExT syntax into JavaScript syntax 236 //newTerm = JXG.GeonextParser.geonext2JS(term, board); 237 //return new Function(variableName,'return ' + newTerm + ';'); 238 239 //term = JXG.GeonextParser.replaceNameById(term, board); 240 //term = JXG.GeonextParser.geonext2JS(term, board); 241 f = board.jc.snippet(term, true, variableName, true); 242 } else if (JXG.isFunction(term)) { 243 f = term; 244 } else if (JXG.isNumber(term)) { 245 /** @ignore */ 246 f = function () { 247 return term; 248 }; 249 } else if (JXG.isString(term)) { 250 // In case of string function like fontsize 251 /** @ignore */ 252 f = function () { 253 return term; 254 }; 255 } 256 257 if (f !== null) { 258 f.origin = term; 259 } 260 261 return f; 262 }, 263 264 /** 265 * Generates a function which calls the function fn in the scope of owner. 266 * @param {Function} fn Function to call. 267 * @param {Object} owner Scope in which fn is executed. 268 * @returns {Function} A function with the same signature as fn. 269 */ 270 bind: function (fn, owner) { 271 return function () { 272 return fn.apply(owner, arguments); 273 }; 274 }, 275 276 /** 277 * If <tt>val</tt> is a function, it will be evaluated without giving any parameters, else the input value 278 * is just returned. 279 * @param val Could be anything. Preferably a number or a function. 280 * @returns If <tt>val</tt> is a function, it is evaluated and the result is returned. Otherwise <tt>val</tt> is returned. 281 */ 282 evaluate: function (val) { 283 if (JXG.isFunction(val)) { 284 return val(); 285 } 286 287 return val; 288 }, 289 290 /** 291 * Search an array for a given value. 292 * @param {Array} array 293 * @param value 294 * @param {String} [sub] Use this property if the elements of the array are objects. 295 * @returns {Number} The index of the first appearance of the given value, or 296 * <tt>-1</tt> if the value was not found. 297 */ 298 indexOf: function (array, value, sub) { 299 var i, s = JXG.exists(sub); 300 301 if (Array.indexOf && !s) { 302 return array.indexOf(value); 303 } 304 305 for (i = 0; i < array.length; i++) { 306 if ((s && array[i][sub] === value) || (!s && array[i] === value)) { 307 return i; 308 } 309 } 310 311 return -1; 312 }, 313 314 /** 315 * Eliminates duplicate entries in an array consisting of numbers and strings. 316 * @param {Array} a An array of numbers and/or strings. 317 * @returns {Array} The array with duplicate entries eliminated. 318 */ 319 eliminateDuplicates: function (a) { 320 var i, 321 len = a.length, 322 result = [], 323 obj = {}; 324 325 for (i = 0; i < len; i++) { 326 obj[a[i]] = 0; 327 } 328 329 for (i in obj) { 330 if (obj.hasOwnProperty(i)) { 331 result.push(i); 332 } 333 } 334 335 return result; 336 }, 337 338 /** 339 * Swaps to array elements. 340 * @param {Array} arr 341 * @param {Number} i 342 * @param {Number} j 343 * @returns {Array} Reference to the given array. 344 */ 345 swap: function (arr, i, j) { 346 var tmp; 347 348 tmp = arr[i]; 349 arr[i] = arr[j]; 350 arr[j] = tmp; 351 352 return arr; 353 }, 354 355 /** 356 * Generates a copy of an array and removes the duplicate entries. The original 357 * Array will be altered. 358 * @param {Array} arr 359 * @returns {Array} 360 */ 361 uniqueArray: function (arr) { 362 var i, j, isArray, ret = []; 363 364 if (arr.length === 0) { 365 return []; 366 } 367 368 for (i = 0; i < arr.length; i++) { 369 isArray = JXG.isArray(arr[i]); 370 371 for (j = i + 1; j < arr.length; j++) { 372 if (isArray && JXG.cmpArrays(arr[i], arr[j])) { 373 arr[i] = []; 374 } else if (!isArray && arr[i] === arr[j]) { 375 arr[i] = ''; 376 } 377 } 378 } 379 380 j = 0; 381 382 for (i = 0; i < arr.length; i++) { 383 isArray = JXG.isArray(arr[i]); 384 385 if (!isArray && arr[i] !== '') { 386 ret[j] = arr[i]; 387 j += 1; 388 } else if (isArray && arr[i].length !== 0) { 389 ret[j] = (arr[i].slice(0)); 390 j += 1; 391 } 392 } 393 394 arr = ret; 395 return ret; 396 }, 397 398 /** 399 * Checks if an array contains an element equal to <tt>val</tt> but does not check the type! 400 * @param {Array} arr 401 * @param val 402 * @returns {Boolean} 403 */ 404 isInArray: function (arr, val) { 405 return JXG.indexOf(arr, val) > -1; 406 }, 407 408 /** 409 * Converts an array of {@link JXG.Coords} objects into a coordinate matrix. 410 * @param {Array} coords 411 * @param {Boolean} split 412 * @returns {Array} 413 */ 414 coordsArrayToMatrix: function (coords, split) { 415 var i, 416 x = [], 417 m = []; 418 419 for (i = 0; i < coords.length; i++) { 420 if (split) { 421 x.push(coords[i].usrCoords[1]); 422 m.push(coords[i].usrCoords[2]); 423 } else { 424 m.push([coords[i].usrCoords[1], coords[i].usrCoords[2]]); 425 } 426 } 427 428 if (split) { 429 m = [x, m]; 430 } 431 432 return m; 433 }, 434 435 /** 436 * Compare two arrays. 437 * @param {Array} a1 438 * @param {Array} a2 439 * @returns {Boolean} <tt>true</tt>, if the arrays coefficients are of same type and value. 440 */ 441 cmpArrays: function (a1, a2) { 442 var i; 443 444 // trivial cases 445 if (a1 === a2) { 446 return true; 447 } 448 449 if (a1.length !== a2.length) { 450 return false; 451 } 452 453 for (i = 0; i < a1.length; i++) { 454 if (a1[i] !== a2[i]) { 455 return false; 456 } 457 } 458 459 return true; 460 }, 461 462 /** 463 * Removes an element from the given array 464 * @param {Array} ar 465 * @param el 466 * @returns {Array} 467 */ 468 removeElementFromArray: function (ar, el) { 469 var i; 470 471 for (i = 0; i < ar.length; i++) { 472 if (ar[i] === el) { 473 ar.splice(i, 1); 474 return ar; 475 } 476 } 477 478 return ar; 479 }, 480 481 /** 482 * Truncate a number <tt>n</tt> after <tt>p</tt> decimals. 483 * @param {Number} n 484 * @param {Number} p 485 * @returns {Number} 486 */ 487 trunc: function (n, p) { 488 p = JXG.def(p, 0); 489 490 /*jslint bitwise: true*/ 491 492 if (p === 0) { 493 n = ~n; 494 n = ~n; 495 } else { 496 n = n.toFixed(p); 497 } 498 499 return n; 500 }, 501 502 /** 503 * Truncate a number <tt>val</tt> automatically. 504 * @param val 505 * @returns {Number} 506 */ 507 autoDigits: function (val) { 508 var x = Math.abs(val); 509 510 if (x > 0.1) { 511 x = val.toFixed(2); 512 } else if (x >= 0.01) { 513 x = val.toFixed(4); 514 } else if (x >= 0.0001) { 515 x = val.toFixed(6); 516 } else { 517 x = val; 518 } 519 return x; 520 }, 521 522 /** 523 * Extracts the keys of a given object. 524 * @param object The object the keys are to be extracted 525 * @param onlyOwn If true, hasOwnProperty() is used to verify that only keys are collected 526 * the object owns itself and not some other object in the prototype chain. 527 * @returns {Array} All keys of the given object. 528 */ 529 keys: function (object, onlyOwn) { 530 var keys = [], property; 531 532 // the caller decides if we use hasOwnProperty 533 /*jslint forin:true*/ 534 for (property in object) { 535 if (onlyOwn) { 536 if (object.hasOwnProperty(property)) { 537 keys.push(property); 538 } 539 } else { 540 keys.push(property); 541 } 542 } 543 /*jslint forin:false*/ 544 545 return keys; 546 }, 547 548 /** 549 * This outputs an object with a base class reference to the given object. This is useful if 550 * you need a copy of an e.g. attributes object and want to overwrite some of the attributes 551 * without changing the original object. 552 * @param {Object} obj Object to be embedded. 553 * @returns {Object} An object with a base class reference to <tt>obj</tt>. 554 */ 555 clone: function (obj) { 556 var cObj = {}; 557 558 cObj.prototype = obj; 559 560 return cObj; 561 }, 562 563 /** 564 * Embeds an existing object into another one just like {@link #clone} and copies the contents of the second object 565 * to the new one. Warning: The copied properties of obj2 are just flat copies. 566 * @param {Object} obj Object to be copied. 567 * @param {Object} obj2 Object with data that is to be copied to the new one as well. 568 * @returns {Object} Copy of given object including some new/overwritten data from obj2. 569 */ 570 cloneAndCopy: function (obj, obj2) { 571 var r, 572 cObj = function () {}; 573 574 cObj.prototype = obj; 575 576 // no hasOwnProperty on purpose 577 /*jslint forin:true*/ 578 /*jshint forin:true*/ 579 580 for (r in obj2) { 581 cObj[r] = obj2[r]; 582 } 583 584 /*jslint forin:false*/ 585 /*jshint forin:false*/ 586 587 588 return cObj; 589 }, 590 591 /** 592 * Recursively merges obj2 into obj1. Contrary to {@link JXG#deepCopy} this won't create a new object 593 * but instead will 594 * @param {Object} obj1 595 * @param {Object} obj2 596 * @returns {Object} 597 */ 598 merge: function (obj1, obj2) { 599 var i, j; 600 601 for (i in obj2) { 602 if (obj2.hasOwnProperty(i)) { 603 if (this.isArray(obj2[i])) { 604 if (!obj1[i]) { 605 obj1[i] = []; 606 } 607 608 for (j = 0; j < obj2[i].length; j++) { 609 if (typeof obj2[i][j] === 'object') { 610 obj1[i][j] = this.merge(obj1[i][j], obj2[i][j]); 611 } else { 612 obj1[i][j] = obj2[i][j]; 613 } 614 } 615 } else if (typeof obj2[i] === 'object') { 616 if (!obj1[i]) { 617 obj1[i] = {}; 618 } 619 620 obj1[i] = this.merge(obj1[i], obj2[i]); 621 } else { 622 obj1[i] = obj2[i]; 623 } 624 } 625 } 626 627 return obj1; 628 }, 629 630 /** 631 * Creates a deep copy of an existing object, i.e. arrays or sub-objects are copied component resp. 632 * element-wise instead of just copying the reference. If a second object is supplied, the two objects 633 * are merged into one object. The properties of the second object have priority. 634 * @param {Object} obj This object will be copied. 635 * @param {Object} obj2 This object will merged into the newly created object 636 * @param {Boolean} [toLower=false] If true the keys are convert to lower case. This is needed for visProp, see JXG#copyAttributes 637 * @returns {Object} copy of obj or merge of obj and obj2. 638 */ 639 deepCopy: function (obj, obj2, toLower) { 640 var c, i, prop, j, i2; 641 642 toLower = toLower || false; 643 644 if (typeof obj !== 'object' || obj === null) { 645 return obj; 646 } 647 648 // missing hasOwnProperty is on purpose in this function 649 /*jslint forin:true*/ 650 /*jshint forin:false*/ 651 652 if (this.isArray(obj)) { 653 c = []; 654 for (i = 0; i < obj.length; i++) { 655 prop = obj[i]; 656 if (typeof prop === 'object') { 657 c[i] = this.deepCopy(prop); 658 } else { 659 c[i] = prop; 660 } 661 } 662 } else { 663 c = {}; 664 for (i in obj) { 665 i2 = toLower ? i.toLowerCase() : i; 666 667 prop = obj[i]; 668 if (typeof prop === 'object') { 669 c[i2] = this.deepCopy(prop); 670 } else { 671 c[i2] = prop; 672 } 673 } 674 675 for (i in obj2) { 676 i2 = toLower ? i.toLowerCase() : i; 677 678 prop = obj2[i]; 679 if (typeof prop === 'object') { 680 if (JXG.isArray(prop) || !JXG.exists(c[i2])) { 681 c[i2] = this.deepCopy(prop); 682 } else { 683 c[i2] = this.deepCopy(c[i2], prop, toLower); 684 } 685 } else { 686 c[i2] = prop; 687 } 688 } 689 } 690 691 /*jslint forin:false*/ 692 /*jshint forin:true*/ 693 694 return c; 695 }, 696 697 /** 698 * Generates an attributes object that is filled with default values from the Options object 699 * and overwritten by the user speciified attributes. 700 * @param {Object} attributes user specified attributes 701 * @param {Object} options defaults options 702 * @param {String} s variable number of strings, e.g. 'slider', subtype 'point1'. 703 * @returns {Object} The resulting attributes object 704 */ 705 copyAttributes: function (attributes, options, s) { 706 var a, i, len, o, isAvail, 707 primitives = { 708 'circle': 1, 709 'curve': 1, 710 'image': 1, 711 'line': 1, 712 'point': 1, 713 'polygon': 1, 714 'text': 1, 715 'ticks': 1, 716 'integral': 1 717 }; 718 719 720 len = arguments.length; 721 if (len < 3 || primitives[s]) { 722 // default options from Options.elements 723 a = JXG.deepCopy(options.elements, null, true); 724 } else { 725 a = {}; 726 } 727 728 // Only the layer of the main element is set. 729 if (len < 4 && this.exists(s) && this.exists(options.layer[s])) { 730 a.layer = options.layer[s]; 731 } 732 733 // default options from specific elements 734 o = options; 735 isAvail = true; 736 for (i = 2; i < len; i++) { 737 if (JXG.exists(o[arguments[i]])) { 738 o = o[arguments[i]]; 739 } else { 740 isAvail = false; 741 break; 742 } 743 } 744 if (isAvail) { 745 a = JXG.deepCopy(a, o, true); 746 } 747 748 // options from attributes 749 o = attributes; 750 isAvail = true; 751 for (i = 3; i < len; i++) { 752 if (JXG.exists(o[arguments[i]])) { 753 o = o[arguments[i]]; 754 } else { 755 isAvail = false; 756 break; 757 } 758 } 759 if (isAvail) { 760 this.extend(a, o, null, true); 761 } 762 763 // Special treatment of labels 764 o = options; 765 isAvail = true; 766 for (i = 2; i < len; i++) { 767 if (JXG.exists(o[arguments[i]])) { 768 o = o[arguments[i]]; 769 } else { 770 isAvail = false; 771 break; 772 } 773 } 774 if (isAvail && JXG.exists(o.label)) { 775 a.label = JXG.deepCopy(o.label, a.label); 776 } 777 a.label = JXG.deepCopy(options.label, a.label); 778 779 return a; 780 }, 781 782 /** 783 * Converts a JavaScript object into a JSON string. 784 * @param {Object} obj A JavaScript object, functions will be ignored. 785 * @param {Boolean} [noquote=false] No quotes around the name of a property. 786 * @returns {String} The given object stored in a JSON string. 787 */ 788 toJSON: function (obj, noquote) { 789 var list, prop, i, s, val; 790 791 noquote = JXG.def(noquote, false); 792 793 // check for native JSON support: 794 if (typeof JSON && JSON.stringify && !noquote) { 795 try { 796 s = JSON.stringify(obj); 797 return s; 798 } catch (e) { 799 // if something goes wrong, e.g. if obj contains functions we won't return 800 // and use our own implementation as a fallback 801 } 802 } 803 804 switch (typeof obj) { 805 case 'object': 806 if (obj) { 807 list = []; 808 809 if (JXG.isArray(obj)) { 810 for (i = 0; i < obj.length; i++) { 811 list.push(JXG.toJSON(obj[i], noquote)); 812 } 813 814 return '[' + list.join(',') + ']'; 815 } 816 817 for (prop in obj) { 818 if (obj.hasOwnProperty(prop)) { 819 try { 820 val = JXG.toJSON(obj[prop], noquote); 821 } catch (e2) { 822 val = ''; 823 } 824 825 if (noquote) { 826 list.push(prop + ':' + val); 827 } else { 828 list.push('"' + prop + '":' + val); 829 } 830 } 831 } 832 833 return '{' + list.join(',') + '} '; 834 } 835 return 'null'; 836 case 'string': 837 return '\'' + obj.replace(/(["'])/g, '\\$1') + '\''; 838 case 'number': 839 case 'boolean': 840 return obj.toString(); 841 } 842 843 return '0'; 844 }, 845 846 /** 847 * Resets visPropOld. 848 * @param {JXG.GeometryElement} el 849 * @returns {GeometryElement} 850 */ 851 clearVisPropOld: function (el) { 852 el.visPropOld = { 853 strokecolor: '', 854 strokeopacity: '', 855 strokewidth: '', 856 fillcolor: '', 857 fillopacity: '', 858 shadow: false, 859 firstarrow: false, 860 lastarrow: false, 861 cssclass: '', 862 fontsize: -1, 863 left: -100000, 864 right: 100000, 865 top: -100000 866 }; 867 868 return el; 869 }, 870 871 /** 872 * Checks if an object contains a key, whose value equals to val. 873 * @param {Object} obj 874 * @param val 875 * @returns {Boolean} 876 */ 877 isInObject: function (obj, val) { 878 var el; 879 880 for (el in obj) { 881 if (obj.hasOwnProperty(el)) { 882 if (obj[el] === val) { 883 return true; 884 } 885 } 886 } 887 888 return false; 889 }, 890 891 /** 892 * Replaces all occurences of & by &, > by >, and < by <. 893 * @param {String} str 894 * @returns {String} 895 */ 896 escapeHTML: function (str) { 897 return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); 898 }, 899 900 /** 901 * Eliminates all substrings enclosed by < and > and replaces all occurences of 902 * & by &, > by >, and < by <. 903 * @param {String} str 904 * @returns {String} 905 */ 906 unescapeHTML: function (str) { 907 // this regex is NOT insecure. We are replacing everything found with '' 908 /*jslint regexp:true*/ 909 return str.replace(/<\/?[^>]+>/gi, '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); 910 }, 911 912 /** 913 * Makes a string lower case except for the first character which will be upper case. 914 * @param {String} str Arbitrary string 915 * @returns {String} The capitalized string. 916 */ 917 capitalize: function (str) { 918 return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase(); 919 }, 920 921 /** 922 * Make numbers given as strings nicer by removing all unnecessary leading and trailing zeroes. 923 * @param {String} str 924 * @returns {String} 925 */ 926 trimNumber: function (str) { 927 str = str.replace(/^0+/, ''); 928 str = str.replace(/0+$/, ''); 929 930 if (str[str.length - 1] === '.' || str[str.length - 1] === ',') { 931 str = str.slice(0, -1); 932 } 933 934 if (str[0] === '.' || str[0] === ',') { 935 str = "0" + str; 936 } 937 938 return str; 939 }, 940 941 /** 942 * Filter an array of elements. 943 * @param {Array} list 944 * @param {Object|function} filter 945 * @returns {Array} 946 */ 947 filterElements: function (list, filter) { 948 var i, f, item, flower, value, visPropValue, pass, 949 l = list.length, 950 result = []; 951 952 if (typeof filter !== 'function' && typeof filter !== 'object') { 953 return result; 954 } 955 956 for (i = 0; i < l; i++) { 957 pass = true; 958 item = list[i]; 959 960 if (typeof filter === 'object') { 961 for (f in filter) { 962 if (filter.hasOwnProperty(f)) { 963 flower = f.toLowerCase(); 964 965 if (typeof item[f] === 'function') { 966 value = item[f](); 967 } else { 968 value = item[f]; 969 } 970 971 if (item.visProp && typeof item.visProp[flower] === 'function') { 972 visPropValue = item.visProp[flower](); 973 } else { 974 visPropValue = item.visProp && item.visProp[flower]; 975 } 976 977 if (typeof filter[f] === 'function') { 978 pass = filter[f](value) || filter[f](visPropValue); 979 } else { 980 pass = (value === filter[f] || visPropValue === filter[f]); 981 } 982 983 if (!pass) { 984 break; 985 } 986 } 987 } 988 } else if (typeof filter === 'function') { 989 pass = filter(item); 990 } 991 992 if (pass) { 993 result.push(item); 994 } 995 } 996 997 return result; 998 }, 999 1000 /** 1001 * Remove all leading and trailing whitespaces from a given string. 1002 * @param {String} str 1003 * @returns {String} 1004 */ 1005 trim: function (str) { 1006 str = str.replace(/^\s+/, ''); 1007 str = str.replace(/\s+$/, ''); 1008 1009 return str; 1010 }, 1011 1012 /** 1013 * Convert HTML tags to entities or use html_sanitize if the google caja html sanitizer is available. 1014 * @param {String} str 1015 * @param {Boolean} caja 1016 * @returns {String} Sanitized string 1017 */ 1018 sanitizeHTML: function (str, caja) { 1019 if (typeof html_sanitize === 'function' && caja) { 1020 return html_sanitize(str, function () { return; }, function (id) { return id; }); 1021 } 1022 1023 if (str) { 1024 str = str.replace(/</g, '<').replace(/>/g, '>'); 1025 } 1026 1027 return str; 1028 }, 1029 1030 /** 1031 * If <tt>s</tt> is a slider, it returns the sliders value, otherwise it just returns the given value. 1032 * @param {*} s 1033 * @retusn {*} s.Value() if s is an element of type slider, s otherwise 1034 */ 1035 evalSlider: function (s) { 1036 if (s.type === Const.OBJECT_TYPE_GLIDER && typeof s.Value === 'function') { 1037 s = s.Value(); 1038 } 1039 1040 return s; 1041 } 1042 }); 1043 1044 return JXG; 1045 }); 1046