I'm building an AIR desktop application. At one point the application loads a popup window (an MXML component based on s:Window), which contains an mx:HTML component which loads a local (in the application directory) html file, blank.html. The relevant elements in blank.html are:
<script src="jw/jwplayer.js"/> <!--JW Player's JS-based Embedder-->
...
<div id="jwtarget" /> <!-- the target that the embedder will use -->
Since the parameters I want to use are determined at runtime, I use the domWindow property to invoke the method which loads the player. Here's an example that works:
private function injectPlayer():void {
var playerVars:Object = {};
playerVars.flashplayer = "jw/player.swf";
playerVars.file = "http://www.archive.org/download/meet_john_doe_ipod/meet_john_doe_512kb.mp4";
playerVars.height = 360;
playerVars.width = 640;
try { // attempt to invoke the js function
htmlComponent.domWindow.jwplayer("jwtarget").setup(playerVars);
} catch(e:Error) {}
}
which is called when the page finishes loading by:
<mx:HTML id="htmlComponent" location="assets/blank.html" complete="injectPlayer()" />
That all works fine.
Now to the question. I need to be able to pass a more complex playerVars Object to the function, but I don't seem to be getting the syntax correct. Here's the simplest example I've been attempting:
private function injectPlayer():void {
var playerVars:Object = {};
//playerVars.flashplayer = "jw/player.swf";
playerVars.file = "http://www.archive.org/download/meet_john_doe_ipod/meet_john_doe_512kb.mp4";
playerVars.height = 360;
playerVars.width = 640;
playerVars.modes = [{"type":"flash","src":"jw/player.swf"}];
try { // attempt to invoke the js function
htmlComponent.domWindow.jwplayer("jwtarget").setup(playerVars);
} catch(e:Error) {}
}
This code should create the exact same thing as the above code, but it fails to execute. I assume I need to change the syntax in some way to allow the array of Objects (modes) to be passed properly as a parameter to the js function.
I've tried various things, like passing the modes as a String, or putting the whole thing through JSON.stringify() first, but to no avail. Anyone know the correct way for constructing a complex object for a parameter?
Other details, if you haven't inferred them by now: Flex 4.5.1 is the SDK I'm building with, including the AIR 3.0 extensions (which means targeting FP11).
Update:
Another configuration I tried, which does work:
playerVars.modes = {"type":"flash", "src":"jw/player.swf"};
However, this still doesn't solve the problem that I should be able to pass an Array of Objects in the modes property. But at least this way loads the video player.
More Update:
So, I found this little section of code from jwplayer.js where I suspected the player loading was failing:
if (typeof parsedConfig.modes == "string") {
_modes = _playerDefaults();
_modes[0].src = parsedConfig.modes;
} else if (parsedConfig.modes instanceof Array) { // I suspect this was eval'd as false
_modes = parsedConfig.modes;
} else if (typeof parsedConfig.modes == "object" && parsedConfig.modes.type) {
_modes = [parsedConfig.modes];
}
And to test my suspicion I added the following function to my blank.html:
<script type="text/javascript">
var instanceOfArrayTest = function(arr) {
return arr instanceof Array;
}
</script>
And in my ActionScript code tried the following:
trace([1,2,3] is Array); // true
trace(htmlComponent.domWindow.instanceOfArrayTest([1,2,3])); // false!!!!
So, it seems that the problem is that ActionScript is not passing AS3 Array objects as JS Array objects!
Try doing this instead:
playerVars.modes = [{type:"flash",src:"jw/player.swf"}];
Unlike the call() method of the ExternalInterface class, the mx:HTML does not automatically convert AS3 classes to corresponding JS classes when they are passed as parameters to a JS function. Instead, the HTML Control maintains an environment where methods and properties native to the AS3 classes are preserved and made accessible to JS directly.
If a JS function requires a JS Array object, one must create the JS Array explicitly using the JavaScript Window object to access the JS Array constructor. The HTML Control provides access to this with it's domWindow property. Otherwise, there is no way to "cast" an AS3 Array to a JS Array.
Here's a basic example:
var JSArray:Function = htmlComponent.domWindow.Array;
htmlComponent.domWindow.instanceOfArrayTest( JSArray(1,2,3) ); // true
And for the more complex example using the config parameter for JW Player:
playerVars.modes = JSArray({"type":"flash","src":"jw/player.swf"},{"type":"html5"});
which creates a JS Array of two Objects.
For more info on the JavaScript environment in the HTML Control, check out the JavaScript in AIR section of Adobe's Developing AIR Applications with Flex.
Related
I am trying to detect when a function is created, preferable through a constructor. Functions are a type of object, right? So it makes sense that when you create a new one, it calls a constructor. Is there a way to override this, for example, something like this
var old = Function.constructor;
Function.constructor = () => {
alert('new function created!');
old();
};
function asdf() {}
var k = new Function();
If this is not possible, is there a way to get all currently defined functions? I am trying to trigger a piece of code on each function run.
You can't detect function creation.
Functions are a type of object, right?
Yes.
So it makes sense that when you create a new one, it calls a constructor.
No. Or - maybe, but that constructor is internal. Just like the construction of objects from array literals, object literals, regex literals, definition of a function directly creates a native object.
Is there a way to override this?
No. You'd need to hook into the JS engine itself for that.
If this is not possible, is there a way to get all currently defined functions?
No. At best, you could try the debugging API of the JS engine and get a heap snapshot, that should contain all function objects.
I am trying to trigger a piece of code on each function run.
Let me guess, that piece of code is a function itself?
Was able to get a semi-working attempt at this. It reads only global functions but it can add code to both the front and beginning of the function. Any tips on how to improve this, as I use classes a lot when I code?
Thanks to Barmar for the idea of looping through window properties, but since you can't access local functions and class functions, this may be the closest way to do this
<script>
function prepend(name) {
console.time(name);
}
function postpend(name) {
console.timeEnd(name);
}
var filter = ['prepend', 'postpend', 'caches'];
function laggyFunction() {
var l = 0;
while (l<1000) {l++}
}
var functions = [];
for (var property in window) {
try {
if (!filter.includes(property)) { // security error on accessing cache in stackoverflow editor along with maximum call stack size exceeded if prepend and postpend are included
if (typeof window[property] === 'function') {
window[property].original = window[property];
window[property].name = property;
window[property] = function() {
prepend(this.name);
console.log(this.original);
this.original.apply(null, arguments);
postpend(this.name);
}.bind(window[property]);
functions.push(property);
}
}
} catch(e) {
console.warn(`Couldn't access property: `+property+' | '+e);
}
}
document.write(functions); // functions the prepend and postpend are applied to
laggyFunction(); // test performance of the function
</script>
So I'm building a node module for use with node-webkit that creates a new object and exports it. Standard fare. But since Node has no access to the nw-gui module of node-webkit, I'm just passing it in as a parameter to the constructor. Something like this:
function Example(gui) {
this.gui = gui; //Save for later
}
Example.prototype.createExampleMenu = function() {
return new this.gui.Menu();
}
exports.example = Example;
Works great. But I'm trying to modify .prototype methods of node-webkit's inner modules, like Menu and MenuItem. Is the only way to modify those methods (or add new ones) in the constructor itself? If I try to add new prototype methods outside, it (obviously) fails since this.gui hasn't been set. Basically, I'm trying to make it nicer to add new prototype methods to node-webkit modules without doing it in the constructor. Anyone?
I'm in no way an expert but from what I understand of the implementation of node-webkit from reading its source code, I doubt you can modify any of the objects defined in nw.gui.
If you look at the implementation of Node's standard require function in a running node-webkit instance, you'll find:
function (name) {
if (name == 'nw.gui')
return nwDispatcher.requireNwGui();
return global.require(name);
}
which means that requires of nw.gui are very special indeed.
Rather than requiring JavaScript code, this returns an internal binary object that only appears to be a required library.
Looking a little deeper, we find the nwDispatcher.nwGui.Menu is defined as:
function Menu(option) {
if (typeof option != 'object')
option = { type: 'contextmenu' };
if (option.type != 'contextmenu' && option.type != 'menubar')
throw new String('Invalid menu type: ' + option.type);
this.type = option.type;
v8_util.setHiddenValue(this, 'items', []);
nw.allocateObject(this, option);
}
which calls methods of the nw object, which is an object that is not available outside of this function, (i.e. the function acts as a closure over it.)
Further inspection of the various prototype methods of nw.gui.Menu shows that each call refers (internally) to this nw object to handle method dispatch to internally defined functions (written in C++).
So, rather than a group of standard JavaScript prototypical objects, the nw.gui module calls internal binary functions within the node-webkit runtime which are not exposed via its defined API.
UPDATE
From the node-webkit wiki:
Do not change UI types' prototype.
Store and retrieve Google Dart objects in JavaScript library containers
In a Dart application I am using an external JavaScript library to do various matrix calculations.
The specific functionality of the library is not important, what it's important is that I need to store and retrieve Dart object that I put in the matrix.
Dart Class - Lets image i have a dart object that which has a parameter called name
MyDartClass mydc = new MyDartClass(something, something);
mydc.name;
// Everything works as planned
Storing
matrix = js.context.matrix
matrix.cell(1,1).store("thing", new MyDartClass(something, something));
Retrieving
matrix.cell(1,1).has_object_of_type("thing");
// true
MyDartClass mydc = matrix.cell(1,1).retrieve("thing");
Do something with the object
mydc.name;
// Exception: The null object does not have a getter 'name'.
// NoSuchMethodError : method not found: 'name'
// Receiver: null
// Arguments: []
Does the library really work?
Yes it does. I have done the exact same thing in pure javascript many times and there are plenty of test to test the behaviour ( in Javascript )
Is Dart Broken?
When I try to use a javascriptified Hash to do the same behavoiur it works like a charm.
var options = js.map({ 'dart' : new MyDartclass(something, something));
var y = options["dart"];
js.context.console.log(y.name);
// Name is printed
What do you get out from the retrieve?
It seems that I get some kind of Dart Proxy
MyDartClass mydc = matrix.cell(1,1). retrieve("thing");
js.context.console.log(mydc);
DartProxy {id: "dart-ref-20", port: DartSendPortSync}
id: "dart-ref-20"
port: DartSendPortSync
__proto__: DartProxy
I belive that the lib stores the objects, deep down, in a hash map. But it seems like when I retrieve the object into the Dart I get something, but not in a way that I can work with it. So i need help since I don't know how to make it work.
Do I need to de-proxify the object?
Perhaps it IS a Dart bug when you try to retrieve objects from hashes inside objects
Perhaps I missunderstod everything that this is not suppose to work.
Passing and retrieving Dart objects inside the same scope is working. There's the following test case in the tests of js-interop to proove it :
test('retrieve same dart Object', () {
final date = new DateTime.now();
js.context.dartDate = date;
expect(js.context.dartDate, equals(date));
});
However there seems to be an issue with multiple scopes (and multiple event loops as well). There is no way to retain a dart object for now. So your dart object reference goes away at the end of scope. Here's a simple test case that fails :
test('retrieve same dart Object', () {
final date = new DateTime.now();
js.scoped(() {
js.context.dartDate = date;
});
js.scoped(() {
expect(js.context.dartDate, equals(date));
});
});
Please file an issue.
I currently have a Web Application that runs off a global Javascript-based API, and it is initialized like this:
var Api = {
someVar: "test",
someFunction: function() {
return "foo";
}
}
This API is shared across many "Widgets" that live in the Web Application, and they should all run off this single Api instance so they can pass data to each other.
AJAX is currently used to load these Widgets, for example in widgets/mywidget.html, and it's placed in, say, <div id='widget_<random number>'>...</div>
Certain other parts of the code may choose to add more functionality to Api, and it's currently done like this:
Api.myExtension = {
myNewFunction: function() {
return "bar";
}
}
However, some issues arise from this kind of usage:
Problem One: What if one Widget (these may be provided by third-parties) decides to hide some code within, and does something similar to Api = {}, destroying the global Api var everything lives on, and breaking the whole Application? Is it possible to protect this Api variable from being overwritten from outside? Only "extending" is allowed (adding new things), but "removing/changing" is not allowed. i.e.:
Api.foo = { test: "bar" } // allowed
Api.someVar = "changing the existing someVar"; // not allowed
The following code is located "inside" Api, for example:
var Api = {
Debug: {
Messages = new Array,
Write: function() {
Api.Debug.Messages.push("test"); // allowed
}
}
}
Api.Debug.Messages.push("test 2"); // not allowed
Probable Solutions I've Thought Of:
Suppose we simply use frames to resolve this issue. The Apis provided are now separate from each other. However, there's additional overhead when loading Api again and again if I have many Widgets running, and they can no longer communicate with the "Host" of the widgets (the page where frames reside in), for example, I may want to tell the host to show a notification: Api.Notify.Show("Test"), but it cannot do so because this Api is completely independent from other instances, and it cannot communicate with the "Host"
Using something like a "getter" and "setter" function for the Api to be read and written. I'm unsure on how to implement this, so any help on directions on how to implement this is welcome!
A mixture of 1/2?
There's no good way to prevent having a "third party" widget overwrite the a global variable. Generally it is the responsibility of whoever is putting together the final application to ensure that whatever JavaScripts they are using aren't littering the global namespace and conflicting. The best thing you can do in that direction is give your "Api" a nice, unique name.
What I think can help you a lot is something like the "revealing pattern", which would be a way of doing the "getters and setters" you mentioned, plus more if you needed it.
A simple, useless example would be like the following:
var Api = (function () {
// private variable
var myArray = [];
return {
addItem: function (newItem) {
myArray.push(newItem);
},
printItems: function () {
console.log("lots if items");
}
};
})();
Api.addItem("Hello, world");
Api.extensionValue = 5;
I think you should make a clear delineation of what is shared, or "singleton" data, and keep those items private, as with myArray in my example.
Make it a constant:
const Api = "hi";
Api = 0;
alert(Api); //"hi"
Take a look at
Object.freeze
More info here
Here is a code example from Mozilla's page:
var obj = {
prop: function (){},
foo: "bar"
};
// New properties may be added, existing properties may be changed or removed
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;
var o = Object.freeze(obj);
assert(Object.isFrozen(obj) === true);
// Now any changes will fail
obj.foo = "quux"; // silently does nothing
obj.quaxxor = "the friendly duck"; // silently doesn't add the property
// ...and in strict mode such attempts will throw TypeErrors
function fail(){
"use strict";
obj.foo = "sparky"; // throws a TypeError
delete obj.quaxxor; // throws a TypeError
obj.sparky = "arf"; // throws a TypeError
}
fail();
// Attempted changes through Object.defineProperty will also throw
Object.defineProperty(obj, "ohai", { value: 17 }); // throws a TypeError
Object.defineProperty(obj, "foo", { value: "eit" }); // throws a TypeError
However browser support is still partial
EDIT: see Kernel James's answer, it's more relevant to your question (freeze will protect the object, but not protect reassigning it. however const will) same issue with limited browser support though.
The only way (at least that I can think of) to protect your global variable is to prevent the Widgets from having a direct access to it. This can be achieved by using frames functions, as you suggested. You should create an object that contains all the functions that the Widgets should be able to use, and pass such to each Widget. For example:
var Api = {
widgetApi = {
someFunction: function(){
// ...
}
},
addWidget:function(){
var temp = this.widgetApi.constructor();
for(var key in this.widgetApi)
temp[key] = clone(this.widgetApi[key]);
return temp;
}
// Include other variables that Widgets can't use
}
This way, the Widgets could execute functions and communicate with the host or global variable Api. To set variables, the Widget would be editing its private object, rather than the global one. For every frame (that represents a Widget), you must initialize or create a copy of the widgetApi object, and probably store it inside an array, in such a way that an instance of a Widget is stored in the main Api object.
For example, given <iframe id="widget"></iframe>
You would do the following:
var widget = document.getElementById("widget");
widget.contentWindow.Api = Api.addWidget();
widget.contentWindow.parent = null;
widget.contentWindow.top = null;
Additionally, in every frame you would need to set the parent and top variables to null so that the Widgets wouldn't be able to access the data of the main frame. I haven't tested this method in a while, so there might be ways to get around setting those variables to null.
I am using an open source javascript library timeline.verite.co
It's a timeline library which works great on page load. But when I try to repaint the timeline on certain condition, it starts giving out weird errors
I would like to modify the init function in the library. But instead of changing it in the original library itself, I would like to rewrite/override this function in another separate .js file so that when this function is called, instead of going to the original function, it must use my modified function.
I'm not sure whether to use prototype/ inheritance and how to use it to solve this problem?
You only need to assign the new value for it. Here is an example:
obj = {
myFunction : function() {
alert('originalValue');
}
}
obj.myFunction();
obj.myFunction = function() {
alert('newValue');
}
obj.myFunction();