jueves, 24 de octubre de 2013

Displaying PDF's from HTTP and FILE protocols

I had to port an Apache Cordova HTML app to a HTML file that will be opened using the "file:///" protocol, as it will be opened doing double click on the HTML file, without any kind of web server.

During 5 hours of annoying fight, these was the wrong steps that i have followed:

  • PDF.js


    Without documentation and not too much good examples, i finally discarded this option.

  • Viewer.js


    An easy implementation of that library using "iframes" and simple HTML anchor links.

    But sadly, due to security reasons that avoid javascript to read local files, i had to discard this option too but keeping bookmarked this project.

  • Simple and successful "Object" element


    How i could be that blind? Using a Bootstrap Modal:

    $modalWrapper
         .find(".modal-body")
         .html(
          "<object width='100%' height='100%'"
          +" data='" + url + "'"
          +" type='" + contentType + "'>"+"<p>Ops! It seems that the browser could not read that file.</p></object>"
        );
    

lunes, 1 de julio de 2013

Mi preciado gruntfile.js

Si no conoces Grunt puede ser de ayuda que veas my presentación sobre nuevas herramientas front en Slid.us.

Estructura principal del contenido.

module.exports = function(grunt) {
    grunt.initConfig({
        //La configuración de tusp lugins y sus tareas va aquí
    });
    grunt.loadNpmTasks("nombre_de_grunt_plugin");

    grunt.registerTask("tarea_personalizada", ["nombre_de_grunt_plugin:tarea_especifica"]);
};

Para las siguientes descripciones de plugins voy a copiar y usar esta estructura, y al final de la entrada mezclaré todo el código en un fichero único.

Una ayuda para el desacoplamiento.

Puedes leer meta-información de tu package.json o definir la tuya propia, para desacoplar fácilmente la estructura de tu proyecto de tus tareas de grunt.

Para una fácil comprensión:

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),
    meta: {
      cssEndpoint: "site/css",
      tuktuk: {
        sourcesBasePath: "sources/tuktuk/sources"
      }
    },
    source: {
      tuktuk: {
        stylus: ["<%= meta.tuktuk.sourcesBasePath %>/stylesheets/tuktuk.*.styl"]
        //EQUIVALENTE A
        //stylus: ["sources/tuktuk/sources/stylesheets/tuktuk.*.styl"]
      }
    }
  });
};

Para mi, construir un bien desacoplado gruntfile ha sido un proceso continuo e iterativo, mi log de confirmaciones está lleno de "refactorización de gruntfile", y para cada persona esta meta-información puede ser distinta. Al final puedo decir que lo que tengo en mente es:

Usa meta para definir rutas y source para archivos específicos, así pues tus tareas usarán siempre un meta y los menos archivos específicos posibles.

El cotnenido completo de esta parte de meta-información que será utilizadas en las siguientes secciones:

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),
    meta: {
      cssEndpoint: "site/css",
      stylusSourceBase: "sources/stylus/",
      jsSourceBase: "sources/js/",
      libsJsSourcesBasePath: "sources/libs",
      libsJsEndPoint: "site/js/libs",
      tuktuk: {
        sourcesBasePath: "sources/tuktuk/sources",
        file: "tuktuk",
        cssEndpoint: "<%= meta.cssEndpoint %>/libs/tuktuk",
        jsEndpoint: "<%= meta.libsJsEndPoint %>"
      }
    },
    source: {
      tuktuk: {
        coffee: ["<%= meta.tuktuk.sourcesBasePath %>/tuktuk.coffee", "<%= meta.tuktuk.sourcesBasePath %>/tuktuk.*.coffee"],
        stylus: ["<%= meta.tuktuk.sourcesBasePath %>/stylesheets/tuktuk.*.styl"],
        theme: ["<%= meta.stylusSourceBase %>/theme.bu.styl"],
        icons: "<%= meta.tuktuk.sourcesBasePath %>/components/lungo.icon/lungo.icon.css"
      },
      stylus: {
          imports:["<%= meta.stylusSourceBase %>/imports.styl"],
          customization: ["<%= meta.stylusSourceBase %>/variables.styl","<%= meta.stylusSourceBase %>/customization.styl"]
      },
      libsJs: {
        jquery: ["<%= meta.libsJsSourcesBasePath %>/jquery.js"],
        require: ["<%= meta.libsJsSourcesBasePath %>/require.js"],
        zepto: ["<%= meta.libsJsSourcesBasePath %>/zepto.js"]
      }
    }
  });
};

Compilando los fuentes coffeescript y stylus de una librería externa.

El principal objetivo de esta tarea es compilar los fuentes de una librería externa en una tarea separada para ejecutarla solo cuando actualicemos los fuentes desde el repositorio original, o cuando modifiquemos la librería, algo que debemos evitar.

Esta sección es específica para Tuktuk por con sus consecuentes modificaciones puede ser usada para cualquier otro framework HTML somo Bootstrap o Zurb, en este caso el framework usa Stylus para compilar a CSS y Coffeescript para compilar a javascript.

Así que necesitamos instalar algunos plugins para compilar estos fuentes, prefiero usar los plugins contrib debido a que los actualizan más frecuentemente,:

npm install grunt-contrib-coffee --save-dev
npm install grunt-contrib-stylus --save-dev
npm install grunt-contrib-copy --save-dev

y esta es mi configuración de plugins para esta compilación:

module.exports = function(grunt) {
  grunt.initConfig({
    coffee: {
      engine: {
        options:{ join: true, },
        files: { 
          "<%= meta.tuktuk.jsEndpoint %>/<%= meta.tuktuk.file %>.js": [
            "<%= source.tuktuk.coffee %>"
          ] 
        }
      }
    },
    stylus: {
      tuktuk: {
        options: { compress: false},
        files: { 
          '<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.css': '<%= source.tuktuk.stylus %>' 
        }
      }
    },
    copy: {
      main: {
        files: [
          {
            src: '<%= source.tuktuk.icons %>',
            dest: "<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.icons.css"
          }
        ]
      }
    }
  });
  grunt.loadNpmTasks("grunt-contrib-coffee");
  grunt.loadNpmTasks("grunt-contrib-stylus");
  grunt.loadNpmTasks("grunt-contrib-copy");

  grunt.registerTask("tuktuk", ["notify:init_tuktuk_compilation", "stylus:tuktuk", "coffee", "notify:end_tuktuk_compilation"]);

};

La tarea coffee compila todos los fuentes en un único archivo Javascript.

La tarea stylus:tuktuk compila cada archivo styluss que se encuentre en sources/tuktuk/sources/stylesheets/tuktuk.*.styl a un archivo CSS de mismo nombre con el contenido sin comprimir.

La última tarea copy copiará la CSS de iconos de los fuentes de la librería a su destino en el sitio.

Compilando mis fuentes Stylus.

El framework Tuktuk tiene archivos separados con propósitos de personalización y he decidido mover esos fuentes del framewrok a mi carpeta Stylus para acceder fácilmente a ellos.

También tengo 2 tareas, una para entorno de desarrollo cuando quiero cada archivo CSS por separado, y otra para producción donde quiero tener un único archivo CSS.

En el entorno de desarrollo no quiero tener que arrancar manualmente las tareas cada vez que actulice un fichero, por lo que necesito una tarea que automáticamente ejecuta mis tareas cuando modifique un archivo específico. Por lo que necesitamos instalarla:

npm install grunt-contrib-watch

Así, el contenido de mi gruntfile para estas tareas es:

module.exports = function(grunt) {
  grunt.initConfig({
    stylus: {
      develop: {
        options: { compress: false},
        files: {
            '<%= meta.cssEndpoint %>/imports.css': '<%= source.stylus.imports %>',
            '<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.theme.css': '<%= source.tuktuk.theme %>',
            '<%= meta.cssEndpoint %>/customization.css': '<%= source.stylus.customization %>'
        }
      },
      production: {
        options: {compress: true },
        files: { 
          '<%= meta.cssEndpoint %>/profile.min.css':[
            '<%= source.stylus.imports %>',
            '<%= source.tuktuk.stylus %>',
            '<%= source.tuktuk.theme %>',
            '<%= source.stylus.customization %>'
          ] 
        }
      }
    },
    copy: {
      main: {
        files: [
          {
            src: '<%= source.tuktuk.icons %>',
            dest: "<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.icons.css"
          },
          { expand: true,
            cwd: "<%= meta.jsSourceBase %>",
            src: ["**"],
            dest: "<%= meta.jsDestBase %>"
          }
        ]
      }
    },
    cssmin: {
      combine: {
        files: {
          '<%= meta.cssEndpoint %>/profile.min.css': [
              '<%= meta.cssEndpoint %>/profile.min.css',
              '<%= meta.tuktuk.cssEndpoint %>/<%= meta.tuktuk.file%>.icons.css'
            ]
        }
      }
    },
    watch: {
      stylus: {
        files: ['<%= source.tuktuk.theme %>', '<%= source.stylus.customization %>'],
        tasks: ["stylus"]
      }
    }
  });
  grunt.loadNpmTasks("grunt-contrib-stylus");
  grunt.loadNpmTasks("grunt-contrib-copy");
  grunt.loadNpmTasks("grunt-contrib-watch");

  grunt.registerTask("develop", ["stylus:develop"]);
  grunt.registerTask("production", ["stylus:production", "cssmin", "copy"]);

};

La tarea watch, bajo una propiedad con nombre de tarea, tiene la lista de archivos que observar por cambios para ejecutar las tareas consecuentes

Compilando HTML con Jade.

Instalar el plugin:

npm install grunt-contirb-jade --save-dev

Y como en la sección de Stylus, tengo 2 tareas una para producción y otra para desarrollo, y una tarea de observación. Por lo que he aquí el contenido (perdonar por usar otro resaltador de código, no sé por que pero el que suelo usar no me funciona con este contenido):

 module.exports = function(grunt) {  
  grunt.initConfig({  
   jade: {  
    develop: {  
     options:{  
      pretty: true,  
      data: function(){return {developing: "true"}; }  
     },  
     files:[{ expand: true, src: "*.jade", dest: "site/", ext: ".html", cwd: "sources/jade/pages" }]  
    },  
    production: {  
     options:{  
      pretty: false,  
      data: function(){return {developing: "false"}; }  
     },  
     files:[{ expand: true, src: "*.jade", dest: "site/", ext: ".html", cwd: "sources/jade/pages" }]  
    }  
   },  
   watch: {  
    jade: {  
     files: ["sources/jade/**/*.jade"],  
     tasks: ["notify:init_develop_compilation", "jade:develop"]  
    }  
   }  
  });  
  grunt.loadNpmTasks("grunt-contrib-jade");  
  grunt.loadNpmTasks("grunt-contrib-watch");  
  grunt.registerTask("default", ["jade:develop", "watch"]);  
  grunt.registerTask("production", ["jade:production"]);  
 };  

Cada tarea jade recibe un objeto que uso en los fuentes de Jade para renderizar distintos bloques de contenido.

He estructurado mis fuentes de jade de esta forma: I have structured my Jade sources in this way:

  • jade
    • extensible
      • tuktuk_layout.jade
        !!! 5
        html(lang='en')
          include ../includes/tuktuk/header
          body
            block content
            include ../includes/tuktuk/footer-scripts
        
    • includes
      • tuktuk
        • footer-scripts.jade
        • header.jade
          meta(charset='utf-8')
          meta(name='viewport', content='width=device-width, initial-scale=1, maximum-scale=1')
          - if (developing == "true")
              link(rel='stylesheet', href='css/imports.css')
              link(rel='stylesheet', href='css/libs/tuktuk/tuktuk.css')
              link(rel='stylesheet', href='css/libs/tuktuk/tuktuk.theme.css')
              link(rel='stylesheet', href='css/libs/tuktuk/tuktuk.icons.css')
              link(rel='stylesheet', href='css/customization.css')
          - else
              link(rel='stylesheet', href='css/profile.min.css')
          
    • pages
      • index.jade
        extends ../extensible/tuktuk_layout
        
        block content
          section.padding.bck.color
              .row.main_hero
        

Growl notify para no tener que mirar el terminal.

Uno de los problemas de Grunt es que como és una herramienta de Node se lanza desde el terminal, así que la información de salida de muestra también en el terminal, y no me gusta tener otra ventana abierta.

Y aquí es donde grunt-notify viene a ayudar. Requiere Growl o Growl para Windows (en sistemas Windows aseguraros de que la ruta a Growl está incluida en la variable PATH del sistema). Así que instalarlo!:

npm install grunt-notify --save-dev

Y en el gruntfile:

module.exports = function(grunt) {
  grunt.initConfig({
    notify: {
      init_develop_compilation: {
        options: {
          title: "Init DEV compilation",
          message: "Tasks: stylus:develop, jade:develop, copy, watch"
        }
      },
      end_develop_compilation: {
        options: {
          title: "Finished DEV compilation",
          message: "Tasks: stylus:develop, jade:develop, copy, watch"
        }
      },
      init_production_compilation: {
        options: {
          title: "Init PRO compilation",
          message: "Tasks: jade:production, uglify, stylus:production, cssmin"
        }
      },
      end_production_compilation: {
        options: {
          title: "Finished PRO compilation",
          message: "Tasks: jade:production, uglify, stylus:production, cssmin"
        }
      },
      init_tuktuk_compilation: {
        options: {
          title: "Init Tuktuk compilation",
          message: "Tasks: stylus:tuktuk, coffee"
        }
      },
      end_tuktuk_compilation: {
        options: {
          title: "Finished Tuktuk compilation",
          message: "Tasks: stylus:tuktuk, coffee"
        }
      }
    }
  });
  grunt.loadNpmTasks('grunt-notify');

  grunt.registerTask("tuktuk", ["notify:init_tuktuk_compilation", "stylus:tuktuk", "copy", "coffee", "notify:end_tuktuk_compilation"]);
  grunt.registerTask("default", ["notify:init_develop_compilation", "stylus:develop", "jade:develop", "watch", "notify:end_develop_compilation"]);
  grunt.registerTask("develop", ["notify:init_develop_compilation","stylus:develop", "jade:develop", "copy", "notify:end_develop_compilation"]);
  grunt.registerTask("production", ["notify:init_production_compilation", "jade:production","uglify","stylus:production", "cssmin", "notify:end_tuktuk_compilation"]);

};

Como puedes ver he puesto una notificación al comienzo de cada tarea para tener un notificación visual de cuando Grunt empieza a trabajar.

Cuando cualquier tarea falla se muestra una notificación, pero tienes que arrancar grunt con la opción "--force".

All together now!

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),
    meta: {
      cssEndpoint: "site/css",
      stylusSourceBase: "sources/stylus/",
      jsSourceBase: "sources/js/",
      jsDestBase: "site/js/",
      libsJsSourcesBasePath: "sources/libs",
      libsJsEndPoint: "site/js/libs",
      tuktuk: {
        sourcesBasePath: "sources/tuktuk/sources",
        file: "tuktuk",
        cssEndpoint: "<%= meta.cssEndpoint %>/libs/tuktuk",
        jsEndpoint: "<%= meta.libsJsEndPoint %>"
      }
    },
    source: {
      tuktuk: {
        coffee: ["<%= meta.tuktuk.sourcesBasePath %>/tuktuk.coffee", "<%= meta.tuktuk.sourcesBasePath %>/tuktuk.*.coffee"],
        stylus: ["<%= meta.tuktuk.sourcesBasePath %>/stylesheets/tuktuk.*.styl"],
        theme: ["<%= meta.stylusSourceBase %>/theme.bu.styl"],
        icons: "<%= meta.tuktuk.sourcesBasePath %>/components/lungo.icon/lungo.icon.css"
      },
      stylus: {
          imports:["<%= meta.stylusSourceBase %>/imports.styl"],
          customization: ["<%= meta.stylusSourceBase %>/variables.styl","<%= meta.stylusSourceBase %>/customization.styl"]
      },
      libsJs: {
        jquery: ["<%= meta.libsJsSourcesBasePath %>/jquery.js"],
        require: ["<%= meta.libsJsSourcesBasePath %>/require.js"],
        zepto: ["<%= meta.libsJsSourcesBasePath %>/zepto.js"]
      }
    },
    coffee: {
      engine: {
        options:{ join: true, },
        files: { "<%= meta.tuktuk.jsEndpoint %>/<%= meta.tuktuk.file %>.js": ["<%= source.tuktuk.coffee %>"] }
      }
    },
    stylus: {
      tuktuk: {
        options: { compress: false},
        files: { '<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.css': '<%= source.tuktuk.stylus %>' }
      },
      develop: {
        options: { compress: false},
        files: {
            '<%= meta.cssEndpoint %>/imports.css': '<%= source.stylus.imports %>',
            '<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.theme.css': '<%= source.tuktuk.theme %>',
            '<%= meta.cssEndpoint %>/customization.css': '<%= source.stylus.customization %>'
        }
      },
      production: {
        options: {compress: true },
        files: { 
          '<%= meta.cssEndpoint %>/profile.min.css':[
            '<%= source.stylus.imports %>',
            '<%= source.tuktuk.stylus %>',
            '<%= source.tuktuk.theme %>',
            '<%= source.stylus.customization %>'
          ] 
        }
      }
    },
    jade: {
      develop: {
        options:{
          pretty: true,
          data: function(){return {developing: "true"}; }
        },
        files:[{ expand: true, src: "*.jade", dest: "site/", ext: ".html", cwd: "sources/jade/pages" }]
      },
      production: {
        options:{
          pretty: false,
          data: function(){return {developing: "false"}; }
        },
        files:[{ expand: true, src: "*.jade", dest: "site/", ext: ".html", cwd: "sources/jade/pages" }]
      }
    },
    copy: {
      main: {
        files: [
          {
            src: '<%= source.tuktuk.icons %>',
            dest: "<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.icons.css"
          },
          { expand: true,
            cwd: "<%= meta.jsSourceBase %>",
            src: ["**"],
            dest: "<%= meta.jsDestBase %>"
          }
        ]
      }
    },
    uglify: {
      separate: {
        files: {
          'site/js/app/config.js': '<%= meta.jsSourceBase %>/app/config.js',
          'site/js/app/main.js': '<%= meta.jsSourceBase %>/app/main.js'
        }
      }
    },
    cssmin: {
      combine: {
        files: {
          '<%= meta.cssEndpoint %>/profile.min.css': [
              '<%= meta.cssEndpoint %>/profile.min.css',
              '<%= meta.tuktuk.cssEndpoint %>/<%= meta.tuktuk.file%>.icons.css'
            ]
        }
      }
    },
    watch: {
      stylus: {
        files: ['<%= source.tuktuk.theme %>', '<%= source.stylus.customization %>'],
        tasks: ["notify:init_develop_compilation", "stylus"]
      },
      jade: {
        files: ["sources/jade/**/*.jade"],
        tasks: ["notify:init_develop_compilation", "jade:develop"]
      }
    },
    notify: {
      init_develop_compilation: {
        options: {
          title: "Init DEV compilation",
          message: "Tasks: stylus:develop, jade:develop, copy, watch"
        }
      },
      end_develop_compilation: {
        options: {
          title: "Finished DEV compilation",
          message: "Tasks: stylus:develop, jade:develop, copy, watch"
        }
      },
      init_production_compilation: {
        options: {
          title: "Init PRO compilation",
          message: "Tasks: jade:production, uglify, stylus:production, cssmin"
        }
      },
      end_production_compilation: {
        options: {
          title: "Finished PRO compilation",
          message: "Tasks: jade:production, uglify, stylus:production, cssmin"
        }
      },
      init_tuktuk_compilation: {
        options: {
          title: "Init Tuktuk compilation",
          message: "Tasks: stylus:tuktuk, coffee"
        }
      },
      end_tuktuk_compilation: {
        options: {
          title: "Finished Tuktuk compilation",
          message: "Tasks: stylus:tuktuk, coffee"
        }
      }
    }
  });
  grunt.loadNpmTasks("grunt-contrib-coffee");
  grunt.loadNpmTasks("grunt-contrib-stylus");
  grunt.loadNpmTasks("grunt-contrib-jade");
  grunt.loadNpmTasks("grunt-contrib-copy");
  grunt.loadNpmTasks("grunt-contrib-watch");
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-notify');

  grunt.registerTask("tuktuk", ["notify:init_tuktuk_compilation", "stylus:tuktuk", "copy", "coffee", "notify:end_tuktuk_compilation"]);
  grunt.registerTask("default", ["notify:init_develop_compilation", "stylus:develop", "jade:develop", "watch", "notify:end_develop_compilation"]);
  grunt.registerTask("develop", ["notify:init_develop_compilation","stylus:develop", "jade:develop", "copy", "notify:end_develop_compilation"]);
  grunt.registerTask("production", ["notify:init_production_compilation", "jade:production","uglify","stylus:production", "cssmin", "notify:end_tuktuk_compilation"]);

};

Plugins extra que usaré:

My dear gruntfile.js

If you dont know Grunt could be helpful to watch my slideshow about new front-end tools at Slid.us.

Main file content structure.

module.exports = function(grunt) {
    grunt.initConfig({
        //Your plugin configuration and it's tasks definitions goes here
    });
    grunt.loadNpmTasks("name_of_the_grunt_plugin");

    grunt.registerTask("custom_taks", ["name_of_the_grunt_plugin:specific_task"]);
};

For the next plugin descriptions i will copy and follow this structure and at the end of the post i will merge all the code in a single file.

A help for decoupling.

You can read meta-data from your package.json or define your own, for easy decoupling your project structure from your grunt tasks. This part will be ommited in my next plugin descriptions.

For a easy comprehension:

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),
    meta: {
      cssEndpoint: "site/css",
      tuktuk: {
        sourcesBasePath: "sources/tuktuk/sources"
      }
    },
    source: {
      tuktuk: {
        stylus: ["<%= meta.tuktuk.sourcesBasePath %>/stylesheets/tuktuk.*.styl"]
        //EQUIVALENT TO
        //stylus: ["sources/tuktuk/sources/stylesheets/tuktuk.*.styl"]
      }
    }
  });
};

For me, build a well decoupled gruntfile has been a continous and iterative process, my commit log is full of "gruntfile refactorization", and for each person this meta-data will be diferent. At the end i could say that what i have in mind is:

Use meta for defining routes and source for specific files, thus your tasks will begin always for a meta and will use the less specific files as possible.

This is the full content of this meta-data part that will be referenced in the next plugins sections:

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),
    meta: {
      cssEndpoint: "site/css",
      stylusSourceBase: "sources/stylus/",
      jsSourceBase: "sources/js/",
      libsJsSourcesBasePath: "sources/libs",
      libsJsEndPoint: "site/js/libs",
      tuktuk: {
        sourcesBasePath: "sources/tuktuk/sources",
        file: "tuktuk",
        cssEndpoint: "<%= meta.cssEndpoint %>/libs/tuktuk",
        jsEndpoint: "<%= meta.libsJsEndPoint %>"
      }
    },
    source: {
      tuktuk: {
        coffee: ["<%= meta.tuktuk.sourcesBasePath %>/tuktuk.coffee", "<%= meta.tuktuk.sourcesBasePath %>/tuktuk.*.coffee"],
        stylus: ["<%= meta.tuktuk.sourcesBasePath %>/stylesheets/tuktuk.*.styl"],
        theme: ["<%= meta.stylusSourceBase %>/theme.bu.styl"],
        icons: "<%= meta.tuktuk.sourcesBasePath %>/components/lungo.icon/lungo.icon.css"
      },
      stylus: {
          imports:["<%= meta.stylusSourceBase %>/imports.styl"],
          customization: ["<%= meta.stylusSourceBase %>/variables.styl","<%= meta.stylusSourceBase %>/customization.styl"]
      },
      libsJs: {
        jquery: ["<%= meta.libsJsSourcesBasePath %>/jquery.js"],
        require: ["<%= meta.libsJsSourcesBasePath %>/require.js"],
        zepto: ["<%= meta.libsJsSourcesBasePath %>/zepto.js"]
      }
    }
  });
};

Compiling coffeescript and stylus sources of a external library.

The main object of these tasks is to compile external libraries sources in a separated task for running it only when we update that sources from the original repo, or when we modify that libraries something that we should avoid.

This section is specific for Tuktuk but wiht the consecuent modifications could be used for any other HTML framework such as Bootsrap or Zurb, in this case that framework use Stylus for compiling to CSS and Coffeescriptfor compiling to Javascript.

So we need to install some plugins for compile these sources, i prefer to use the contrib plugins due to are more frecuently updated:

npm install grunt-contrib-coffee --save-dev
npm install grunt-contrib-stylus --save-dev
npm install grunt-contrib-copy --save-dev

And this is my plugin configuration for this compilation:

module.exports = function(grunt) {
  grunt.initConfig({
    coffee: {
      engine: {
        options:{ join: true, },
        files: { 
          "<%= meta.tuktuk.jsEndpoint %>/<%= meta.tuktuk.file %>.js": [
            "<%= source.tuktuk.coffee %>"
          ] 
        }
      }
    },
    stylus: {
      tuktuk: {
        options: { compress: false},
        files: { 
          '<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.css': '<%= source.tuktuk.stylus %>' 
        }
      }
    },
    copy: {
      main: {
        files: [
          {
            src: '<%= source.tuktuk.icons %>',
            dest: "<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.icons.css"
          }
        ]
      }
    }
  });
  grunt.loadNpmTasks("grunt-contrib-coffee");
  grunt.loadNpmTasks("grunt-contrib-stylus");
  grunt.loadNpmTasks("grunt-contrib-copy");

  grunt.registerTask("tuktuk", [ "stylus:tuktuk", "coffee", "copy"]);

};

Thea coffee task compile all the coffeescript soruces of Tuktuk in a single Javascript file.

The task stylus:tuktuk compiles each styluss file that were found in sources/tuktuk/sources/stylesheets/tuktuk.*.styl to a separated CSS file with the same name and the content un-compressed.

And the last task copy will copy the icons CSS from the library sources to the site destination.

Compiling my Stylus sources.

The Tuktuk framework has some separated files for theming purpouses and i decided to move them form the framework sources to my Stylus folder for easy access to them.

I also have two task, one for development environment when i want to have each CSS file separated, and other one for production where i want to have a unique CSS file.

In the development environment i don't want to manually run each task everytime i update a file, so i need a task that automatically run my tasks when i modify a source file. So we need to install it:

npm install grunt-contrib-watch

So, the gruntfile content for these task :

module.exports = function(grunt) {
  grunt.initConfig({
    stylus: {
      develop: {
        options: { compress: false},
        files: {
            '<%= meta.cssEndpoint %>/imports.css': '<%= source.stylus.imports %>',
            '<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.theme.css': '<%= source.tuktuk.theme %>',
            '<%= meta.cssEndpoint %>/customization.css': '<%= source.stylus.customization %>'
        }
      },
      production: {
        options: {compress: true },
        files: { 
          '<%= meta.cssEndpoint %>/profile.min.css':[
            '<%= source.stylus.imports %>',
            '<%= source.tuktuk.stylus %>',
            '<%= source.tuktuk.theme %>',
            '<%= source.stylus.customization %>'
          ] 
        }
      }
    },
    copy: {
      main: {
        files: [
          {
            src: '<%= source.tuktuk.icons %>',
            dest: "<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.icons.css"
          },
          { expand: true,
            cwd: "<%= meta.jsSourceBase %>",
            src: ["**"],
            dest: "<%= meta.jsDestBase %>"
          }
        ]
      }
    },
    cssmin: {
      combine: {
        files: {
          '<%= meta.cssEndpoint %>/profile.min.css': [
              '<%= meta.cssEndpoint %>/profile.min.css',
              '<%= meta.tuktuk.cssEndpoint %>/<%= meta.tuktuk.file%>.icons.css'
            ]
        }
      }
    },
    watch: {
      stylus: {
        files: ['<%= source.tuktuk.theme %>', '<%= source.stylus.customization %>'],
        tasks: ["stylus"]
      }
    }
  });
  grunt.loadNpmTasks("grunt-contrib-stylus");
  grunt.loadNpmTasks("grunt-contrib-copy");
  grunt.loadNpmTasks("grunt-contrib-watch");

  grunt.registerTask("develop", ["stylus:develop"]);
  grunt.registerTask("production", ["stylus:production", "cssmin", "copy"]);

};

The watch task, under a task name property, has the list of files to watch for updates and run the consecuent tasks.

Compiling to HTML with Jade.

Install the plugin:

npm install grunt-contirb-jade --save-dev

And as in the Stylus section, i have two task, one per production and other for development, and a watch task. So here is the content:

 module.exports = function(grunt) {  
  grunt.initConfig({  
   jade: {  
    develop: {  
     options:{  
      pretty: true,  
      data: function(){return {developing: "true"}; }  
     },  
     files:[{ expand: true, src: "*.jade", dest: "site/", ext: ".html", cwd: "sources/jade/pages" }]  
    },  
    production: {  
     options:{  
      pretty: false,  
      data: function(){return {developing: "false"}; }  
     },  
     files:[{ expand: true, src: "*.jade", dest: "site/", ext: ".html", cwd: "sources/jade/pages" }]  
    }  
   },  
   watch: {  
    jade: {  
     files: ["sources/jade/**/*.jade"],  
     tasks: ["notify:init_develop_compilation", "jade:develop"]  
    }  
   }  
  });  
  grunt.loadNpmTasks("grunt-contrib-jade");  
  grunt.loadNpmTasks("grunt-contrib-watch");  
  grunt.registerTask("default", ["jade:develop", "watch"]);  
  grunt.registerTask("production", ["jade:production"]);  
 };  

Each Jade tasks receive data object that i use on the Jade sources for rendering diferent blocks of content.

I have structured my Jade sources in this way:

  • jade
    • extensible
      • tuktuk_layout.jade
        !!! 5
        html(lang='en')
          include ../includes/tuktuk/header
          body
            block content
            include ../includes/tuktuk/footer-scripts
        
    • includes
      • tuktuk
        • footer-scripts.jade
        • header.jade
          meta(charset='utf-8')
          meta(name='viewport', content='width=device-width, initial-scale=1, maximum-scale=1')
          - if (developing == "true")
              link(rel='stylesheet', href='css/imports.css')
              link(rel='stylesheet', href='css/libs/tuktuk/tuktuk.css')
              link(rel='stylesheet', href='css/libs/tuktuk/tuktuk.theme.css')
              link(rel='stylesheet', href='css/libs/tuktuk/tuktuk.icons.css')
              link(rel='stylesheet', href='css/customization.css')
          - else
              link(rel='stylesheet', href='css/profile.min.css')
          
    • pages
      • index.jade
        extends ../extensible/tuktuk_layout
        
        block content
          section.padding.bck.color
              .row.main_hero
        

Growl notify for not to watch the terminal.

One of the problems of Grunt it's that as a Node tool it's launched from the terminal, so the output information is shown in the terminal too, and i dislike to have another window open.

And here is when grunt-notify comes to help. It requires Growl or Growl for WIndows (in windows systems be sure to put the Growl path into your PATH system variable). SO install it!:

npm install grunt-notify --save-dev

And in the gruntfile:

module.exports = function(grunt) {
  grunt.initConfig({
    notify: {
      init_develop_compilation: {
        options: {
          title: "Init DEV compilation",
          message: "Tasks: stylus:develop, jade:develop, copy, watch"
        }
      },
      end_develop_compilation: {
        options: {
          title: "Finished DEV compilation",
          message: "Tasks: stylus:develop, jade:develop, copy, watch"
        }
      },
      init_production_compilation: {
        options: {
          title: "Init PRO compilation",
          message: "Tasks: jade:production, uglify, stylus:production, cssmin"
        }
      },
      end_production_compilation: {
        options: {
          title: "Finished PRO compilation",
          message: "Tasks: jade:production, uglify, stylus:production, cssmin"
        }
      },
      init_tuktuk_compilation: {
        options: {
          title: "Init Tuktuk compilation",
          message: "Tasks: stylus:tuktuk, coffee"
        }
      },
      end_tuktuk_compilation: {
        options: {
          title: "Finished Tuktuk compilation",
          message: "Tasks: stylus:tuktuk, coffee"
        }
      }
    }
  });
  grunt.loadNpmTasks('grunt-notify');

  grunt.registerTask("tuktuk", ["notify:init_tuktuk_compilation", "stylus:tuktuk", "copy", "coffee", "notify:end_tuktuk_compilation"]);
  grunt.registerTask("default", ["notify:init_develop_compilation", "stylus:develop", "jade:develop", "watch", "notify:end_develop_compilation"]);
  grunt.registerTask("develop", ["notify:init_develop_compilation","stylus:develop", "jade:develop", "copy", "notify:end_develop_compilation"]);
  grunt.registerTask("production", ["notify:init_production_compilation", "jade:production","uglify","stylus:production", "cssmin", "notify:end_tuktuk_compilation"]);

};

As you can see i put a notification at the begining of each task in order to get a visual notification of when Grunt begin to work.

When any task fails a notification is shown, but you have to run grunt with the option "--force".

All together now!

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),
    meta: {
      cssEndpoint: "site/css",
      stylusSourceBase: "sources/stylus/",
      jsSourceBase: "sources/js/",
      jsDestBase: "site/js/",
      libsJsSourcesBasePath: "sources/libs",
      libsJsEndPoint: "site/js/libs",
      tuktuk: {
        sourcesBasePath: "sources/tuktuk/sources",
        file: "tuktuk",
        cssEndpoint: "<%= meta.cssEndpoint %>/libs/tuktuk",
        jsEndpoint: "<%= meta.libsJsEndPoint %>"
      }
    },
    source: {
      tuktuk: {
        coffee: ["<%= meta.tuktuk.sourcesBasePath %>/tuktuk.coffee", "<%= meta.tuktuk.sourcesBasePath %>/tuktuk.*.coffee"],
        stylus: ["<%= meta.tuktuk.sourcesBasePath %>/stylesheets/tuktuk.*.styl"],
        theme: ["<%= meta.stylusSourceBase %>/theme.bu.styl"],
        icons: "<%= meta.tuktuk.sourcesBasePath %>/components/lungo.icon/lungo.icon.css"
      },
      stylus: {
          imports:["<%= meta.stylusSourceBase %>/imports.styl"],
          customization: ["<%= meta.stylusSourceBase %>/variables.styl","<%= meta.stylusSourceBase %>/customization.styl"]
      },
      libsJs: {
        jquery: ["<%= meta.libsJsSourcesBasePath %>/jquery.js"],
        require: ["<%= meta.libsJsSourcesBasePath %>/require.js"],
        zepto: ["<%= meta.libsJsSourcesBasePath %>/zepto.js"]
      }
    },
    coffee: {
      engine: {
        options:{ join: true, },
        files: { "<%= meta.tuktuk.jsEndpoint %>/<%= meta.tuktuk.file %>.js": ["<%= source.tuktuk.coffee %>"] }
      }
    },
    stylus: {
      tuktuk: {
        options: { compress: false},
        files: { '<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.css': '<%= source.tuktuk.stylus %>' }
      },
      develop: {
        options: { compress: false},
        files: {
            '<%= meta.cssEndpoint %>/imports.css': '<%= source.stylus.imports %>',
            '<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.theme.css': '<%= source.tuktuk.theme %>',
            '<%= meta.cssEndpoint %>/customization.css': '<%= source.stylus.customization %>'
        }
      },
      production: {
        options: {compress: true },
        files: { 
          '<%= meta.cssEndpoint %>/profile.min.css':[
            '<%= source.stylus.imports %>',
            '<%= source.tuktuk.stylus %>',
            '<%= source.tuktuk.theme %>',
            '<%= source.stylus.customization %>'
          ] 
        }
      }
    },
    jade: {
      develop: {
        options:{
          pretty: true,
          data: function(){return {developing: "true"}; }
        },
        files:[{ expand: true, src: "*.jade", dest: "site/", ext: ".html", cwd: "sources/jade/pages" }]
      },
      production: {
        options:{
          pretty: false,
          data: function(){return {developing: "false"}; }
        },
        files:[{ expand: true, src: "*.jade", dest: "site/", ext: ".html", cwd: "sources/jade/pages" }]
      }
    },
    copy: {
      main: {
        files: [
          {
            src: '<%= source.tuktuk.icons %>',
            dest: "<%= meta.tuktuk.cssEndpoint %>/<%=meta.tuktuk.file%>.icons.css"
          },
          { expand: true,
            cwd: "<%= meta.jsSourceBase %>",
            src: ["**"],
            dest: "<%= meta.jsDestBase %>"
          }
        ]
      }
    },
    uglify: {
      separate: {
        files: {
          'site/js/app/config.js': '<%= meta.jsSourceBase %>/app/config.js',
          'site/js/app/main.js': '<%= meta.jsSourceBase %>/app/main.js'
        }
      }
    },
    cssmin: {
      combine: {
        files: {
          '<%= meta.cssEndpoint %>/profile.min.css': [
              '<%= meta.cssEndpoint %>/profile.min.css',
              '<%= meta.tuktuk.cssEndpoint %>/<%= meta.tuktuk.file%>.icons.css'
            ]
        }
      }
    },
    watch: {
      stylus: {
        files: ['<%= source.tuktuk.theme %>', '<%= source.stylus.customization %>'],
        tasks: ["notify:init_develop_compilation", "stylus"]
      },
      jade: {
        files: ["sources/jade/**/*.jade"],
        tasks: ["notify:init_develop_compilation", "jade:develop"]
      }
    },
    notify: {
      init_develop_compilation: {
        options: {
          title: "Init DEV compilation",
          message: "Tasks: stylus:develop, jade:develop, copy, watch"
        }
      },
      end_develop_compilation: {
        options: {
          title: "Finished DEV compilation",
          message: "Tasks: stylus:develop, jade:develop, copy, watch"
        }
      },
      init_production_compilation: {
        options: {
          title: "Init PRO compilation",
          message: "Tasks: jade:production, uglify, stylus:production, cssmin"
        }
      },
      end_production_compilation: {
        options: {
          title: "Finished PRO compilation",
          message: "Tasks: jade:production, uglify, stylus:production, cssmin"
        }
      },
      init_tuktuk_compilation: {
        options: {
          title: "Init Tuktuk compilation",
          message: "Tasks: stylus:tuktuk, coffee"
        }
      },
      end_tuktuk_compilation: {
        options: {
          title: "Finished Tuktuk compilation",
          message: "Tasks: stylus:tuktuk, coffee"
        }
      }
    }
  });
  grunt.loadNpmTasks("grunt-contrib-coffee");
  grunt.loadNpmTasks("grunt-contrib-stylus");
  grunt.loadNpmTasks("grunt-contrib-jade");
  grunt.loadNpmTasks("grunt-contrib-copy");
  grunt.loadNpmTasks("grunt-contrib-watch");
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-notify');

  grunt.registerTask("tuktuk", ["notify:init_tuktuk_compilation", "stylus:tuktuk", "copy", "coffee", "notify:end_tuktuk_compilation"]);
  grunt.registerTask("default", ["notify:init_develop_compilation", "stylus:develop", "jade:develop", "watch", "notify:end_develop_compilation"]);
  grunt.registerTask("develop", ["notify:init_develop_compilation","stylus:develop", "jade:develop", "copy", "notify:end_develop_compilation"]);
  grunt.registerTask("production", ["notify:init_production_compilation", "jade:production","uglify","stylus:production", "cssmin", "notify:end_tuktuk_compilation"]);

};

Extra plugins that i will use:

miércoles, 19 de junio de 2013

Unwritten Javascript style rules

In english please!

Nombres de iteradores.

La gente nunca deja de sorprender, ni uno a si mismo, y hoy un compañero me ha hecho ponerme tierno, primero por tenerme en tan alta estima técnica como para preguntarme algo, y segundo por la pregunta tan fina que me ha hecho:

tengo que hacer 3 for dentro del for principal porque son arrays dentro del principal uso el i y j para el principal (nombres de las variables) ¿y para los demás?

Como ante todo soy un poco idiota le he contestado:

o p q a barra espaceadora

Cuando la norma no escrita es:

  1. i - j : para el bulce más externo
  2. k - l : para el bucle siguiente
  3. m - n: para el bucle más interior

Y la norma no escrita sería que en el 4º nivel fuesen o y p los nombres de los iteradores, pero ¡oh misterios de la vida! nunca he visto algo así, ¿por qué? Pues por que cuando llegamos a anidar 2 o más bucles, o cuando repetimos cualquier trozo de código 2 o más veces, lo suyo es dividirlo en funciones, de una forma tal que así:

var primero = function(){  
  for( var i=0,j=cosas.length; i&lt;j; i++){  
   segundo(cosas[i]);  
  }  
 };  
 var segundo = function(masCosas){  
  for( var i=0,j=masCosas.length; i&lt;j; i++){  
   tercero(masCosas[i]);  
  }  
 };  
 var tercero= function(aunMasCosas){  
  for( var i=0,j=aunMasCosas.length; i&lt;j; i++){  
   aunMasCosas[i] = "Ya me he cansado de dar vueltas";  
  }  
 };  

Mucho más elegante, más cómodo de desarrollar y mantener, más fácil copiar el código de la estructura iteradora, y así solo necesitamos asociar 2 nombres de variables de iterador en nuestra cabeza.

Si, nuestra cabeza también tiene memoria, y aunque digan que el saber no ocupa lugar, en tiempo de desarrollo conviene mantener el "porta papeles" de nuestra cabeza lo más ligerito posible para así escribir código con mayor soltura.

For, for y nada más que for

En Javascript no uso otros bucles, son los más eficientes, ni siquiera la estructura for( var a in arrayDeCosas>, y mucho menos en jQuery el maldito $cosas.each(), solo uso una estructura para iterar sobre todas las cosas y atarlas en las tinieblas ( guiño guiño ).

Que se agarre los machos al que vea hacer bucles así:

var i;  
for(i=0;i<cosas.length; i++){  
  usa( cosas[i] );  
  desordena( cosas[i] );  
  guarda( cosas[i] );  
}  

La mejor forma de hacerlo

Y creo que no solo hablo de Javascript, sino que lo podría hacer extensible a cualquier lenguaje.

Gracias a que en la primera sentencia dentro de la estructura for podemos declarar no solo la variable iterador, sino todas las que queramos, es una buena práctica declarar también la variable delimitadora del bucle.

Y como no me cansaré de decir: cachea todo lo que puedas (y debas); declaramos una variable para cachear el elemento concreto de la iteración sobre el que vamos a trabajar:

var cosaConcreta;
for(var i=0, j=cosas.length; i <j ; i++){  
  cosaConcreta = cosas[i];
  usa( cosaConcreta );  
  desordena( cosaConcreta );  
  guarda( cosaConcreta );  
}  

Agrupar funcionalidad.

Cuando tengo que trabajar con un montón de código que no es mío, voy a estar con el cierto tiempo y está desorganizado, lo que primero que hago es agrupar funcionalidad de la siguiente manera:

var concretModule = {
    utils: {
        dateUtil: {},
        crapyStringCommonStuff: {}
    },
    handlers: {
        linkClickHandler: {}
    }
};
//...
$("#static_element").on("click", ".dynamic_generated_element", concretModule.handlers.linkClickHandler );

Declaración de variables.

Mal

var uno;
var dos = 2;
var tres = "3";
functionCall();
//... other stuff
var cuatro = dos + dos;

Varias sentencias de declaración mezcladas con código...

Mejor

var uno, dos = 2, tres = "3", cuatro = dos + dos;
functionCall();
//... other stuff

Una única sentencia de declaración para varias variables.

Mi preferida.

var uno,
    dos = 2,
    tres = "3",
    cuatro = dos + dos;
functionCall();
//... other stuff

Una única sentencia de declaración identada y agrupada de la siguiente forma:

  1. Variables no inicializadas
  2. Variables, objetos, arrays... inicializados a 0, null, false, [], {} o cualquier valor que indique vacío
  3. Variables instanciadas a un valor concreto

Lo que realmente hace el interprete de JS.

var uno, dos, tres, cuatro;
dos = 2;
tres = "3";
cuatro = dos + dos;
functionCall();
//... other stuff

Una única sentencia de declaración en una sola linea sin inicialización alguna, seguida de la inicialización de las variables en el orden que hayamos escrito.

Y esto se debe al hoisting.

Name of iterators.

People will never stop surprising you, neither you to yourself, and today a co-worker has made me tender, firstly for having me in such high technical consideration for asking me something, and secondly for the thin ask that he has wonder me:

i have to do 3 for's inside a main for because there is nested arrays inside, i use "i" and "j" for the main (names of the variables) and for the rest?

The unwritten rule is:

  1. i - j: for the external loop
  2. k - l: for the next loop
  3. m - n: for the inner loop

And the unwritten rule will folow with for the 4th level the vars will be o y p, but oh life misteries! i haven't seen such thing before, why? Well that's why when we reach to nest 2 or more loops, or when we repeat any piece of code 2 or more times, what we have to do it's to divide it into functions in a way like this:

var first = function(){  
  for( var i=0,j=things.length; i&lt;j; i++){  
   second(things[i]);  
  }  
 };  
 var second = function(moreThings){  
  for( var i=0,j=moreThings.length; i&lt;j; i++){  
   third(moreThings[i]);  
  }  
 };  
 var third= function(evenMoreThings){  
  for( var i=0,j=evenMoreThings.length; i&lt;j; i++){  
   evenMoreThings[i] = "I'm tired of looping";  
  }  
 };  

Much more stylish, easier to develop and mantain, easier to copy the loop code, and only we need to use a single iterator var name.

Yes, our head also has memory, and even some people say that the know doesn't occupy space, in developing time it's convenient to keep the "clipboard" of our head as light as possible in order to write code with more looseness.

For, for and nothing more than for

In Javascript i don't use other loops, are the most efficient ones, even the structure for( var a in arrayOfThings), and even much less in jQuery the damned $things.each(), i only use one structure for looping across things and tie it into the shadows (blink blink).

Be ready which will do loops like this:

var i;  
for(i=0;i<things.length; i++){  
  useIt( things[i] );  
  randomizeIt( things[i] );  
  saveIt( things[i] );  
}  

The better way to do this:

And I do not just talk about Javascript, but it could be extended to any language.

Thanks to that in the first sentence inside the structure for we could declare not only the iterator variable, but that all that we like, it's a good practice declare also the loop terminator variable.

And such i'll never get tired of saying: caches all you can (and you should); we should declare a variable for caching the element on each iteration which we are going to work:

var specificThing;
for(var i=0, j=things.length; i <j ; i++){  
  specificThing = things[i];
  useIt( specificThing );  
  randomizeIt( specificThing );  
  saveIt( specificThing );  
}  

Grouping functionality.

When i have to work with a lot of code that isn't mine, i'll going to be with it a certain time and is unordered, the first thing that i usually do is to group the code in a way like this:

var concretModule = {
    utils: {
        dateUtil: {},
        crapyStringCommonStuff: {}
    },
    handlers: {
        linkClickHandler: {}
    }
};
//...
$("#static_element").on("click", ".dynamic_generated_element", concretModule.handlers.linkClickHandler );

Declaring variables.

Bad

var one;
var two = 2;
var three = "3";
functionCall();
//... other stuff
var four= two + two;

Several variables declaration sentences mixed with other code...

Better

var one, two = 2, three = "3", four= two + two;
functionCall();
//... other stuff

A single declaration sentence for several variables.

My prefered.

var one, 
    two = 2, 
    three = "3", 
    four= two + two;
functionCall();
//... other stuff

A single declaration sentence for several variables indented and groupped in this way:

  1. Not initialized vars
  2. Variables, objetcs, arrays... initialized to 0, null, false, [], {} o other values that means empty
  3. Declared variables to a specific value

What really makes the JS interpreter.

var one, two, three, four;
two = 2;
three = "3";
four= two + two;
functionCall();
//... other stuff

A single declaration sentence in a single line with no initialization, followed by the rest of the code.

And this is due to hoisting.