1 /*
  2     Copyright 2008-2021
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Bianca Valentin,
  7         Alfred Wassermann,
  8         Andreas Walter,
  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      */
 57     var jxg = {};
 58 
 59     // Make sure JXG.extend is not defined
 60     // If jsxgraph is loaded via loadjsxgraph.js, this is required, but JXG.extend will be undefined
 61     // If jsxgraph is compiled as an amd module, it is possible that another jsxgraph version is already loaded and we
 62     // therefore must not re-use the global JXG variable. But in this case JXG.extend will already be defined.
 63     // This is the reason for this check.
 64     if (typeof JXG === 'object' && !JXG.extend) {
 65         jxg = JXG;
 66     }
 67 
 68     // We need the following two methods "extend" and "shortcut" to create the JXG object via JXG.extend.
 69 
 70     /**
 71      * Copy all properties of the <tt>extension</tt> object to <tt>object</tt>.
 72      * @param {Object} object
 73      * @param {Object} extension
 74      * @param {Boolean} [onlyOwn=false] Only consider properties that belong to extension itself, not any inherited properties.
 75      * @param {Boolean} [toLower=false] If true the keys are convert to lower case. This is needed for visProp, see JXG#copyAttributes
 76      */
 77     jxg.extend = function (object, extension, onlyOwn, toLower) {
 78         var e, e2;
 79 
 80         onlyOwn = onlyOwn || false;
 81         toLower = toLower || false;
 82 
 83         // the purpose of this for...in loop is indeed to use hasOwnProperty only if the caller
 84         // explicitly wishes so.
 85         for (e in extension) {
 86             if (!onlyOwn || (onlyOwn && extension.hasOwnProperty(e))) {
 87                 if (toLower) {
 88                     e2 = e.toLowerCase();
 89                 } else {
 90                     e2 = e;
 91                 }
 92 
 93                 object[e2] = extension[e];
 94             }
 95         }
 96     };
 97 
 98     /**
 99      * Set a constant <tt>name</tt> in <tt>object</tt> to <tt>value</tt>. The value can't be changed after declaration.
100      * @param {Object} object
101      * @param {String} name
102      * @param {Number|String|Boolean} value
103      * @param {Boolean} ignoreRedefine This should be left at its default: false.
104      */
105     jxg.defineConstant = function (object, name, value, ignoreRedefine) {
106         ignoreRedefine = ignoreRedefine || false;
107 
108         if (ignoreRedefine && jxg.exists(object[name])) {
109             return;
110         }
111 
112         Object.defineProperty(object, name, {
113             value: value,
114             writable: false,
115             enumerable: true,
116             configurable: false,
117         });
118     };
119 
120     /**
121      * Copy all properties of the <tt>constants</tt> object in <tt>object</tt> as a constant.
122      * @param {Object} object
123      * @param {Object} constants
124      * @param {Boolean} [onlyOwn=false] Only consider properties that belong to extension itself, not any inherited properties.
125      * @param {Boolean} [toUpper=false] If true the keys are convert to lower case. This is needed for visProp, see JXG#copyAttributes
126      */
127     jxg.extendConstants = function (object, constants, onlyOwn, toUpper) {
128         var e, e2;
129 
130         onlyOwn = onlyOwn || false;
131         toUpper = toUpper || false;
132 
133         // the purpose of this for...in loop is indeed to use hasOwnProperty only if the caller explicitly wishes so.
134         for (e in constants) {
135             if (!onlyOwn || (onlyOwn && extension.hasOwnProperty(e))) {
136                 if (toUpper) {
137                     e2 = e.toUpperCase();
138                 } else {
139                     e2 = e;
140                 }
141 
142                 this.defineConstant(object, e2, constants[e]);
143             }
144         }
145     };
146 
147     jxg.extend(jxg, /** @lends JXG */ {
148         /**
149          * Store a reference to every board in this central list. This will at some point
150          * replace JXG.JSXGraph.boards.
151          * @type Object
152          */
153         boards: {},
154 
155         /**
156          * Store the available file readers in this structure.
157          * @type Object
158          */
159         readers: {},
160 
161         /**
162          * Associative array that keeps track of all constructable elements registered
163          * via {@link JXG.JSXGraph.registerElement}.
164          * @type Object
165          */
166         elements: {},
167 
168         /**
169          * This registers a new construction element to JSXGraph for the construction via the {@link JXG.Board.create}
170          * interface.
171          * @param {String} element The elements name. This is case-insensitive, existing elements with the same name
172          * will be overwritten.
173          * @param {Function} creator A reference to a function taking three parameters: First the board, the element is
174          * to be created on, a parent element array, and an attributes object. See {@link JXG.createPoint} or any other
175          * <tt>JXG.create...</tt> function for an example.
176          */
177         registerElement: function (element, creator) {
178             element = element.toLowerCase();
179             this.elements[element] = creator;
180         },
181 
182         /**
183          * Register a file reader.
184          * @param {function} reader A file reader. This object has to provide two methods: <tt>prepareString()</tt>
185          * and <tt>read()</tt>.
186          * @param {Array} ext
187          */
188         registerReader: function (reader, ext) {
189             var i, e;
190 
191             for (i = 0; i < ext.length; i++) {
192                 e = ext[i].toLowerCase();
193 
194                 if (typeof this.readers[e] !== 'function') {
195                     this.readers[e] = reader;
196                 }
197             }
198         },
199 
200         /**
201          * Creates a shortcut to a method, e.g. {@link JXG.Board#createElement} is a shortcut to {@link JXG.Board#create}.
202          * Sometimes the target is undefined by the time you want to define the shortcut so we need this little helper.
203          * @param {Object} object The object the method we want to create a shortcut for belongs to.
204          * @param {String} fun The method we want to create a shortcut for.
205          * @returns {Function} A function that calls the given method.
206          */
207         shortcut: function (object, fun) {
208             return function () {
209                 return object[fun].apply(this, arguments);
210             };
211         },
212 
213         /**
214          * s may be a string containing the name or id of an element or even a reference
215          * to the element itself. This function returns a reference to the element. Search order: id, name.
216          * @param {JXG.Board} board Reference to the board the element belongs to.
217          * @param {String} s String or reference to a JSXGraph element.
218          * @returns {Object} Reference to the object given in parameter object
219          * @deprecated Use {@link JXG.Board#select}
220          */
221         getRef: function (board, s) {
222             jxg.deprecated('JXG.getRef()', 'Board.select()');
223             return board.select(s);
224         },
225 
226         /**
227          * This is just a shortcut to {@link JXG.getRef}.
228          * @deprecated Use {@link JXG.Board#select}.
229          */
230         getReference: function (board, s) {
231             jxg.deprecated('JXG.getReference()', 'Board.select()');
232             return board.select(s);
233         },
234 
235         /**
236          * s may be the string containing the id of an HTML tag that hosts a JSXGraph board.
237          * This function returns the reference to the board.
238          * @param  {String} s String of an HTML tag that hosts a JSXGraph board
239          * @returns {Object} Reference to the board or null.
240          */
241         getBoardByContainerId: function (s) {
242             var b;
243             for (b in JXG.boards) {
244                 if (JXG.boards.hasOwnProperty(b) &&
245                     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 
351     return jxg;
352 });
353