Source: communicator.js

if (typeof yasp == 'undefined') yasp = { };

(function() {
  var debug = false;

  /**
   * Responsible for communicating with webworkers.
   * Additional documentation can be found in the {@link https://github.com/yasp/yasp/blob/master/doc/communicator.md|GitHub repository}.
   * @param path {String} is the path to the specified file
   * @constructor
   */
  yasp.Communicator = function(path) {
    if (!window.Worker) throw "Worker Unsupported";
    
    this.worker = new Worker(path);
    this.listener = { };
    this.id = 0;
    this.openMessages = { };

    var lastSlash = path.lastIndexOf("/") + 1;
    var filename = path.substring(lastSlash);

    this.worker.addEventListener("message", (function(event) {
      var data = event.data;
      if (!!data.id) {
        if (!this.openMessages[data.id]) {
          throw "Message with ID "+data.id+" does not exist.";
        } else {
          // call
          if (!!this.openMessages[data.id].callback) this.openMessages[data.id].callback(data);
          // delete
          delete this.openMessages[data.id];
        }
      } else if (data.action == 'internal_error') {
        throw "Error ("+data.payload.code+") "+data.payload.msg;
      } else if (data.action == 'internal_log') {
        if(debug) console.log("[" + filename + "] Communicator Log: "+data.payload);
      } else {
        // broadcast
        var events = this.listener[data.action];
        if (!!events) { // check if any subscriber
          for (var i = 0; i < events.length; i++) {
            events[i](data);
          }
        }
      }
    }).bind(this), false);
  };

  /**
   * Sends a message to the Worker
   * @param action {String}
   * @param payload {object}
   * @param cb {function}
   */
  yasp.Communicator.prototype.sendMessage = function(action, payload, cb) {
    var id = ++this.id;
    this.worker.postMessage({
      action: action.toUpperCase(),
      id: id,
      payload: payload
    });
    
    this.openMessages[id] = {
      callback: cb,
      action: action,
      originalPayload: payload
    };
  };

  /**
   * Starts listening to broadcast events
   * @param event {string}
   * @param cb {function}
   */
  yasp.Communicator.prototype.subscribe = function(event, cb) {
    event = event.toUpperCase();
    if (!this.listener[event]) this.listener[event] = [ ];
    this.listener[event].push(cb);
  };

  /**
   * Quits listening to Broadcast events
   * @param event {string}
   * @param cb {function}
   */
  yasp.Communicator.prototype.unsubscribe = function(event, cb) {
    if (!!this.listener[event]) {
      var data = this.listener[event];
      for (var i = 0; i < data.length; i++) {
        if (data[i] == cb) {
          data.splice(i, 1);
          break;
        }
      }
    }
  };

  /**
   * Terminates the worker and shuts down communication
   */
  yasp.Communicator.prototype.terminate = function() {
    this.worker.terminate();
  }
  
  yasp.Communicator.UNKNOWN_ACTION = {
    payload: null,
    error: {
      code: 0,
      msg: "Unknown action"
    }
  }

  /**
   * Responsible in the Web Worker for communicating with the client
   * @param self The self variable which is set in the web worker environment
   * @param listener The listener has to call "ready" if the results are available
   * @constructor
   */
  yasp.CommunicatorBackend = function(self, listener) {
    console = {
      log: function(msg) {
        self.postMessage({
          action: "internal_log",
          payload: msg
        });
      }
    };
    
    listener = listener.bind(this);

    /**
     * Sends a broadcast to all registered listeners
     * @param action {String}
     * @param result {object}
     * @see Communicator#subscribe
     * @see Communicator#unsubscribe
     */
    this.broadcast = function(action, result) {
      console.log("Send broadcast: " + action + " result " + JSON.stringify(result));
      self.postMessage({
        action: action,
        id: null,
        error: result.error,
        payload: result.payload
      });
    };
    
    self.addEventListener('message', function(e) {
      e = e.data;
      console.log("Receive message: " + e.action+" payload " + JSON.stringify(e.payload));
      var ready = function(result) {
        console.log("Send response: " + e.action+" result " + JSON.stringify(result));
        self.postMessage({
          action: e.action,
          id: e.id,
          error: result.error,
          payload: result.payload
        });
      };
      listener(e, ready);
    });
    self.addEventListener('error', function(e) {
      self.postMessage({
        action: 'internal_error',
        payload: {
          code: 0,
          msg: "Worker error " + e.message + " in line " + e.lineno + " in file " + e.filename + "\n"
        }
      });
    });
  }
})();