how to use TDD in complex system? - javascript

So I have been making a game up to a point where I want to try TDD, so most of my working code don't have any test but I would like to try TDD for every new features.
My problem is my game comprises of tons of interdependent systems (sort of like I can't use the camera without the level in place, objects keep lots of references and initializing things take other things as argument). So just to test the fog system I need to initialize the level, the physic, the camera, the collision (because they all depend on each other to some degrees) and that create lots of duplications. Here's the code:
test( "shadow test", function() {
var b2world=new b2World(new b2Vec2(0, 0), false);
var contactListener = new collisionHandler.CollisionHandler(MASK_BITS);
b2world.SetContactListener(contactListener);
var map = gamejs.http.load('images/prot8.json');
var level = new Level.Level({
map: map,
size: 0.5,
nMaskBits: MASK_BITS.node,
nCategoryBits: MASK_BITS.player | MASK_BITS.birdy | MASK_BITS.innerBody,
world: b2world,
scale: SCALE});
var cam = new Camera.Camera({
lvlWid: this.level.width*SCALE*this.level.blockSize,
lvlHei: this.level.height*SCALE*this.level.blockSize,
yBand: 2,
maxSpeed: 20,
peerWindow: new b2Vec2(350, 300),
scrWid: scrWid,
scrHei: scrHei});
var shadow = new Shadow.Shadow({
width : 300,
height : 300,
level : level,
eye : new b2Vec2(600, 600),
});
ok( shadow.blit, "Shadow is extended from surface" );
ok( shadow.level, "Shadow has reference to the level" );
ok( shadow.eye, "Shadow has reference to player's eye" );
ok( (function() {
for (var i = 0; i < shadow.onScreenBlocks.length; i++) {
var rect = level.boxes[ shadow.onScreenBlocks[i] ];
//this is half finished
}
return true;
}), "Shadow do picks the blocks that are visible on screen" );
ok( (function() {
for (var i = 0; i < level.boxes.length; i++) if ( shadow.notProcessBlock(i) ) {
var rect = level.boxes[i];
if (rect.left < cam.offsetX //at this point I just realized that camera need to be setup in a more complex way...
}
return true;
}), "Shadow only process those blocks that are visible on screen" );
});
overall it just has a bad vibe to it. It's harder to wrap my mind around and harder to maintain I think.

When writing unit testing in a non-TDD way, you have to ask yourself for each piece of code you write: 'How can I test this'. This forces you to look at your code and make sure that all dependencies can be replaced when testing.
When doing TDD, this 'How can I test this' is baked in from the beginning. Introducing TDD in the middle of a project leads to problems when you can't replace all dependencies.
When Unit Testing, you need to make sure that you can test a unit in complete isolation and replace all dependencies with mocks or fakes. Then you can control all the inputs to your unit test so can you make sure that all code paths are tested.
To make unit testing work in your project you will have to refactor your code to really support unit testing.
I think TDD is not the main issue in this case. The question is if you want to use Unit testing at all and I think the answer to that question should be yes! Unit testing has many advantages. Now, you face the problem that the code you have written is hard to test. Making sure your code is testable is something that can be done after you've written your code but as you now experience that's quite hard. That's the point where TDD can help. TDD will make sure your code is easily testable so you can have all the benefits of unit testing.
I wrote a blog some time ago about unit testing.. maybe it can help: Unit Testing, hell or heaven?

Related

javascript games ThreeJS and Box2D conflicts?

I've been trying to experiment with box2d and threejs.
So box2d has a series of js iterations, I've been successful at using them so far in projects as well as threejs in others, but I'm finding when including the latest instance of threejs and box2dweb, threejs seems to be mis-performing when just close to box2dweb but maybe I'm missing something really simple, like a better way to load them in together, or section them off from one another?
I've tried a few iterations of the box2d js code now and I always seemed to run into the same problem with later versions of threejs and box2d together! - currently version 91 threejs.
The problem I'm seeing is very weird.
I'm really hoping someone from either the box2d camp or threejs camp can help me out with this one, please?
Below is a very simple example where I don't initialize anything to do with box2d, but just by having the library included theres problems and you can test by removing that resource, then it behaves like it should.
The below demo uses threejs 91 and box2dweb. It is supposed to every couple of seconds create a box or a simple sphere each with a random colour. Very simple demo, you will see the mesh type never changes and the colour seems to propagate across all mesh instances. However if you remove the box2dweb resource from the left tab then it functions absolutely fine, very odd :/
jsfiddle link here
class Main {
constructor(){
this._container = null;
this._scene = null;
this._camera = null;
this._renderer = null;
console.log('| Main |');
this.init();
}
init(){
this.initScene();
this.addBox(0, 0, 0);
this.animate();
}
initScene() {
this._container = document.getElementById('viewport');
this._scene = new THREE.Scene();
this._camera = new THREE.PerspectiveCamera(75, 600 / 400, 0.1, 1000);
this._camera.position.z = 15;
this._camera.position.y = -100;
this._camera.lookAt(new THREE.Vector3());
this._renderer = new THREE.WebGLRenderer({antialias:true});
this._renderer.setPixelRatio( 1 );
this._renderer.setSize( 600, 400 );
this._renderer.setClearColor( 0x000000, 1 );
this._container.appendChild( this._renderer.domElement );
}
addBox(x,y,z) {
var boxGeom = new THREE.BoxGeometry(5,5,5);
var sphereGeom = new THREE.SphereGeometry(2, 5, 5);
var colour = parseInt(Math.random()*999999);
var boxMat = new THREE.MeshBasicMaterial({color:colour});
var rand = parseInt(Math.random()*2);
var mesh = null;
if(rand == 1) {
mesh = new THREE.Mesh(boxGeom, boxMat);
}
else {
mesh = new THREE.Mesh(sphereGeom, boxMat);
}
this._scene.add(mesh);
mesh.position.x = x;
mesh.position.y = y;
mesh.position.z = z;
}
animate() {
requestAnimationFrame( this.animate.bind(this) );
this._renderer.render( this._scene, this._camera );
}
}
var main = new Main();
window.onload = main.init;
//add either a box or a sphere with a random colour every now and again
setInterval(function() {
main.addBox(((Math.random()*100)-50), ((Math.random()*100)-50), ((Math.random()*100)-50));
}.bind(this), 4000);
so the way im including the library locally is just a simple...
<script src="js/vendor/box2dweb.js"></script>
So just by including the box2d library threejs starts to act weird, I have tested this across multiple computers too and multiple version of both box2d (mainly box2dweb) and threejs.
So with later versions of threejs it seems to have some comflicts with box2d.
I found from research that most of the box2d conversions to js are sort of marked as not safe for thread conflicts.
Im not sure if this could be the cause.
I also found examples where people have successfully used box2d with threejs but the threejs is always quite an old version, however you can see exactly the same problems occurring in my example, when I update them.
So below is a demo I found and I wish I could credit the author, but here is a copy of the fiddle using threejs 49
jsfiddle here
.....and then below just swapping the resource of threejs from 49 to 91
jsfiddle here
its quite an odd one and maybe the two libraries just don't play together anymore but would be great if someone can help or has a working example of them working together on latest threejs version.
I have tried a lot of different box2d versions but always found the same problem, could this be a problem with conflicting libraries or unsafe threads?
but also tried linking to the resource include in the fiddles provided.
Any help really appreciated!!

Extending JavaScript Classes for use on Canvas

I am working on a project to help me gain a better understanding of JavaScript and building applications with it. I am currently making a game utilizing createjs. I'd like to extend the Shape "class" by adding some additional methods to it but can't seem to get my head around the issue.
I am not getting any runtime errors by this approach, however I think it's failing silently at a particular point.
My Subclass:
ShipGFX.prototype = new createjs.Shape();
function ShipGFX()
{
createjs.Shape.apply( this );
this.draw = function()
{
var g = this.graphics;
g.clear();
g.setStrokeStyle( 1 );
g.beginStroke( createjs.Graphics.getRGB( 0x00ff00 ) );
g.beginFill( createjs.Graphics.getRGB( 0x00ff00, 0.5 ) );
g.moveTo( 0, 0 );
g.lineTo( -5, -5 );
g.lineTo( 10, 0 );
g.lineTo( -5, 5 );
g.lineTo( 0, 0 );
g.endFill();
}
}
Where it's used:
var shape = new ShipGFX();
shape.draw();
So the console logs no errors and I can even log via the draw command. I think the issue is scope of this.graphics even though it doesn't fail there either. Is it a matter of scope? Is the this.graphics a reference to the prototype instance (if I understand that correctly). I'd ideally like to use Class-js as I have a better understanding of how it works but I can't even get this way to work.
Any input would be great. Thank you.
The reason why this was failing silently was because little did I realize that the draw function was preexisting in the createjs.Shape APIs. Changing the name to "render" solved my issue.

Best way to organize jQuery/JavaScript code (2013) [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
The Problem
This answer has been answered before but are old and not up to date. I have over 2000 lines of code in a single file, and as we all know this is bad practice, especially when i'm looking through code or adding new features. I want to better organize my code, for now and for the future.
I should mention that I'm building a tool (not a simple website) with lots of buttons, UI elements, drag, drops, action listeners/handlers and function in the global scope where several listeners may use the same function.
Example code
$('#button1').on('click', function(e){
// Determined action.
update_html();
});
... // Around 75 more of this
function update_html(){ .... }
...
More example code
Conclusion
I really need to organize this code for best use and not to repeat myself and be able to add new features and update old ones. I will be working on this by myself. Some selectors can be 100 lines of code others are 1. I have looked a bit at require.js and found it kinda repetitive, and actually writing more code than needed . I'm open to any possible solution that fit this criteria and link to resource / examples are always a plus.
Thanks.
I'll go over some simple things that may, or may not, help you. Some might be obvious, some might be extremely arcane.
Step 1: Compartmentalize your code
Separating your code into multiple, modular units is a very good first step. Round up what works "together" and put them in their own little encased unit. don't worry about the format for now, keep it inline. The structure is a later point.
So, suppose you have a page like this:
It would make sense to compartmentalize so that all the header-related event handlers/binders are in there, for ease of maintenance (and not having to sift through 1000 lines).
You can then use a tool such as Grunt to re-build your JS back to a single unit.
Step 1a: Dependency management
Use a library such as RequireJS or CommonJS to implement something called AMD. Asynchronous Module Loading allows you to explicitely state what your code depends on, which then allows you to offload the library-calling to the code. You can just literally say "This needs jQuery" and the AMD will load it, and execute your code when jQuery is available.
This also has a hidden gem: the library loading will be done the second the DOM is ready, not before. This no longer halts load-up of your page!
Step 2: Modularize
See the wireframe? I have two ad units. They'll most likely have shared event listeners.
Your task in this step is to identify the points of repetition in your code and to try to synthesise all this into modules. Modules, right now, will encompass everything. We'll split stuff as we go along.
The whole idea of this step is to go from step 1 and delete all the copy-pastas, to replace them with units that are loosely coupled. So, instead of having:
ad_unit1.js
$("#au1").click(function() { ... });
ad_unit2.js
$("#au2").click(function() { ... });
I will have:
ad_unit.js:
var AdUnit = function(elem) {
this.element = elem || new jQuery();
}
AdUnit.prototype.bindEvents = function() {
... Events go here
}
page.js:
var AUs = new AdUnit($("#au1,#au2"));
AUs.bindEvents();
Which allows you to compartmentalize between your events and your markup in addition to getting rid of repetition. This is a pretty decent step and we'll extend this further later on.
Step 3: Pick a framework!
If you'd like to modularize and reduce repetitions even further, there are a bunch of awesome frameworks around that implement MVC (Model - View - Controller) approaches. My favourite is Backbone/Spine, however, there's also Angular, Yii, ... The list goes on.
A Model represents your data.
A View represents your mark-up and all the events associated to it
A Controller represents your business logic - in other words, the controller tells the page what views to load and what models to use.
This will be a significant learning step, but the prize is worth it: it favours clean, modular code over spaghetti.
There are plenty of other things you can do, those are just guidelines and ideas.
Code-specific changes
Here are some specific improvements to your code:
$('.new_layer').click(function(){
dialog("Create new layer","Enter your layer name","_input", {
'OK' : function(){
var reply = $('.dialog_input').val();
if( reply != null && reply != "" ){
var name = "ln_"+reply.split(' ').join('_');
var parent = "";
if(selected_folder != "" ){
parent = selected_folder+" .content";
}
$R.find(".layer").clone()
.addClass(name).html(reply)
.appendTo("#layer_groups "+parent);
$R.find(".layers_group").clone()
.addClass(name).appendTo('#canvas '+selected_folder);
}
}
});
});
This is better written as:
$("body").on("click",".new_layer", function() {
dialog("Create new layer", "Enter your layer name", "_input", {
OK: function() {
// There must be a way to get the input from here using this, if it is a standard library. If you wrote your own, make the value retrievable using something other than a class selector (horrible performance + scoping +multiple instance issues)
// This is where the view comes into play. Instead of cloning, bind the rendering into a JS prototype, and instantiate it. It means that you only have to modify stuff in one place, you don't risk cloning events with it, and you can test your Layer stand-alone
var newLayer = new Layer();
newLayer
.setName(name)
.bindToGroup(parent);
}
});
});
Earlier in your code:
window.Layer = function() {
this.instance = $("<div>");
// Markup generated here
};
window.Layer.prototype = {
setName: function(newName) {
},
bindToGroup: function(parentNode) {
}
}
Suddenly, you have a way to create a standard layer from anywhere in your code without copy pasting. You're doing this in five different places. I've just saved you five copy-pastes.
One more:
// Ruleset wrapper for actions
var PageElements = function(ruleSet) {
ruleSet = ruleSet || [];
this.rules = [];
for (var i = 0; i < ruleSet.length; i++) {
if (ruleSet[i].target && ruleSet[i].action) {
this.rules.push(ruleSet[i]);
}
}
}
PageElements.prototype.run = function(elem) {
for (var i = 0; i < this.rules.length; i++) {
this.rules[i].action.apply(elem.find(this.rules.target));
}
}
var GlobalRules = new PageElements([
{
"target": ".draggable",
"action": function() { this.draggable({
cancel: "div#scrolling, .content",
containment: "document"
});
}
},
{
"target" :".resizable",
"action": function() {
this.resizable({
handles: "all",
zIndex: 0,
containment: "document"
});
}
}
]);
GlobalRules.run($("body"));
// If you need to add elements later on, you can just call GlobalRules.run(yourNewElement);
This is a very potent way to register rules if you have events that are not standard, or creation events. This is also seriously kick-ass when combined with a pub/sub notification system and when bound to an event you fire whenever you create elements. Fire'n'forget modular event binding!
Here is a simple way to split your current codebase into multiple files, using require.js.
I will show you how to split your code into two files. Adding more files will be straightforward after that.
Step 1) At the top of your code, create an App object (or whatever name you prefer, like MyGame):
var App = {}
Step 2) Convert all of your top-level variables and functions to belong to the App object.
Instead of:
var selected_layer = "";
You want:
App.selected_layer = "";
Instead of:
function getModified(){
...
}
You want:
App.getModified = function() {
}
Note that at this point your code will not work until you finish the next step.
Step 3) Convert all global variable and function references to go through App.
Change stuff like:
selected_layer = "."+classes[1];
to:
App.selected_layer = "."+classes[1];
and:
getModified()
to:
App.GetModified()
Step 4) Test Your code at this stage -- it should all work. You will probably get a few errors at first because you missed something, so fix those before moving on.
Step 5) Set up requirejs. I assume you have a web page, served from a web server, whose code is in:
www/page.html
and jquery in
www/js/jquery.js
If these paths are not exactly like this the below will not work and you'll have to modify the paths.
Download requirejs and put require.js in your www/js directory.
in your page.html, delete all script tags and insert a script tag like:
<script data-main="js/main" src="js/require.js"></script>
create www/js/main.js with content:
require.config({
"shim": {
'jquery': { exports: '$' }
}
})
require(['jquery', 'app']);
then put all the code you just fixed up in Steps 1-3 (whose only global variable should be App) in:
www/js/app.js
At the very top of that file, put:
require(['jquery'], function($) {
At the bottom put:
})
Then load page.html in your browser. Your app should work!
Step 6) Create another file
Here is where your work pays off, you can do this over and over.
Pull out some code from www/js/app.js that references $ and App.
e.g.
$('a').click(function() { App.foo() }
Put it in www/js/foo.js
At the very top of that file, put:
require(['jquery', 'app'], function($, App) {
At the bottom put:
})
Then change the last line of www/js/main.js to:
require(['jquery', 'app', 'foo']);
That's it! Do this every time you want to put code in its own file!
For your question and comments I'll assume you are not willing to port your code to a framework like Backbone, or use a loader library like Require. You just want a better way to orgainze the code that you already have, in the simplest way possible.
I understand it is annoying to scroll through 2000+ lines of code to find the section that you want to work on. The solution is to split your code in different files, one for each functionality. For example sidebar.js, canvas.js etc. Then you can join them together for production using Grunt, together with Usemin you can have something like this:
In your html:
<!-- build:js scripts/app.js -->
<script src="scripts/sidebar.js"></script>
<script src="scripts/canvas.js"></script>
<!-- endbuild -->
In your Gruntfile:
useminPrepare: {
html: 'app/index.html',
options: {
dest: 'dist'
}
},
usemin: {
html: ['dist/{,*/}*.html'],
css: ['dist/styles/{,*/}*.css'],
options: {
dirs: ['dist']
}
}
If you want to use Yeoman it will give you a boilerplate code for all this.
Then for each file itself, you need to make sure you follow best practices and that all the code and variables are all in that file, and don't depend on other files. This doesn't mean you can't call functions of one file from other, the point is to have variables and functions encapsulated. Something similar to namespacing. I'll assume you don't want to port all your code to be Object Oriented, but if you don't mind refactoring a bit, I'd recommend to add something equivalent with what is called a Module pattern. It looks something like this:
sidebar.js
var Sidebar = (function(){
// functions and vars here are private
var init = function(){
$("#sidebar #sortable").sortable({
forceHelperSize: true,
forcePlaceholderSize: true,
revert: true,
revert: 150,
placeholder: "highlight panel",
axis: "y",
tolerance: "pointer",
cancel: ".content"
}).disableSelection();
}
return {
// here your can put your "public" functions
init : init
}
})();
Then you can load this bit of code like this:
$(document).ready(function(){
Sidebar.init();
...
This will allow you to have a much more maintainable code without having to rewrite your code too much.
Use javascript MVC Framework in order to organize the javascript code in a standard way.
Best JavaScript MVC frameworks available are:
Backbone
Angular
CanJS
Ember
ReactJS
Selecting a JavaScript MVC framework required so many factors to consider. Read the following comparison article that will help you to select best framework based on the factors important for your project:
http://sporto.github.io/blog/2013/04/12/comparison-angular-backbone-can-ember/
You can also use RequireJS with the framework to support Asynchrounous js file & module loading.
Look the below to get started on JS Module loading:
http://www.sitepoint.com/understanding-requirejs-for-effective-javascript-module-loading/
Categorize your code. This method is helping me a lot and does work with any js framework:
(function(){//HEADER: menu
//your code for your header
})();
(function(){//HEADER: location bar
//your code for your location
})();
(function(){//FOOTER
//your code for your footer
})();
(function(){//PANEL: interactive links. e.g:
var crr = null;
$('::section.panel a').addEvent('click', function(E){
if ( crr) {
crr.hide();
}
crr = this.show();
});
})();
In your preferred editor (the best is Komodo Edit) you may fold in by collapsing all entries and you will see only the titles:
(function(){//HEADER: menu_____________________________________
(function(){//HEADER: location bar_____________________________
(function(){//FOOTER___________________________________________
(function(){//PANEL: interactive links. e.g:___________________
I would suggest:
publisher/subscriber pattern for event management.
object orientation
namespacing
In your case Jessica, divide the interface into pages or screens. Pages or screens can be objects and extended from some parent classes. Manage the interactions among pages with a PageManager class.
I suggest that you use something like Backbone. Backbone is a RESTFUL supported javascript library. Ik makes your code cleaner and more readable and is powerful when used together with requirejs.
http://backbonejs.org/
http://requirejs.org/
Backbone isn't a real library. It is meant to give structure to your javascript code. It is able to include other libraries like jquery, jquery-ui, google-maps etc. Backbone is in my opinion the closest javascript approach to Object Oriented and Model View Controller structures.
Also regarding your workflow.. If you build your applications in PHP use the Laravel library. It'll work flawlessly with Backbone when used with the RESTfull principle. Below the link to Laravel Framework and a tutorial about building RESTfull APIs:
http://maxoffsky.com/code-blog/building-restful-api-in-laravel-start-here/
http://laravel.com/
Below is a tutorial from nettuts. Nettuts has a lot of High Quality tutorials:
http://net.tutsplus.com/tutorials/javascript-ajax/understanding-backbone-js-and-the-server/
Maybe its time for you start implementing a whole development workflow using such tools as yeoman http://yeoman.io/. This will help control all your dependencies, build process and if you wanted, automated testing. Its a lot of work to start with but once implemented will make future changes a lot easier.

Destroy a Backbone.Router and all Side effects after Creation

Let's say you want to be able to create a Backbone.Router instance that calls Backbone.history.start, destroy all traces of it, and create it again.
I need to do this for unit testing.
The problem is that creating a Backbone Router has global implications. Like Backbone.history, which is undefined until you make it.
var ReversibleRouter = Backbone.Router.extend({
initialize: function(){
_buildAppPaths(this.options) // Implementation not shown. Builds the application paths
Backbone.history.start()
},
destroy: function(){
// TODO: Implement this
}
})
I'd like to be able to create and completely destroy so that I can do some unit testing on my application's implementation of the Router.
It it enough to call Backbone.history.stop() and set Backbone.history = undefined?
If you're looking to do more full stack integration types of testing, you can reset your application's router by using:
Backbone.history.stop()
Backbone.history = undefined // Could also probably use delete here :)
This has been successful so far--our tests probably instantiate and kill the router 5 times during our qunit testing. Works great.
Maybe mocking like #AdamTerlson is saying is the better way to go long term, but I'm still learning unit and integration testing philosophy. I don't mind having a full stack test or two, though.
This might be cheating, but I'm of the opinion you should be mocking functions outside of your unit of functionality that your testing and be especially sure your unit tests are not be allowed to hit the live DOM, server or the browser.
Look into SinonJS, it's great for this.
Here's an example of testing the initialize method of the router without ever affecting the browser history state.
test('...', function () {
// Arrange
var buildAppPaths = sinon.stub(ReversibleRouter.prototype, '_buildAppPaths');
var startHistory = sinon.stub(Backbone.history, 'start');
var options = {};
// Act
new ReversibleRouter(options);
// Assert
ok(buildAppPaths.calledWith(options));
ok(startHistory.calledOnce); // or ok(Backbone.history.start.calledOnce);
});
Under this approach, you have successfully asserted that all calls were made to external units of functionality, including params. At that point, you can trust that the external call will do its job (proven by additional unit tests).
To make calls outside of this one unit would be doing integration tests and not unit tests.

javascript packer versus minifier

I was wondering what the differences/benefits of the packer vs the minifier were, i.e. Should you deploy a packed or minified version in your web app?
Example code:
var layout = {
NAVVISIBLE : 1,
Init : function()
{
this.Resize();
},
Dimensions : function()
{
var d = document, s = self, w, h;
if (s.innerHeight)
{ w = s.innerWidth; h = s.innerHeight; }
else if (d.documentElement && d.documentElement.clientHeight)
{ w = d.documentElement.clientWidth; h = d.documentElement.clientHeight; }
else if (d.body)
{ w = d.body.clientWidth; h = d.body.clientHeight; }
return new Array(parseInt(w), parseInt(h));
},
Resize : function()
{
var dim = this.Dimensions();
try
{
$('tbl_container').width = px(dim[0] - 25);
$('row_container').height = px(dim[1] - 100);
$('dat_container').width = px(dim[0] - (this.NAVVISIBLE ? 275 : 25));
$('dat_container').height = px(dim[1] - 100);
}
catch(e) {}
},
GoSideways : function()
{
var nc = $('nav_container');
var dc = $('dat_container');
nc.style.display = this.NAVVISIBLE ? 'none' : '';
dc.width = px(parseInt(dc.width) + (this.NAVVISIBLE ? 250 : -250));
this.NAVVISIBLE ^= 1;
},
FrameLoad : function(url)
{
if (url)
content_frame.document.location = url;
}
};
minified:
var layout={NAVVISIBLE:1,Init:function()
{this.Resize();},Dimensions:function()
{var d=document,s=self,w,h;if(s.innerHeight)
{w=s.innerWidth;h=s.innerHeight;}
else if(d.documentElement&&d.documentElement.clientHeight)
{w=d.documentElement.clientWidth;h=d.documentElement.clientHeight;}
else if(d.body)
{w=d.body.clientWidth;h=d.body.clientHeight;}
return new Array(parseInt(w),parseInt(h));},Resize:function()
{var dim=this.Dimensions();try
{$('tbl_container').width=px(dim[0]-25);$('row_container').height=px(dim[1]-100);$('dat_container').width=px(dim[0]-(this.NAVVISIBLE?275:25));$('dat_container').height=px(dim[1]-100);}
catch(e){}},GoSideways:function()
{var nc=$('nav_container');var dc=$('dat_container');nc.style.display=this.NAVVISIBLE?'none':'';dc.width=px(parseInt(dc.width)+(this.NAVVISIBLE?250:-250));this.NAVVISIBLE^=1;},FrameLoad:function(url)
{if(url)
content_frame.document.location=url;}};
packed:
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('5 B={3:1,C:6(){2.n()},v:6(){5 d=k,s=y,w,h;9(s.u){w=s.A;h=s.u}r 9(d.a&&d.a.c){w=d.a.p;h=d.a.c}r 9(d.b){w=d.b.p;h=d.b.c}D z x(g(w),g(h))},n:6(){5 7=2.v();F{$(\'N\').8=4(7[0]-o);$(\'P\').m=4(7[1]-l);$(\'i\').8=4(7[0]-(2.3?E:o));$(\'i\').m=4(7[1]-l)}L(e){}},H:6(){5 t=$(\'I\');5 j=$(\'i\');t.J.G=2.3?\'Q\':\'\';j.8=4(g(j.8)+(2.3?q:-q));2.3^=1},M:6(f){9(f)O.k.K=f}};',53,53,'||this|NAVVISIBLE|px|var|function|dim|width|if|documentElement|body|clientHeight|||url|parseInt||dat_container|dc|document|100|height|Resize|25|clientWidth|250|else||nc|innerHeight|Dimensions||Array|self|new|innerWidth|layout|Init|return|275|try|display|GoSideways|nav_container|style|location|catch|FrameLoad|tbl_container|content_frame|row_container|none'.split('|'),0,{}))
Packed is smaller but is slower.
And even harder to debug.
Most of the well known frameworks and plugins are only minified.
Take a look at the google minifier: http://code.google.com/intl/en-EN/closure/compiler/
They offer a firebug plugin for debugging minified code.
Packer does more then just rename vars and arguments, it actually maps the source code using Base62 which then must be rebuilt on the client side via eval() in order to be usable.
Side stepping the eval() is evil issues here, this can also create a large amount of overhead on the client during page load when you start packing larger JS libraries, like jQuery. This why only doing minify on your production JS is recommend, since if you have enough code to need to do packing or minify, you have enough code to make eval() choke the client during page load.
For a good minifier, I would look to using Google's Closure Compiler
http://code.google.com/closure/compiler/
The SIMPLE_OPTIMIZATIONS mode is what I would recommend using, as it cleans whitespace/comments and munges(reduces) variables. It also does some simple code changes that basically amount to code clean up and micro optimizations. You can see more about this on the Getting Started with the Closure Compiler Application
or the checking out the packaged README.
YUI Compressor is another option(from Yahoo) but it doesn't reduce the file size as much as CC does.
There is also a tool from Microsoft, the name escapes me at the moment but that apparently delivers similar results to CC. That one could be a better or worse option, depending on your environment. I've only read about it in passing, so further investigation would be required.
If your server gzips files before sending them to the browser (which is very often the case) then packer is not the way to go. I've tested a number of files, and even though packer makes smaller files than minification, it makes larger zipped files. While I'm not an expert, I think the reason is fairly straight-forward.
A big part of zipping is to find repeated character sequences and replace them with a shorter place holder to be unpacked later. This is the same thing packer does, except zip algorithms are much more efficient. So when you pack a file you are in a way pre-zipping it, but with an algorithm that is less efficient than an actual zip file. This leaves less work for the zip algorithm to do, with a subsequent decrease in zipping efficiency.
So if you are zipping the files, then packer will actually produce larger downloads. Add to this the additional downsides of packer mentioned in the above answers, and there is really no good reason to use packer.
Both aims at lowering the size of JavaScript to enable fast download on the client browser.
Minifier only removes unnecessary things like white space characters and renaming variable to smaller names wherever possible. But a Packer goes one step further and does whatever it can do to minimize the size of JavaScript. For e.g. it converts source code to Base62 while preserving it's mappings to be evaluated by client.
Depending on the code packed, the packed solution ca lead to script-errors, while the minified will work.
So test with different browsers, after packing your code. If it doesn't work anymore, try the minified version, which always should work.
A "packer" is the same as a "minifier". The most common tool that calls itself a "packer" is http://dean.edwards.name/packer/ which gives the option (turned off by default) to base62 encode. Base62 encoding is probably a bad idea: https://stackoverflow.com/a/1351624/24267.

Categories

Resources