In my ANT scripts, I sometimes write tasks runnning javascript with Rhino.
However, I am at a loss as to how pass parameters into these tasks. Any ideas?
For example... here is an example of such a task:
<script language="javascript"> <![CDATA[
//some nonsense to fake out rhino into thinking we've a dom, etc.
this.document = { "fake":true };
this.window = new Object( );
this.head = new Object( );
eval(''+new String(org.apache.tools.ant.util.FileUtils.readFully(new java.io.FileReader('coolJavascript.js'))));
//... do some stuff
var s = java.io.File.separator;
var fstream = new java.io.FileWriter( ".." + s + "build" + s + "web" + s + "js" + s + "coolChangedJavascript.js" );
var out = new java.io.BufferedWriter( fstream );
out.write( jsCode );
out.close( );
]]> </script>
For using scripting to define an ant task you can use the scriptdef task instead of script. With scriptdef there are predefined objects to access the attributes and nested elements in your task.
This works for accessing attributes from javascript in Ant:
<scriptdef name="myFileCheck" language="javascript">
<attribute name="myAttribute" />
<![CDATA[
importClass(java.io.File);
importClass(java.io.FileReader);
importClass(java.io.BufferedReader);
var fileName = attributes.get("myAttribute"); //get attribute for scriptdef
var reader = new BufferedReader(new FileReader(new File(fileName)));
//... etc
project.setProperty("my.result", result));
]]>
</scriptdef>
Then can just go: <myFileCheck myAttribute="./some.file" /> same as you would for a regular ant task.
Also possible to use filesets etc if you want, more details at: http://ant.apache.org/manual/Tasks/scriptdef.html
The nice thing is you can define your tasks inline in your ant script, instead of writing them in Java then having to build and include the class files.
You will need to use Java1.6 (or later), or have apache BSF in your classpath.
Two suggestions come to mind. First, you can access Ant properties from within the javascript. There are examples in the documentation you refer to:
var x = project.getProperty( "my.property" );
can be used in the script to get the value of a property set in the XML, perhaps like this:
<property name="my.property" value="x" />
Second, you might consider using a scriptdef, which will allow you to define attributes and child elements that you can easily access from the javascript.
Related
I created a javascript application with all of the code in one file. The application has grown quite a bit and I think it's time to split it up into multiple files, but I'm having a hard time figuring out how to do this. I think the problem lies with how I have decided to build the app, which uses the following template:
var myApp = function(){
//there are several global variables that all of the modules use;
var global1, global2, module1, module2;
global1 = {
prop1:1
};
//There are also several functions that are shared between modules
function globalFunction(){
}
var ModuleOne = function(){
function doSomething(){
//using the global function
globalFunction();
}
return{
doSomething:doSomething
}
};
var ModuleTwo = function(){
function doSomething(){
//module 2 also needs the global function
globalFunction();
//Use the functionality in module 1
//I can refactor this part to be loosely coupled/event driven
module1.doSomething();
}
};
module1 = new ModuleOne();
module2 = new ModuleTwo();
};
Even if all of the modules were loosely coupled and event driven, I still don't know how I would go about splitting this into multiple files given each module's reliance on the shared functions/variables. Does anyone have suggestions?
Take a look at the design pattern in this article: http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth - you can split your module definition across multiple files in a way that lets common properties be shared but also lets you create variables or methods that are private just to a particular file.
The basic idea is that the individual JS files add to the same module with code like this:
var MODULE = (function (my) {
var privateToThisFile = "something";
// add capabilities...
my.publicProperty = "something";
return my;
}(MODULE || {}));
Where in each JS file if MODULE is already defined (from another JS file) you add to it otherwise you create it. You can set it up so that it (mostly) doesn't matter what order the various files are included in.
The article details several variations, and of course you'll probably come up with your own tweaks...
not to add to the confusion, but coming from a C++ background, I've tried to construct something that resembles something like a c++ namespace in the manner described below. it works, but I'd like to know if this is an acceptable pattern for the OP ?
--------------------------------file main.js:----------------
var namespacename = function(){}
namespacename.mainvalue = 5;
namespacename.maintest = function() {
var cm = new namespacename.game();
cm.callme();
}
--------------------------------file game.js:----------------
namespacename.gamevalue = 15;
namespacename.game = function(){
this.callme = function(){
console.log( "callme" );
}
}
namespacename.gametest = function() {
console.log( "gametest:gamevalue:" + this.gamevalue );
console.log( "gametest:mainvalue:" + this.mainvalue );
}
--------------------------------file index.html:--------------
<html>
<head>
<title>testbed</title>
</head>
<body onload="init();">
</body>
<script type="text/javascript" src="main.js"></script>
<script type="text/javascript" src="game.js"></script>
<script type="text/javascript">
init = function()
{
namespacename.maintest();
namespacename.gametest();
console.log( "html main:" + namespacename.mainvalue );
console.log( "html game:" + namespacename.gamevalue );
}
</script>
</html>
Give require.js a shot. http://requirejs.org/
Example:
require(["dir/file"], function() {
// Called when file.js loads
});
You can put the shared functions and shared modules on the myApp object so they don't pollute the global namespace, but can be accessed anywhere without being inside the same closure.
myApp.moduleOne = function() {...}
myApp.moduleTwo = function() {...}
myApp.globalFunction = function() {...}
Then, you can define them in any file and use them in any file.
You could also just break the file up into multiple files, but require them to be included in a specific order that preserves your closure. If you're breaking up the files for practical editing reasons, but recombining and minimizing them for actual deployment, then this wouldn't cost you anything in terms of how you write code or how it's deployed, but would give you lots of smaller files for editing convenience.
My favorite solution to this is use server side scripting to include the other files inside my "main" file. For example, using Perl's Template Toolkit:
var myApp = function(){
[% INCLUDE lib/module1.js %]
[% INCLUDE lib/module2.js %]
[% INCLUDE lib/module3.js %]
}
Or PHP:
var myApp = function(){
<?php
include 'lib/module1.js';
include 'lib/module2.js';
?>
}
I want to create a task defined by a macrodef within a script element. I was hoping to find that there would be 'set' functions corresponding to each attribute. No such luck. Is there some other API for specifying the attributes?
var statustask = project.createTask("service-status");
statustask.setPath(project.getProperty("path"));
statustask.setStatusproperty("status");
statustask.setTimeout=("1"); // this isn't suppose to take a long time.
statustask.perform();
You can probably achieve what you want using methods of the MacroInstance (a subclass of Task) you'll get from the createTask method for a macro. This:
<macrodef name="my.macro">
<attribute name="attr1" default="NOT SET"/>
<sequential>
<echo message="attr1=#{attr1}" />
</sequential>
</macrodef>
<script language="javascript"><![CDATA[
var macro = project.createTask( "my.macro" );
macro.setDynamicAttribute( "attr1", "value_1" );
macro.execute();
]]></script>
Produces this when run:
[echo] attr1=value_1
What I'm trying to do is compile to a file which takes it's version from a constant inside my source files.
I have a setup like this (or at least the significant bits):
tasks/compile.xml
<project name="Compile">
<target name="check.version">
<script language="javascript">
regex = /VERSION.+?"([\d\.]+)";/;
r = new java.io.BufferedReader(new java.io.FileReader(new java.io.File(file)));
line = r.readLine();
while ( (line = r.readLine()) != null) {
m = regex.exec(line);
if (m) {
project.setProperty( "project.version" , m[1] );
break;
}
}
r.close();
</script>
<echo>${ant.project.name}-${project.version}</echo> <!-- = Fail-0.2 -->
</target>
</project>
And a build.xml:
<project name="Fail">
<import file="${basedir}/build/tasks/compile.xml"/>
<target name="test">
<antcall target="check.version">
<param name="file" value="${basedir}/Source.as"/>
</antcall>
<echo>${project.version}</echo> <!-- = ${project.version} -->
<echoproperties></echoproperties>
</target>
</project>
So, it seems that the property set by the script is only locally defined in that target, if i specify another target in the same project ("Compile") it won't know of that property either.
I've also tried to set a in the "Compile"-project xml but it won't be overwritten by the target anyway.
So how can I access that property generated by the script? Or is there another way for doing something like this?
I'd really like to keep that part in a separate xml as it makes the project build script so much cleaner.
Thanks!
If you call the other target via antcall, properties set within it won't be in the scope of the caller.
If you need to access properties set by another target, you could declare that target as a dependency to ensure that it gets executed before your target. Like this:
<target name="test" depends="check.version">
<echo>${project.version}</echo> <!-- = value set in check.version -->
<echoproperties></echoproperties>
</target>
Edit: There's also the AntCallBack task which is available from Ant-Contrib and Antelope:
AntCallBack is identical to the
standard 'antcall' task, except that
it allows properties set in the called
target to be available in the calling
target.
I have a flash movie that requires some JavaScript from an include file but I'd like to be able to embed it anywhere without requiring a script tag.
Is there a way to include an external JavaScript file using the ExternalInterface library so I can call the methods in the JavaScript include file later?
Thanks!
Not many people realize it, but you can write inline Javascript in your .as files, and even pass in values, like so:
var someVarInAS : String = 'foo';
var someOtherVarInAS : int = 10;
var jsXML : XML =
<script type="text/javascript">
var someVarInJS = '{someVarInAS}';
var someOtherVarInJS = {someOtherVarInAS};
<![CDATA[
//here be code
alert( 'this comes from flash: ' + someVarInJS + ', ' + someOtherVarInJS );
]]>
</script>;
ExternalInterface.call( "function js_" + ( new Date().getTime() ) + "(){ " + jsXML + " }" );
A few things to note:
the {} inside the javascript code will translate to the value of whatever variable you put in between
the cdata section enables you to write whatever javascript code you want, otherwise the compiler can complain.
All javascript code called through externalinterface should be placed in a named function, otherwise it will not work in a few browsers. In this code snippet I employ a little trick (new Date().getTime()) to ensure the function always has a unique name and can't conflict with another one with possibly the same name.
don't forget the ; behind </script> it tells the compiler your javascript ends there
everything javascript can do, can be done with externallinterface. i think the best way would be to port the js script to ac.
this is how you can include a tag:
ExternalInterface.call("document.getElementsByTagName('head')[0].appendChild(document.createElement('script'))");
ExternalInterface.call("document.getElementsByTagName('script')[document.getElementsByTagName('head')[0].getElementsByTagName('script').length-1].setAttribute('type', 'text/javascript')");
ExternalInterface.call("document.getElementsByTagName('script')[document.getElementsByTagName('head')[0].getElementsByTagName('script').length-1].setAttribute('src', 'http://your.server/your.js')");
In my HTML file I have linked to the JS with:
src="myscript.js?config=true"
Can my JS directly read the value of this var like this?
alert (config);
This does not work, and the FireFox Error Console says "config is not defined". How do I read the vars passed via the src attribute in the JS file? Is it this simple?
<script>
var config=true;
</script>
<script src="myscript.js"></script>
You can't pass variables to JS the way you tried. SCRIPT tag does not create a Window object (which has a query string), and it is not server side code.
Yes, you can, but you need to know the exact script file name in the script :
var libFileName = 'myscript.js',
scripts = document.head.getElementsByTagName("script"),
i, j, src, parts, basePath, options = {};
for (i = 0; i < scripts.length; i++) {
src = scripts[i].src;
if (src.indexOf(libFileName) != -1) {
parts = src.split('?');
basePath = parts[0].replace(libFileName, '');
if (parts[1]) {
var opt = parts[1].split('&');
for (j = opt.length-1; j >= 0; --j) {
var pair = opt[j].split('=');
options[pair[0]] = pair[1];
}
}
break;
}
}
You have now an 'options' variable which has the arguments passed. I didn't test it, I changed it a little from http://code.google.com/p/canvas-text/source/browse/trunk/canvas.text.js where it works.
You might have seen this done, but really the JS file is being preprocessed server side using PHP or some other language first. The server side code will print/echo the javascript with the variables set. I've seen a scripted ad service do this before, and it made me look into seeing if it can be done with plain ol' js, but it can't.
You need to use Javascript to find the src attribute of the script and parse the variables after the '?'. Using the Prototype.js framework, it looks something like this:
var js = /myscript\.js(\?.*)?$/; // regex to match .js
var jsfile = $$('head script[src]').findAll(function(s) {
return s.src.match(js);
}).each(function(s) {
var path = s.src.replace(js, ''),
includes = s.src.match(/\?.*([a-z,]*)/);
config = (includes ? includes[1].split('=');
alert(config[1]); // should alert "true" ??
});
My Javascript/RegEx skills are rusty, but that's the general idea. Ripped straight from the scriptaculous.js file!
Your script can however locate its own script node and examine the src attribute and extract whatever information you like.
var scripts = document.getElementsByTagName ('script');
for (var s, i = scripts.length; i && (s = scripts[--i]);) {
if ((s = s.getAttribute ('src')) && (s = s.match (/^(.*)myscript.js(\?\s*(.+))?\s*/))) {
alert ("Parameter string : '" + s[3] + "'");
break;
}
}
Whether or not this SHOULD be done, is a fair question, but if you want to do it, http://feather.elektrum.org/book/src.html really shows how. Assuming your browser blocks when rendering script tags (currently true, but may not be future proof), the script in question is always the last script on the page up to that point.
Then using some framework and plugin like jQuery and http://plugins.jquery.com/project/parseQuery this becomes pretty trivial. Surprised there's not a plugin for it yet.
Somewhat related is John Resig's degrading script tags, but that runs code AFTER the external script, not as part of the initialization: http://ejohn.org/blog/degrading-script-tags/
Credits: Passing parameters to JavaScript files , Passing parameters to JavaScript files
Using global variables is not a so clean or safe solution, instead you can use the data-X attributes, it is cleaner and safer:
<script type="text/javascript" data-parameter_1="value_1" ... src="/js/myfile.js"></script>
From myfile.js you can access the data parameters, for instance with jQuery:
var parameter1 = $('script[src*="myfile.js"]').data('parameter_1');
Obviously "myfile.is" and "parameter_1" have to match in the 2 sources ;)
You can do that with a single line code:
new URL($('script').filter((a, b, c) => b.src.includes('myScript.js'))[0].src).searchParams.get("config")
It's simpler if you pass arguments without names, just like function calls.
In HTML:
<script src="abc.js" data-args="a,b"></script>
Then, in JavaScript:
const args=document.currentScript.dataset.args.split(',');
Now args contains the array ['a','b'].