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