1 /*
  2     Copyright 2008-2023
  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  * @exports jxg as JXG
 50  */
 51 var jxg = {};
 52 
 53 // Make sure JXG.extend is not defined
 54 // If jsxgraph is loaded via loadjsxgraph.js, this is required, but JXG.extend will be undefined
 55 // If jsxgraph is compiled as an amd module, it is possible that another jsxgraph version is already loaded and we
 56 // therefore must not re-use the global JXG variable. But in this case JXG.extend will already be defined.
 57 // This is the reason for this check.
 58 if (typeof JXG === "object" && !JXG.extend) {
 59     jxg = JXG;
 60 }
 61 
 62 // We need the following two methods "extend" and "shortcut" to create the JXG object via JXG.extend.
 63 
 64 /**
 65  * Copy all properties of the <tt>extension</tt> object to <tt>object</tt>.
 66  * @param {Object} object
 67  * @param {Object} extension
 68  * @param {Boolean} [onlyOwn=false] Only consider properties that belong to extension itself, not any inherited properties.
 69  * @param {Boolean} [toLower=false] If true the keys are convert to lower case. This is needed for visProp, see JXG#copyAttributes
 70  */
 71 jxg.extend = function (object, extension, onlyOwn, toLower) {
 72     var e, e2;
 73 
 74     onlyOwn = onlyOwn || false;
 75     toLower = toLower || false;
 76 
 77     // the purpose of this for...in loop is indeed to use hasOwnProperty only if the caller
 78     // explicitly wishes so.
 79     for (e in extension) {
 80         if (!onlyOwn || (onlyOwn && extension.hasOwnProperty(e))) {
 81             if (toLower) {
 82                 e2 = e.toLowerCase();
 83             } else {
 84                 e2 = e;
 85             }
 86 
 87             object[e2] = extension[e];
 88         }
 89     }
 90 };
 91 
 92 /**
 93  * Set a constant <tt>name</tt> in <tt>object</tt> to <tt>value</tt>. The value can't be changed after declaration.
 94  * @param {Object} object
 95  * @param {String} name
 96  * @param {Number|String|Boolean} value
 97  * @param {Boolean} ignoreRedefine This should be left at its default: false.
 98  */
 99 jxg.defineConstant = function (object, name, value, ignoreRedefine) {
100     ignoreRedefine = ignoreRedefine || false;
101 
102     if (ignoreRedefine && jxg.exists(object[name])) {
103         return;
104     }
105 
106     Object.defineProperty(object, name, {
107         value: value,
108         writable: false,
109         enumerable: true,
110         configurable: false
111     });
112 };
113 
114 /**
115  * Copy all properties of the <tt>constants</tt> object in <tt>object</tt> as a constant.
116  * @param {Object} object
117  * @param {Object} constants
118  * @param {Boolean} [onlyOwn=false] Only consider properties that belong to extension itself, not any inherited properties.
119  * @param {Boolean} [toUpper=false] If true the keys are convert to lower case. This is needed for visProp, see JXG#copyAttributes
120  */
121 jxg.extendConstants = function (object, constants, onlyOwn, toUpper) {
122     var e, e2;
123 
124     onlyOwn = onlyOwn || false;
125     toUpper = toUpper || false;
126 
127     // The purpose of this for...in loop is indeed to use hasOwnProperty only if the caller explicitly wishes so.
128     for (e in constants) {
129         if (!onlyOwn || (onlyOwn && constants.hasOwnProperty(e))) {
130             if (toUpper) {
131                 e2 = e.toUpperCase();
132             } else {
133                 e2 = e;
134             }
135 
136             this.defineConstant(object, e2, constants[e]);
137         }
138     }
139 };
140 
141 jxg.extend(
142     jxg,
143     /** @lends JXG */ {
144         /**
145          * Store a reference to every board in this central list. This will at some point
146          * replace JXG.JSXGraph.boards.
147          * @type Object
148          */
149         boards: {},
150 
151         /**
152          * Store the available file readers in this structure.
153          * @type Object
154          */
155         readers: {},
156 
157         /**
158          * Associative array that keeps track of all constructable elements registered
159          * via {@link JXG.registerElement}.
160          * @type Object
161          */
162         elements: {},
163 
164         /**
165          * This registers a new construction element to JSXGraph for the construction via the {@link JXG.Board.create}
166          * interface.
167          * @param {String} element The elements name. This is case-insensitive, existing elements with the same name
168          * will be overwritten.
169          * @param {Function} creator A reference to a function taking three parameters: First the board, the element is
170          * to be created on, a parent element array, and an attributes object. See {@link JXG.createPoint} or any other
171          * <tt>JXG.create...</tt> function for an example.
172          */
173         registerElement: function (element, creator) {
174             element = element.toLowerCase();
175             this.elements[element] = creator;
176         },
177 
178         /**
179          * Register a file reader.
180          * @param {function} reader A file reader. This object has to provide two methods: <tt>prepareString()</tt>
181          * and <tt>read()</tt>.
182          * @param {Array} ext
183          */
184         registerReader: function (reader, ext) {
185             var i, e;
186 
187             for (i = 0; i < ext.length; i++) {
188                 e = ext[i].toLowerCase();
189 
190                 if (typeof this.readers[e] !== "function") {
191                     this.readers[e] = reader;
192                 }
193             }
194         },
195 
196         /**
197          * Creates a shortcut to a method, e.g. {@link JXG.Board#createElement} is a shortcut to {@link JXG.Board#create}.
198          * Sometimes the target is undefined by the time you want to define the shortcut so we need this little helper.
199          * @param {Object} object The object the method we want to create a shortcut for belongs to.
200          * @param {String} fun The method we want to create a shortcut for.
201          * @returns {Function} A function that calls the given method.
202          */
203         shortcut: function (object, fun) {
204             return function () {
205                 return object[fun].apply(this, arguments);
206             };
207         },
208 
209         /**
210          * s may be a string containing the name or id of an element or even a reference
211          * to the element itself. This function returns a reference to the element. Search order: id, name.
212          * @param {JXG.Board} board Reference to the board the element belongs to.
213          * @param {String} s String or reference to a JSXGraph element.
214          * @returns {Object} Reference to the object given in parameter object
215          * @deprecated Use {@link JXG.Board#select}
216          */
217         getRef: function (board, s) {
218             jxg.deprecated("JXG.getRef()", "Board.select()");
219             return board.select(s);
220         },
221 
222         /**
223          * This is just a shortcut to {@link JXG.getRef}.
224          * @deprecated Use {@link JXG.Board#select}.
225          */
226         getReference: function (board, s) {
227             jxg.deprecated("JXG.getReference()", "Board.select()");
228             return board.select(s);
229         },
230 
231         /**
232          * s may be the string containing the id of an HTML tag that hosts a JSXGraph board.
233          * This function returns the reference to the board.
234          * @param  {String} s String of an HTML tag that hosts a JSXGraph board
235          * @returns {Object} Reference to the board or null.
236          */
237         getBoardByContainerId: function (s) {
238             var b;
239             for (b in JXG.boards) {
240                 if (JXG.boards.hasOwnProperty(b) && JXG.boards[b].container === s) {
241                     return JXG.boards[b];
242                 }
243             }
244 
245             return null;
246         },
247 
248         /**
249          * This method issues a warning to the developer that the given function is deprecated
250          * and, if available, offers an alternative to the deprecated function.
251          * @param {String} what Describes the function that is deprecated
252          * @param {String} [replacement] The replacement that should be used instead.
253          */
254         deprecated: function (what, replacement) {
255             var warning = what + " is deprecated.";
256 
257             if (replacement) {
258                 warning += " Please use " + replacement + " instead.";
259             }
260 
261             jxg.warn(warning);
262         },
263 
264         /**
265          * Outputs a warning via console.warn(), if available. If console.warn() is
266          * unavailable this function will look for an HTML element with the id 'warning'
267          * and append the warning to this element's innerHTML.
268          * @param {String} warning The warning text
269          */
270         warn: function (warning) {
271             if (typeof window === "object" && window.console && console.warn) {
272                 console.warn("WARNING:", warning);
273             } else if (typeof document === "object" && document.getElementById("warning")) {
274                 document.getElementById("debug").innerHTML += "WARNING: " + warning + "<br />";
275             }
276         },
277 
278         /**
279          * Add something to the debug log. If available a JavaScript debug console is used. Otherwise
280          * we're looking for a HTML div with id "debug". If this doesn't exist, too, the output is omitted.
281          * @param s An arbitrary number of parameters.
282          * @see JXG#debugWST
283          */
284         debugInt: function (s) {
285             var i, p;
286 
287             for (i = 0; i < arguments.length; i++) {
288                 p = arguments[i];
289                 if (typeof window === "object" && window.console && console.log) {
290                     console.log(p);
291                 } else if (typeof document === "object" && document.getElementById("debug")) {
292                     document.getElementById("debug").innerHTML += p + "<br/>";
293                 }
294             }
295         },
296 
297         /**
298          * Add something to the debug log. If available a JavaScript debug console is used. Otherwise
299          * we're looking for a HTML div with id "debug". If this doesn't exist, too, the output is omitted.
300          * This method adds a stack trace (if available).
301          * @param s An arbitrary number of parameters.
302          * @see JXG#debug
303          */
304         debugWST: function (s) {
305             var e = new Error();
306 
307             jxg.debugInt.apply(this, arguments);
308 
309             if (e && e.stack) {
310                 jxg.debugInt("stacktrace");
311                 jxg.debugInt(e.stack.split("\n").slice(1).join("\n"));
312             }
313         },
314 
315         /**
316          * Add something to the debug log. If available a JavaScript debug console is used. Otherwise
317          * we're looking for a HTML div with id "debug". If this doesn't exist, too, the output is omitted.
318          * This method adds a line of the stack trace (if available).
319          *
320          * @param s An arbitrary number of parameters.
321          * @see JXG#debug
322          */
323         debugLine: function (s) {
324             var e = new Error();
325 
326             jxg.debugInt.apply(this, arguments);
327 
328             if (e && e.stack) {
329                 jxg.debugInt("Called from", e.stack.split("\n").slice(2, 3).join("\n"));
330             }
331         },
332 
333         /**
334          * Add something to the debug log. If available a JavaScript debug console is used. Otherwise
335          * we're looking for a HTML div with id "debug". If this doesn't exist, too, the output is omitted.
336          * @param s An arbitrary number of parameters.
337          * @see JXG#debugWST
338          * @see JXG#debugLine
339          * @see JXG#debugInt
340          */
341         debug: function (s) {
342             jxg.debugInt.apply(this, arguments);
343         }
344     }
345 );
346 
347 export default jxg;
348