      1 /*
      2     Copyright 2008-2024
      3         Matthias Ehmann,
      4         Michael Gerhaeuser,
      5         Carsten Miller,
      6         Bianca Valentin,
      7         Andreas Walter,
      8         Alfred Wassermann,
      9         Peter Wilfahrt
     10
     11     This file is part of JSXGraph and JSXCompressor.
     12
     13     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
     14     JSXCompressor is free software dual licensed under the GNU LGPL or Apache License.
     15
     16     You can redistribute it and/or modify it under the terms of the
     17
     18       * GNU Lesser General Public License as published by
     19         the Free Software Foundation, either version 3 of the License, or
     20         (at your option) any later version
     21       OR
     22       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
     23       OR
     24       * Apache License Version 2.0
     25
     26     JSXGraph is distributed in the hope that it will be useful,
     27     but WITHOUT ANY WARRANTY; without even the implied warranty of
     28     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     29     GNU Lesser General Public License for more details.
     30
     31     You should have received a copy of the GNU Lesser General Public License, Apache
     32     License, and the MIT License along with JSXGraph. If not, see
     33     <https://www.gnu.org/licenses/>, <https://www.apache.org/licenses/LICENSE-2.0.html>,
     34     and <https://opensource.org/licenses/MIT/>.
     35
     36  */
     37
     38 /*global JXG: true, define: true, jQuery: true, window: true, document: true, navigator: true, require: true, module: true, console: true */
     39 /*jslint nomen:true, plusplus:true, forin:true*/
     40
     41 /**
     42  * @fileoverview The JSXGraph object is defined in this file. JXG.JSXGraph controls all boards.
     43  * It has methods to create, save, load and free boards. Additionally some helper functions are
     44  * defined in this file directly in the JXG namespace.
     45  */
     46
     47 /**
     48  * JXG is the top object of JSXGraph and defines the namespace
     49  *
     50  * @name JXG
     51  * @exports jxg as JXG
     52  * @namespace
     53  */
     54 var jxg = {};
     55
     56 // Make sure JXG.extend is not defined.
     57 // If JSXGraph is compiled as an amd module, it is possible that another JSXGraph version is already loaded and we
     58 // therefore must not re-use the global JXG variable. But in this case JXG.extend will already be defined.
     59 // This is the reason for this check.
     60 // The try-statement is necessary, otherwise an error is thrown in certain imports, e.g. in deno.
     61 try {
     62     if (typeof JXG === "object" && !JXG.extend) {
     63         jxg = JXG;
     64     }
     65 } catch (e) {}
     66
     67 // We need the following two methods "extend" and "shortcut" to create the JXG object via JXG.extend.
     68
     69 /**
     70  * Copy all properties of the <tt>extension</tt> object to <tt>object</tt>.
     71  * @param {Object} object
     72  * @param {Object} extension
     73  * @param {Boolean} [onlyOwn=false] Only consider properties that belong to extension itself, not any inherited properties.
     74  * @param {Boolean} [toLower=false] If true the keys are convert to lower case. This is needed for visProp, see JXG#copyAttributes
     75  */
     76 jxg.extend = function (object, extension, onlyOwn, toLower) {
     77     var e, e2;
     78
     79     onlyOwn = onlyOwn || false;
     80     toLower = toLower || false;
     81
     82     // the purpose of this for...in loop is indeed to use hasOwnProperty only if the caller
     83     // explicitly wishes so.
     84     for (e in extension) {
     85         if (!onlyOwn || (onlyOwn && extension.hasOwnProperty(e))) {
     86             if (toLower) {
     87                 e2 = e.toLowerCase();
     88             } else {
     89                 e2 = e;
     90             }
     91
     92             object[e2] = extension[e];
     93         }
     94     }
     95 };
     96
     97 /**
     98  * Set a constant <tt>name</tt> in <tt>object</tt> to <tt>value</tt>. The value can't be changed after declaration.
     99  * @param {Object} object
    100  * @param {String} name
    101  * @param {Number|String|Boolean} value
    102  * @param {Boolean} ignoreRedefine This should be left at its default: false.
    103  */
    104 jxg.defineConstant = function (object, name, value, ignoreRedefine) {
    105     ignoreRedefine = ignoreRedefine || false;
    106
    107     if (ignoreRedefine && jxg.exists(object[name])) {
    108         return;
    109     }
    110
    111     Object.defineProperty(object, name, {
    112         value: value,
    113         writable: false,
    114         enumerable: true,
    115         configurable: false
    116     });
    117 };
    118
    119 /**
    120  * Copy all properties of the <tt>constants</tt> object in <tt>object</tt> as a constant.
    121  * @param {Object} object
    122  * @param {Object} constants
    123  * @param {Boolean} [onlyOwn=false] Only consider properties that belong to extension itself, not any inherited properties.
    124  * @param {Boolean} [toUpper=false] If true the keys are convert to lower case. This is needed for visProp, see JXG#copyAttributes
    125  */
    126 jxg.extendConstants = function (object, constants, onlyOwn, toUpper) {
    127     var e, e2;
    128
    129     onlyOwn = onlyOwn || false;
    130     toUpper = toUpper || false;
    131
    132     // The purpose of this for...in loop is indeed to use hasOwnProperty only if the caller explicitly wishes so.
    133     for (e in constants) {
    134         if (!onlyOwn || (onlyOwn && constants.hasOwnProperty(e))) {
    135             if (toUpper) {
    136                 e2 = e.toUpperCase();
    137             } else {
    138                 e2 = e;
    139             }
    140
    141             this.defineConstant(object, e2, constants[e]);
    142         }
    143     }
    144 };
    145
    146 jxg.extend(
    147     jxg,
    148     /** @lends JXG */ {
    149         /**
    150          * Store a reference to every board in this central list. This will at some point
    151          * replace JXG.JSXGraph.boards.
    152          * @type Object
    153          */
    154         boards: {},
    155
    156         /**
    157          * Store the available file readers in this structure.
    158          * @type Object
    159          */
    160         readers: {},
    161
    162         /**
    163          * Associative array that keeps track of all constructable elements registered
    164          * via {@link JXG.registerElement}.
    165          * @type Object
    166          */
    167         elements: {},
    168
    169         /**
    170          * This registers a new construction element to JSXGraph for the construction via the {@link JXG.Board.create}
    171          * interface.
    172          * @param {String} element The elements name. This is case-insensitive, existing elements with the same name
    173          * will be overwritten.
    174          * @param {Function} creator A reference to a function taking three parameters: First the board, the element is
    175          * to be created on, a parent element array, and an attributes object. See {@link JXG.createPoint} or any other
    176          * <tt>JXG.create...</tt> function for an example.
    177          */
    178         registerElement: function (element, creator) {
    179             element = element.toLowerCase();
    180             this.elements[element] = creator;
    181         },
    182
    183         /**
    184          * Register a file reader.
    185          * @param {function} reader A file reader. This object has to provide two methods: <tt>prepareString()</tt>
    186          * and <tt>read()</tt>.
    187          * @param {Array} ext
    188          */
    189         registerReader: function (reader, ext) {
    190             var i, e;
    191
    192             for (i = 0; i < ext.length; i++) {
    193                 e = ext[i].toLowerCase();
    194
    195                 if (typeof this.readers[e] !== "function") {
    196                     this.readers[e] = reader;
    197                 }
    198             }
    199         },
    200
    201         /**
    202          * Creates a shortcut to a method, e.g. {@link JXG.Board#createElement} is a shortcut to {@link JXG.Board#create}.
    203          * Sometimes the target is undefined by the time you want to define the shortcut so we need this little helper.
    204          * @param {Object} object The object the method we want to create a shortcut for belongs to.
    205          * @param {String} fun The method we want to create a shortcut for.
    206          * @returns {Function} A function that calls the given method.
    207          */
    208         shortcut: function (object, fun) {
    209             return function () {
    210                 return object[fun].apply(this, arguments);
    211             };
    212         },
    213
    214         /**
    215          * s may be a string containing the name or id of an element or even a reference
    216          * to the element itself. This function returns a reference to the element. Search order: id, name.
    217          * @param {JXG.Board} board Reference to the board the element belongs to.
    218          * @param {String} s String or reference to a JSXGraph element.
    219          * @returns {Object} Reference to the object given in parameter object
    220          * @deprecated Use {@link JXG.Board#select}
    221          */
    222         getRef: function (board, s) {
    223             jxg.deprecated("JXG.getRef()", "Board.select()");
    224             return board.select(s);
    225         },
    226
    227         /**
    228          * This is just a shortcut to {@link JXG.getRef}.
    229          * @deprecated Use {@link JXG.Board#select}.
    230          */
    231         getReference: function (board, s) {
    232             jxg.deprecated("JXG.getReference()", "Board.select()");
    233             return board.select(s);
    234         },
    235
    236         /**
    237          * s may be the string containing the id of an HTML tag that hosts a JSXGraph board.
    238          * This function returns the reference to the board.
    239          * @param  {String} s String of an HTML tag that hosts a JSXGraph board
    240          * @returns {Object} Reference to the board or null.
    241          */
    242         getBoardByContainerId: function (s) {
    243             var b;
    244             for (b in JXG.boards) {
    245                 if (JXG.boards.hasOwnProperty(b) && JXG.boards[b].container === s) {
    246                     return JXG.boards[b];
    247                 }
    248             }
    249
    250             return null;
    251         },
    252
    253         /**
    254          * This method issues a warning to the developer that the given function is deprecated
    255          * and, if available, offers an alternative to the deprecated function.
    256          * @param {String} what Describes the function that is deprecated
    257          * @param {String} [replacement] The replacement that should be used instead.
    258          */
    259         deprecated: function (what, replacement) {
    260             var warning = what + " is deprecated.";
    261
    262             if (replacement) {
    263                 warning += " Please use " + replacement + " instead.";
    264             }
    265
    266             jxg.warn(warning);
    267         },
    268
    269         /**
    270          * Outputs a warning via console.warn(), if available. If console.warn() is
    271          * unavailable this function will look for an HTML element with the id 'warning'
    272          * and append the warning to this element's innerHTML.
    273          * @param {String} warning The warning text
    274          */
    275         warn: function (warning) {
    276             if (typeof window === "object" && window.console && console.warn) {
    277                 console.warn("WARNING:", warning);
    278             } else if (typeof document === "object" && document.getElementById("warning")) {
    279                 document.getElementById("debug").innerHTML += "WARNING: " + warning + "<br />";
    280             }
    281         },
    282
    283         /**
    284          * Add something to the debug log. If available a JavaScript debug console is used. Otherwise
    285          * we're looking for a HTML div with id "debug". If this doesn't exist, too, the output is omitted.
    286          * @param s An arbitrary number of parameters.
    287          * @see JXG#debugWST
    288          */
    289         debugInt: function (s) {
    290             var i, p;
    291
    292             for (i = 0; i < arguments.length; i++) {
    293                 p = arguments[i];
    294                 if (typeof window === "object" && window.console && console.log) {
    295                     console.log(p);
    296                 } else if (typeof document === "object" && document.getElementById("debug")) {
    297                     document.getElementById("debug").innerHTML += p + "<br/>";
    298                 }
    299             }
    300         },
    301
    302         /**
    303          * Add something to the debug log. If available a JavaScript debug console is used. Otherwise
    304          * we're looking for a HTML div with id "debug". If this doesn't exist, too, the output is omitted.
    305          * This method adds a stack trace (if available).
    306          * @param s An arbitrary number of parameters.
    307          * @see JXG#debug
    308          */
    309         debugWST: function (s) {
    310             var e = new Error();
    311
    312             jxg.debugInt.apply(this, arguments);
    313
    314             if (e && e.stack) {
    315                 jxg.debugInt("stacktrace");
    316                 jxg.debugInt(e.stack.split("\n").slice(1).join("\n"));
    317             }
    318         },
    319
    320         /**
    321          * Add something to the debug log. If available a JavaScript debug console is used. Otherwise
    322          * we're looking for a HTML div with id "debug". If this doesn't exist, too, the output is omitted.
    323          * This method adds a line of the stack trace (if available).
    324          *
    325          * @param s An arbitrary number of parameters.
    326          * @see JXG#debug
    327          */
    328         debugLine: function (s) {
    329             var e = new Error();
    330
    331             jxg.debugInt.apply(this, arguments);
    332
    333             if (e && e.stack) {
    334                 jxg.debugInt("Called from", e.stack.split("\n").slice(2, 3).join("\n"));
    335             }
    336         },
    337
    338         /**
    339          * Add something to the debug log. If available a JavaScript debug console is used. Otherwise
    340          * we're looking for a HTML div with id "debug". If this doesn't exist, too, the output is omitted.
    341          * @param s An arbitrary number of parameters.
    342          * @see JXG#debugWST
    343          * @see JXG#debugLine
    344          * @see JXG#debugInt
    345          */
    346         debug: function (s) {
    347             jxg.debugInt.apply(this, arguments);
    348         },
    349
    350         themes: {}
    351     }
    352 );
    353
    354 export default jxg;
    355