github.com/secoba/wails/v2@v2.6.4/internal/frontend/runtime/desktop/events.js (about)

     1  /*
     2   _       __      _ __
     3  | |     / /___ _(_) /____
     4  | | /| / / __ `/ / / ___/
     5  | |/ |/ / /_/ / / (__  )
     6  |__/|__/\__,_/_/_/____/
     7  The electron alternative for Go
     8  (c) Lea Anthony 2019-present
     9  */
    10  /* jshint esversion: 6 */
    11  
    12  // Defines a single listener with a maximum number of times to callback
    13  
    14  /**
    15   * The Listener class defines a listener! :-)
    16   *
    17   * @class Listener
    18   */
    19  class Listener {
    20      /**
    21       * Creates an instance of Listener.
    22       * @param {string} eventName
    23       * @param {function} callback
    24       * @param {number} maxCallbacks
    25       * @memberof Listener
    26       */
    27      constructor(eventName, callback, maxCallbacks) {
    28          this.eventName = eventName;
    29          // Default of -1 means infinite
    30          this.maxCallbacks = maxCallbacks || -1;
    31          // Callback invokes the callback with the given data
    32          // Returns true if this listener should be destroyed
    33          this.Callback = (data) => {
    34              callback.apply(null, data);
    35              // If maxCallbacks is infinite, return false (do not destroy)
    36              if (this.maxCallbacks === -1) {
    37                  return false;
    38              }
    39              // Decrement maxCallbacks. Return true if now 0, otherwise false
    40              this.maxCallbacks -= 1;
    41              return this.maxCallbacks === 0;
    42          };
    43      }
    44  }
    45  
    46  export const eventListeners = {};
    47  
    48  /**
    49   * Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
    50   *
    51   * @export
    52   * @param {string} eventName
    53   * @param {function} callback
    54   * @param {number} maxCallbacks
    55   * @returns {function} A function to cancel the listener
    56   */
    57  export function EventsOnMultiple(eventName, callback, maxCallbacks) {
    58      eventListeners[eventName] = eventListeners[eventName] || [];
    59      const thisListener = new Listener(eventName, callback, maxCallbacks);
    60      eventListeners[eventName].push(thisListener);
    61      return () => listenerOff(thisListener);
    62  }
    63  
    64  /**
    65   * Registers an event listener that will be invoked every time the event is emitted
    66   *
    67   * @export
    68   * @param {string} eventName
    69   * @param {function} callback
    70   * @returns {function} A function to cancel the listener
    71   */
    72  export function EventsOn(eventName, callback) {
    73      return EventsOnMultiple(eventName, callback, -1);
    74  }
    75  
    76  /**
    77   * Registers an event listener that will be invoked once then destroyed
    78   *
    79   * @export
    80   * @param {string} eventName
    81   * @param {function} callback
    82   * @returns {function} A function to cancel the listener
    83   */
    84  export function EventsOnce(eventName, callback) {
    85      return EventsOnMultiple(eventName, callback, 1);
    86  }
    87  
    88  function notifyListeners(eventData) {
    89  
    90      // Get the event name
    91      let eventName = eventData.name;
    92  
    93      // Check if we have any listeners for this event
    94      if (eventListeners[eventName]) {
    95  
    96          // Keep a list of listener indexes to destroy
    97          const newEventListenerList = eventListeners[eventName].slice();
    98  
    99          // Iterate listeners
   100          for (let count = eventListeners[eventName].length - 1; count >= 0; count -= 1) {
   101  
   102              // Get next listener
   103              const listener = eventListeners[eventName][count];
   104  
   105              let data = eventData.data;
   106  
   107              // Do the callback
   108              const destroy = listener.Callback(data);
   109              if (destroy) {
   110                  // if the listener indicated to destroy itself, add it to the destroy list
   111                  newEventListenerList.splice(count, 1);
   112              }
   113          }
   114  
   115          // Update callbacks with new list of listeners
   116          if (newEventListenerList.length === 0) {
   117              removeListener(eventName);
   118          } else {
   119              eventListeners[eventName] = newEventListenerList;
   120          }
   121      }
   122  }
   123  
   124  /**
   125   * Notify informs frontend listeners that an event was emitted with the given data
   126   *
   127   * @export
   128   * @param {string} notifyMessage - encoded notification message
   129  
   130   */
   131  export function EventsNotify(notifyMessage) {
   132      // Parse the message
   133      let message;
   134      try {
   135          message = JSON.parse(notifyMessage);
   136      } catch (e) {
   137          const error = 'Invalid JSON passed to Notify: ' + notifyMessage;
   138          throw new Error(error);
   139      }
   140      notifyListeners(message);
   141  }
   142  
   143  /**
   144   * Emit an event with the given name and data
   145   *
   146   * @export
   147   * @param {string} eventName
   148   */
   149  export function EventsEmit(eventName) {
   150  
   151      const payload = {
   152          name: eventName,
   153          data: [].slice.apply(arguments).slice(1),
   154      };
   155  
   156      // Notify JS listeners
   157      notifyListeners(payload);
   158  
   159      // Notify Go listeners
   160      window.WailsInvoke('EE' + JSON.stringify(payload));
   161  }
   162  
   163  function removeListener(eventName) {
   164      // Remove local listeners
   165      delete eventListeners[eventName];
   166  
   167      // Notify Go listeners
   168      window.WailsInvoke('EX' + eventName);
   169  }
   170  
   171  /**
   172   * Off unregisters a listener previously registered with On,
   173   * optionally multiple listeneres can be unregistered via `additionalEventNames`
   174   *
   175   * @param {string} eventName
   176   * @param  {...string} additionalEventNames
   177   */
   178  export function EventsOff(eventName, ...additionalEventNames) {
   179      removeListener(eventName)
   180  
   181      if (additionalEventNames.length > 0) {
   182          additionalEventNames.forEach(eventName => {
   183              removeListener(eventName)
   184          })
   185      }
   186  }
   187  
   188  /**
   189   * Off unregisters all event listeners previously registered with On
   190   */
   191   export function EventsOffAll() {
   192      const eventNames = Object.keys(eventListeners);
   193      for (let i = 0; i !== eventNames.length; i++) {
   194          removeListener(eventNames[i]);
   195      }
   196  }
   197  
   198  /**
   199   * listenerOff unregisters a listener previously registered with EventsOn
   200   *
   201   * @param {Listener} listener
   202   */
   203   function listenerOff(listener) {
   204      const eventName = listener.eventName;
   205      // Remove local listener
   206      eventListeners[eventName] = eventListeners[eventName].filter(l => l !== listener);
   207  
   208      // Clean up if there are no event listeners left
   209      if (eventListeners[eventName].length === 0) {
   210          removeListener(eventName);
   211      }
   212  }