Skip to main content
Inspiring
August 24, 2015
Answered

How should I handle multiple event listeners in my HTML5 extension?

  • August 24, 2015
  • 2 replies
  • 3051 views

Hi all,

I want to listen for make ("Mk  ") events and duplicate ("Dplc") events, each using a different handler. Currently both event handlers are triggered when either event is received.

The relevant javascript code is:

var make_e = new CSEvent(

  'com.adobe.PhotoshopRegisterEvent',

  'APPLICATION',

  csInterface.getApplicationID(),

  csInterface.getExtensionID()

);

make_e.data = 1298866208; // stringIDToTypeID('make')

csInterface.dispatchEvent(make_e);

csInterface.addEventListener( 'PhotoshopCallback', make_callback );

var dplc_e = new CSEvent(

  'com.adobe.PhotoshopRegisterEvent',

  'APPLICATION',

  csInterface.getApplicationID(),

  csInterface.getExtensionID()

);

dplc_e.data = 1148218467; // stringIDToTypeID('duplicate')

csInterface.dispatchEvent(dplc_e);

csInterface.addEventListener( 'PhotoshopCallback', dplc_callback );

function make_callback(e){ console.log('make'); }

function make_callback(e){ console.log('duplicate'); }

When either event is received the console logs the output of both handlers. In the below example I first created a new layer then duplicated a layer.

make c_LayerPanelSection.js:90

duplicate c_LayerPanelSection.js:94

make c_LayerPanelSection.js:90

duplicate c_LayerPanelSection.js:94

This topic has been closed for replies.
Correct answer clunty

Answering my own question again

I've written this class based on a similar one from com.adobe.webpa.crema.

You can use it by registering events with eventDelegate.addEventListener

var PhotoshopCallback = function(e) {

  var callback;

  try {

  if( e.data ) {

  //  + before variable uses numerical representation of the variable

  eventDelegate.invoke( +e.data.split(',')[0], e );

  }

  } catch(err) {

  console.log( 'PhotoshopCallback Error : ' + err );

  }

}

function EventDelegate() {

  this._events = [];

}

EventDelegate.prototype.invoke = function( eventType, e ) {

  var callback = this._events[eventType];

  if( callback ) {

  callback(e);

  }

}

EventDelegate.prototype.__registerPSEvent = function( typeID, clear ) {

  var eventEnding = null;

  if( clear ) {

  console.log( 'Unregistering ' + typeID );

  eventEnding = 'PhotoshopUnRegisterEvent';

  csInterface.removeEventListener("PhotoshopCallback", PhotoshopCallback);

  } else {

  console.log( 'Registering ' + typeID );

  eventEnding = 'PhotoshopRegisterEvent';

  csInterface.addEventListener("PhotoshopCallback", PhotoshopCallback);

  }

    var e = new CSEvent(

    "com.adobe." + eventEnding,

    "APPLICATION",

    csInterface.getApplicationID(),

    csInterface.getExtensionID()

    );

    // e.data = Object.keys(this._events).join(", ");

    e.data = typeID;

    csInterface.dispatchEvent(e);

}

EventDelegate.prototype.addEventListener = function( typeStrings, callback ) {

  csInterface.evalScript( "s2t('"+typeStrings+"')", function(r){

  this._events[+r.split(',')[0]] = callback;

  this.__registerPSEvent( r, true );

  this.__registerPSEvent( r );

  }.bind(this) );

}

EventDelegate.prototype.removeEventListener = function( typeStrings, callback ) {

  csInterface.evalScript( "s2t('"+typeStrings+"')", function(r){

  this._events[+r.split(',')[0]] = null;

  }.bind(this) );

}

var eventDelegate = new EventDelegate();

// module.exports = eventDelegate;

2 replies

cluntyAuthorCorrect answer
Inspiring
August 27, 2015

Answering my own question again

I've written this class based on a similar one from com.adobe.webpa.crema.

You can use it by registering events with eventDelegate.addEventListener

var PhotoshopCallback = function(e) {

  var callback;

  try {

  if( e.data ) {

  //  + before variable uses numerical representation of the variable

  eventDelegate.invoke( +e.data.split(',')[0], e );

  }

  } catch(err) {

  console.log( 'PhotoshopCallback Error : ' + err );

  }

}

function EventDelegate() {

  this._events = [];

}

EventDelegate.prototype.invoke = function( eventType, e ) {

  var callback = this._events[eventType];

  if( callback ) {

  callback(e);

  }

}

EventDelegate.prototype.__registerPSEvent = function( typeID, clear ) {

  var eventEnding = null;

  if( clear ) {

  console.log( 'Unregistering ' + typeID );

  eventEnding = 'PhotoshopUnRegisterEvent';

  csInterface.removeEventListener("PhotoshopCallback", PhotoshopCallback);

  } else {

  console.log( 'Registering ' + typeID );

  eventEnding = 'PhotoshopRegisterEvent';

  csInterface.addEventListener("PhotoshopCallback", PhotoshopCallback);

  }

    var e = new CSEvent(

    "com.adobe." + eventEnding,

    "APPLICATION",

    csInterface.getApplicationID(),

    csInterface.getExtensionID()

    );

    // e.data = Object.keys(this._events).join(", ");

    e.data = typeID;

    csInterface.dispatchEvent(e);

}

EventDelegate.prototype.addEventListener = function( typeStrings, callback ) {

  csInterface.evalScript( "s2t('"+typeStrings+"')", function(r){

  this._events[+r.split(',')[0]] = callback;

  this.__registerPSEvent( r, true );

  this.__registerPSEvent( r );

  }.bind(this) );

}

EventDelegate.prototype.removeEventListener = function( typeStrings, callback ) {

  csInterface.evalScript( "s2t('"+typeStrings+"')", function(r){

  this._events[+r.split(',')[0]] = null;

  }.bind(this) );

}

var eventDelegate = new EventDelegate();

// module.exports = eventDelegate;

DBarranca
Legend
August 28, 2015

Hi,

thanks for sharing this!

I've checked the original photoshopDelegate (the one in the Adobe's panel) and I'm not sure what the event.delegate is (line 65..75)

PhotoshopDelegate.prototype.addEvents = function(event) {

   /* if(!_.isArray(events)) {

        events = [events];

    }

    */

   csInterface.evalScript( "app.stringIDToTypeID('"+ event.name + "');",

        function(result) {

            this._eventMap[+result] = event.delegate;

            this._registerEvent();

        }.bind(this) );

};

It looks like they use addEvents passing an array of event objects in which the event.name is the typeID and the event.delegate is the callback - am I right?

---

UPDATE

Besides,

I've seen your removeEventListener, which I append below as a handy reference:

EventDelegate.prototype.removeEventListener = function( typeStrings, callback ) {

    csInterface.evalScript( "s2t('"+typeStrings+"')", function(r){

    this._events[+r.split(',')[0]] = null;

    }.bind(this) );

}

I have a couple of questions for you, if I may ask:

1. Is there a reason not to include a this.__registerPSEvent( r, true ); in there too? Basically in your code when the removeEventListener is called, the com.adobe.photoshopUnRegisterEvent is not actually dispatched - it's just removed the callback in the this._events array. So you don't remove the listener, just set the callback as null.

2. It looks like your s2t accepts / returns an array, since the param is typeStrings (plural) and you r.split(',')[0] - but you hardwire the first returned value so I guess you plan to use removeEventListener for a single event - am I correct?

---

Thank you,

Davide Barranca

---

www.davidebarranca.com

www.cs-extensions.com

cluntyAuthor
Inspiring
August 29, 2015

Hey,

As seen in LayerCollectionView line 58, event.delegate is the callback of the event.


photoshopDelegate.addEvents({name: "extractAssets", delegate: this.addSettingsToSelected.bind(this)});

I also found this, along with much of this project, confusing. It looks as though there was plans to be able to add more than one event listener at a time but due to the commented out section checking whether the paramo is an array or not PhotoshopDelegate.addEvents currently only accepts one event.

Much of what is confusing about this project is the names given to variables and functions. In this example, the event param is not actually an event object but an object describing the event listener; name is the event type ('make' would be a simple example) and delegate is the callback.

To answer your questions:

Is there a reason not to include a this.__registerPSEvent( r, true );

I don't unregister the photoshop event in removeEventListener because it is possible (and perhaps likely) that multiple callbacks will be triggered by one event type. For safety I just remove the callback so it won't be triggered but any others listening for the same event will be safe.

It looks like your s2t accepts / returns an array, since the param is typeStrings (plural) and you r.split(',')[0] - but you hardwire the first returned value so I guess you plan to use removeEventListener for a single event - am I correct?

For clarity, below is the s2t function in extendscript:

function s2t() {

  if( Object.prototype.toString.call( arguments[0] ) === '[object Array]' ) {

  log( 's2t apply' );

  return s2t.apply( null, arguments[0] );

  }

  var r = new Array();

  for( var i=0; i<arguments.length; i++) {

  var arg = arguments;

  r.push( stringIDToTypeID( arg ) );

  }

  return r.toString();

}

I use this function in other situations in which I want to convert multiple strings to type numbers so yes it returns a list. As you can see it can accept both a single string or an array of strings. It will always return an array though so in the addEventListener method I use the first item in the returned list. addEventListener could obviously be extended to add more than one event at once but I think for the sake of readability it's best to add each event one at a time.

This class was knocked up fairly quickly to get over this particular hurdle so by all means contribute any improvements you can see. It's working well enough for me at the moment but I expect it could be further generalised or improved. I plan to add some checks for the class of the object that triggered the event (see one of my other questions for an explanation: How can I find the target/result of a photoshop event? )

Hope this answers your questions

All the best,

Tom

cluntyAuthor
Inspiring
August 24, 2015

I've found a good solution in the Library extension (com.adobe.webpa.crema).

It's in the system level extensions dir (/Library/Application Support/Adobe/CEP/extensions/com.adobe.webpa.crema on OS X).

The file to look at is:

com.adobe.webpa.crema/PSPanel/js/PhotoshopDelegate/index.js

This implements a delegate system which will lookup the callback based on the event type and execute it.

Once I've figured out how to use this effectively I'll try to pose something here.