Plugincc
Toolbox for building Adobe CC extensions with CEP
Install / Use
/learn @bigarobas/PluginccREADME
Toolbox for building Adobe CC extensions with CEP
It probably needs a better name ^^
Purpose :
- minimize code duplication in both contexts (JS / JSX)
- ease communication between both contexts (JS / JSX)
- bring independent tools as well as a full framework and workflow based on those tools.
- no dependencies (except JSON and of course CEP)
To have a better understanding of what is at stake here I recommand you to read this wiki page about "mixed contexts" : Mixed context in Adobe CC extensions with CEP
JSXBridge
This is the central module around which this toolset is made. When creating a JSXBridge for an Object this object is auto implemented with the following methodes :
- mirror(function_name,function_args,callback_or_expression)
- bridgeCall(client_id,function_name,function_args,callback_or_expression,scope)
- getContext()
- checkContext(ctx)
- listen(type,handler)
- dispatch(type,data,scope)
These methodes allow us to :
- easy mirroring methodes on both sides (JS/JSX)
//Let's say you have 2 files one on JS and the other on JSX context with code on global level :
//on JS side
a = new ClassA();
a.log("HELLO FROM JS");
function ClassA () {
this.bridge = new JSXBridge(this,"BRIDGE_ID_X");
this.log = function(message) {
//console is defined is JS context so we can use it
console.log(message);
}
}
//on JSX side
b = new ClassB();
b.log("HELLO FROM JSX");
function ClassB () {
this.bridge = new JSXBridge(this,"BRIDGE_ID_X");
this.log = function(message) {
//console is NOT defined is JSX context so we mirror the action to the JS side
this.mirror('log',message);
}
}
//RESULT in chrome console (JS context) :
> HELLO FROM JS
> HELLO FROM JSX
- MIXED CONTEXT : you can also do the same thing with only 1 file (loaded both on JS and JSX context)
var a = new ClassA();
a.log();
function ClassA () {
this.bridge = new JSXBridge(this,"BRIDGE_ID_X");
this.log("HELLO FROM "+this.getContext());
}
ClassA.prototype.log = function (message) {
if (this.checkContext("js") {
//console is defined is JS context so we can use it
console.log(message);
} else {
//console is NOT defined is JSX context so we mirror the action to the JS side
this.mirror('log',message);
}
}
//RESULT in chrome console (JS context) :
> HELLO FROM js
> HELLO FROM jsx
- it's also possible to easyly call methodes on both context using bridge names with 5 different scopes :
- JS (only on JS context)
- JSX (only on JSX context)
- CURRENT (only in the CURRENT context (JS or JSX))
- MIRROR (only in the MIRROR ("opposite") context (JS or JSX))
- BOTH (in BOTH contexts (JS and JSX))
//on JS side
var a = new ClassA();
function ClassA () {
this.bridge = new JSXBridge(this,"BRIDGE_ID_X");
this.doSomething = function() {
//DOES SOMETHING
}
}
//on JSX side
var b = new ClassB();
function ClassB () {
this.bridge = new JSXBridge(this,"BRIDGE_ID_X");
this.doSomething = function() {
//DOES SOMETHING ELSE
}
}
//SOME WHERE IN JS OR JSX CONTEXT
var c = new ClassC();
function ClassC () {
this.bridge = new JSXBridge(this,"ANOTHER_BRIDGE_ID");
this.bridgeCall("BRIDGE_ID_X","doSomething",{some_args_if_needed},callback_function_or_expression,"both");
}
//RESULT
> c calls methode "doSomething" on "BRIDGE_ID_X" bridge (linked to a in JS conetxt and b in JSX context) with scope = "both".
> which means that both "doSomething" methodes from both context (a in JS and b in JSX) are called :
> a DOES SOMETHING in JS context
> b DOES SOMETHING ELSE in JSX context
- the mirror methode takes a callback_or_expression argument depending on the context it's called on :
- a callback function if it's called from JS context (that will be called with the mirror JSX function return value).
- a callback expression if it's called from JSX context (that will be evaluated with the JS function return value). This expression contain key_words ({bridge} and {args}) which will dynamically be replaced before the expression is evaluated.
// imagine we want to synch a MIXED OBJECT (1 jsx file loaded in both contexts JS & JSX)
// we need to push the update to the other context and retrieve the new updated state to synch back with the first object
//
this.synch = function(onComplete) {
this.onSynchComplete = onComplete;
var _self = this;
if (this.checkContext("jsx")) {
this.mirror(
'update',
this.data,
'(function() {\
{bridge}.update({args});\
{bridge}.onSynchComplete({args});\
})();'
);
} else {
this.mirror(
'update',
this.data,
function(json) {
_self.update(json);
_self.onSynchComplete(json);
}
);
}
}
this.update(data) {
//update with data
//return new state
}
- easy communication between objects in both contexts with a custom Observer pattern that let you dispatch custom JSXBridgeEvents with 5 different scopes :
- JS (only JSXBridge objects on JS context can receive the event)
- JSX (only JSXBridge objects on JSX context can receive the event)
- SAME (only JSXBridge objects in the SAME context (JS or JSX) can receive the event)
- MIRROR (only JSXBridge objects in the MIRROR ("opposite") context (JS or JSX) can receive the event)
- BOTH (JSXBridge objects in BOTH contexts (JS and JSX) can receive the event)
//IMPORTING THE MODULE
// JS SIDE
Configuration = require(__EXTENTION_PATH__ + "/CORE/mixed/JSXBridge.jsx");
// JSX SIDE
$.evalFile(__EXTENTION_PATH__ + "/CORE/mixed/JSXBridge.jsx");
// IN AN OBJECT IN JS CONTEXT
var _bridge = new JSXBridge(this,"SOME_BRIDGE_ID");
this.listen("a_custom_event_type",function(event) {(...)});
this.dispatch("a_custom_event_type",some_data_object,"mirror");
// IN AN OBJECT IN JSX CONTEXT
var _bridge = new JSXBridge(this,"SAME_OR_OTHER_BRIDGE_ID");
this.listen("a_custom_event_type",function(event) {(...)});
this.dispatch("a_custom_event_type",some_data_object,"both")
/*
# RESULT ON JSX SIDE :
The JSX bridge will receive 2 events with "a_custom_event_type".
1 from its own dispatch because it was on "both" scope.
1 from JS context's dispatch because it was on "mirror" scope = the opposite context of JS = JSX.
In this case the mirror (opposite) of JS is of course JSX.
# RESULT ON JS SIDE :
The JS bridge will receive only 1 event with "a_custom_event_type".
0 from its own dispatch because it was on "mirror" scope = the opposite context of it's own = JSX.
1 from JSX context's dispatch because it was on "both" scope.
*/
Configuration
- Mixed Configuration object synched and available in both contexts (JS ad JSX)
//IMPORTING THE MODULE
// JS SIDE
Configuration = require(__EXTENTION_PATH__ + "/CORE/mixed/Configuration.jsx");
// JSX SIDE
$.evalFile(__EXTENTION_PATH__ + "/CORE/mixed/Configuration.jsx");
// ON BOTH SIDES
CONFIG = new Configuration("CONFIG");
CONFIG.update(json); // update with some json. existing keys are updated / non existing keys are created
CONFIG.set("some_key",some_value); // set a value to a key
CONFIG.get("some_key"); // get a value of a key
CONFIG.synch(); // synch the config with the other context
synch() is the most interresting part :
It synchronizes the config with the other context (JSX if you are in JS and JS if you're in JSX).
For now the synch is a "push prioritised". This means that the values are pushed to the the other side. Existing keys are updated / non existing keys are created. Then key/values are pulled to synch. Existing keys are updated (which should not happen) / non existing keys are created.
We might have the other option "pull prioritised" too in the future. This means the key/values are pulled from the other context. Existing keys are updated / non existing keys are created. Then key/values are pushed to synch. Existing keys are updated (which should not happen) / non existing keys are created.
Debugger
- Mixed Multichanel Debugger available in both contexts (JS ad JSX)
// IMPORTING THE MODULE
// JS SIDE
Debugger = require(__EXTENTION_PATH__ + "/CORE/mixed/Debugger.jsx");
// JSX SIDE
$.evalFile(__EXTENTION_PATH__ + "/CORE/mixed/Debugger.jsx");
// USING THE MODULE (ON BOTH SIDE)
DEBUG = new Debugger();
DEBUG.log("Hello World");
- create channels and sub channels :
DEBUG.channel("main_channel_branch_name").channel("sub_channel_branch_name").log("Hello World");
- manage / costumise channels :
DEBUG.channel("main_channel_branch_name").mute(isCompletelyMuted);
DEBUG.channel("main_channel_branch_name").setVerbose(isWriteMuted,isAlertMutedn,isChannelIdPrefixDisplayed);
DEBUG.channel("main_channel_branch_name").setSeparator("-._.-._.-._.-._.-._.-._.-._.-._.-._.-");
- supporting multiple write & alert methodes :
- write(message)
DEBUG.channel("some_channel_name").write("Hello World"); // JSX : JSX : mirrored to chrome.console.log instead of $.write / JS : chrome.console.log- writeln(message)
DEBUG.channel("some_channel_name").writeln("Hello World"); // JSX : mirrored to chrome.console.log instead of $.writeln / JS : chrome.console.log- log(message)
DEBUG.channel("some_channel_name").log("Hello World"); // JSX : mirrored to chrome.console.log instead of $.writeln / JS : chrome.console.log // the same than .writeln()- json(someObject)
DEBUG.channel("some_channel_name").json(someObject); // .writeln() + JSON.stringify()- popup(message)
DEBUG.channel("some_channel_name").popup(message); // application alert
