      1 /*
      2     Copyright 2008-2022
      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     <http://www.gnu.org/licenses/>, <https://www.apache.org/licenses/LICENSE-2.0.html>,
     34     and <http://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 /* depends:
     42  */
     43
     44 /**
     45  * @fileoverview The JSXGraph object is defined in this file. JXG.JSXGraph controls all boards.
     46  * It has methods to create, save, load and free boards. Additionally some helper functions are
     47  * defined in this file directly in the JXG namespace.
     48  */
     49
     50 define([], function () {
     51
     52     'use strict';
     53
     54     /**
     55      * JXG is the top object of JSXGraph and defines the namespace
     56      * @exports jxg as JXG
     57      */
     58     var jxg = {};
     59
     60     // Make sure JXG.extend is not defined
     61     // If jsxgraph is loaded via loadjsxgraph.js, this is required, but JXG.extend will be undefined
     62     // If jsxgraph is compiled as an amd module, it is possible that another jsxgraph version is already loaded and we
     63     // therefore must not re-use the global JXG variable. But in this case JXG.extend will already be defined.
     64     // This is the reason for this check.
     65     if (typeof JXG === 'object' && !JXG.extend) {
     66         jxg = JXG;
     67     }
     68
     69     // We need the following two methods "extend" and "shortcut" to create the JXG object via JXG.extend.
     70
     71     /**
     72      * Copy all properties of the <tt>extension</tt> object to <tt>object</tt>.
     73      * @param {Object} object
     74      * @param {Object} extension
     75      * @param {Boolean} [onlyOwn=false] Only consider properties that belong to extension itself, not any inherited properties.
     76      * @param {Boolean} [toLower=false] If true the keys are convert to lower case. This is needed for visProp, see JXG#copyAttributes
     77      */
     78     jxg.extend = function (object, extension, onlyOwn, toLower) {
     79         var e, e2;
     80
     81         onlyOwn = onlyOwn || false;
     82         toLower = toLower || false;
     83
     84         // the purpose of this for...in loop is indeed to use hasOwnProperty only if the caller
     85         // explicitly wishes so.
     86         for (e in extension) {
     87             if (!onlyOwn || (onlyOwn && extension.hasOwnProperty(e))) {
     88                 if (toLower) {
     89                     e2 = e.toLowerCase();
     90                 } else {
     91                     e2 = e;
     92                 }
     93
     94                 object[e2] = extension[e];
     95             }
     96         }
     97     };
     98
     99     /**
    100      * Set a constant <tt>name</tt> in <tt>object</tt> to <tt>value</tt>. The value can't be changed after declaration.
    101      * @param {Object} object
    102      * @param {String} name
    103      * @param {Number|String|Boolean} value
    104      * @param {Boolean} ignoreRedefine This should be left at its default: false.
    105      */
    106     jxg.defineConstant = function (object, name, value, ignoreRedefine) {
    107         ignoreRedefine = ignoreRedefine || false;
    108
    109         if (ignoreRedefine && jxg.exists(object[name])) {
    110             return;
    111         }
    112
    113         Object.defineProperty(object, name, {
    114             value: value,
    115             writable: false,
    116             enumerable: true,
    117             configurable: false,
    118         });
    119     };
    120
    121     /**
    122      * Copy all properties of the <tt>constants</tt> object in <tt>object</tt> as a constant.
    123      * @param {Object} object
    124      * @param {Object} constants
    125      * @param {Boolean} [onlyOwn=false] Only consider properties that belong to extension itself, not any inherited properties.
    126      * @param {Boolean} [toUpper=false] If true the keys are convert to lower case. This is needed for visProp, see JXG#copyAttributes
    127      */
    128     jxg.extendConstants = function (object, constants, onlyOwn, toUpper) {
    129         var e, e2;
    130
    131         onlyOwn = onlyOwn || false;
    132         toUpper = toUpper || false;
    133
    134         // The purpose of this for...in loop is indeed to use hasOwnProperty only if the caller explicitly wishes so.
    135         for (e in constants) {
    136             if (!onlyOwn || (onlyOwn && constants.hasOwnProperty(e))) {
    137                 if (toUpper) {
    138                     e2 = e.toUpperCase();
    139                 } else {
    140                     e2 = e;
    141                 }
    142
    143                 this.defineConstant(object, e2, constants[e]);
    144             }
    145         }
    146     };
    147
    148     jxg.extend(jxg, /** @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) &&
    246                     JXG.boards[b].container === s) {
    247                     return JXG.boards[b];
    248                 }
    249             }
    250
    251             return null;
    252         },
    253
    254         /**
    255          * This method issues a warning to the developer that the given function is deprecated
    256          * and, if available, offers an alternative to the deprecated function.
    257          * @param {String} what Describes the function that is deprecated
    258          * @param {String} [replacement] The replacement that should be used instead.
    259          */
    260         deprecated: function (what, replacement) {
    261             var warning = what + ' is deprecated.';
    262
    263             if (replacement) {
    264                 warning += ' Please use ' + replacement + ' instead.';
    265             }
    266
    267             jxg.warn(warning);
    268         },
    269
    270         /**
    271          * Outputs a warning via console.warn(), if available. If console.warn() is
    272          * unavailable this function will look for an HTML element with the id 'warning'
    273          * and append the warning to this element's innerHTML.
    274          * @param {String} warning The warning text
    275          */
    276         warn: function (warning) {
    277             if (typeof window === 'object' && window.console && console.warn) {
    278                 console.warn('WARNING:', warning);
    279             } else if (typeof document === 'object' && document.getElementById('warning')) {
    280                 document.getElementById('debug').innerHTML += 'WARNING: ' + warning + '<br />';
    281             }
    282         },
    283
    284         /**
    285          * Add something to the debug log. If available a JavaScript debug console is used. Otherwise
    286          * we're looking for a HTML div with id "debug". If this doesn't exist, too, the output is omitted.
    287          * @param s An arbitrary number of parameters.
    288          * @see JXG#debugWST
    289          */
    290         debugInt: function (s) {
    291             var i, p;
    292
    293             for (i = 0; i < arguments.length; i++) {
    294                 p = arguments[i];
    295                 if (typeof window === 'object' && window.console && console.log) {
    296                     console.log(p);
    297                 } else if (typeof document === 'object' && document.getElementById('debug')) {
    298                     document.getElementById('debug').innerHTML += p + '<br/>';
    299                 }
    300             }
    301         },
    302
    303         /**
    304          * Add something to the debug log. If available a JavaScript debug console is used. Otherwise
    305          * we're looking for a HTML div with id "debug". If this doesn't exist, too, the output is omitted.
    306          * This method adds a stack trace (if available).
    307          * @param s An arbitrary number of parameters.
    308          * @see JXG#debug
    309          */
    310         debugWST: function (s) {
    311             var e = new Error();
    312
    313             jxg.debugInt.apply(this, arguments);
    314
    315             if (e && e.stack) {
    316                 jxg.debugInt('stacktrace');
    317                 jxg.debugInt(e.stack.split('\n').slice(1).join('\n'));
    318             }
    319         },
    320
    321         /**
    322          * Add something to the debug log. If available a JavaScript debug console is used. Otherwise
    323          * we're looking for a HTML div with id "debug". If this doesn't exist, too, the output is omitted.
    324          * This method adds a line of the stack trace (if available).
    325          *
    326          * @param s An arbitrary number of parameters.
    327          * @see JXG#debug
    328          */
    329         debugLine: function (s) {
    330             var e = new Error();
    331
    332             jxg.debugInt.apply(this, arguments);
    333
    334             if (e && e.stack) {
    335                 jxg.debugInt('Called from', e.stack.split('\n').slice(2, 3).join('\n'));
    336             }
    337         },
    338
    339         /**
    340          * Add something to the debug log. If available a JavaScript debug console is used. Otherwise
    341          * we're looking for a HTML div with id "debug". If this doesn't exist, too, the output is omitted.
    342          * @param s An arbitrary number of parameters.
    343          * @see JXG#debugWST
    344          * @see JXG#debugLine
    345          * @see JXG#debugInt
    346          */
    347         debug: function (s) {
    348             jxg.debugInt.apply(this, arguments);
    349         }
    350     });
    351
    352     return jxg;
    353 });
    354