I recently had to use Ext that is by the way a wonderfull library, no matter the licencing issues that occured lately. Ext.util.MixedCollection is a class allowing to store any type of data and to add, remove, filter and sort it.
Adding or removing data is quite easy, you just have to read the documentation, to filter, just give the field you want to filter and the pattern you want to match, but when it comes to sort, the documentation isn't really helpfull.
We'll first have a look to the code and then we'll give a simple sort example.
As we can see, it is really short, it just calls a private function named _sort. Let's go deeper into the layers and study the _sort method ;)
_sort : function(property, dir, fn){
var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
fn = fn || function(a, b){
return a-b;
};
var c = [], k = this.keys, items = this.items;
for(var i = 0, len = items.length; i < len; i++){
c[c.length] = {key: k[i], value: items[i], index: i};
}
c.sort(function(a, b){
var v = fn(a[property], b[property]) * dsc;
if(v == 0){
v = (a.index < b.index ? -1 : 1);
}
return v;
});
for(var i = 0, len = c.length; i < len; i++){
items[i] = c[i].value;
k[i] = c[i].key;
}
this.fireEvent("sort", this);
}
As we can see, the default sort callback is defined as follows : return a - b ;, and then this callback is passed to the array sorter. So, the function format is the same as for the Array.prototype.sort function.
As I said in this article, I recently used JST. After playing around with it for 2 or 3 days, I came up with the conclusion that an abstraction layer is needed, except if you want to rewrite the whole code every time !
Note that the '''contextObject''' can contain any JavaScript object, including strings, numbers, date, objects and functions. So, calling ${groupCalender(new Date())} would call contextObject.groupCalender(new Date()). Of course, you would have to supply the groupCalender() function, which should return a string value.
So, we've got a way to define our helpers !
Let's try it :
// As usual our namespace
var App = App || {};
/**
* a little binding utility relying on closure.
*
* This function can be improved by handling the
* merge of given parameters on binding and given
* parameters on call so that :
* <pre>
* var myBindedFunc = App.bind( myFunc, myContext, param1, param2 );
* myBindedFunc ( param3 );
* <pre>
* results being the same as :
* <pre>
* var myBindedFunc = App.bind( myFunc, myContext );
* myBindedFunc ( param1, param2, param3 );
* <pre>
* or, using the call utility :
* <pre>
* myFunc.call( myContext, param1, param2, param3 );
* <pre>
*
* @param {Function} afCallback function to apply
* @param {Object} aoContext the context to apply the function in
*
* @return {Function}
*/
App.bind = function( afCallback, aoContext ){
/* make a copy */
var _fCallback = afCallback;
var _oContext = aoContext;
/* create the closure */
return function(){
return _fCallback.apply(_oContext, arguments);
};
}
//let's define our helpers
App.Helpers = {
/**
* used as an helper, writes 'foo'
*/
test:function(){
return 'foo';
},
/**
* increments a value, to demonstrate
* the binding necessity for helpers
* that needs to access main context
*/
count:App.bind(function(){
App._counter = App._counter || 0;
return App._counter++;
},window)
};
/* parse and process */
function parse_and_process(){
var dest = document.getElementById('jst_add_helpers_template_test_1_dest');
var tpl = document.getElementById('jst_add_helpers_template_test_1_template');
dest.innerHTML = TrimPath.parseTemplate( tpl.innerHTML).process( App.Helpers );
}
Ready to test ?
Give it a try !
Here will come the processed template
So, It seems that works, but why the hell did I use a binding ?
Well, it's all about contexts, the template is executed in process function's first parameter context. That means you are enclosed in this context, so you can't access the other variables ( such as App in the count helper's case ).
So, whenever we need to create an helper that has to access other namespaces, it is necessary to bind it to needed context. Application field of such a technique can be:
Internationalization (to access datas that are not presents)
add custom events
register or modify variables
...
Well, That's cool, we've done with helpers definition understanding, but it can be exhausting to select helpers every-time you call a template, let's see how to make it simple.
The parser wrapper needs first to keep a trace of our parsed templates, so let's have a storage facility :
/* namespace */
var App;
App.Templates = App.Templates || {};
/* utilities */
/**
* copy all the properties from aoSource to aoDestination
* @param {Object} aoDestination
* @param {Object} aoSource
* @return {Object}
*/
App.extend = function(aoDestination, aoSource){
for (var property in aoSource)
aoDestination[property] = aoSource[property];
return aoDestination;
};
/* storage utilities */
App.Templates.stored = {};
/**
* stores a template
*
* @param {String} asTemplate the template name
* @param {OGF.Templates.templateObject} asTemplate the parsed template
*
* @return {OGF.Templates.templateObject}
*/
App.Templates.store = function( asTemplateName, aoTemplate ){
return App.Templates.stored[asTemplateName] = aoTemplate;
};
/**
* reads a template
*
* @param {String} asTemplate the template name
*
* @return {OGF.Templates.templateObject}
*/
App.Templates.find = function( asTemplateName ){
return App.Templates.stored[asTemplateName];
};
Easy isn't it ? Now the parsing layer :
/* parser */
/**
* parses a template
* this function is a wrapper for the template.
*
* @param {String} asTemplate the template string to parse
*
* @return {OGF.Templates.templateObject}
*/
App.Templates.parse = function( asTemplate ){
return new App.Templates.templateObject( TrimPath.parseTemplate(asTemplate) );
};
/**
* parses a template and store it.
* This function main goal is to be binded on the request return.
*
* @param {String} asTemplate the template string to parse
*
* @return {OGF.Templates.templateObject}
*/
App.Templates.parseAndStore = function( asName, asTemplate ){
App.Template.store(
asName,
App.Templates.parse( asTemplate )
);
};
Well, you maybe noticed that we used an undeclared class called App.Templates.templateObject. Let's declare it.
You maybe noticed I deported the initialization function to one in the prototype, it's a simple trick to be able to port it later on your favorite class implementation (such as Dean Edwards' base / base2, prototype class implementation or any other )
Well, to define helpers you just need to add them to the App.Templates.Helpers namespace. Here are some of mine :
App.Templates.Helpers = {
/**
* include an already compiled template
*
* @param {Object} asTplName
* @param {Object} context
*/
include:(function( asTplName, aoContext){
return App.Templates.find(asTplName).process(aoContext);
}).bind(window),
/**
* write a tag opening with the given tagname and the given properties
* example :
* ${%open_tag('div', {id:"foo",class:"bar",style:{width:'50%',background:'#000',color:'#fff'}})%}
* I'm currently looking for a solution to make it more readable
*/
open_tag:function( asTagName, aoProperties ){
var _oProperties = aoProperties || {};
var _aProperties = [asTagName];
for( var _i in _oProperties)
{
var _val = '';
if(typeof(_oProperties[_i]) == 'object')
{
_val = [];
for(var _j in _oProperties[_i] )
{
_val.push(_j+':'+_oProperties[_i][_j]);
}
_val = _val.join(';');
}
else
{
_val = _oProperties[_i];
}
_aProperties.push(_i+'="'+_val+'"');
}
return '<'+_aProperties.join(" ")+'>';
}
}
To conclude, I'll say that I use this for the binding when I include libraries into a closure (As I do for every code in this blog) to avoid polluting the main namespace with utilities functions or prototype extensions (just as used in the JST code with the array prototype for IE5 bugfix)
Some improvements can be done, such as utilities functions to register new helpers or a function to update the element with template result (and optionally evaluate scripts).
You can also allow some options to determines which modifiers to use, to auto-load them...
Hy there,
I just started to use Ant build, and I figured out that starting learning it wasn't as obvious as expected.
I first read the Manual homepage but it wasn't clear enought for a stupid guy as I am, so I decided to create this post with some Javascript dedicated code snippets and a (very) quick introduction to ant scripting.
I hope it will be usefull to someone ^^
An entry point in an Ant script is a stand alone task. It is represented with a target node. This node can accept following attributes :
name :
your task / entry point name
depends :
more or less the scripts to execute before.
So as an ant build is generally used to create or deploy a project, let's assume there is at least the two following tasks : CLEAN and DEPLOY.
So our script now looks like :
<project name="yourProjectName" default="defaultTask" >
<description>
This project does....
</description>
<target name="CLEAN">
<!-- include your tasks here -->
</target>
<target name="DEPLOY">
<!-- include your tasks here -->
</target>
</project>
So, here is our first ant script with two tasks. You can run it in any eclipse environnement by dragging this xml file to the ant view and double-clicking on one of the created tasks name.
Before going further, let's see 2 usefull functionnalities : how to call a task within your tasks and print a message.
To print a message use the echo tag. Two ways are possible :
Just print your message between the node like this :
<echo>
Your message to print
</echo>
or use the message attribute :
<echo message="Your message to print" />
Now to call a task while processing an other, just use the antcall tag.
For example to call clean while processing the deploy task, just modify previous code with following :
<target name ="DEPLOY">
<!-- include your tasks here -->
<antcall target ="CLEAN">
</target>
Easy isn't it ?
Well before giving some usefull(?) code snippets for Javascript developpement, here is the ant core functions references
Didn't you dream of a world in which you can define the version of your project in only one place ? A world where you can change your library namespace as many time as you want ?
So, Ant makes it possible whatever the platform you're working on !
First of all, you have to define all your variables in a myFile.properties file. Then, everywhere you want thoose variables to be replaced, just write it into dollar baces :
${myVar}
${PROJECT_VERSION}
Then in your myFile.properties file, define your values in following way :
myVar=MyVarReplaced
PROJECT_VERSION=1.0
It is recommended you externalizes the variables used for input and output :
destDir=absolute/path/or/relative/to/basedir
sourceDir=absolute/path/or/relative/to/basedir
So, here we are ready to process the datas using following code :
Javascript is a a script language, so it is delivered as you publish it with comments and whitespaces. Well what if I tell you that you can compress it up to 80% ! Let's see how to do that ;)
First, you need to install some JAR files defining the functions we need ANT to use. I'm using Eclipse 3.3, so I will detail how to do with this platform. If you uses something else, just search the web !
open the window > preferences pannel
Choose the Ant > Ressources in the left box
Select the ClassPath tab and click on global entries to enlight the line
Press the Add external JARs... button
select your JAR file and validate. You're now ready to use the newly intalled library !
I choosed the DOJO compressor ( relying on rhino ) you can download on the lcasoft website.
Just add rhino.jar and compress-js.jar to your ant libraries to make the compress-js function available in your ant build.
Let's see an example on how to use it :
Ca y est, j'ai enfin décidé d'ouvrir un blog.
Le but est de faire partager mes expériences de programmation, publier les quelques bouts de code que je développe, et surtout concentrer en un seul lieu l'ensemble des articles que je trouve intéressants.
bonne lecture à tous ! (bon ok, bonne lecture à moi ;p)
Ce premier article est en francais, mais comme tout outil technique, les prochains postes seront en anglais.
Développeur web en freelance, je suis passionné par le javascript, ce petit langage qui ne paie pas de mine mais permet de faire plein de jolies chose ( tout en restant accessible )
Vous trouverez ici les différents problèmes que je rencontre et comment je m'en suis sorti pour les résoudre.
en espérant que ce sera utile à certains, bon code !