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