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.
Related
I've been using a pattern in my node.js modules that seems so obvious to me that I assume there must be something wrong with it or I would see more people doing it. To keep private variables that are global to the module, I simply attach them as properties on the module object. Like so:
module.exports = {
init: function() {
module.someClient = initializeSomethingHere()
},
someMethod: function(done) {
module.someClient.doSomething(done)
}
}
This seems preferable to me than something like this...
var someClient;
module.exports = {
init: function() {
someClient = initializeSomethingHere()
},
someMethod: function(done) {
someClient.doSomething(done)
}
}
...because in the second example you need to go searching for var someClient at the top of the file to make sure that the omission of the var keyword is intentional within the init method. I've never seen this pattern used elsewhere though, so I wonder if I'm missing something that makes it less than ideal.
Thoughts?
There are a couple of possible downsides that come to mind.
1) It's technically possible for those properties to be accessed and modified outside the module, but only if a reference to the module itself is available outside it. Something that makes the module available through its own exports (module.exports = module; being the simplest example) would expose them.
2) You could have a naming conflict with a builtin property or a future builtin property that doesn't exist yet (which would cause your code to break in future versions of node.js). This could be very problematic and very hard to debug. Currently the built-in properties on a module object are: children, exports, filename, id, loaded, paths, and parent.
because in the second example you need to go searching for var someClient at the top of the file to make sure that the omission of the var keyword is intentional within the init method.
If that is the reason, you could just use a namespace that isn't module. For instance by adding var private = {}; to the top of each file and then using private.someClient instead of module.someClient.
Also 'use strict'; so that the accidental omission of var is an error and not an accidental global.
Drawbacks
Option 1: This practice is prone to naming conflict with a builtin property or a future builtin property that doesn't exist yet, as #paulpro stated in his answer.
Option 2: If you miss var keyword and expose the someClient globally. Besides you need to go searching for var someClient at the top of the file all the time.
Alternative
As JavaScript has function scope it's better to define everything within a function. Creating private and public members within a function. Below is a design pattern which one can follow:
'use strict';
module.exports = (function(){
//private
var someClient;
//public properties
var that={};
that.init = function() {
someClient = initializeSomethingHere()
},
that.someMethod: function(done) {
someClient.doSomething(done)
}
//expose only the required methods
return that;
})();
Here only exposed methods are those which are attached to that object. And all rest are private to the function scope. Even if you miss the var keyword in someClient it won't be accessible out side of the function, which won't ever happen if you use 'use strict' at the top.
Logic is first time when you require the xyz.js file it will return the that object rather than the function.
So I've been looking at this post: What is the purpose of Node.js module.exports and how do you use it? and have been trying to make sense of it within the scope of JavaScript objects.
So if I have this in game.js:
var myObj = function() {
this.function1 = function(){
...
};
};
exports.myObj = myObj;
In the other file I'm assuming that I would do something like this:
var RequiredObj = require('game.js');
or
var RequiredObj = require('game.js').myObj;
Where I get lost is how to then use this object and its function, also how to create a new instance of this object.
Would it be something like this?
RequiredObj.myObj.function1();
and
RequiredObj.myObj = new RequiredObj.myObj();
Also is there any limit to how big the object I'm passing is? Because in my actual script my game object is huge.
Assuming the "other file" is in the same directory as game.js, something like this should work:
var game = require('./game.js');
var mo = new game.myObj();
mo.function1();
A few things to note:
Modules within your project should be specified with a path -- leading with ./, ../, or /. Otherwise, require will only search for the module as a core module or in node_modules folders (e.g., via npm install).
myObj appears to be a constructor and function1 would be an instance method. So, RequiredObj.myObj.function1() will error as no instance is created.
On the other hand, RequiredObj.myObj = new RequiredObj.myObj() should work... once. This line sets over the reference to the constructor with a reference to an instance, rendering the constructor (mostly) inaccessible. ("Mostly" because it can still be available as RequiredObj.myObj.constructor, but declaring a new variable for the instance is generally simpler.)
Also is there any limit to how big the object I'm passing is?
ECMAScript doesn't define any explicit limits on the size of an Object. Though, you'll of course be limited by the hardware used to execute the application.
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.
I am using prototypal inheritance in JavaScript and have hit an issue I can't quite figure out. Many JS inheritance examples show accessing a super's members from a sub, but I need to access the sub's members from the super.
I'm building a tool to performance test mapping web services. I want to support multiple versions of the WMS protocol. I want to keep all shared functionality / properties in a WMS base class wherever possible, and only provide specific version implementation details where necessary. My WMS v1.1.1 function looks like this:
function wms111() {
this.version = '1.1.1';
}
wms111.prototype = new wms();
My wms function (short version) is as follows:
function wms() {
var that = this;
this.HTTPMethod = 'GET';
this.descriptionParameters = {
service: 'wms',
version: that.version,
request: 'getcapabilities'
};
}
I then test this with a call like
var service = new wms111();
var descriptionParameters = service.descriptionParameters;
I get the descriptionParameters object with the service and request properties correctly defined, but version is undefined.
Can anyone help me figure out how I access the correct properties from wms111?
Any help much appreciated.
This should make it work as intended:
function wms111() {
this.descriptionParameters.version = '1.1.1';
}
Instead of defining a brand new property, just overwrite the property that should be different in the child.
Here it is in action: http://jsfiddle.net/Wd9vE/1/
I could be wrong on this one, but I'm pretty sure you can't. Inheritance works top-down, not bottom-up. You can overload the function in the subclass, but that's essentially treating that function in the superclass as an abstract function, which has to be overloaded.
I have worked with oop style scripting before and trying to get some kind of system with javascript. I wanted to try the most basic pattern, Constructor Pattern.
So I setup one js file called ImageView with a constructor matching the name of the js file.
function ImageView(){
alert( 'this is working');
}
Then I set up another js file called Main.js which will be the main instantiation class.
$(document).ready(function(){
var imageViewer = new ImageView();
//ImageView();
});
Now what I don't get is I can call this object ImageView without even the new constructor call. For example ImageView(). From what I gather this is just another global function and not a encapsulated class. I'm trying to get away from global crap and separate my methods and properties to their own class. What am I missing her.
Others have already answered what the difference is between using new and not using it, so I'll answer your entirely separate question: how do I avoid globals in JS?
The answer is that you can't entirely. You will always have at least one, in which you can stuff your other stuff. So for example if you wanted a "namespace" of xyz, you would do:
// global:
var xyz = {}; // or, window.xyz = {} if you are in a browser and want to be more explicit.
// "encapsulated" within the xyz "namespace":
xyz.ImageView = function () { alert("This is working"); };
There is a better solution: use the emerging concept of JavaScript modules. These are not language features (at least not in the current version of JavaScript), so they are really just hacks introduced by very clever libraries that overwrite a couple of global variables to let you avoid creating any more than the ones provided by those libraries. A good example is RequireJS, where you could do something like the following:
// In xyz.js, define the xyz module (name automatically derived from filename).
// Whatever is returned from the function you pass to define is "the xyz module"
define(function () {
return {
ImageView: function () { alert("This is working"); }
};
});
// In other code, in a different file, you can say "I require the xyz module
// to do my work," and pass require a function saying "once you've got the xyz module
// for me, here's the work I will do with it".
require(["xyz"], function (xyz) { // dependency array maps to callback arguments
// I got the xyz module, including the ImageView function it exported. Use it!
var imageViewer = new xyz.ImageView();
});
Here the clever globals RequireJS introduces are the functions define and require, but if you use them right, you can avoid ever introducing any further globals beside those two.
Inside of ImageView, the value of this will be different if you call it with new. Without, it's just another function. With new it will create a new ImageView instance and bind it to the variable this.
First off JavaScript doesn't have built in namespaces. It can only be simulated. You must also include each javascript file you plan on using.
Your right about just calling ImageView() that basically invokes the constructor on this which is next level of scope.
Using new ImageView() creates a new Object of constructor ImageView and this points to the new instance.
JavaScript is a prototype language with loose typing.