tag:blogger.com,1999:blog-76551199765535637162024-02-21T11:24:53.086+01:00Sithmel BlogAnonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comBlogger28125tag:blogger.com,1999:blog-7655119976553563716.post-7062681402403804522015-04-06T14:57:00.001+02:002015-04-07T13:43:20.728+02:00Agile, how to embrace the changeThe <a href="http://agilemanifesto.org/">agile manifesto</a> gives us a better way for developing software, putting:<br />
<br />
<ul>
<li>Individuals and interactions over processes and tools</li>
<li>Working software over comprehensive documentation</li>
<li>Customer collaboration over contract negotiation</li>
<li>Responding to change over following a plan</li>
</ul>
<br />
This overview tells a lot from a project perspective, but what about developers perspective ? how is it possible to adapt to continuous changes?<br />
Writing a good piece of code, fully tested, requires a lot of time and effort. Throwing it into the bin is not only a waste, it is also very bad for the morale.<br />
<br />
Luckily enough there are a few tricks that can solve most of the problems. Not a big deal, just common sense, but still thing worth to be said:<br />
<br />
<h3>
Automation, automation, automation</h3>
Ok, you are writing a piece of software and you already know that you are going to rewrite part of it, probably more than once. Automate all tedious tasks, most of the time is it a pretty good investment, especially at the beginning of the project.<br />
I can safely assume you can automate:<br />
<br />
<ul>
<li>building</li>
<li>testing</li>
<li>releasing</li>
<li>deploying </li>
</ul>
All this can take a bit to set up, the key part is that you can reuse them across many projects.<br />
<h3>
Divide and conquer</h3>
The common mistake in approaching the design of a software is looking at it as a single entity. The best approach instead is to split it into simple and configurable libraries/modules, they can be assembled at the last minutes to get what you want.<br />
This approach minimizes the waste and enforces the separation of concerns. And it is also a overall win in maintainability.<br />
A good library should solve a common problem, always. A very specific problem does not deserve to be taken into consideration. What happen if your beautifully crafted, business specific library, needs to be thrown away because of requirements change ?<br />
Instead make the business requirement a configurable option of your generic library.<br />
There is also a huge bonus: generic libraries can be easily reused across many projects.<br />
<br />
<h3>
Documentation</h3>
Writing a good documentation improves the thing that matter most: your code should be reused more than once. A good documentation should contain purpose, a design explanation, installation instruction, API and some examples. These are particularly important.<br />
In this case documentation is more important than working software. Bugs can be fixed, a missing/outdated documentation means your library can't be used by anyone, even you after a month.<br />
<br />
<h3>
The open source power</h3>
A fully documented and tested generic library is a big investment, and it is often valuable enough for other people as well. Why don't share it with others ?<br />
It can enrich the ecosystem of the platform that you are using. You can also get valuable feedback.<br />
<br />
<h3>
This method, applied: a true story</h3>
The first example it comes to my mind is several years old. I was working in a company where one of the most successful product was a solution for building on line catalogs.<br />
When I got hired my first task was the third attempt to build this software. The first two attempts were way too vertical on the previous customers to be reused for the next ones.<br />
The main problem was embedding specific customer requirement in the project, for example the design of the product page was something every customer wanted to be different.<br />
<br />
The business input was: "I want a catalog product", way too generic and misleading from the developer point of view. There were though, a certain amount of loosely couple, common functionalities. The real point here was to identify these functionalities and build all of them as separate product.<br />
This is a classic case in which a developer should not do what is being told, he should use his expertise to build a system that fulfil the requirements.<br />
The product page, for example, was never a part of the product but I developed a system to build a page with subcomponent, all of them configurable.<br />
<br />
After a few attempt we managed to have a very flexible collection of products for building catalog applications. By the time I left the company this was used several times ...<br />
<br />
<h3>
Bottom line</h3>
It is possible to be agile, to foresee and to adapt, after all is our job.<br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-54822371867782913722015-02-25T10:35:00.000+01:002015-02-26T14:21:50.051+01:00High performance animations (a few tricks)The web browser is capable of very smooth animations, but there are a few gotchas. Here is what you should know.<br />
<br />
<h3>
jQuery</h3>
With jQuery animations went mainstream. But <a href="http://api.jquery.com/animate/">jQuery.animate</a> is possibly the less efficient way for animating elements nowadays:<br />
<ul>
<li><a href="http://bugs.jquery.com/ticket/9381">it doesn't use requestAnimationFrame</a></li>
<li>it doesn't relay on css transitions and css animations</li>
</ul>
It can be still useful on old browsers or in specific cases. Better not using it on mobile browsers.<br />
<br />
<h3>
CSS transitions and animations</h3>
Native animations runs usually faster than JS ones. So it is quite obvious to use them when possible. There are plenty of tutorial on how to use them so google for it!<br />
<br />
<h3>
Composite layer css properties</h3>
When you change a CSS property you trigger some operations in the browser. These are:<br />
<br />
<ul>
<li>recalculating sizes and positions (layout)</li>
<li>redrawing elements on the screen (paint)</li>
<li>composite all elements together (composite)</li>
</ul>
<br />
The topmost operations triggers the ones below. Furthermore the more elements are involved the worst the animation is , performance wise.<br />
You can visualize and debug this process in the useful <a href="https://developer.chrome.com/devtools/docs/timeline">timeline panel</a> (inside Chrome developer tools).<br />
The trick here is to use css properties that triggers only the composite step. These are opacity<br />
and transform. <a href="http://www.html5rocks.com/en/tutorials/speed/high-performance-animations/">This is article</a> contains what you need to know.<br />
Sadly it is not enough to use these for getting the performance jump, you should also trigger the creation of a new composite layer using these CSS rules:<br />
<br />
<pre class="prettyprint">.will-change{
transform: translate3d(0, 0, 0);
perspective: 1000;
backface-visibility: hidden;
}
</pre>
<br />
For a better support you can add these browser prefixes:<br />
<br />
<pre class="prettyprint">.will-change{
-webkit-transform: translate3d(0, 0, 0); /*old chrome and safari*/
-o-transform: translate3d(0, 0, 0); /*old opera*/
-moz-transform: translate3d(0, 0, 0); /*old FF*/
-ms-transform: translate3d(0, 0, 0); /*IE9*/
transform: translate3d(0, 0, 0);
-webkit-perspective: 1000;
-o-perspective: 1000; /*old opera*/
perspective: 1000;
-webkit-backface-visibility: hidden;
-o-backface-visibility: hidden; /*old opera*/
backface-visibility: hidden;
}
</pre>
<br />
Doing this opacity and transform are managed by the GPU if possible.<br />
This can be a bit awkward and for this reason browser vendors have created a new css rules: <a href="http://aerotwist.com/blog/bye-bye-layer-hacks/">will-change</a>.<br />
<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/will-change">This</a> will make the browser know that an element is going to change and to put it inside a compositing layer (this is not yet widely available so sadly, for now, it is better to stick with the hack).<br />
<br />
<h3>
Request Animation Frame</h3>
<br />
The way js animations work is changing a numeric CSS property over time. The only way to schedule an event in js was setTimeout (and its brother setInterval). As I mentioned they are still used by jQuery.<br />
A while ago browser vendors introduced "requestAnimationFrame". It is a much better way to do it as it execute a piece of code right before the page refresh (approximately 60 times a second).<br />
This is a <a href="http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/">polyfill</a> for any browser (in the worst case it uses setTimeout).<br />
<br />
If your animation depends on user interactions it can be a good idea to throttle the changes to the CSS using requestAnimationFrame. In this example I am using a queue with a simple policy that returns only the last function discarding the others.<br />
<br />
<pre class="prettyprint">function lastOne(q){
return q.length ? [q.pop()] : [];
}
function getRenderingQueue(policy){
var queue = [];
var isRunning = false;
policy = policy || function (q){return q;};
var render = function (){
var f;
queue = policy(queue);
isRunning = false;
while (f = queue.shift()){
f();
}
};
return {
empty: function (){
queue = [];
},
push: function (func){
queue.push(func);
if (!isRunning){
isRunning = true;
window.requestAnimationFrame(render);
}
}
}
}
var renderingQueue = getRenderingQueue(lastOne);
renderingQueue.push(function (){
//changing a piece of CSS
});
</pre>
<br />
<br />
Depending on your application you can decide using a different policy.<br />
<br />
This is it, I'll soon put this in context.Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-41031833167383654292015-01-30T10:19:00.000+01:002015-01-30T10:22:14.570+01:00urlwalker.js, a context aware router for express<a href="https://github.com/sithmel/urlwalker.js">Urlwalker.js</a> is a router middleware for <a href="https://github.com/senchalabs/connect">Connect</a>/<a href="http://expressjs.com/">Express.js</a>.<br />
<br />
It is not a substitute of the default Express.js router but it works together with the latter, trying to get an object from a fragment of URL (It literally walks it segment by segment, hence the name). You can then use this object as model in the function called by the default Express.js router.<br />
This process is called URL traversal. This concept is not by any means original: I took the inspiration from other web frameworks such as Zope and <a href="http://docs.pylonsproject.org/docs/pyramid/en/latest/narr/muchadoabouttraversal.html">Pyramid</a>.<br />
<br />
Confused ? Let's make a step behind<br />
<br />
<h3>
URL, model and view</h3>
Using <a href="http://en.wikipedia.org/wiki/Representational_state_transfer">REST</a> principles it seems to be natural mapping a URLs to a hierarchy of objects:<br />
<br />
<ul>
<li>http://www.example.com/roald_dahl/the_chocolate_factory</li>
</ul>
<br />
This URL represents a relation between two objects: the author (roald_dahl) and one of his books (the_chocolate_factory). The last is the model used by the function. Let's put this thing together using express.js:<br />
<pre class="prettyprint">app.get("/:author/:book", function (req, res){
// getting the book object
// doing something with the object
// return the result
});
</pre>
The original "Expressjs" way to get the model is to do it directly inside the function (like the previous example) or (better) using <a href="http://expressjs.com/4x/api.html#app.param">app.param</a>. But it is not flexible enough for managing a deeply arbitrary nested structure.<br />
<div>
Furthermore I believe it can be useful to split the URL in two different parts. The first part is for getting an object and the second one to <i>transform</i> the object:<br />
<br />
<ul>
<li>http://www.example.com/roald_dahl/the_chocolate_factory/index.json</li>
<li>http://www.example.com/roald_dahl/the_chocolate_factory/index.html</li>
</ul>
<br />
Both of these URLS point to the same object but return a different representations of that object.<br />
<div>
</div>
</div>
Urlwalker.js follows this convention.<br />
<br />
<h3>
How to use it</h3>
<br />
The middleware is initialized with a function and a "root" object.<br />
<br />
<pre class="prettyprint">var traversal = require('urlwalkerjs').traversal;
var traversal_middleware = traversal(function (obj, segment, cb){
return cb({ ... new obj ... })
// or
return cb(); // end of traversing
},root_object);
</pre>
<br />
Then you can use it as a normal middleware and add the regular routing:<br />
<br />
<pre class="prettyprint">app.use(traversal_middleware);
app.get('index.json', function(req, res) {
res.send(req.context);
});
</pre>
<br />
The routing process starts with an object. I call it the "root object" and it is the second argument passed to the middleware. It can be anything, even undefined.<br />
The function (the first argument of the middleware) is invoked for any URL segment. The first time is invoked with the first segment and the root object. It returns an object. The second time is called with the second segment and the object returned previously. The process is repeated until it can't find a match. Then it returns the last object in "req.context" and pass the control to the next middleware.<br />
For this URL:<br />
<br />
<ul>
<li>http://www.example.com/roald_dahl/the_chocolate_factory/index.json</li>
</ul>
<br />
The function is invoked twice:<br />
<br />
<ul>
<li>from the root object and the segment "roald_dahl" I get an author object</li>
<li>from the author object and "the_chocolate_factory" I get a book object</li>
</ul>
<br />
Then the express.js function is called with the book object inside req.context.<br />
For clarifying the process I have added an <a href="https://github.com/sithmel/urlwalker.js/blob/master/examples/example1.js">example here</a>.<br />
<br />
<h3>
An example with occamsrazor.js</h3>
<br />
Defining this function with such a complex behaviour can be difficult and not very flexible.<br />
For this reason you can use<a href="https://github.com/sithmel/occamsrazor.js"> occamsrazor.js</a> for adding dinamically new behaviours to the function (see <a href="https://github.com/sithmel/urlwalker.js/blob/master/examples/example2.js">example 2</a>).<br />
So it becomes:<br />
<br />
<pre class="prettyprint">var getObject = occamsrazor();
var has_authors = occamsrazor.validator().has("authors");
var has_books = occamsrazor.validator().has("books");
getObject.add(null, function (obj, id, cb){
return cb(); // this will match if no one else match
});
getObject.add(has_authors, function (obj, id, cb){
return cb(obj.authors[id]);
});
getObject.add(has_books, function (obj, id, cb){
return cb(obj.books[id]);
});
var traversal_middleware = traversal(getObject, data);
app.use(traversal_middleware);
app.get('index.json', function(req, res) {
res.send(req.context);
});
</pre>
<br />
At the beginning it might seem a bit cumbersome until you realize you can easily extend the behaviour so easily:<br />
<br />
<pre class="prettyprint">var has_year = occamsrazor.validator().has("year");
getObject.add(has_year, function (obj, id, cb){
return cb(obj.year[id]);
});
</pre>
<br />
<h3>
Plugin all the things</h3>
But why stops here? why can't we get the view with a similar mechanism (<a href="https://github.com/sithmel/urlwalker.js/blob/master/examples/example3.js">example 3</a>) ? Let's replace the Express.js routing completely with this:<br />
<br />
<pre class="prettyprint">...
var view = require('urlwalkerjs').view;
var getView = occamsrazor();
var view_middleware = view(getView);
getView.add(null, function (url, method, context, req, res, next){
next(); // this will match if no one else match
});
getView.add(["/index", "GET", has_books], function (url, method, context, req, res, next){
res.send('this is the author name: ' + req.context.name);
});
getView.add(["/index", "GET", has_authors], function (url, method, context, req, res, next){
res.send('these are the authors available: ' + Object.keys(req.context.authors));
});
getView.add(["/index", "GET", has_title], function (url, method, context, req, res, next){
res.send('Book: ' + req.context.title + " - " + req.context.year);
});
app.use(view_middleware);
</pre>
<br />
A plugin architecture is very helpful, even though you don't need plugins at all. It allows you to apply the <a href="http://en.wikipedia.org/wiki/Open/closed_principle">open/close principle</a> and extend your application safely.<br />
<br />Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-46118120694949887772015-01-15T23:03:00.000+01:002015-01-19T14:55:36.045+01:00Why I have stopped using requirejs (and you should too)I have used <a href="http://requirejs.org/">requirejs</a> extensively and I have written many posts about it. I think it is very ingenious and well designed.<br />
It tries to solve more than one problem at the same time (in a very elegant way) but nowadays these problems are not so important and they have better solutions.<br />
<h3>
Loading scripts asynchronously</h3>
This was one of the main selling point of requirejs in the past. Now it is not necessary anymore. It is much better moving (back) the script tags on the top and use the async attribute as described by <a href="https://www.igvita.com/2014/05/20/script-injected-async-scripts-considered-harmful/">this great article</a>. The async attribute now is <a href="http://caniuse.com/#feat=script-async">very well supported</a> !<br />
<h3>
Loading dependencies</h3>
Requirejs can dinamically load dependencies when they are required. But often you want to have the control. Sometime is better to include a library when you load the page (bundling more than a library together, for having them saved in the cache) and sometime you want to load it on demand. In case it is very easy do something like:<br />
<br />
<pre class="prettyprint">var script = document.createElement('script');
script.src = "http://www.example.com/script.js";
document.head.appendChild(script);</pre>
<h3>
Isolate dependencies</h3>
Requirejs is even able to run 2 different versions of the same library. But is a feature rarely used and to be honest in 99.9 % of the cases using the module pattern is more than enough.<br />
<h2>
The only issue</h2>
The only issue of having all these asynchronous bundles (using the async attribute) is managing the execution order. You can use a tiny library like this one:<br />
<br />
<pre class="prettyprint">(function (w){
var go = {}, wait = {};
w.later = function (dep, func){
if (go[dep]) func();
else {
wait[dep] = wait[dep] || [];
wait[dep].push(func);
}
}
w.later.go = function (dep){
var funcs = wait[dep] || [], l = funcs.length;
delete wait[dep];
go[dep] = true;
for (var i = 0; i < l; i++){
try{
funcs[i]();
}
catch (e){
console && console.error(e);
}
}
}
}(window));
</pre>
<br />
This could be the only JS to be loaded synchronously. For maximum performances you can also minify it and inline in the HTML.<br />
Then you can manage the dependencies at execution time:<br />
<br />
<pre class="prettyprint">later('foo', function (){
// waiting for the bundle named foo (it is an arbitrary string)
});
</pre>
<br />
You only need to put this instruction at the end of the bundle "foo":
<br />
<br />
<pre class="prettyprint">later.go("foo");
</pre>
<div style="font-family: 'Times New Roman'; white-space: normal;">
<br />
There are still valid use cases for requirejs but I suggest to keep your build process lean, tweak performances by hand, use the async attribute and the module pattern.</div>
<div style="font-family: 'Times New Roman'; white-space: normal;">
Simpler and more performant !<br />
<h3>
Edited: and what about "defer"?</h3>
<a href="https://www.igvita.com/2014/05/20/script-injected-async-scripts-considered-harmful/">This article</a> suggests to use async and defer together for improving performances on older browsers. I suggest to not do that, unless you know what your script is doing. This is because of <a href="https://github.com/h5bp/lazyweb-requests/issues/42">this bug</a>. The bug is even worst of what it seems, if you inject a script tag inside a DEFERred script the execution will stop waiting for the injected script to be downloaded and executed. So be careful!<br />
<br />
<br /></div>
<div>
</div>
Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-22818173440888722222015-01-06T18:56:00.000+01:002015-04-01T15:42:28.206+02:00Less Sass and (mo)ReworkI apologize for the pun. In this blog post I'd like to give my opinion about css preprocessors.<br />
<br />
<h3>
Sass and Less</h3>
<a href="http://sass-lang.com/">Sass</a> and <a href="http://lesscss.org/">Less</a> are the most popular CSS preprocessors. I think CSS preprocessors are powerful tools but often they are not going to help you writing better CSS. Most of their powerful features can be abused to make the wrong choices in term of code reuse. This is because <i>they don't promote reusability</i> of the produced css rules.<br />
There are still (a few) acceptable use cases: producing css demos, bulding very repetitive css rules as responsive grids and things like that.<br />
<br />
But in the hands of inexperienced developers, they produce a mess: in my experience a bad less/sass is much worst than a bad css.<br />
<br />
<h3>
Writing proper css</h3>
Writing CSS is not too bad. The real challenge is to keep it maintainable. In this challenge Sass and Less are not helping. What is useful is design the css for the reuse, following these simple principles for example:<br />
<ul>
<li>Naming is particularly important. With a namespace you can avoid conflicts. You can also separate rules in "general rules", "module rules", "exceptions". For this one you can adhere or find inspiration in <a href="https://smacss.com/">SMACSS</a>.</li>
<li>Avoid at any cost working with specificity. Use namespacing instead! So don't use selectors as ".my-module .my-special-class" but ".my-module-special-class"</li>
<li>a rule, a feature. Any rule should just contains a single functionality. This functionality can be a single rule or a combination of rules. CSS frameworks (like <a href="http://getbootstrap.com/">Bootstrap</a>) are full of examples.</li>
</ul>
<h3>
Enter reworkcss (and Myth.io)</h3>
Actually there are a couple of things that are really useful in CSS preprocessors. They can address automatically browsers prefixes, they can let you use variables and calculations (server side).<br />
My favourite tool for doing this is <a href="https://github.com/reworkcss/rework">rework</a>. Although it is not a CSS preprocessor but a css parser. It produce an AST (that is a js object), you can change this object and write back the css.<br />
It has a plugin system so it is very easy to create custom extensions.<br />
You always start and end with a syntactically valid css (selector {property: value;}). You don't have to change syntax, syntax highlighters keeps working fine and the result can be easily used together with other static analysis tools.<br />
You can roll out your own plugin for doing complex operations that are not possible at all with Sass/Less.<br />
There are already a lot of plugins like one for using variables, another one for addressing browser prefixes etc.<br />
<h3>
grunt-css-annotator</h3>
<a href="https://github.com/MailOnline/grunt-css-annotator">This grunt plugin</a> is an example of using rework. It scans some webpage (using PhantomJS) and adds an annotation in a comment if a css selector is used in those pages.<br />
There is also an option to remove rules with specific annotations. It can be useful if you want to do spring cleaning in your css and this is only an example of the power of rework!<br />
<div>
<h3>
Myth.io</h3>
<a href="http://www.myth.io/">Myth.io</a> is a CSS preprocessors built using a collection of useful rework plugins. It is designed for polyfilling some of the CSS features of tomorrow, like variables and calculations. But you can also extend it with other custom plugins!<br />
<br />
<b>EDIT: I have used myth.io for a serious project. It was mostly a pleasant experience but there is a severe limitation in the use of css variables! https://github.com/segmentio/myth/issues/10 .</b></div>
<div>
<br /></div>
Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-27429568850005083702014-04-17T19:45:00.001+02:002014-04-17T23:06:43.100+02:00A trip into Clojure, lesson learnedIn my current job I had the chance to learn and use Clojure (<a href="http://www.pitheringabout.com/?p=1018">this is the experience reported by a former collegue</a>). Having worked with Python and Javascript I had consided myself well skilled in functional programming. But working with a more opinionated functional programming language was an eye opener. The benefits? robustness, better code reuse, a more organic workflow.<br />
<br />
My main takeaway is that I can apply the lesson learned to Python and Javascript (and many others) as well. Let's see how.<br />
<br />
<h3>
Languages</h3>
Many languages enforce a specific point of view. Java for example with its pervasive object orientation enforce to model problems in objects. Python tries to please everyone with a solid OO implementation and a good deal of functional constructs. Javascript is similar (but with a different OO implementation).<br />
Clojure, from this point of view, is more like Java: it enforces a specific way to model problems: a functional model.<br />
<br />
<h3>
Functions</h3>
Of course functions are first class citizen in a functional language so they can be stored in variables, passed as argument etc.<br />
This allows a lot of useful programming patterns. These are just few examples:<br />
<br />
<ul>
<li>Passing a <a href="http://en.wikipedia.org/wiki/Callback_(computer_programming)">callback</a>: in event oriented programming (like events in browser or node.js) it's useful to pass a specific callback to run after an event</li>
<li>Decorator: you wrap a function in another function (useful for access control, <a href="http://en.wikipedia.org/wiki/Memoization">memoization</a> etc.). In Python there is a <a href="http://freepythontips.wordpress.com/2013/12/05/python-decorators-finally-demystified/">programming feature called "decorator"</a> that makes easier to use this pattern.</li>
<li>Lazyness and partials: a function retuning not the actual result but another function that return the result. Useful to defer the actual task and if you need only a part of the result.</li>
</ul>
<br />
A brilliant example of what functional programming allows are <a href="http://legacy.python.org/dev/peps/pep-0333/#middleware-components-that-play-both-sides">wsgi middlewares</a> (in the Python world) and <a href="http://www.senchalabs.org/connect/">Connect middlewares</a> (in the node.js/Javascript world). As a matter of fact you can find a similar pattern in <a href="http://www.learningclojure.com/2013/01/getting-started-with-ring.html">Clojure ring</a>.<br />
<br />
<h3>
No objects - nothing enforce a local state</h3>
While the classic OO tries to encapsulate logic and state in the same entity (objects) a functional language keeps the two things well separated. This seems to be weird if you are used to OO but it has a lot of sense.<br />
It is easy and more predictable to test the code because you are sure there is no mutation of the local state during the method calls. Any function has arguments and output: there is no side effect based on value of "this". Actually in Clojure you can have side effects (mutating atoms for example) but this is an exception, not the rule. And this is clearly explicit.<br />
<br />
<h3>
Types and common interface</h3>
Standard maps/lists offer a common and simple interface to a lot of generic functions. In the classic OO every object has a different interface requiring specific code to interact with.<br />
<br />
In Javascript there are fantastic libraries like <a href="http://underscorejs.org/">underscore</a>, <a href="http://lodash.com/">lodash</a> to interact with standard arrays, objects (intended as maps in this case). You can also use hash maps and sets (Ecmascript 6). If you use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty">field descriptor</a> you can access getters and setters using the usual semantic.<br />
<br />
If you use Python, you should use more list an dict and less custom objects. Python can help you to keep the interface simpler using <a href="https://docs.python.org/2/howto/descriptor.html">field descriptors</a> and <a href="http://rgruet.free.fr/PQR26/PQR2.6.html#SpecialMethods">operators overloading</a>. There are a lot of functional built in (like zip, map, filter, list comprehensions ...) and the fantastic <a href="https://docs.python.org/2/library/itertools.html">itertools</a> library.<br />
<br />
<h3>
(Im)Mutability</h3>
An immutable object cannot be changed. Any change creates a new object.<br />
Mutable objects are a dangerous source of bugs. It's very easy to change an object passed as argument or by another function call without realizing that you are changing permanently the original object. This is <a href="http://stackoverflow.com/questions/2322068/python-passing-list-as-argument">a classic python gotcha</a>.<br />
It's not only a newbie's mistake: I have spotted the same nasty bug in <a href="https://github.com/jashkenas/backbone/issues/2470">Backbone.js</a>.<br />
<br />
In this case Clojure shines because everything is immutable by default.<br />
If you use Python, you should never mutate an object/dictionary/list passed as argument. If you really need to do that, <a href="https://docs.python.org/2/library/copy.html">clone it before</a>. Remember that list comprehensions, slice operator, and many built-ins return a brand new object and are safe. But it is better to check the doc because for example the "sort" method mutate the original list and the "sorted" built-in no.<br />
<br />
The same advice is valid for Javascript too. Check the usual underscore and lodash for <a href="http://lodash.com/docs#cloneDeep">clone and deepClone method</a>. Remember that some array method returns a brand new array. This is a shallow clone but sometimes is good enough. A fantastic option is to <a href="http://swannodette.github.io/mori/">use mori</a> a library to use Clojure(script) data type and sequences in Javascript.<br />
<br />
<h3>
Lazyness</h3>
Working with sequences in Clojure is amazing because any operation is deferred. This is memory and computational efficient. This is not a new thing if you use Python generators, itertools or the new Javascript <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators">iterators</a> and the<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of"> for-of</a> statement (ES 6). You can also use the Clojure sequence library with the aforementioned Mori library.<br />
<br />
<h3>
Development process</h3>
The functional approach (in my personal opinion) allows to develop in a more gradual way. While in the OO you have better results deciding the object model upfront, this is a bit complex in the long run. Some changes will require to move methods around the class hierarchy, create common base classes. And this kind of refactoring often invalidates a lot of unit tests too. So you often ends with small patches and code duplication.<br />
The evolution of a functional program can be a bit chaotic but changes are more granular and it's easier to reuse code without doing a lot of impact.<br />
<br />
<br />
This is the lesson learned: be more functional!<br />
<br />
P.S. This is a nice book if you want to improve your <a href="http://shop.oreilly.com/product/0636920028857.do">Functional Javascript</a> skillsAnonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-63172401831057429012014-03-23T16:19:00.004+01:002014-03-24T10:08:43.685+01:00When scripts should run?A quick walkthrough on when executing a script in the browser.<br />
<br />
A lot of examples on the internet show to wrap the code inside the jQuery "ready" event handler. It is not always the case. As a matter of fact you can execute a script in three different fashions:<br />
<ul>
<li>immediately</li>
<li>in the ready event handler</li>
<li>in the load event handler</li>
<li>under another event handler (but this is a different story though)</li>
</ul>
<h3>
Immediately</h3>
You can execute a script immediately if your script doesn't rely on the DOM, or rely on a fragment of DOM that is already been loaded (because is placed before your script).<br />
I think this is often the preferred case.<br />
<h3>
JQuery ready</h3>
The <a href="http://api.jquery.com/ready/">jQuery ready event</a> handler uses the <a href="https://developer.mozilla.org/en-US/docs/Web/Reference/Events/DOMContentLoaded">domcontentloaded event</a> but is guaranteed to be executed right away if this event is already been fired.<br />
<blockquote class="tr_bq">
"The handler passed to .ready() is guaranteed to be executed after the DOM is ready"</blockquote>
You should execute your script here if it runs <b>before </b>the fragment of DOM targeted by the script. Given this, if you are loading synchronously you should definitely put the script at the bottom of the HTML and just execute it immediately.<br />
<h3>
On load</h3>
There are rare cases when you need the CSS and images loaded before executing the script. A suggestion: <a href="http://sithmel.blogspot.co.uk/2011/10/how-to-write-good-javascript-widget.html">avoid it if you can</a>! For example you could (and definitely should) add the width and height attribute on any <i>img </i>and you can use a bit of inline css styling (in some edge case).<br />
In the few cases you really need this you can use the <i>load </i>event handler.<br />
<h2>
Asynchronous loading</h2>
If you use requirejs you really don't have a clue on when your script is executed. Luckily enough executing it immediately or inside a jQuery ready event is always safe. <b>It's not so using the load event.</b><br />
Your script could be loaded after the load event is already been fired. For this reason is a <a href="https://developer.mozilla.org/en-US/docs/Web/API/document.readyState">good idea to check it</a> and, in case, executing the script immediately.<br />
<br />
<pre class="prettyprint">function mycode(){
...
}
if (document.readyState == "complete"){
mycode();
}
else {
$(document).load(mycode);
}
</pre>
<h2>
Ajax fragments</h2>
When you load or create a new fragment of html sometimes you need to run the some code against the new fragment. This can be avoided the vast majority of time using <a href="https://api.jquery.com/on/">event delegation</a> adding your event handlers to a wrapper node (or to the document itself). If you really need to manage this I suggest to generate a custom event to manage these cases. For example:<br />
<br />
<pre class="prettyprint">// loading a fragment
var $result = $("#result")
$result.load("ajax/test.html", function (){
...
$result.trigger('nodeloaded');
...
});
...
// trigger on the document ready
// for the whole document
$(document).ready(function (evt){
$(document).trigger('nodeloaded');
});
...
// manage the event
$(document).on("nodeloaded", function (evt){
$('.your-class', evt.target).yourplugin();
});
</pre>
<br />
You can find <a href="http://demos.jquerymobile.com/1.2.1/docs/api/events.html">something similar used in jquery mobile</a>.<br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-73877439380324016062014-01-26T15:20:00.000+01:002014-01-26T15:20:15.897+01:00Requirejs: edge cases<br />
I have already written about <a href="http://requirejs.org/">requirejs</a> in <a href="http://sithmel.blogspot.co.uk/2013/07/client-side-modules-with-requirejs.html">this post</a>.<br />
In this new post I'm going to explain something you should know using require in complex environments.<br />
<h2>
You definitely should use the optimizer</h2>
<div>
<div>
Not using the optimizer in a production environment is highly discouraged.</div>
<div>
<br /></div>
<div>
To explain why, it can be useful thinking about how requirejs works:</div>
<div>
All the modules together form a dependency tree. The process of loading this tree is not completely parallel because requirejs is aware of specific dependencies <b>only after</b> loading a module.</div>
<div>
Requirejs optimizes this process executing a module only once and caching its result.</div>
<div>
The optimizer simply merges a group of modules in a single file and this makes the process much faster.</div>
</div>
<h2>
Inline scripts</h2>
<div>
Sometimes you'll need to add online scripts to the HTML. Doing this you should consider that your module could be not loaded yet. To fix this problem you can "require" your bundle as a dependency before running the code:<br />
<br /></div>
<pre class="prettyprint">require(["app"], function (){ // app is the name of the bundle
require(["yourdependency"], function (){
//inline script
});
});
</pre>
<div>
<br />
But there is another problem!<br />
Many examples shows to put your <a href="http://requirejs.org/docs/api.html#jsfiles">requirejs configuration inside the first module</a>.<br />
Sometimes you'll need a specific configuration to load your module (for example the baseUrl). But the configuration could not be available in your inline scripts (it's async!).<br />
<br />
There is a trick for this: requirejs allows to load the configuration in a synchronous way. You just need to define a global object "require" with the configuration right before loading requirejs script.<br />
<br />
<pre class="prettyprint"><script>
var require = {
"baseUrl": "http://example.com/js/lib",
"paths": {
"app": "main/app"
},
"shim": {
...
}
};
</script>
<script data-main="scripts/main.js" src="scripts/require.js"></script>
</pre>
<br />
<h2>
Mixing synchronous and asynchronous scripts</h2>
Performance wise all the scripts should be loaded asynchronously but real world applications need some compromise.<br />
<blockquote class="tr_bq">
A typical case is using the infamous document.write that wipes out the page when executed asynchronously.</blockquote>
<br />
In <a href="http://www.slideshare.net/nzakas/enough-withthejavascriptalready">this slideshow</a> <a href="https://twitter.com/slicknet">@slicknet</a> explains why loading scripts in different positions.<br />
<br />
But mixing synchronous and asynchronous script can be tricky. How can do it safely?<br />
<br />
First of all be careful where you load the requirejs script in the page. Many libraries check for the presence of the "define" global variable to be loaded asynchronously (using the <a href="https://github.com/umdjs/umd">UMD pattern</a>):<br />
<br />
<pre class="prettyprint">(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['b'], factory);
} else {
// Browser globals
root.amdWeb = factory(root.b);
}
}(this, function (b) {
//use b in some fashion.
// Just return a value to define the module export.
// This example returns an object, but the module
// can return a function as the exported value.
return {};
}));
</pre>
<br />
<br />
Jquery for example is AMD compatible so, if you load requirejs before jquery, this will behave in a different way.<br />
My suggestion is to concatenate all the synchronous files of your application in a single script, putting requirejs in the last position.<br />
<br />
Second problem: what happen if you are requiring a module and, in this particular page, this module is already loaded synchronously ?<br />
Requirejs will download the module again, this time as a module. This is not ideal so you need to trick requirejs prepending to our main module something like this:<br />
<br />
<pre class="prettyprint">define('jquery', [], function() { // jquery is loaded synchronously before this code
return jQuery;
});
</pre>
<br />
With the previous code requirejs will use the global jquery instead of requiring a new jquery module to the server.<br />
<blockquote class="tr_bq">
I am not advocating to load jQuery syncronously. This is just an example!</blockquote>
Another issue: the optimizer will include all the scripts that in your particular case are synchronous.<br />
You need to exclude these files from the optimization (this is an example using the <a href="https://github.com/gruntjs/grunt-contrib-requirejs">grunt-contrib-requirejs</a> task):<br />
<br />
<pre class="prettyprint"> requirejs: {
dev: {
options: {
"appDir": "www",
"baseUrl": "js/lib",
... other options ...
"modules": [
{
"name": "app",
"exclude": [
"jquery",
... other exclusions ...
]
},
... other bundles ...
]
}
},
</pre>
<br />
<h2>
Loading dynamically</h2>
In some case you'll want to download a different group of modules in a more dynamic way. Based on a certain configurations or user event (like in <a href="https://alexsexton.com/blog/2013/03/deploying-javascript-applications/">this example</a>)<br />
For doing this you can create one or more different bundles (with the optimizer).<br />
You need to remember to exclude from the bundles the resource that should be already loaded in the page (use the previous option).<br />
<h2>
Scripts not bundled</h2>
<div>
In some case you don't want to include your file in a bundle because is served by a CDN or directly from your application (for example <a href="http://socket.io/">socket.io</a>). The requirejs optimizator uses the "empty:" option to <a href="http://requirejs.org/docs/optimization.html#empty">manage this</a>:</div>
<div>
<br />
<pre class="prettyprint"> requirejs: {
compile: {
options: {
mainConfigFile: "static/js/main.js",
baseUrl: "static/js",
name: "main",
paths: {
'socketio': 'empty:',
'backboneio': 'empty:'
},
out: "static/js/main-built.js"
}
}
}
</pre>
</div>
<div>
<br /></div>
Requirejs is a very good choice to manage scripts loading and dependencies. I hope to have brought light in some of the dark spots of using this in a complex environment.</div>
Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-34356486979629742942013-10-18T18:51:00.002+02:002013-10-18T18:56:10.617+02:00Reusable javascript modules with BowerIn a <a href="http://sithmel.blogspot.co.uk/2013/07/client-side-modules-with-requirejs.html">previous post</a> I have used require.js to download javascript files and optimize them.<br />
Here I'll introduce a useful tool to manage your javascript and css assets. But first of all let's talk about modules definition in Javascript.<br />
<h3>
Universal module definition</h3>
At the moment there is no standard way to define a module (<a href="http://wiki.ecmascript.org/doku.php?id=harmony:modules">we are waiting for EcmaScript6</a>) but you can easily define your module in a way compatible with AMD, UMD and regular script import. Just pick one of these <a href="https://github.com/umdjs/umd">snippets</a>. I recommend to use one of those because declaring object in the global namespace is not a good practice and libraries like requirejs can help to load and optimize your scripts.<br />
<h3>
Bower</h3>
<a href="http://bower.io/">Bower</a> is a package manager designed for the web. You can install it using:<br />
<br />
<pre class="prettyprint">npm install -g bower
</pre>
<br />
And then start downloading your first package (jquery for example):<br />
<br />
<pre class="prettyprint">bower install jquery
</pre>
<br />
You'll find jquery in the jquery folder inside the "bower_components" folder. Bower packages are versioned so you could (and should) specify what version install.<br />
<pre class="prettyprint">bower install jquery#1.10.2
</pre>
If this package has some dependencies (other bower packages of course) those will be installed in the same "bower_components" folder. For those who are using node.js: be aware that <a href="https://github.com/bower/bower/issues/157">it is quite different from how npm works</a>! In this case all the dependencies are resolved and flatten in the same folder.<br />
<h3>
Bower package</h3>
A bower package, in fact, is a simple folder with a bower.json file:<br />
<pre class="prettyprint">{
"name": "mypackage",
"version": "0.0.1",
"main": [
"script.js",
"style.css"
],
"homepage": "https://github.com/User/myproject",
"authors": [
"Donald Duck "<donald.duck@gmail.com>";
],
"description": "an example package",
"keywords": [
"js",
"css",
],
"license": "private",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"lodash": "~1.3.1",
}
}
</pre>
Let's see the most important fields:<br />
<br />
<b>name</b>: the name of the package<br />
<b><br /></b>
<b>version</b>: the version of the package. It should be the same of the tag on git.<br />
<b><br /></b>
<b>main</b>: these are the main files of your project. For example a jquery repository has a lot of stuff (unbuilded file, test, fixtures etc.) but the main file is just jquery.js.<br />
<b><br /></b>
<b>dependencies</b>: an array of bower packages needed to run this one. You should always specific the version <a href="https://npmjs.org/doc/misc/semver.html">using this syntax</a>.<br />
<h3>
Bower repository</h3>
The published packages are just git tags with a bower.json. The bower command anyway uses a central repository to store the metadata (name, url of repository, keywords etc.). This repository is on https://bower.herokuapp.com/. If you have a public project you can publish on the central repository using:<br />
<pre class="prettyprint">bower register mypackage https://github.com/user/mypackage.git
</pre>
The versions available will be taken from the available tags on you repo.<br />
<h3>
Running your own repository</h3>
Let's say you are working on a project and you don't want to share every package you are working on.<br />
You can easily set up your <a href="https://github.com/neoziro/bower-registry">own bower registry</a> and configure bower to use it. You can do this putting a ".bowerrc" in your working directory (or in your home):<br />
<pre class="prettyprint">{
"registry": {
"search": ["http://bower.myserver.com", "https://bower.herokuapp.com/"],
"register": "http://bower.myserver.com"
}
}</pre>
<br />
This instructs bower to download packages from your server or the official one (the first has the priority), and to register the packages on your server only.<br />
<h3>
Automate everything !</h3>
Using <a href="https://github.com/yatskevich/grunt-bower-task">grunt-bower-task</a> you can create a build step that install all the dependencies required by your bower.json and copy all the files defined in "main" in a folder. If you use require.js you can also use <a href="https://github.com/yeoman/grunt-bower-requirejs">grunt-bower-requirejs</a> to build the require.js configuration automatically.<br />
<br />
I think this is enough to start working with bower. Enjoy!<br />
<br />
<br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-47773276058324715492013-07-25T15:03:00.001+02:002013-07-25T15:03:15.594+02:00Server side modules with ArchitectAfter my last notes on a <a href="http://sithmel.blogspot.it/2013/07/client-side-modules-with-requirejs.html">client side modular architecture using require.js</a> I'll talk about server side modules using <a href="https://github.com/c9/architect">Architect</a>.<br />
<h3>
Modules</h3>
<div>
In my opinion every application (not only the bigger ones) needs to be divided into interdependent modules. This helps a lot when you deal with testing and enhancing your application.</div>
<div>
In node.js applications, you will see and use this pattern very often:<br />
<br /></div>
<pre class="prettyprint">//import a module
var Database = require('./db');
// initialize a module using some options
var db = new Database('http://my.db.connection');
// initializing a module injecting a dependency
var usermanager = require('./usermanager')(db);
</pre>
<div>
<br /></div>
<div>
But what if you want to use this module in other applications ? In this case you should make an npm independent package for each module, of course. And, in the main application you would initialize each package in the proper order. This is repeatitive and tedious furthermore, when the number of the packages starts to get bigger, initializing each module in the proper order can be an issue.<br />
<h3>
From modules to plugins</h3>
Architect uses a simple declarative syntax to explain the interconnections between packages. It also starts the system in the correct order initializing each package just once.<br />
I recommend to read the <a href="https://github.com/c9/architect#architect">documentation on github</a>. It's very clear and detailed.<br />
<h3>
Express.js and Architect</h3>
I have set up a simple <a href="https://github.com/sithmel/expressjs-architect-demo">express.js/architect boilerplate</a> to show how to use Architect to make an extensible express.js application.<br />
<br />
<br />
I hope this will be useful ...<br />
<br />
P.S.<br />
A <a href="https://twitter.com/mastrolinux">friend of mine</a> suggest me to use a <a href="https://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt">git subtree</a> for each package. Nice idea!<br />
<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-90833198337405192242013-07-18T14:04:00.000+02:002014-05-20T10:29:17.620+02:00Client side modules with require.js<br />
In this post I will show you how to use <a href="http://requirejs.org/">Require.js</a> to split a project into simple and manageable modules.<br />
<h2>
UMD and AMD</h2>
I really didn't want to dive into differences of these 2 module systems but I think it's very important to realize of what module system we are talking about.<br />
<br />
UMD is the module system used by node.js. You define modules doing this (foobar.js):<br />
<br />
<pre class="prettyprint">module.exports = {
foo: 'bar'
};
</pre>
<br />
And load a module doing this:<br />
<br />
<pre class="prettyprint">var foobar = require('./foobar');
console.log(foobar.foo); // prints bar
</pre>
<br />
You can use UMD in the browser using <a href="http://browserify.org/">browserify</a>.<br />
If you usually work with Javascript in the browser you will notice two issues:<br />
<ul>
<li>the exports object would be overwritten every time by differents modules</li>
<li>it uses a synchronous approach</li>
</ul>
As a matter of fact it cannot work in the browser without a build step (and this is the browserify's task).<br />
<br />
AMD instead is designed from the ground up to work in the browser.<br />
<br />
Saying this I am not advocating one of these systems. They are both very useful even though they use different approaches.<br />
<br />
I started by explaining UMD because, unfortunately, both systems use a function called "require".<br />
Now that you can't be fooled anymore by this let's go on.<br />
<h2>
What is an AMD module</h2>
An <a href="http://requirejs.org/docs/whyamd.html">AMD module</a> must be contained in a single file and is encapsulated by the global function <b>define</b>.<br />
"define" takes two parameters: an array of dependencies and the actual module code.<br />
<br />
<pre class="prettyprint">//module3.js
define(['module1', 'module2'], function(module1, module2) {
'use strict';
var namespace = {};
return namespace;
});
</pre>
<br />
In this example I have defined a module called "module3". This module needs module1 and module2 to run.<br />
The return value of <b>define</b> will be returned if another module requires module3.<br />
module1 and module2 are resolved loading with AJAX module1.js and module2.js (both AMD modules).<br />
The job of require.js is basically to resolve the dependency tree and to make sure that every module would be run just once.<br />
<br />
A module has a pair of interesting properties:<br />
<br />
<ul>
<li>It is loaded the first time is required by another module.</li>
<li>not a single variable is added to the global namespace. For this reason you can even use different versions of the same library if you need to</li>
</ul>
<h2>
Bootstrap and configuration</h2>
After defining modules you will need to bootstrap your application (configure and load the first module). For doing this you will define in your page a script tag with require.js and the url of the bootstrap script (main.js).<br />
<br />
<pre class="prettyprint"><script data-main="/js/main" src="js/vendor/require.js"></script>
</pre>
<br />
After loading require.js it will load the "main.js" bootstrap using ajax. From this point every script will be loaded asynchronously and the <b>DOMContentLoaded</b> event (the jquery ready event) will be fired independently from the script loading.<br />
<br />
Main.js is made of 2 part. The first one is require.js configuration:<br />
<br />
<pre class="prettyprint">require.config({
baseUrl: "js/",
paths: {
jquery: 'vendor/jquery-1.9.1',
underscore: 'vendor/underscore',
backbone: 'vendor/backbone',
},
shim: {
underscore: {
exports: "_"
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'jquery.bootstrap': {
deps: ['jquery'],
exports: '$'
}
}
});
</pre>
<br />
Here are the most important parameters:<br />
<b>baseUrl</b>: this is the path used to resolve modules.. So if you require "module1" this will be loaded from "js/module1.js".<br />
<br />
<b>paths</b> is very useful to define script that are in some other paths. When you require "jquery" will be loaded the script "js/vendor/jquery-1.9.1.js".<br />
<h3>
Shims</h3>
Until now I assumed that every script is an AMD module but this is not always true.<br />
require.js shim option allows to wrap automatically a non AMD script into an AMD define.<br />
<br />
"<b>deps</b>" are dependencies to be injected and "<b>exports</b>" is the value to be returned:<br />
<br />
For example your backbone.js will become:<br />
<br />
<pre class="prettyprint">define(['underscore', 'jquery'], function (_, $){
..actual backbone.js source ...
return Backbone;
});
</pre>
<br />
The trick works even when you have to add attributes to an existing object (like defining a jquery plugin):<br />
<br />
<pre class="prettyprint">define(['jquery'], function ($){
$.fn.myJqueryPlugin = function (){
};
return $;
});
</pre>
<br />
This is the case of Twitter bootstrap.<br />
<br />
<h3>
require</h3>
The second part of main.js is the actual bootstrap. It loads and execute the first module.<br />
<br />
<pre class="prettyprint">require([
// Load our app module and pass it to our definition function
'app',
], function(App){
// The "app" dependency is passed in as "App"
App.start();
});
</pre>
The app module will start a chain of loading that will load the entire application's scripts.<br />
<br />
<h2>
The useful text plugin</h2>
The <a href="https://github.com/requirejs/text">text plugin</a> allows you to add text files as dependencies and this is very useful for client side templating.<br />
Just add "text" in the path config:<br />
<br />
<pre class="prettyprint">require.config({
...
paths: {
...
text: 'vendor/text'
</pre>
<br />
and you can load your templates:<br />
<br />
<pre class="prettyprint">define(['underscore', "text!templates/template.html"], function (_, template_html){
var template = _.template(template_html);
...
});
</pre>
<br />
You can also write your own plugin as <a href="http://requirejs.org/docs/plugins.html">described here</a>.<br />
<h2>
Script optimization</h2>
Using a lot of modules have an obvious drawback: the time spent to load these modules in the browser.<br />
Require.js comes with a <a href="http://requirejs.org/docs/optimization.html">useful tool for optimizing scripts</a>: r.js. It analizes, concatenates and minifies all the dependencies in one single script.<br />
<br />
I warmly recommend to use a build step to make this operation automatically.<br />
<br />
I use <a href="http://gruntjs.com/">grunt</a> and a <a href="https://npmjs.org/package/grunt-contrib-requirejs">grunt plugin</a> to automate everything.<br />
<br />
<h3>
Installing grunt and the require.js optimizer plugin</h3>
Grunt is structured in 2 different modules: "grunt-cli" and "grunt" and a series of plugins. "grunt-cli" can be installed globally:<br />
<br />
<pre class="prettyprint">npm install -g grunt-cli
</pre>
grunt and the plugins should be installed locally pinning a release version. This system allows to use different versions and plugins for each project.<br />
<pre class="prettyprint">npm install grunt --save-dev
npm install grunt-contrib-requirejs --save-dev
</pre>
The save-dev option adds the modules in the package.json under the "devDependencies" key and using the latest release.<br />
<pre class="prettyprint">...
"devDependencies": {
"grunt": "~0.4.1",
"grunt-contrib-requirejs": "~0.4.1"
},
...
</pre>
<div>
<br /></div>
<div>
You can also do this manually and launch "npm install".</div>
<h4>
Grunt configuration</h4>
Grunt needs a configuration file called <i>Gruntfile.js </i>this is an example from a past project of mine:<br />
<br />
<pre class="prettyprint">module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
requirejs: {
compile: {
options: {
mainConfigFile: "static/js/main.js",
baseUrl: "static/js",
name: "main",
paths: {
'socketio': 'empty:',
'backboneio': 'empty:'
},
out: "static/js/main-built.js"
}
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-requirejs');
// Default task(s).
grunt.registerTask('default', ['requirejs']);
};
</pre>
<br />
In the "requirejs" configuration I have:<br />
<b>mainConfigFile</b>: the path of the bootstrap script<br />
<b>baseUrl</b>: the same as configured in the main.js configuration<br />
<b>name</b>: the name of the main script<br />
<b>paths</b>: in this section I added a pair of script to not be included in the optimized script. These 2 files are served directly by my node module. You can also do the same for script served through a CDN or an external service.<br />
<b>out</b>: the output file<br />
<br />
"registerTask" allows me to launch the whole optimization step with the "grunt" command (using no options).<br />
<br />
At the end of the task I can load my new optimized script using:<br />
<br />
<pre class="prettyprint"><script data-main="/js/main-built" src="js/vendor/require.js"></script>
</pre>
I think this is all. Stay tuned for the next!<br />
<br />
Edit: If you liked this you'll probably be interested in this other blog post on <a href="http://sithmel.blogspot.co.uk/2014/01/requirejs-edge-cases.html">require.js edge cases</a>.Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-6220260761619914502013-07-01T21:44:00.000+02:002013-07-01T21:44:06.381+02:00Writing a real time single page application - server sideIn the <a href="http://sithmel.blogspot.it/2013/06/writing-real-time-single-page.html">first part</a> I highlighted how to take care of the frontend of a real time web app. Now I'll explain how to face the backend. As usual I will not dive into details, follow the links if you search in depth explanations.<br />
<h2>
Server side</h2>
I wrote the server side of my application using <a href="http://nodejs.org/">node.js</a> and a very lightweight framework called <a href="http://expressjs.com/">express.js</a><br />
The most important feature of this framework is the middleware system.<br />
This is a middleware:<br />
<br />
<pre class="prettyprint">function middleware(req, res, next){
...
}
</pre>
<br />
A middleware is a sort of russian doll. You can put a middleware inside another middleware (the "next" argument).<br />
<br />
A middleware can basically:<br />
<ul>
<li>call the next middleware in the chain ( next() )</li>
<li>call the output function ( res() )</li>
<li>change the input (req)</li>
<li>overwrite the output function (res)</li>
</ul>
<br />
You can use a middleware for:<br />
<ul>
<li>authenticate and get user informations</li>
<li>route to a specific middleware using the URL (req.url) and the method (req.method)</li>
<li>add a specific header to the HTTP response</li>
<li>etc.</li>
</ul>
<br />
Using middlewares is very common because it allows to build simpler and reusable components.<br />
<br />
Express.js gives you already a lot of middlewares but I used <a href="http://passportjs.org/">passportjs</a> to get a complete solution for authentication.<br />
In this example I will store users into couchdb using <a href="https://github.com/dscape/nano">nano</a><br />
<br />
<pre class="prettyprint">var config = require('./config'), // I used a config.json to store configuration parameters
db_url = config.db_protocol + "://" + config.db_user + ":" + config.db_password + "@" + config.db_url;
nano = require('nano')(db_url),
setupAuth = require('./auth'),
MemoryStore = express.session.MemoryStore,
sessionStore = new MemoryStore(), // nano will store user id in this session
passport = setupAuth(userdb);
var app = express(),
server = http.createServer(app);
// configure Express
app.configure(function() {
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({ store: sessionStore, secret: config.session_secret, key: config.session_key }))
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(__dirname + '/' + config.static_dir));
});
var ensureAuthenticated = function(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login');
};
// I check if a user is authenticated before accessing this URL
app.get('/', ensureAuthenticated, function(req, res){
res.render('index', { user: req.user});
});
// login
app.get('/login', function(req, res){
res.render('login', { user: req.user });
});
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login', failureFlash: true }),
function(req, res) {
res.redirect('/');
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/login');
});
server.listen(config.http_port);
</pre>
<br />
The auth module contains functions used by passport:<br />
<br />
<pre class="prettyprint">var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
crypto = require('crypto');
module.exports = function (userdb){
var getUserFromId = function(id, done) {
userdb.get(id, { revs_info: false }, function(err, body) {
if (!err){
return done(null, body);
}
else {
return done(null, false, { message: 'Invalid credentials' });
}
});
};
passport.getUserFromId = getUserFromId;
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(getUserFromId);
passport.use(new LocalStrategy(
function(username, password, done) {
var shasum = crypto.createHash('sha1').update(password),
key = [username, shasum.digest('hex')]
// this view's keys are [username, password]
// the password is hashed of course
userdb.view("user", "login", { keys: [key] }, function (err, body){
if (!err) {
if (body.rows.length){
// logged in !!!
return done(null, body.rows[0].value);
}
else {
return done(null, false, { message: 'Invalid credentials' });
}
}
else {
console.error(err);
return done(null, false, { message: 'Db error, try later' });
}
});
})
);
return passport;
};
</pre>
<br />
This example is explained in the <a href="http://passportjs.org/guide/configure/">passport documentation</a>.<br />
If you are careful you will notice that the getUserFromId function is called every time to get the complete object from the database (couchdb in this case).<br />
This is not very optimal and it's better to cache users for some time. I used this <a href="https://npmjs.org/package/memoizee">nice memoization module</a>:<br />
<br />
<pre class="prettyprint"> var memoize = require('memoizee');
// cache this function for optimal performance (2 minutes)
// https://npmjs.org/package/memoizee
getUserFromId = memoize(getUserFromId, { maxAge: 120000, async: true});
</pre>
<h2>
Backbone.io in the server</h2>
At this point I will define a backbone.io backend (as explained here: <a href="https://github.com/scttnlsn/backbone.io">https://github.com/scttnlsn/backbone.io</a>)<br />
<br />
<pre class="prettyprint">var items_backend = backboneio.createBackend();
</pre>
<br />
A backend is very similar to an express.js middleware.<br />
<br />
<pre class="prettyprint">var backendMiddleware1 = function(req, res, next) {
...
};
items_backend.use(backendMiddleware1);
items_backend.use(backendMiddleware2);
</pre>
<br />
Once defined, I will connect the backend.<br />
<br />
<pre class="prettyprint">var io = backboneio.listen(server, {items: items_backend});
</pre>
<br />
The io object which is returned by the listen method is a <a href="http://socket.io/">socket.io</a> object.<br />
<br />
When a websocket is connected for the first time it makes a basic handshake. This phase can be used to perform the passport authentication.<br />
<br />
<pre class="prettyprint">var cookie = require('cookie'),
cookiesig = require('cookie-signature');
// socket io authorization
io.set('authorization', function (data, accept) {
if (data.headers.cookie) {
data.cookie = cookie.parse(data.headers.cookie);
// cookies are signed for better security:
//
// s:name:signature
//
// s: is a prefix for signed cookies
// name is the cookie name
// signature is an hmac of the value
// doing this the client cannot change the cookie value
// without invalidate the cookie
if (data.cookie[session_key].indexOf('s:') === 0){
data.sessionID = cookiesig.unsign(data.cookie[session_key].slice(2), session_secret);
}
else {
data.sessionID = data.cookie[session_key];
}
// (literally) get the session data from the session store
sessionStore.get(data.sessionID, function (err, session) {
if (err || !session) {
// if we cannot grab a session, turn down the connection
accept('Cannot get sessionid', false);
} else {
// save the session data and accept the connection
data.session = session;
if("passport" in session && "user" in session.passport){
passport.getUserFromId(session.passport.user, function (err, user, message){
if(err || !user){
accept('Cannot find user', false);
}
else {
try{
data.user = user;
accept(null, true);
}
catch (e){
accept('Error: ' + e.toString(), false);
}
}
});
}
else {
accept('Session does not contain userid', false);
}
}
});
} else {
return accept('No cookie transmitted.', false);
}
});
</pre>
<br />
The tricky part here is to extract the session from the (signed) cookie. The <b>passport</b> and <b>sessionStore</b> objects are the same defined before for normal authentication.<br />
The backend authentication middleware can get the object through req.socket.handshake object:<br />
<br />
<pre class="prettyprint">var authMiddleware = function(req, res, next) {
var user = req.socket.handshake.user;
if (!req.user){
next(new Error('Unauthorized'));
}
else {
req.user = user;
next();
}
};
</pre>
<h2>
Backend events and channels</h2>
When a backend changes something backbone.io automatically broadcasts the change to every node connected (and triggers the events <a href="http://sithmel.blogspot.it/2013/06/writing-real-time-single-page.html">I talked before</a>).<br />
You often need to notify only a subset of clients. For this reason you can define channels. Every changes will be notified to clients connected to a certain channel.<br />
The channel <a href="https://github.com/sithmel/backbone.io">can be defined client side</a> but I added a useful feature to define channel server side, <a href="https://github.com/sithmel/backbone.io#channels">during the handshake</a>.<br />
<br />
There is also another case where you need to detect whether couchdb has been changed by another application.<br />
In this case I recommend you to use <a href="https://github.com/sithmel/backbone.io">my fork of backbone.io</a> because it supports channels.<br />
<br />
<h2>
Database and flow control with promises</h2>
The last piece of the application is talking to the database. In my application I used couchdb but it is really not important which database will you use.<br />
In the first paragraph I have underlined that a single resource operation could cause many operations in the backend. For this reason is very important using a smarter way to control the flow. I have chosen promises.<br />
<br />
Promises are a standard pattern to manage asynchronous tasks. I used this library: <a href="https://github.com/kriskowal/q">https://github.com/kriskowal/q</a><br />
The advantage of promises is to avoid the "pyramid of doom" of nested callbacks:<br />
<br />
<pre class="prettyprint">step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// Do something with value4
});
});
});
});
</pre>
<br />
And transform that in something more manageable:<br />
<br />
<pre class="prettyprint">Q.fcall(step1)
.then(step2)
.then(step3)
.then(step4)
.then(function (value4) {
// Do something with value4
}, function (error) {
// Handle any error from step1 through step4
})
.done();
</pre>
<br />
With promises, managing errors is very easy.<br />
This is an example using nano:<br />
<br />
<pre class="prettyprint">var getUser = function (id) {
var deferred = Q.defer();
userdb.get(id, {}, function(err, body) {
if (err) {
deferred.reject(new Error('Not found'));
}
else {
deferred.resolve(body);
}
});
return deferred.promise;
};
var getGroup = function (user) {
var deferred = Q.defer();
userdb.get(user.groupid, {}, function(err, body) {
if (err) {
deferred.reject(new Error('Not found'));
}
else {
deferred.resolve(body);
}
});
return deferred.promise;
};
function getGroupFromUserId(id, callback){
getUser(id)
.then(getGroup)
.then(function (group){
callback(group);
})
.done();
}
</pre>
<br />
<h2>
Backbone.io and databases</h2>
Backbone.io has some <a href="https://github.com/sithmel/backbone.io/tree/master/middleware">ready to use backend</a> and it is quite easy to write your own following the examples (I added the <a href="https://github.com/sithmel/backboneio.couchdbstorage">couchdb backend</a>).<br />
<br />
<h2>
The end?</h2>
This ends this whirlwind tour. I hope someone will find this useful.Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-84331905531366311752013-06-28T15:59:00.000+02:002013-07-04T09:36:30.028+02:00Writing a real time single page application - client side<h2>
Introduction</h2>
In this two parts guide I will explain how to use backbone.js, node.js and socket.io (and many others libraries) to write a single page/real time application.<br />
The guide's goal is to explain a system and I will not dive into details of every component, for this reason I suggest you to follow the links to get more informations.<br />
The topic is quite hot, so let's get started !<br />
<h2>
First step: defining resources</h2>
This part is the most boring but it is very important.<br />
In my application I should model every client server exchange using <a href="http://en.wikipedia.org/wiki/Representational_state_transfer">REST principles</a> (even though I will eventually use websockets instead of HTTP protocol).<br />
<br />
Following these principles I need to define resources.<br />
A resource is a well defined piece of information<br />
<br />
Every resource should:<br />
<ul>
<li>have an id (the URL)</li>
<li>be atomic (every exchange will be stateless)</li>
</ul>
A very important detail here is that resources don't need to match exactly with backend models (database schemas). For example a resource can be the result of a join between tables.<br />
<br />
I will leave to node.js backend to manage the differences between the actual db models and resources.<br />
The backend can update more than one model in a reliable way (and, in case, rolling back changes) while it's not so reliable do this directly from the client.<br />
<br />
From this point when I talk about models in the client I am talking about an HTTP resources.<br />
<br />
<h2>
Client side</h2>
For the front end I will use <a href="http://backbonejs.org/">backbone.js</a>. This library's task is basically to organize homogeneous models (they are resources!) into collections and keep these data synchronized with the UI (view) and the backend.<br />
This is a model:<br />
<br />
<pre class="prettyprint">var Item = Backbone.Model.extend({
defaults: {
"name": "",
"description": "",
},
validate: function(attrs, options) {
// return a string is attributes are wrong
}
});
</pre>
<br />
This is a simple collection:<br />
<br />
<pre class="prettyprint">var Items = Backbone.Collection.extend({
model: Item,
initialize: function (){
//...
}
});
</pre>
<br />
This collection contains a group of Items.<br />
A backbone collection usually has an url attribute that is used to download data from the server. So I'll rewrite the last collection:<br />
<br />
<pre class="prettyprint">var Items = Backbone.Collection.extend({
url: "/items"
model: Item,
initialize: function (){
//...
}
});
</pre>
<br />
Backbone.js uses AJAX by default to download and update collections. It uses the standard HTTP methods: GET POST PUT DELETE (optionally PATCH if supported).<br />
If the collection's url is "/items" the single model urls will be "/items/model_id".<br />
<h2>
Backbone.IO</h2>
One of the nicest feature of Backbone.js is that you can use different ways to save your data (overwriting the Backbone.sync function).<br />
In this example I will use <a href="https://github.com/scttnlsn/backbone.io">backbone.io</a>. This component sends data to the backend using a websocket or another available socket like transport (it uses <a href="http://socket.io/">socket.io</a> under the hood).<br />
<br />
<pre class="prettyprint">Backbone.io.connect();
var Items = Backbone.Collection.extend({
backend: 'items',
model: Item,
initialize: function (){
this.bindBackend();
//...
}
});
</pre>
<br />
Backbone.io.connect is used to initialize websockets. I replaced the url attribute with a backend identifier.<br />
I have also launched "bindBackend". This method connect backbone.io events to the collection events. So when the server broadcasts that something is changed the collection triggers the event and views are refreshed.<br />
<h2>
Views</h2>
Backbone.js main feature is to be unopinionated. Views, for example, are mostly boilerplate code. For this reason, you often need to build a more high level framework on top of it.<br />
It is usually a good idea to use something like <a href="http://marionettejs.com/">Marionette</a>.<br />
Anyway I tried to build something simpler. This is a model's view:<br />
<br />
<pre class="prettyprint">var ModelView = Backbone.View.extend({
initialize: function (){
this.initialEvents();
},
initialEvents: function (){
if (this.model){
this.listenTo(this.model, "change", this.render);
}
},
serialize: function (){
var attrs = _.clone(this.model.attributes);
attrs.id = attrs[this.model.idAttribute];
return attrs;
},
render: function (){
this.preRender();
this.$el.html(this.template(this.serialize()));
this.postRender();
return this;
},
preRender: function (){
// this runs before the rendering
},
postRender: function (){
// this runs after the rendering
}
});
</pre>
<br />
I also build a collection's view:<br />
<br />
<pre class="prettyprint">var CollectionView = Backbone.View.extend({
contentSelector: '.append-item-here',
// this is the class where I append the model views
viewOptions: {},
initialize: function (){
this.children = {}; // this will contain the models views
this.initialEvents();
},
initialEvents: function (){
if (this.collection){
this.listenTo(this.collection, "add", this.addChildView);
this.listenTo(this.collection, "remove", this.removeChildView);
this.listenTo(this.collection, "reset", this.render);
this.listenTo(this.collection, "sort", this.render);
}
},
render: function (){
var that = this;
this.preRender();
// I remove the old models views
_.each(this.children, function (view){
view.remove();
});
this.children = {};
// I rebuild the models views
this.collection.chain()
.each(function(item, index){
// every model view have a reference to its collection view
var options = _.extend({index: index, parentView: that}, that.viewOptions);
that.addChildView(item, that.collection, options);
});
this.postRender();
return this;
},
// I can use this to filter some view if it is necessary
filterView: function (model){
return true;
},
addChildView: function (item, collection, options){
// add a view to this.children trying to respect the sorting order
var index;
if (!this.filterView(item)){
return;
}
options.model = item;
if (options && options.index){
index = options.index;
}
else {
index = collection.chain().filter(this.filterView, this).indexOf(item).value();
if (index === -1){
index = 0;
}
}
var view = new this.itemView(options);
view.render();
this.children[item.cid] = view;
this.addToView(view, index);
},
removeChildView: function (item, collection, options){
this.children[item.cid].remove();
delete this.children[item.cid];
},
addToView: function (view, index){
// append a model view to the collection view in the correct sorting order
var $el = this.$el.find(this.contentSelector),
$children = $el.children();
if (index >= $children.length ){
$el.append(view.el);
}
else {
$children.eq(index).before(view.el);
}
},
preRender: function (){
// this runs before the rendering
},
postRender: function (){
// this runs after the rendering
}
});
</pre>
<br />
Extending these base views I can write a very simple item view:<br />
<br />
<pre class="prettyprint">var ItemView = ModelView.extend({
template: _.template(html_item),
tagName: 'div',
events: {
//...
}
});
</pre>
<br />
and, of course, a very simple collection view:<br />
<br />
<pre class="prettyprint">var ItemsView = CollectionView.extend({
itemView: ItemView,
el: $("#items"),
events: {
//...
}
});
</pre>
<br />
You should notice that I am using the underscore template engine (_.template).<br />
<h2>
Routing and bootstrapping</h2>
Backbone uses a router object to keep the state of your application. The state is represented by the current URL.<br />
<br />
<pre class="prettyprint">var Router = Backbone.Router.extend({
routes: {
"item/:id": "changeitem", // #item/id
},
changeitem: function(id) {
// this is called when the URL is changed
}
});
</pre>
<br />
Like others backbone primitives you can react to an URL change handling an event (inside a view for example):<br />
<br />
<pre class="prettyprint">var ItemsView = CollectionView.extend({
itemView: ItemView,
el: $("#items"),
initialize: function (){
this.listenTo(router, 'route:changeitem', this.update);
},
update: function (){
// ...
},
events: {
//...
}
});
</pre>
<br />
When every component is in place you can instance it and bootstrap the application.<br />
<br />
<pre class="prettyprint">var router = new Router(); // router instance
var items = new Items(); // collection instance
var itemsView = new ItemsView({collection: items}); // collection view instance
Backbone.history.start(); // this initialize the router
items.fetch(); // this loads initially the collection from the server
</pre>
<br />
The fetch method is not your best option if you want to load models for the first time. It's better to <a href="http://backbonejs.org/#FAQ-bootstrap">load models inline</a>.<br />
<div>
<br />
<br />
I hope everything is clear. Next part: <a href="http://sithmel.blogspot.it/2013/07/writing-real-time-single-page.html">the server side</a>!</div>
Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-62063340021767822692013-04-26T11:12:00.001+02:002013-04-26T11:12:23.477+02:00Objects in HTML5 canvas (part 3: interacting with images)This article is the third of a series of simple recipes that explains how to interact with objects in HTML5 canvas.<br />
<ul>
<li><a href="http://sithmel.blogspot.it/2013/04/objects-in-html5-canvas-part-1-drawing.html">part 1</a> (Drawing shapes)</li>
<li><a href="http://sithmel.blogspot.it/2013/04/objects-in-html5-canvas-part-2.html">part 2</a> (Interacing with objects)</li>
</ul>
In this part we will solve the last problem: how to interact with objects with irregular shapes.<br />
<br />
The simple answer is that you don't have to. You can wrap any image inside a rectangular box and detect where the image is transparent.<br />
<h3>
Introducing offscreen canvas</h3>
An offscreen canvas is a canvas outside the DOM. It's very useful for 2 reasons:<br />
<ul>
<li>you can do complex drawing once and reuse the result with just an instruction</li>
<li>you can use it to check where an image is transparent</li>
</ul>
<h3>
Let's draw</h3>
<div>
First of all I start with an images array (I used inline images, but you can use images urls):<br />
<br /></div>
<pre class="prettyprint">var objs = [
{name:'firefox',
src:'data:image/png;base64,iVBORw0KGgoAAAA.....',
x:30,
y:50,
angle:Math.PI/2,
},
{name:'opera',
src:'data:image/png;base64,iVBORw0KGgoAAAA...',
x:40,
y:30,
angle:Math.PI/4,
},
{name:'chrome',
src:'data:image/png;base64,iVBORw0...',
x:60,
y:100,
angle:-Math.PI/4,
}
];
</pre>
Then I draw each image inside an offscreen canvas and, then the offscreen canvas in the main canvas:<br />
<br />
<pre class="prettyprint">var i, obj;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (i = 0;i < objs.length;i++){
obj = objs[i];
(function (obj){
var img = new Image(); // Creating a new img element
img.onload = function(){
// It's very important to wait until the image loads!!!
// otherwise I have no clue of the real size of the image
ctx.save();
ctx.translate(obj.x, obj.y);
ctx.rotate(obj.angle);
obj.width = img.width;
obj.height = img.height;
//I attach the offscreen canvas to the object
obj.canvas = document.createElement("canvas");
obj.canvas.width = img.width;
obj.canvas.height = img.height;
obj.ctx = obj.canvas.getContext('2d');
// I draw the image on a off-screen canvas
obj.ctx.drawImage(img, 0 , 0);
// I draw the off-screen canvas on the main canvas
ctx.drawImage(obj.canvas, -img.width/2 , -img.height/2);
ctx.restore();
};
img.src = obj.src; // Set source path
}(objs[i]));
}
</pre>
<div>
Pay attention! I have applied transformations on the main canvas and not on each off-screen canvases.<br />
<h3>
Detecting mouse clicks</h3>
Let's finish detecting on which shape a user is pointing. <a href="http://sithmel.blogspot.it/2013/04/objects-in-html5-canvas-part-2.html">As usual</a> I check each object in reverse order and transform pointer coordinates. Then I must retrieve the image from the offscreen canvas and test the color on the resulting coordinates. If the alpha value is opaque the pointer is on the image.<br />
<br />
<pre class="prettyprint"> // I get a single pixel
imageData = obj.ctx.getImageData(p.x+(obj.width/2), p.y+(obj.height/2), 1, 1);
// imageData contains r,g,b,a
if(imageData.data[3] > 50){ // 50 is the threeshold
log.innerHTML = 'clicked on ' + obj.name;
return;
}
</pre>
I have taken a pixel from the canvas using <a href="https://developer.mozilla.org/en-US/docs/HTML/Canvas/Pixel_manipulation_with_canvas">getImageData</a>. This function returns an array that include 4 values for each pixel (just one in my case).<br />
The values are Red, Green, Blue and Alpha (transparency) and they have values between 0 and 255.<br />
I set a threeshold of 50 on alpha.<br />
This is the whole code:
<br />
<br />
<pre class="prettyprint">canvas.addEventListener('click', function (evt){
var rect = canvas.getBoundingClientRect(),
posx = evt.clientX - rect.left,
posy = evt.clientY - rect.top,
i = objs.length,
obj,p;
for (; i > 0; i--){
obj = objs[i-1];
// translate coordinates
p = new Point(posx, posy);
p.translate(-obj.x, -obj.y);
p.rotate(-obj.angle);
imageData = obj.ctx.getImageData(p.x+(obj.width/2), p.y+(obj.height/2), 1, 1);
if(imageData.data[3] > 50){
log.innerHTML = 'clicked on ' + obj.name;
return;
}
}
log.innerHTML = '';
}, false);
</pre>
<br />
And this is the results:<br />
<canvas id="mycanvas3">Your browser doesn't support canvas!</canvas><br />
<br />
<div id="mylog">
</div>
<script>
(function (){
var objs = [
{name:'firefox',
src:'data:image/png;base64,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',
x:30,
y:50,
angle:Math.PI/2,
},
{name:'opera',
src:'data:image/png;base64,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',
x:40,
y:30,
angle:Math.PI/4,
},
{name:'chrome',
src:'data:image/png;base64,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',
x:60,
y:100,
angle:-Math.PI/4,
}
];
var Point = function (x, y){
this.x = x;
this.y = y;
};
Point.prototype.rotate = function (angle){
var x, y;
x = this.x*Math.cos(angle) - this.y * Math.sin(angle);
y = this.x*Math.sin(angle) + this.y * Math.cos(angle);
this.x = x;
this.y = y;
return this;
};
Point.prototype.translate = function (x, y){
this.x = this.x + x;
this.y = this.y + y;
return this;
};
Point.prototype.distanceFrom = function (x, y){
var dx = this.x - x,
dy = this.y - y;
return Math.sqrt(dx*dx + dy*dy);
};
var canvas = document.getElementById('mycanvas3');
var log = document.getElementById('mylog');
var ctx = canvas.getContext('2d');
var i, obj;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (i = 0;i < objs.length;i++){
obj = objs[i];
(function (obj){
var img = new Image(); // Create new img element
img.onload = function(){
ctx.save();
ctx.translate(obj.x, obj.y);
ctx.rotate(obj.angle);
obj.width = img.width;
obj.height = img.height;
//offscreen canvas
obj.canvas = document.createElement("canvas");
obj.canvas.width = img.width;
obj.canvas.height = img.height;
obj.ctx = obj.canvas.getContext('2d');
obj.ctx.drawImage(img, 0 , 0);
ctx.drawImage(obj.canvas, -img.width/2 , -img.height/2);
ctx.restore();
};
img.src = obj.src; // Set source path
}(objs[i]));
}
canvas.addEventListener('click', function (evt){
var rect = canvas.getBoundingClientRect(),
posx = evt.clientX - rect.left,
posy = evt.clientY - rect.top,
i = objs.length,
obj,p;
for (; i > 0; i--){
obj = objs[i-1];
// translate coordinates
p = new Point(posx, posy);
p.translate(-obj.x, -obj.y);
p.rotate(-obj.angle);
imageData = obj.ctx.getImageData(p.x+(obj.width/2), p.y+(obj.height/2), 1, 1);
if(imageData.data[3] > 50){
log.innerHTML = 'clicked on ' + obj.name;
return;
}
}
log.innerHTML = '';
}, false);
}());
</script>
</div>
<div>
<br />
I hope this is helpful!
</div>
Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-59550766444238581602013-04-19T10:09:00.000+02:002013-04-19T12:28:19.000+02:00Objects in HTML5 canvas (part 2: interacting with objects)<a href="http://sithmel.blogspot.it/2013/04/objects-in-html5-canvas-part-1-drawing.html">In the previous part</a> I have shown how to draw simple shapes to the canvas.<br />
<br />
If you need to interact with a specific object in the canvas you have to face a problem.<br />
<br />
Using DOM you can click on a node and the browser manages the whole interaction (triggers the event, puts the DOM node in <a href="https://developer.mozilla.org/en-US/docs/DOM/event.target">event.target</a>, etc.).<br />
<br />
But, as explained before, once you have drawn an object to a canvas it become a part of the canvas. So the question is: how to detect when a user click on a specific shape?<br />
<h3>
Getting canvas related coordinates</h3>
<div>
The first obstacle to get through is getting where a user clicked on the canvas. When a user clicks you can get the mouse coordinates relative to the page and you must subtract the canvas position:<br />
<br /></div>
<pre class="prettyprint">canvas.addEventListener('click', function (evt){
var rect = canvas.getBoundingClientRect(),
posx = evt.clientX - rect.left,
posy = evt.clientY - rect.top;
...
</pre>
<div>
<br />
It's not too complicate, isn't it?</div>
<h3>
Checking in reverse order</h3>
<div>
In the <a href="http://sithmel.blogspot.it/2013/04/objects-in-html5-canvas-part-1-drawing.html">first part</a> of this tutorial I put the objects to draw in an array.<br />
<br /></div>
<pre class="prettyprint">var objs = [
{name:'shape2',
color:'green',
x:30,
y:50,
angle:Math.PI/2,
width: 40,
height: 50
},
{name:'shape1',
color:'red',
x:40,
y:30,
angle:Math.PI/4,
width: 30,
height: 40
}
];
</pre>
<div>
<br />
Then each shape is drawn in order over the previous.<br />
For this reason I have to check shapes in reverse order, from the object in foreground to the one in background.<br />
<br /></div>
<pre class="prettyprint"> for (i = objs.length - 1; i >= 0; i--){
obj = objs[i];
...
</pre>
<h3>
Pointer transformations</h3>
<div>
Now I have pointer coordinates and shape transformations:<br />
<br />
<pre class="prettyprint"> {name:'shape1',
color:'red',
x:40, // translation x
y:30, // translation y
angle:Math.PI/4, // rotation
width: 30,
height: 40
}
</pre>
<br />
How to detect if pointer coordinates are inside this shape?<br />
<br />
My solution is a bit imaginative but it seems to work well.</div>
<div>
I apply transformations to pointer coordinates. These are the opposite that I have applied to the object before drawing it to the canvas. Then I check if the new pointer coordinates are inside the object shape pretending that the shape has drawn around 0, 0.<br />
<br />
<pre class="prettyprint">// pointer transformation
p = new Point(posx, posy);
p.translate(-obj.x, -obj.y);
p.rotate(-obj.angle);
if (p.x > -(obj.width / 2) && p.x < obj.width / 2 && p.y > - (obj.height / 2) && p.y < obj.height / 2){
log.innerHTML = 'clicked on ' + obj.name;
return;
}
</pre>
<br /></div>
<div>
I wrote this simple object to help with transformations (<a href="http://sithmel.blogspot.it/2013/04/objects-in-html5-canvas-part-1-drawing.html">see part 1 for an explanation on transformations</a>):<br />
<br /></div>
<pre class="prettyprint">var Point = function (x, y){
this.x = x;
this.y = y;
};
Point.prototype.rotate = function (angle){
var x, y;
x = this.x*Math.cos(angle) - this.y * Math.sin(angle);
y = this.x*Math.sin(angle) + this.y * Math.cos(angle);
this.x = x;
this.y = y;
return this;
};
Point.prototype.translate = function (x, y){
this.x = this.x + x;
this.y = this.y + y;
return this;
};
</pre>
<div>
<br />
This is the whole code:<br />
<br />
<pre class="prettyprint">canvas.addEventListener('click', function (evt){
var rect = canvas.getBoundingClientRect(),
posx = evt.clientX - rect.left,
posy = evt.clientY - rect.top,
i, obj,p;
// I get the position of the pointer
// relative to the canvas
for (i = objs.length - 1; i >= 0; i--){ // cycling in inverse order
obj = objs[i];
// pointer transformation
p = new Point(posx, posy);
p.translate(-obj.x, -obj.y);
p.rotate(-obj.angle);
if (p.x > -(obj.width / 2) && p.x < obj.width / 2 && p.y > - (obj.height / 2) && p.y < obj.height / 2){
log.innerHTML = 'clicked on ' + obj.name;
return;
}
}
log.innerHTML = '';
}, false);
</pre>
<br />
This is the result (try to click on a shape):<br />
<canvas id="mycanvas">Your browser doesn't support canvas!</canvas>
<br />
<div id="mylog">
</div>
<script>
(function (){
var objs = [
{name:'shape2',
color:'green',
x:30,
y:50,
angle:Math.PI/2,
width: 40,
height: 50
},
{name:'shape1',
color:'red',
x:40,
y:30,
angle:Math.PI/4,
width: 30,
height: 40
}
];
var Point = function (x, y){
this.x = x;
this.y = y;
};
Point.prototype.rotate = function (angle){
var x, y;
x = this.x*Math.cos(angle) - this.y * Math.sin(angle);
y = this.x*Math.sin(angle) + this.y * Math.cos(angle);
this.x = x;
this.y = y;
return this;
};
Point.prototype.translate = function (x, y){
this.x = this.x + x;
this.y = this.y + y;
return this;
};
Point.prototype.distanceFrom = function (x, y){
var dx = this.x - x,
dy = this.y - y;
return Math.sqrt(dx*dx + dy*dy);
};
var canvas = document.getElementById('mycanvas');
var log = document.getElementById('mylog');
var ctx = canvas.getContext('2d');
var i, obj;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (i = 0;i < objs.length;i++){
obj = objs[i];
ctx.save();
ctx.translate(obj.x, obj.y);
ctx.rotate(obj.angle);
ctx.fillStyle = obj.color;
ctx.fillRect(
-(obj.width / 2),
-(obj.height / 2),
obj.width,
obj.height
);
ctx.restore();
}
canvas.addEventListener('click', function (evt){
var rect = canvas.getBoundingClientRect(),
posx = evt.clientX - rect.left,
posy = evt.clientY - rect.top,
i = objs.length,
obj,p;
for (; i > 0; i--){
obj = objs[i-1];
// translate coordinates
p = new Point(posx, posy);
p.translate(-obj.x, -obj.y);
p.rotate(-obj.angle);
if (p.x > -(obj.width / 2) && p.x < obj.width / 2 && p.y > - (obj.height / 2) && p.y < obj.height / 2){
log.innerHTML = 'clicked on ' + obj.name;
return;
}
}
log.innerHTML = '';
}, false);
}());
</script>
In the next part I'll add a more fine control on images using off-screen canvases. Stay tuned!!!
</div>
Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-84585847104086895632013-04-12T10:24:00.000+02:002013-04-19T12:29:39.996+02:00Objects in HTML5 canvas (part 1: drawing shapes)Canvas is an HTML5 element which can be used to draw graphics in the browser. Its <a href="https://developer.mozilla.org/en-US/docs/HTML/Canvas/Tutorial">API</a> is very low level and it has no notion of shapes or paths after they are drawn.<br />
It contains just a bidimensional matrix of points.<br />
<br />
This little guide introduces you to use effectively this API to write shapes and control them with the mouse.<br />
<br />
In this simple example we are going to draw some rectangular shapes.<br />
<h3>
Save the shapes to draw</h3>
The first thing to do is to save the shapes inside an array:<br />
<br />
<pre class="prettyprint">var objs = [
{name:'shape2',
color:'green',
x:30,
y:50,
angle:Math.PI/2,
width: 40,
height: 50
},
{name:'shape1',
color:'red',
x:40,
y:30,
angle:Math.PI/4,
width: 30,
height: 40
}
];
</pre>
Objects inside the array will be drawn one after the another with the first object in the background and the last object in foreground.<br />
<br />
<pre class="prettyprint">var i, obj;
ctx.clearRect(0, 0, canvas.width, canvas.height); //empty the canvas
for (i = 0;i < objs.length;i++){
obj = objs[i];
ctx.save(); // save context
ctx.translate(obj.x, obj.y); // apply transformations (I'll explain later)
ctx.rotate(obj.angle);
ctx.fillStyle = obj.color; // draw
ctx.fillRect(
-(obj.width / 2),
-(obj.height / 2),
obj.width,
obj.height
);
ctx.restore(); // restore the context saved
}
</pre>
The canvas coordinates start with 0,0 on the upper left corner. but you usually don't need to calculate the position of each shape. The canvas API gives the power to move the axis instead, and then draw the shapes around the point 0,0.<br />
<br />
<h3>
Introducing transformations</h3>
Transformations moves X and Y axis in the space. In the bidimensional world there are 3 main type of transformations:<br />
<br />
<ul>
<li>translation</li>
<li>skew</li>
<li>scale</li>
</ul>
<br />
They are usually represented with a <a href="http://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations">matrix</a><br />
<br />
x a1,b1,c1<br />
y a2,b2,c2<br />
1 0,0,1<br />
<br />
that express these three equations<br />
<br />
x' = a1x + b1y + c1<br />
y' = a2x + b2y + c2<br />
1 = 0x + 0y + 1<br />
<br />
These equations transform the original (x, y) coordinates in a new pair of coordinates (x', y').<br />
In bidimensional transformations the last equation is an identity (It is alway true).<br />
If you pay enough attention you will notice that you can define a matrix that doesn't change the original coordinates. This is called the identity matrix:<br />
<br />
x 1,0,0<br />
y 0,1,0<br />
1 0,0,1<br />
<br />
x' = 1x + 0y + 0 = x<br />
y' = 0x + 1y + 0 = y<br />
1 = 0x + 0y + 1<br />
<br />
Now starting with the identity matrix I'll try to explain transformations (I am not a mathematician so forgive me if the explanation is not formally correct or inaccurate).<br />
<br />
t1 and t2 are translations (in the x and y axis respectively). They move the axis left/right and up/down.<br />
A translation of 0 means no translation.<br />
In fact:<br />
<br />
x 1,0,t1<br />
y 0,1,t2<br />
1 0,0,1<br />
<br />
x' = 1x + 0y + t1 = x + t1<br />
y' = 0x + 1y + t2 = y + t2<br />
1 = 0x + 0y + 1<br />
<br />
s1 and s2 means scale. s1 scales the x axis while s2 scales the y axis. A multiplication of 1 means no scale.<br />
<br />
x s1,0,0<br />
y 0,s2,0<br />
1 0,0,1<br />
<br />
x' = s1x + 0y + 0 = s1*x<br />
y' = 0x + s2y + 0 = s2*y<br />
1 = 0x + 0y + 1<br />
<br />
sk2 and sk1 skew respectively the x and y axis<br />
<br />
x 1,sk1,0<br />
y sk2,1,0<br />
1 0,0,1<br />
<br />
x' = 1x + sk1y + 0 = x + sk1*y<br />
y' = sk2x + 1y + 0 = y + sk2*x<br />
1 = 0x + 0y + 1<br />
<br />
Now the question is: and the rotation ?<br />
<br />
The rotation is a combination of two transformations: skew and scale.<br />
The formula is<br />
<br />
x cos(angle),-sin(angle),0<br />
y sin(angle),cos(angle),0<br />
1 0,0,1<br />
<br />
x = x * cos(angle) + y * -sin(angle)<br />
y = x * sin(angle) + y * cos(angle)<br />
<br />
The order in which you apply the tranformation is very important:<br />
In fact if we first rotate and then translate we are translating the axis using the new rotated axis.<br />
<br />
As a rule of thumb, we usually need to:<br />
<ul>
<li>translate</li>
<li>rotate</li>
<li>scale</li>
</ul>
<h3>
Back to canvas</h3>
<div>
<div>
Canvas has a complete API for transformations. </div>
<div>
You can get more information on <a href="https://developer.mozilla.org/en-US/docs/Canvas_tutorial/Transformations">MDN</a>.</div>
<div>
In the previous example we applied translate and rotate tranformations.</div>
<div>
<br /></div>
<div>
These functions change the state of our drawing context. And affects all drawing operations so, in order to apply different transformations on each shape, we need to reset the drawing context every time.</div>
<div>
Otherwise the next transformation would be applied over the previous.</div>
</div>
<div>
<br /></div>
<div>
<br /></div>
<canvas id="mycanvas1">Your browser doesn't support canvas!</canvas><br />
<script>
(function (){
var objs = [
{name:'shape2',
color:'green',
x:30,
y:50,
angle:Math.PI/2,
width: 40,
height: 50
},
{name:'shape1',
color:'red',
x:40,
y:30,
angle:Math.PI/4,
width: 30,
height: 40
}
];
var canvas = document.getElementById('mycanvas1');
var ctx = canvas.getContext('2d');
var i, obj;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (i = 0;i < objs.length;i++){
obj = objs[i];
ctx.save();
ctx.translate(obj.x, obj.y);
ctx.rotate(obj.angle);
ctx.fillStyle = obj.color;
ctx.fillRect(
-(obj.width / 2),
-(obj.height / 2),
obj.width,
obj.height
);
ctx.restore();
}
}());
</script>
Next part I'll show how to interact with objects.Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-91091092401681195812013-01-11T23:46:00.000+01:002013-01-11T23:49:40.441+01:00Backbone.occamsrazor.jsDespite of the awful name<a href="https://github.com/sithmel/backbone.occamsrazor.js"> this is one of the nicest experiments</a> I ever done (at least in Javascript :-) ).<br />
I used occamsrazor.js to enable heterogeneous collections in backbone.js.<br />
<h2>
What ?</h2>
<div>
Backbone.js api is based on a <a href="http://en.wikipedia.org/wiki/Representational_state_transfer">RESTful architecture</a>. This architecture is based on "resources" and "verbs". Resources are atomic unit of information identified by URLS. Verbs are an unified interface to make basic operations on resources (GET, PUT, POST, DELETE). </div>
<div>
3 of these VERBS operates on a single resource (GET, PUT and DELETE). POST operate on a special resource called "collection". A POST request on a collection causes the creation of a new resource. Backbone.js simplify the work allowing to issue a GET request to a collection. It is very useful to fetch all the models contained in a collection.</div>
<div>
All of these client/server exchanges use basic JSON objects. These objects are just a bunch of attributes put together without a notions of the original model.</div>
<div>
In order to transform this bunch of attributes in a full fledged object (with methods, prototype etc.) the backbone collection makes a simple assumption: all the object contained in a collection must be of the same type (model).</div>
<h2>
A positive side effect: models and views are now plugins!</h2>
<div>
<a href="https://github.com/sithmel/occamsrazor.js">occamsrazor.js</a> enable collections formed by different models. As a (positive) side effect the models and model views are now plugins. I wrap the results in a <a href="https://github.com/sithmel/backbone.occamsrazor.js">simple backbone ehnancement</a>.</div>
<div>
<h3>
Models and collections</h3>
<div>
Let's start with a simple empty collection:</div>
<pre class="prettyprint"> var shapesCollection = new Backbone.Occamsrazor.Collection;
</pre>
<div>
With classic backbone collection you should define the model of the objects contained.</div>
<div>
With Backbone.Occamsrazor.Collection instead you set what kinds of models it could contain. But first of all define some validator:</div>
<pre class="prettyprint"> var hasWidth = function (obj){
if (obj instanceof Backbone.Model){
return obj.has('width');
}
return 'width' in obj;
},
hasHeight = function (obj){
if (obj instanceof Backbone.Model){
return obj.has('height');
}
return 'height' in obj;
},
hasRadius = function (obj){
if (obj instanceof Backbone.Model){
return obj.has('radius');
}
return 'radius' in obj;
},
hasWidthHeight = occamsrazor.chain(hasWidth, hasHeight);
</pre>
<div>
These validators take an object and returns a positive number if the object has a feature.</div>
<div>
In this case is convenient validate both a simple object and a Backbone model.</div>
<div>
You can find further explanations about validators in the <a href="https://github.com/sithmel/occamsrazor.js">occamsrazor.js documentation</a>.</div>
<pre class="prettyprint"> shapesCollection.model.addConstructor(hasWidth, Backbone.Model.extend({
getArea: function (){
var w = this.get('width');
return w*w;
}
}));
shapesCollection.model.addConstructor(hasWidthHeight, Backbone.Model.extend({
getArea: function (){
var w = this.get('width'),
h = this.get('height');
return w*h;
}
}));
shapesCollection.model.addConstructor(hasRadius, Backbone.Model.extend({
getArea: function (){
var r = this.get('radius');
return Math.round(Math.PI * r * r);
}
}));
</pre>
<div>
From now the collection can work with three kind of models transparently:</div>
<pre class="prettyprint"> shapesCollection.add([{width: 10, height: 5}, {width: 10}, {radius: 3}]);
console.log(shapesCollection.at(0).getArea()); // 50
console.log(shapesCollection.at(1).getArea()); // 100
console.log(shapesCollection.at(2).getArea()); // 28
</pre>
<div>
<h3>
Views</h3>
</div>
<div>
If you use an heterogeneus collection you will surely need something analogous for the views.</div>
<div>
You will probably need a different view for each model. This works almost the same as models. First create a collection view::</div>
<pre class="prettyprint"> var shapesView = new Backbone.Occamsrazor.CollectionView({collection: shapesCollection, el: $('#myid')});
</pre>
<div>
And then add the views:</div>
<pre class="prettyprint"> shapesView.itemView.addConstructor([null, hasRadius], Backbone.Occamsrazor.ItemView.extend({
tagName: 'div',
render: function (){
this.$el.html('The area of the circle is ' + this.model.getArea());
return this;
}
}));
shapesView.itemView.addConstructor([null, hasWidth], Backbone.Occamsrazor.ItemView.extend({
tagName: 'div',
render: function (){
this.$el.html('The area of the square is ' + this.model.getArea());
return this;
}
}));
shapesView.itemView.addConstructor([null, hasWidthHeight], Backbone.Occamsrazor.ItemView.extend({
tagName: 'div',
render: function (){
this.$el.html('The area of the rectangle is ' + this.model.getArea());
return this;
}
}));
</pre>
<div>
You should notice that I used Backbone.Occamsrazor.ItemView as view constructor function. This is nearly identical to Backbone.View: the only difference is in the way the model is passed to the costructor.</div>
<div>
This emphasizes the fact that you must pass the model as argument and allows occamsrazor to pick the right view for a that model.</div>
<div>
<br /></div>
<div>
<a href="https://github.com/sithmel/backbone.occamsrazor.js">On github there is a more in-depth documentation</a> <br />
<br />
<h2>
A simple example</h2>
</div>
</div>
<div>
I have prepared a simple demostration of the potential of this library <a href="http://sithmel.github.com/backbone.occamsrazor.js/example%20todomvc">here</a>. This is the classic todomvc application by <a href="https://github.com/addyosmani/todomvc">Addy Osmani</a>. The interesting part is in how it is simple adding plugins to enhance the application <b>without touching</b> the original code.<br />
<br />
I hope this makes clear how occamsrazor.js can be helpful !<br />
<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-31047712331912315092012-12-20T13:53:00.002+01:002012-12-20T15:15:09.033+01:00Occamsrazor.js 2.0 API ehnancement<div>
I released version 2.0 of <a href="https://github.com/sithmel/occamsrazor.js">occamsrazor.js</a>. My goal is to further simplify the API. This example is taken from my last post:<br />
<br /></div>
<pre class="prettyprint">//mediator
var pubsub = occamsrazor();
// from now a validator is a simple function
var is_archive = function (obj){
return 'getNumbers' in obj;
};
// the archive object
var archive = (function (){
var numbers = [];
return {
getNumbers: function (){
return numbers;
},
addItem: function (number){
numbers.push(number);
// this notify the event to the mediator
pubsub.publish('changed', this);
}
};
}());
// the printsum isn't changed
var printsum = (function (){
return {
print: function (n){
console.log(n);
},
sum_and_print: function (archive){
var i,sum = 0;
for(i = 0;i < archive.length; i++){
sum += archive[i];
}
this.print(sum);
}
};
}());
// subscribe the event
// subscribe is an alias of "add" and "on"
// the list of validators now is before the function
pubsub.subscribe(["changed",is_archive], function (evt, archive){
printsum.sum_and_print(archive.getNumbers());
});
//you can use a single string instead of a function as a validator
</pre>Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-48022296741603659162012-12-06T14:19:00.001+01:002012-12-06T14:19:10.612+01:00Mediator pattern with occamsrazor.js<div>
<i>The mediator design pattern can be very useful to decouple a group of objects. Let's see how.</i><br />
<i><br /></i></div>
<div>
Let's say we have two very simple objects. The first is an archive of numbers:<br />
<br /></div>
<pre class="prettyprint">var archive = (function (){
var numbers = [];
return {
getNumbers: function (){
return archive;
},
addItem: function (number){
archive.push(number);
}
};
}());
</pre>
<div>
<br />
The second object prints the sum of the numbers of the previous object:<br />
<br /></div>
<pre class="prettyprint">var printsum = (function (){
return {
print: function (n){
console.log(n);
},
sum_and_print: function (archive){
var i,sum = 0;
for(i = 0;i < archive.length; i++){
sum += archive[i];
}
this.print(n)
}
};
}());
</pre>
<div>
<br />
Ok. Now we need to recalculate and print the sum each time a new item is added to the archive. The first solution is straightforward:<br />
<br /></div>
<pre class="prettyprint">...
addItem: function (number){
archive.push(number);
// we call directly the function
printsum.sum_and_print(archive);
}
...
</pre>
<div>
<br />
This could be a solution but It has a drawback: we bind explicity the two objects together. With many objects and direct dependencies this could be very difficult to manage. Moreover, chaging or adding other dependencies could translate in changing extensively the (already tested) code.</div>
<h2>
The mediator</h2>
<div>
The mediator is an object that mediates between other objects. Instead of calling functions directly we notify events to the mediator. The mediator calls every function that is subscribed to a certain event.</div>
<div>
Let's rewrite the example using occamsrazor.js to build a mediator:<br />
<br /></div>
<pre class="prettyprint">//mediator
var pubsub = occamsrazor();
// we need 2 validators, one for the event and the second for the object
// this validate the type of the event
var is_changed_event = occamsrazor.validator(function (evt){
return evt === 'changed';
});
// this validate the type of the object
var is_archive = occamsrazor.validator(function (obj){
return 'getNumbers' in obj;
});
// the archive object
var archive = (function (){
var numbers = [];
return {
getNumbers: function (){
return numbers;
},
addItem: function (number){
numbers.push(number);
// this notify the event to the mediator
pubsub.publish('changed', this);
}
};
}());
// the printsum isn't changed
var printsum = (function (){
return {
print: function (n){
console.log(n);
},
sum_and_print: function (archive){
var i,sum = 0;
for(i = 0;i < archive.length; i++){
sum += archive[i];
}
this.print(sum);
}
};
}());
// subscribe the event
pubsub.subscribe(function (evt, archive){
printsum.sum_and_print(archive.getNumbers());
}, [is_changed_event,is_archive])
</pre>
<div>
<br />
Build a mediator with occamsrazor.js has another advantage: events are notified only for the objects that pass every validator.<br />
<br /></div>
<b>Next challenge: try to integrate occamsrazor.js with backbone.js </b><br />
<b>Stay tuned !
</b>Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-29236668160630854142012-10-30T17:23:00.001+01:002013-01-13T21:41:58.829+01:00Enhancing Backbone.js with Occamsrazor.js<blockquote class="tr_bq">
WARNING. This article is an early experiment ! <a href="http://sithmel.blogspot.it/2013/01/backboneoccamsrazorjs.html">This way</a> to enhance backbone.js is much better!</blockquote>
<br />
In this post I want to share a little experiment I made with Backbone.js and Occamsrazor.js. But before that I want to explain briefly what Backbone.js and Occamsrazor.js are.<br />
<h3>
Backbone.js</h3>
Backbone.js is a Javascript library useful to build structured UI. It's main role is to coordinate one or more models with their visual representations (views). It also manage the data exchange with the backend. (It is designed to be used in the frontend).<br />
The core of Backbone.js is made of three powerful types of object:<br />
<ul>
<li>models</li>
<li>collections</li>
<li>views</li>
</ul>
The model is an atomic unit of data. The collection is a group of objects that share the same model. The view keeps updated the visual representation of a model or a collection. The coordination between each these object is decoupled using events (<a href="http://addyosmani.com/resources/essentialjsdesignpatterns/book/">observer pattern</a>).<br />
<br />
The web is plenty of Backbone.js documentation. For example:<br />
<ul>
<li><a href="http://backbonejs.org/">The official web site</a></li>
<li><a href="http://addyosmani.github.com/backbone-fundamentals/">An interesting Book by Addy Osmani</a></li>
<li><a href="http://backbonetutorials.com/">A series of tutorials</a></li>
</ul>
<h3>
Occamsrazor.js</h3>
<a href="https://github.com/sithmel/occamsrazor.js">Occamsrazor.js</a> is a plugin system I wrote some time ago. I wrote about that <a href="http://sithmel.blogspot.it/2012/05/occamsrazorjs-javascript-component.html">here</a> and <a href="http://sithmel.blogspot.it/2012/08/api-design-in-javascript.html">here</a>.<br />
<br />
<h2>
Why use these libraries together ?</h2>
<div>
With Occamsrazor.js a collection can contain objects of different types. To say the truth, in my experiment there is only one type of model but it can behave differently based on its fields.<br />
For example we can decide which view is more appropriate.</div>
<h2>
The results</h2>
<div>
<a href="http://sithmel.github.com/b2dplayground/">The demo is here</a> (it is quite funny, I put in box2d and other libraries).<br />
The "<span style="background-color: white; color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; line-height: 16px; white-space: pre;">CanvasPlayView</span>" view is responsible to draw all of the bodies on a canvas. In the <b>render</b> method we found this:</div>
<div>
<br /></div>
<pre class="prettyprint"> models.bodies.each(function (element, index){
var pos, x, y, angle;
pos = positions[element.id];
if (! pos){
return;
}
x = pos.x;
y = pos.y;
angle = pos.angle;
canvasLayer.ctx.save();
canvasLayer.ctx.translate(x * canvasLayer.scale, y * canvasLayer.scale);
canvasLayer.ctx.rotate(angle);
plugins.drawer(element).render(canvasLayer);
canvasLayer.ctx.restore();
});
</pre>
<div>
<br />
"Plugin.drawer" is not a regular function. It's a plugin!<br />
This plugin is added in the rect module (the module containing all the plugins for the rectangular bodies) :<br />
<br /></div>
<pre class="prettyprint">plugins.drawer.add(function (element){
var GenericModelView = Backbone.View.extend({
render: function(canvasLayer){
var m = this.model,
scale = canvasLayer.scale,
w = m.get('width') * scale,
h = m.get('height') * scale;
canvasLayer.ctx.fillStyle = m.get('color');
canvasLayer.ctx.fillRect(
-(w / 2),
-(h / 2),
w,
h
);
return this;
}
});
return new GenericModelView({model:element });
}, plugins.validators.isBox);
</pre>
<div>
<br />
More info on how Occamsrazor.js works<a href="https://github.com/sithmel/occamsrazor.js"> are here</a>.<br />
<br />
I consider this experiment a step in the right direction.<br />
Soon I'll try to make further steps.<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-14985746932766463812012-08-09T00:04:00.001+02:002012-08-09T00:07:43.212+02:00API design in Javascript<br />
Three months ago I released an open source js library: occamsrazor.js<br />
I firmly believe that it is very useful. Sadly I haven't had any real feedback (excepts Simone Orsi who stated "Cool experiment!" ... Thank you ).<br />
I think It's important to listen to the feedback, even when there is not. Often people are too kind to give a sincere feedback when your work sucks. And if they don't understand what a library is for. They simply don't use it.<br />
<br />
I slowly understood that a real example is very important to understand how a library can be useful.<br />
So I started to work on a little demo (I'll release it as soon as possible).<br />
<br />
Using my own library I started to think: "wow the library is useful but the API sucks !!!"<br />
<br />
So I rewrote the API.<br />
<br />
These are the lessons I learned:<br />
<br />
<h2>
Remove all the feature except the fundamentals</h2>
I usually follow this design principle:<br />
"It seems that perfection is attained not when there is nothing more to add, but when there is nothing more to remove." [<a href="http://en.wikiquote.org/wiki/Antoine_de_Saint_Exup%C3%A9ry">Antoine de Saint Exupéry</a>]<br />
<br />
<br />
So I decided to cut some features and voilà ! Everything starts to make sense.<br />
<br />
<h2>
Keep it simple</h2>
A good API must be easy to understand and remember: this can be obtained using short but descriptive names.<br />
It is also important not to use an unusual calling scheme:<br />
<br />
<pre>var x = Lib.executeFunc('foo', bar);
</pre>
better:<br />
<br />
<pre>var x = Lib.foo(bar);
</pre>
<h2>
Add syntactic sugar</h2>
A good API must be elegant. For this reason I borrow some nice Javascript tricks from jQuery:<br />
<br />
<ul>
<li>chaining</li>
</ul>
If a method doesn't need to return a value is very useful if it returns the object itself. This pattern is called chaining. An example from jQuery:<br />
<br />
<pre>$('p').addClass('foo').fadeOut();</pre>
<ul>
<li>object as function</li>
</ul>
A function in Javascript is an object. Like other objects we can freely add properties: to make an example jQuery has many methods (ajax, each, map, grep) but, at the same time, is a function returning a jQuery object.<br />
<br />
<br />
<ul>
<li>signature polymorphism</li>
</ul>
Sometimes can be useful if a function acts differently when the parameters are different. If we don't pass an argument to a function that argument will be undefined. Furthermore every function has a local variable called "<a href="https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments">arguments</a>" (an array like structure) that contains all the arguments passed to the function.<br />
<br />
I hope this article will be useful.<br />
<br />
The new version of <a href="https://github.com/sithmel/occamsrazor.js">occamsrazor.js</a> is on githubAnonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-33638074385046945942012-05-15T15:43:00.002+02:002012-08-05T18:52:18.432+02:00Occamsrazor.js: A Javascript component registry<br />
<a href="http://en.wikipedia.org/wiki/Occam%27s_razor">Occam's Razor</a><br />
<i>This principle is often summarized as "other things being equal, a simpler explanation is better than a more complex one."</i><br />
<div style="text-align: right;">
http://en.wikipedia.org/wiki/Occam%27s_razor</div>
<br />
<br />
I am a <a href="http://plone.org/">Plone</a> developer. For those who do not know, Plone is a powerful CMS based on the <a href="http://zope.org/">Zope</a> application server (written in <a href="http://python.org/">Python</a>).<br />
<br />
One of the best features of this CMS is the <a href="http://wiki.zope.org/zope3/ComponentArchitecture">Zope Component Architecture (ZCA)</a>. It borns as a part of the Zope 3 project but It's a quite independent set of modules. <strong>ZCA</strong> allows you to build component based application.<br />
<div>
<br /></div>
<br />
I really missed a Javascript implementation of the ZCA or something similar. But I haven't found anything !!! Maybe, building complex and pluggable CMS in Javascript is not yet so common (no, I'm not surprised).<br />
<br />
<h2>
ZCA in Javascript ?</h2>
So I started to think to port the ZCA in Javascript, unfortunately It's not easily portable (It's written in Python and rely on Python inheritance implementation).<br />
Furthermore some aspects of the ZCA perplexed me. These are the dark spots of the ZCA (all in my own opinion):<br />
<br />
<br />
<h4>
<br />
<ul>
<li>Interfaces</li>
</ul>
</h4>
Interfaces are an unusual concept for dynamic languages like Python and Javascript. For example the concept of "interface" (design-by-contract programming) doesn't fit perfectly in an environment where you can create, delete or modify any attribute of an object at run time.<br />
Python and Javascript coding style privilege attribute checking over type checking. For this reason in the day-to-day work the main use of Zope interfaces is often to "mark" an object without enforcing a specific interface (intended as a subset of attributes/methods).<br />
<br />
<h4>
<ul>
<li>Interface inheritance</li>
</ul>
</h4>
Inheritance is a tricky matter.<br />
Zope interface implementation is tightly coupled with the Python inheritance implementation. While it's powerful, it is also quite complex to figure out in complex inheritance chains.<br />
Furthermore it's quite difficult to port the library in a different environment (for example Javascript).<br />
<br />
<h4>
<ul>
<li>Unnecessary functions and component types</li>
</ul>
</h4>
ZCA API could be simpler furthermore it uses an unusual and rich terminology (components, adapters, utilities etc.).<br />
<div>
<br /></div>
<h2>
occamsrazor.js</h2>
I eventually decided to work around some ideas and try to improve and simplify the ZCA.<br />
The result is <a href="https://github.com/sithmel/occamsrazor.js">Occamsrazor.js</a><br />
<br />
<a href="https://github.com/sithmel/occamsrazor.js">Occamsrazor.js</a> is a library useful to decouple a functionality from the code that provide it.<br />
<br />
<a href="https://github.com/sithmel/occamsrazor.js">Documentation and source code is here.</a><br />
<br />
I'm very curious to have some feedback ...<br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-88968409894934193632011-12-12T22:41:00.002+01:002011-12-12T22:51:08.256+01:00Adaptative web design: loading resources dinamicallyAdaptative web design (also known as responsive web design) is a tecnique that adapts web pages to the devices (desktops, tablets and phones).<br />
<br />
This tecnique uses many differents technologies. In this blog post I don't want to explain every detail of this tecnique but it could be useful reading an in-depth tutorial:<br />
<a href="http://webdesignerwall.com/tutorials/responsive-design-with-css3-media-queries">http://webdesignerwall.com/tutorials/responsive-design-with-css3-media-queries</a><br />
<br />
<span class="Apple-style-span" style="font-size: large;">The remaining Issue</span><br />
Using media queries to adapts a web page to mobile has a huge drawback. We load to a mobile phone every bit of the web page and then we hide text and shrink full size images and video. The results is great, but the speed ? Is it possible to avoid downloading all this unused stuff ?<br />
<br />
I think I found an elegant solution with these scripts <a href="https://github.com/sithmel/jQuery-decomment">https://github.com/sithmel/jQuery-decomment</a> .<br />
They are very simple (only 31 LOC) and are designed to work together. They are <b>doWhenVisible</b> and <b>decomment</b>.<br />
<br />
An example:<br />
Our Web page contains a very big and complex slideshow but in the mobile version of our web site we'll hide it using css (is very big and heavy to load).<br />
<br />
HTML<br />
<br />
<pre class="prettyprint"><div class="bigslideshow">
<!--
<img src="bigimage1.jpg" />
<img src="bigimage2.jpg" />
<img src="bigimage3.jpg" />
-->
</div>
</pre>
<div>
<br />
CSS</div>
<div>
<br /></div>
<pre class="prettyprint">@media screen and (max-width: 650px) {
.bigslideshow{
display: none;
}
}
</pre>
<br />
Javascript<br />
<br />
<pre class="prettyprint">$.doWhenVisible('.bigslideshow', function (){
this.decomment(); // remove the comments
this.jcarousel(); // initialize the slideshow
});
</pre>
<div>
<br /></div>
<div>
Very easy ! doWhenVisible execute a callback (only once) when a DOM element becomes visible (it checks every time the page is resized).<br />
Decomment obviously removes comments. Removing comments is just like adding dinamically DOM nodes. The browser reacts downloading the resources and rendering the whole.</div>
<div>
<br /></div>
We can go even further using a script loader to load our js only if effectively used (we use <a href="http://requirejs.org/">require.js</a> here):<br />
<br />
<pre class="prettyprint">$.doWhenVisible('.bigslideshow', function (){
require(["jquery.jcarousel.js"], function() {
//load jcarousel
this.decomment(); // remove the comments
this.jcarousel(); // initialize the slideshow
});
});
</pre>
<div>
</div>
Right now I'm very proud of these little scripts!<br />
<br />
<div>
</div>Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-54092841976454991912011-10-11T23:43:00.002+02:002011-12-28T11:33:58.466+01:00How to write a good Javascript widget<br />
Writing a Javascript widget is almost easy but I learnt at my expense that it may be tricky. So I wrote some simple rule that help to do the job well.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">1 - keep in mind: progressive ehnancement</span><br />
I already wrote about it, just a recap: write html semantically correct (your mark up MUST have sense even WITHOUT Javascript)<br />
<span class="Apple-style-span" style="font-size: large;">2 - do not use inline styling (use CSS)</span><br />
When your script are about to create your widget, find the outermost element and give to it a specifical class. Then do not attach style directly to the DOM elements, instead use CSS.<br />
In this way you can customize the style of your widget without touching a single line of Javascript.<br />
<span class="Apple-style-span" style="font-size: large;">3 - avoid FOUC</span><br />
Usually you modify the DOM after it is loaded. That way you may see the page while change. This is called <b>FOUC</b> (flash of unstyled content). You can avoid this hiding the DOM nodes you are about to change (using CSS) and show after (using Javascript).<br />
Just a suggestion: you can use a noscript tag to show what you have hidden before in case Javascript is not enabled.<br />
<span class="Apple-style-span" style="font-size: large;">4 - use a library</span><br />
A library like jQuery helps a lot with the DOM manipulation, It helps to forget the differences between the browsers. You can find a thorough guide to make a plugin <a href="http://fuelyourcoding.com/jquery-plugin-design-patterns-part-i/">here</a>.<br />
<span class="Apple-style-span" style="font-size: large;">5 - do not calculate size and position during the widget creation</span><br />
You can't assume what part of your page will be displayed and when. So you can't calculate position and size of elements at creation time. An example: I used this spinner widget on my application (<a href="http://btburnett.com/spinner/example/example.html">http://btburnett.com/spinner/example/example.html</a>). The widget calculates the input dimension and adds a lot of on-line styling for the little arrows. But in my application display the widget inside an hidden tab and all the size calculation returns wrong results.Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.comtag:blogger.com,1999:blog-7655119976553563716.post-67411604747504230852011-10-11T23:26:00.001+02:002011-10-11T23:26:43.399+02:00Javascript script injectionJavascript script injection is a tecnique that has two main uses. You can use to load and execute a script in asynchronous way and to build a sort of ajax call without using the xmlhttprequest object.<br />
<span class="Apple-style-span" style="font-size: large;">How the browser work</span><br />
Before showing how this tecnique works, let me explain how the javascript is loaded and executed in the browser.<br />
The main concept is that there is multiple threads for downloading the resources (a limited number for every subdomain) but <b>only one thread</b> of execution renders the page and execute the javascript code.<br />
The first thing a browser does when loading a page is starts to parse the page and create the DOM tree. When it meet an inline script this is executed right away (even if the DOM tree is not completely loaded). When the parser met an external resource, it stops and download them (mainly stylesheets, and external javascripts. Images are loaded asynchronously).<br />
<blockquote>
It's usually a good idea to put your script inside a function executed when the DOM is fully loaded, for example the <i>onload</i> event or jquery's <i>ready</i> event.</blockquote>
The external resources are executed as soon as they are loaded, in the order they appear in the page. Since the execution of code and page rendering use the same thread, during the scripts execution and loading the page stops to render and become unresponsive.<br />
<span class="Apple-style-span" style="font-size: large;">Download scripts asynchronously</span><br />
Javascript injections permits to load a script asynchronously and execute just after loaded. This technique use DOM manipulation to inject a script node in the DOM tree:<br />
<pre class="prettyprint">var script = document.createElement('script');
script.setAttribute('src', 'script.js'); // load the script
document.getElementsByTagName('head')[0].appendChild(script);
</pre>
This technique has some limitation: if you use it to download more than one script the order execution is not guaranteed. Some browser execute the code in order, others execute the code as the download is finished.<br />
There are many Javascript libraries trying to generalize this approach: give a look to <a href="http://yepnopejs.com/">YEPNOPE</a>, <a href="http://labjs.com/">LABJS</a>, <a href="http://requirejs.org/">REQUIRE.JS</a> (they use other techniques as well).<br />
<span class="Apple-style-span" style="font-size: large;">JSONP</span><br />
Another useful way to use this technique is to inject a call to a URL that respond with a <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>.<br />
<pre class="prettyprint">var script = document.createElement('script');
script.setAttribute('src', url + '?callback=handler');
// load the script
document.getElementsByTagName('head')[0].appendChild(script);
function handler(data){
//do stuff with data
}
</pre>
<span class="Apple-style-span" style="font-family: 'Times New Roman'; white-space: normal;">A JSONP is a JSON wrapped inside a function call. For example:</span>
<br />
<pre class="prettyprint">handler({...various data...});
</pre>
<span class="Apple-style-span" style="font-family: 'Times New Roman';"><span class="Apple-style-span" style="white-space: normal;">The script tag <b>execute </b></span></span><span class="Apple-style-span" style="font-family: 'Times New Roman';"><span class="Apple-style-span" style="white-space: normal;">the code (using our handler). This is a clever hack but it has some drawbacks:</span></span><br />
<br />
<ul>
<li>the way the callback is defined pollutes the namespace (It must be a window attribute)</li>
<li>the data can be sended using GET method and urlencoded (a limited amount of data)</li>
<li>downloading a file this way triggers many "busy indicators": the various way the browser has to say "I'm not finished yet". Read something of <a href="http://stevesouders.com/">Steve Souders</a> to learn more ...</li>
</ul>
<br />
A more "appropriate" tecnique to make cross site ajax request is <a href="http://www.kendoui.com/blogs/teamblog/posts/11-10-04/using_cors_with_all_modern_browsers.aspx">CORS</a> (but is not yet widely used).<br />
<span class="Apple-style-span" style="font-size: large;">Not only Javascript</span><br />
I read an interesting post of Stoyan Stefanov who investigates the injection of <a href="http://www.phpied.com/when-is-a-stylesheet-really-loaded/">stylesheets </a>.<br />
To say the truth the browsers load dinamically every type of external resources .... this is how the browser works ... and it's amazing :-)Anonymoushttp://www.blogger.com/profile/10409061315805603415noreply@blogger.com