1 /*
  2     Copyright 2008-2023
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Bianca Valentin,
  7         Alfred Wassermann,
  8         Peter Wilfahrt
  9 
 10     This file is part of JSXGraph.
 11 
 12     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
 13 
 14     You can redistribute it and/or modify it under the terms of the
 15 
 16       * GNU Lesser General Public License as published by
 17         the Free Software Foundation, either version 3 of the License, or
 18         (at your option) any later version
 19       OR
 20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
 21 
 22     JSXGraph is distributed in the hope that it will be useful,
 23     but WITHOUT ANY WARRANTY; without even the implied warranty of
 24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 25     GNU Lesser General Public License for more details.
 26 
 27     You should have received a copy of the GNU Lesser General Public License and
 28     the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/>
 29     and <https://opensource.org/licenses/MIT/>.
 30  */
 31 
 32 /*global JXG: true, define: true*/
 33 /*jslint nomen: true, plusplus: true*/
 34 
 35 /**
 36  * @fileoverview In this file the EventEmitter interface is defined.
 37  */
 38 
 39 import JXG from "../jxg";
 40 import Type from "./type";
 41 
 42 /**
 43  * Event namespace
 44  * @namespace
 45  */
 46 JXG.EventEmitter = {
 47     /**
 48      * Holds the registered event handlers.
 49      * @type Object
 50      */
 51     eventHandlers: {},
 52 
 53     /**
 54      * Events can be suspended to prevent endless loops.
 55      * @type Object
 56      */
 57     suspended: {},
 58 
 59     /**
 60      * Triggers all event handlers of this element for a given event.
 61      * @param {Array} event
 62      * @param {Array} args The arguments passed onto the event handler
 63      * @returns Reference to the object.
 64      */
 65     trigger: function (event, args) {
 66         var i, j, h, evt, len1, len2;
 67 
 68         len1 = event.length;
 69         for (j = 0; j < len1; j++) {
 70             evt = this.eventHandlers[event[j]];
 71             if (!this.suspended[event[j]]) {
 72                 this.suspended[event[j]] = true;
 73                 if (evt) {
 74                     len2 = evt.length;
 75                     for (i = 0; i < len2; i++) {
 76                         h = evt[i];
 77                         h.handler.apply(h.context, args);
 78                     }
 79                 }
 80 
 81                 this.suspended[event[j]] = false;
 82             }
 83         }
 84 
 85         return this;
 86     },
 87 
 88     /**
 89      * Register a new event handler. For a list of possible events see documentation
 90      * of the elements and objects implementing
 91      * the {@link EventEmitter} interface.
 92      *
 93      * As of version 1.5.0, it is only possible to access the element via "this" if the event listener
 94      * is supplied as regular JavaScript function and not as arrow function.
 95      *
 96      * @param {String} event
 97      * @param {Function} handler
 98      * @param {Object} [context] The context the handler will be called in, default is the element itself.
 99      * @returns Reference to the object.
100      */
101     on: function (event, handler, context) {
102         if (!Type.isArray(this.eventHandlers[event])) {
103             this.eventHandlers[event] = [];
104         }
105 
106         context = Type.def(context, this);
107 
108         this.eventHandlers[event].push({
109             handler: handler,
110             context: context
111         });
112 
113         return this;
114     },
115 
116     /**
117      * Unregister an event handler.
118      * @param {String} event
119      * @param {Function} [handler]
120      * @returns Reference to the object.
121      */
122     off: function (event, handler) {
123         var i;
124 
125         if (!event || !Type.isArray(this.eventHandlers[event])) {
126             return this;
127         }
128 
129         if (handler) {
130             i = Type.indexOf(this.eventHandlers[event], handler, "handler");
131             if (i > -1) {
132                 this.eventHandlers[event].splice(i, 1);
133             }
134 
135             if (this.eventHandlers[event].length === 0) {
136                 delete this.eventHandlers[event];
137             }
138         } else {
139             delete this.eventHandlers[event];
140         }
141 
142         return this;
143     },
144 
145     /**
146      * @description Implements the functionality from this interface in the given object.
147      * All objects getting their event handling
148      * capabilities from this method should document it by adding
149      * the <tt>on, off, triggerEventHandlers</tt> via the
150      * borrows tag as methods to their documentation:
151      * <pre>@borrows JXG.EventEmitter#on as this.on</pre>
152      * @param {Object} o
153      */
154     eventify: function (o) {
155         o.eventHandlers = {
156             clicks: 0 // Needed to handle dblclicks
157         };
158         o.on = this.on;
159         o.off = this.off;
160         o.triggerEventHandlers = this.trigger;
161         o.trigger = this.trigger;
162         o.suspended = {};
163     }
164 };
165 
166 export default JXG.EventEmitter;
167