Minimalistic example of IPython kernel javascript bi-directional communication - javascript

My goal is to write an interactive variable viewer for IPython, i.e. which allows one to see, say nested dicts/lists as a tree, and drill down (a bit like the console.log in Javascript).
I spent a lot of time trying to extract minimalistic code out of the directview example in IPython but still can't get my head around how it all works. My Python's ok but my jQuery experience is quite limited.
So I got to stripping down directview.js to the following
container.show();
var widget = $('<div/>')
element.append(widget);
var output = $('<div></div>');
$(widget).append(output)
var output_area = new IPython.OutputArea(output, false);
var callbacks = { 'output': $.proxy(output_area.handle_output, output_area) };
var code = 'print 1+1'
var msg_id = IPython.notebook.kernel.execute(code, callbacks, {silent: false});
This works when I load the directview.ipynb. However I am still not clear how to make it completely standalone (i.e. not require the directview.py, and pass another callback than the standard handle_output of IPython.OutputArea). One of the issues is the container.show() which fails when called in a standalone way.
I am lost on several aspects:
Why is there an element.append(widget) and then $(widget).append(output)? Also why is there also a need to create an Ipython.OutputArea. Isn't there a way to just create a <div id=my_output_area>blah</div> and then have the output callback fill it with the relevant data?
What's the whole .proxy thing about? I would like to create my own callback for output but when I do that and console.log() the arguments passed to the callback, they're undefined or just useless.
I appreciate that the authors of the IPython notebook have done an incredible job creating such a beautiful front-end using jQuery/websockets, and that creating developer documentation that allows beginners like me to tweak with it is asking much, but if anyone can lend a hand that would be great!

I can answer to your second question. The fact is when JavaScript calls your callback, it makes it so without specifying the context, i.e. without setting this (Pythonistas call it self). But it's possible to bound a function to this via $.proxy, which you saw in:
var callbacks = { 'output': $.proxy(output_area.handle_output, output_area) };

Related

How to pass a json object from a Python function to Javascript with Eel where you can use and manipulate the json

This question may be a bit confusing, so let me give you some background. Eel is a Python module where you can take functions made in Python and use them in Javascript, and vice versa. What I want to do is take a json made from a Python function, put it in Javascript, and make a table based on the json that was taken from the Python side. Here's an example.
python.py
def json_example():
json = [
{
"key": "value1"
},
{
"key": "value2"
}
]
return json
js.html
<body>
<div></div>
</body>
<script>
function js_example() {
# This is where the function from Python is called
var json_obj = eel.json_example();
var tbl = $("<table/>").attr("id", "example_table");
$("div").append(tbl);
for(var i=0; i<json_obj.length; i++){
var tr="<tr>";
var td="<td>"+obj[i]["key"]+"</td></tr>";
$('#example_table').append(tr+td);
}
}
I tested both of these functions out separately with a few changes and they both work. However, here's where the problem starts. For some reason, the Javascript part is not getting anything from the function it calls from the Python code. the variable "json_obj" should be equal to the json I made in the Python function, but for some reason the return value of the function isn't creating tangible data that can be manipulated in the Javascript, basically returning nothing. And the eel transfer itself works as well. If you replace "return" with "print", it will print the json in the console.
Also, please don't tell me to just put the json itself in the Javascript. I have a reason for needing the json to come from the Python side.
So basically, here's my question: how do you get a Python function to create a value that can be manipulated in Javascript?
The problem is that when eel exposes a function what it actually does is it creates a new function that will return a promise containing the return value of your python function.
So you should have something like this instead:
let json_obj = '';
eel.json_example()(x => json_obj = x);
If you need more help on callbacks, refer to https://github.com/ChrisKnott/Eel.
Convert to json within Python if you're calling Python to begin with and send json to JS in the return value.
See: https://github.com/ChrisKnott/Eel/tree/master/examples/03%20-%20sync_callbacks
To do a synchronous operation that will take time to complete in Python and then return a value into JS, use;
let n = await eel.py_random()();
which is really
let mySlowReturnValueFromPython = await eel.myExposedPythonFunction()();
In fact I tried to code in my own promises and I was getting back garbage that looked like eel promises. The browser maintains a thread while you call this way, so the user can kick off a long Python operation and still interact with the GUI.
I'd note that you can still call things that update the GUI asynchronously. If you have a bunch of Python functions which return ready-made HTML as I do, then you can kick them all off in a row without waiting and they will update the divs whenever they return. I use this;
eel.expose(updateDiv);
function updateDiv(newData, divToUpdate)
{
var fieldToUpdate = document.getElementById(divToUpdate)
fieldToUpdate.innerHTML = newData
}
Then I call my Python function which gets the data synchronously, packs it up into a ready-made HTML chunk for the GUI, and then calls updateDiv from Python. I'm actually really enjoying the power that this "ping pong" interaction between a synchronous codebase and an asynchronous one give me when working with a GUI. Worlds better than arfing about with TK.
I hope this helps you and you can struggle less with it than I did. Once you understand how this works, Eel is really great. It apparently handles sync for you, just hand it a blank callback (or whatever black magic that is). What a great lib! Eel is just perfect for locally hosted GUI's. I'd like to see a better GUI framework than HTML/CSS/JS - the truth is that there really isn't one, those things are so well tested and stable, with so many available examples for whatever you could want to create.
I'd really like to see this become the native Python GUI solution. Browsers are extremely cross-platform and the only problem to solve when porting becomes interfacing the browser to Python.

How to re-evaluate a script that doesn't expose any global in a declarative-style component

I have been writing a reusable script, let's call it a plugin although it's not jQuery, that can be initialised in a declarative way from the HTML. I have extremely simplified it to explain my question so let's say that if a user inserts a tag like:
<span data-color="green"></span>
the script will fire because the attribute data-color is found, changing the color accordingly.
This approach proved very handy because it avoids anyone using the plugin having to initialise it imperatively in their own scripts with something like:
var elem = document.getElementsByTagName('span')[0];
myPlugin.init(elem);
Moreover by going the declarative way I could get away without defining any global (in this case myPlugin), which seemed to be a nice side effect.
I simplified this situation in an example fiddle here, and as you can see a user can avoid writing any js, leaving the configuration to the HTML.
Current situation
The plugin is wrapped in a closure like so:
;(function(){
var changeColor = {
init : function(elem){
var bg = elem.getAttribute('data-color');
elem.style.background = bg;
}
};
// the plugin itslef looks for appropriate HTML elements
var elem = document.querySelectorAll('[data-color]')[0];
// it inits itself as soon as it is evaluated at page load
changeColor.init(elem);
})();
The page loads and the span gets the correct colour, so everything is fine.
The problem
What has come up lately, though, is the need to let the user re-evaluate/re-init the plugin when he needs to.
Let's say that in the first example the HTML is changed dynamically after the page is loaded, becoming:
<span data-color="purple"></span>
With the first fiddle there's no way to re-init the plugin, so I am now testing some solutions.
Possible solutions
Exposing a global
The most obvious is exposing a global. If we go this route the fiddle becomes
http://jsfiddle.net/gleezer/089om9z5/4/
where the only real difference is removing the selection of the element, leaving it to the user:
// we remove this line
// var elem = document.querySelectorAll('[data-color]')[0];
and adding something like (again, i am simplifying for the sake of the question):
window.changeColor = changeColor;
to the above code in order to expose the init method to be called from anywhere.
Although this works I am not satisfied with it. I am really looking for an alternative solution, as I don't want to lose the ease of use of the original approach and I don't want to force anyone using the script adding a new global to their projects.
Events
One solution I have found is leveraging events. By putting something like this in the plugin body:
elem.addEventListener('init', function() {
changeColor.init(elem);
}, false);
anybody will be able to just create an event an fire it accordingly. An example in this case:
var event = new CustomEvent('init', {});
span.dispatchEvent(event);
This would re-init the plugin whenever needed. A working fiddle is to be found here:
http://jsfiddle.net/gleezer/tgztjdzL/1/
The question (finally)
My question is: is there a cleaner/better way of handling this?
How can i let people using this plugin without the need of a global or having to initialise the script themselves the first time? Is event the best way or am I missing some more obvious/better solutions?
You can override Element.setAttribute to trigger your plugin:
var oldSetAttribute = Element.prototype.setAttribute;
Element.prototype.setAttribute = function(name, value) {
oldSetAttribute.call(this, name, value);
if (name === 'data-color') {
changeColor.init(this);
}
}
Pros:
User does not have to explicitly re-initialize the plugin. It will happen automatically when required.
Cons:
This will, of course, only work if the user changes data-color attributes using setAttribute, and not if they create new DOM elements using innerHTML or via some other approach.
Modifying host object prototypes is considered bad practice by many, and for good reasons. Use at your own risk.

Iterate over DOM elements in DalekJS

Does anyone know a simple way to iterate over, and operate on, all elements matching a query using the API?
My simplest use case is something like this - not real code, but hopefully you know what im looking for.
test.query('textarea').each(function(v, i){
this.type(v.id, 'test' + i);
});
I can do it with arbitrary JS exec, jQuery etc. I just figure it's something that Dalek probably does that I've missed
Thanks!
EDIT
Turns out my JS exec backup doesn't work...
test
.execute(function(){
var textareaIds = [];
(function($){
$('textarea').each(function(){
textareaIds.push($(this).attr('id'));
});
})(window.jQuery);
this.data('textareaIds', textareaIds);
});
But possibly due to the nature of how the code is queued then executed you can't use the array in a for loop afterwards. Have I missed something?
We are going to improve the API session in the upcoming version of DalekJS, so iterating over DOM elements & interaction between JavaScript executed on the client side & JavaScript executed on the server as part of the tests will be a lot smother.
Regarding your not working execute example. You can not just use the data passed via this.data in your Node code afterwards, you need to add it to the chain. As this is an very experimental feature, we did not really implement an API that can work with the data later.
One thing you can do, is abusing the log.message method like this:
.execute(// your code)
.log.message(function () {
// get the data
var myArray = test.data('textareaIds');
// do something with it
require('fs').writeFileSync('myFile.txt', JSON.stringify(myArray));
})
I suppose that this is not exactly what you want/need, but reusing that data for further tests and assertions is not yet possible. But we are working on it.

Cannot wrap my head around Immediately Invoking Anonymous Functions in Javascript

I have been studying framework development for a few weeks, and I ran across what is highly suggested and pressured in the world of lib development, Immediately-invoking Anonymous Functions.
I never can get it to work, and I have failed to find a resource that explains in-detail the use and logic behind it.
Here's what I know so far:
It's immediately invoking - It runs everything anonymously, immediately.
It's anonymous - It does not carry a name therefore the code inside of it is not "reachable" by exterior code.
You can pass global window, object and undefined parameters - That's about all I know on that, but do not understand them completely.
I am looking not only for a detailed resource, but one that explains the logic behind it. Because I find it very illogical.
Here's what I have:
(function( window, document, undefined ) {
window.myThingy = myThingy;
var myThingy = function() {
};
myThingy.prototype = {
constructor: myThingy,
create: function( elementToBeCreated ) {
return document.createElement( elementToBeCreated );
}
};
})( window, document );
Then,
myThingy().create("div");
But it is still saying myThingy() [object] is not a function.
What am I doing wrong? Why should I use immediately-invoking functions and not just create a global myThingy = function() object? Why do I have to use window?
I know there are several resources on the net about this, but I can't understand any of it. Some of them go half-way into detail, some of them try to go into detail, but fail to explain the critical stuff. Why is this so stressed when developing a framework?
Don't worry, I'm not trying to "re-invent the wheel", but I am trying, however, to actually learn JavaScript, not just the pre-packaged stuff.
A good answer should contain:
A good resource where it explains the logic behind immediately invoking anonymous functions
An insight to that link
What I am doing wrong with the code I provided
First off, you have not yet defined your function when you try to assign it to the global object so it is undefined:
window.myThingy = myThingy;
console.log(myThingy);//undefined
You need to do the assignment after myThingy is defined:
(function( window, document, undefined ) {
var myThingy = function() {
};
myThingy.prototype = {
constructor: myThingy,
create: function( elementToBeCreated ) {
return document.createElement( elementToBeCreated );
}
};
window.myThingy = myThingy;
})( window, document );
Okay, next, you cannot use
myThingy.create("div");
because myThingy is a function and not an object. Function objects are created when the new keyword is issued to a function. You can make this change to convert your function into a function object:
window.myThingy = new myThingy();//create a function object
This pattern is not how all frameworks are implemented, but similar. Sometimes there is more abstraction. However, making these changes will allow your approach to work.
Here is a demo of your code: http://jsfiddle.net/ZjRJW/
Links
Here are some of my favorites:
http://ejohn.org/blog/simple-class-instantiation/
http://ejohn.org/apps/learn/
http://ejohn.org/blog/simple-javascript-inheritance/
http://jibbering.com/faq/notes/closures/
https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Details_of_the_Object_Model
http://javascript.crockford.com/prototypal.html
If you want to learn about JS design patterns, I highly recommend Addy Osmani's books/articles. He keeps things very simple and usually supplies quite a bit of example code to help you understand. As far as your code and implementing a design pattern is concerned, it depends on what you want your code to do, and how you want your code/objects to behave. Understanding your requirements/goals are very important before you start coding so you don't get lost in a spaghetti of patterns that really aren't solving a specific problem.
In some cases, implementing a pattern intended for a complex, large application is simply overkill.
Since someone else already correctly pointed out the issues with your code, I'll just leave it there.

referencing the html object that made the ExternalInterface.call to the javascript function called

i apologize if my terminology is off, my actionscript skills are pretty weak sauce.
so, i have some actionscript that makes a
ExternalInterface.call('someFunction');
call.
is it possible to reference the html object that made the call to someFunction directly using the ExternalInterface.call call?
Assume that the object that makes the call also has some Callbacks (via ExternalInterface.addCallback) that are accessible via javascript.
Currently:
Actionscript source
ExternalInterface.call("someFunction");
ExternalInterface.addCallback("someCallback",someASfunction);
Javascript source
function someFunction(){
document.getElementById('idOfSWFObject').someCallback();
}
I'm thinking there must be a way of:
Actionscript source
ExternalInterface.call("someFunction",THE_OBJECT_MAKING_THE_CALL);
ExternalInterface.addCallback("someCallback",someASfunction);
Javascript source
function someFunction(o){
o.someCallback();
}
once again, sorry about the terminology. tried to lace it with as many keywords for future searches.
thanks!
I guess you are talking about ExternalInterface.objectID. This property returns an id associated with flash container in object or embed tag.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/external/ExternalInterface.html?filter_flex=4.1&filter_flashplayer=10.2&filter_air=2.en#objectID
I suggest that you should also pass the name of "someCallback" to you JS method. This way there will be no need to hardcode it in JS.
Here's an example
// Actionscript source
const jsMethodName:String = "someFunction";
const asCallbackName:String = "someCallback";
ExternalInterface.call(jsMethodName+"(document.getElementById("++")"++");");
ExternalInterface.addCallback(asCallbackName,someASfunction);
// Javascript source
function someFunction(flashId, callbackName)
{
var flashContainer = document.getElementById(flashId);
flashContainer["callbackName"]();
}
EDIT: If you really want to get a reference to flash DOM object in someFunction arguments, you may achieve it in a bit tricky way (I would rather not, but just for your interest).
// Actionscript source
const jsMethodName:String = "someFunction";
const asCallbackName:String = "someCallback";
ExternalInterface.addCallback(asCallbackName,someASfunction);
ExternalInterface.call(
"function(){"+
jsMethodName+"("+
"document.getElementById('"+ExternalInterface.objectID+"'),"+
"'"+asCallbackName+"'"+
");"+
"}"
);
// Javascript source
function someFunction(flashContainer, callbackName)
{
flashContainer[callbackName]();
}
This way you inject some JS code from flash into js. It works, but looks messy.

Categories