JSONP with Express.js API – cross domain request

The problem:

So you have this awesome API endpoint, but of course it is another domain as the static site.

Web browsers are pretty moody with Cross Domain and there are actually very good security reasons for that, but even using good practices the old Internet Explorer don’t care.

Welcome ancient jsonp techniques!

Solution

For some Express.js API running on Node.js and client using jQuery (or something alike)

In your server:

app.get('/my-unique-endpoint', function(req, res, next) {
  var myAwesomeResource = ["hello", "mundo"];
  if (req.query && req.query.callback) {
    res.set({
      // nice to have, but Chrome dont seem to mind either way
      'Access-Control-Allow-Origin': '*',
      // right content type prevent warnings and errors
      'Content-Type': 'text/javascript; charset=UTF-8',
      // optional, this is in seconds, equivalent to 8h
      'Cache-Control': 'public, max-age=28800'
    });
    return res.send(200, "" + req.query.callback + "(" + JSON.stringify(myAwesomeResource) + ");");
  } else {
    return res.send(200, myAwesomeResource);
  }
});

In the client:

// see the data in your console
$.getJSON("http://myawesomewebsiteurl/my-unique-endpoint?callback=?", function(data){ console.log(data) } )

Caveats

jsonp is works for GET verb, because it effectively execute the code received as if it was downloading a script.
for the same as above, the source should also be fully trusted!

2 jquery animation parallel (or a effect)

via Axiom Zen

This post is kinda drafty, but since the Google’s first page for this were outdated, here we go!

When trying running 2 animations (or multiple) side by side (in parallel) at a same element, via JQuery .animate() they run one after another; like they are supposed to do by default, but there is a argument to override;  queue. In a silly example:

  j('body').animate({ paddingTop: 100}, 500, function(){ alert('Took half a second!')}).animate({paddingLeft: 100}, {duration: 500, queue:false})

Here we are animating both in parallel, and the callback alert, run after half a second. (*be careful about callbacks on animations with queue:false, since they don’t seem to trigger )

In another example, fairly more complex (and real :)), I am using JQueryUI for both  shake, and color effects.
The objective is to simulate a div that gets overheated and waves to chill, real fast.

var pointsE = j('#health_bar .stats')
pointsE.css( {color: 'rgb(255,0,0)' })
        .stop(true)
        .effect('bounce', {times: 5, distance:10, easing: 'easeOutElastic'}, 300 )
        .html('OUCH!')
        .animate({ color: 'rgb(255,255,255)' }, { duration: 500, queue: false})

Just be aware, that it doesn’t seen .effect(), supporting queue: false –it breaks. That’s why I put it in the .animate()
About the .stop(true), it is just to be sure that the queue is empty before starting the animation (in practice, it is a minor fix for consecutive .effect(‘shake’))

References:
jQuery .animate() API
jQueryUI Color
jQueryUi Shake Effect

Very simple invisible JavaScript & Rails captcha

Hello!

Visual captchas are far from being desirable on most public sites, but spam is even less desirable, mainly in contact forms. This solution I am implementing is dead simple, but also, weaker than reCaptcha.

snippet:

Put this in the application_controller.rb

  before_filter :form_spam_control

  private

  def form_spam_control
    if request.post? || request.put?
      unless params['agent_smith'] == 'Mr Anderson, welcome back'
        render :text => "Please make sure cookies and js are enabled"
        return false
      end
    end
  end

Put this in a javascript that is executed on every public page, typically, application.js (*does require jQuery loaded)

$(document).ready( function(){
  $('form').append( j('<input/>', {
    type: 'hidden',
    id: 'agent_smith',
    name: 'agent_smith',
    value: 'Mr Anderson, welcome back'
  }) )
})
//UPDATE! in order to support AJAX without extra params add:
j('body').ajaxSend(function(a,b,c){ if( c.type == 'POST' || c.type == 'PUT' ) c.data = c.data.length > 0 ? c.data+'&agent_smith=Mr+Anderson%2C+welcome+back' : 'agent_smith=Mr+Anderson%2C+welcome+back'})

Discussion:
This is totally invisible and harass-free for the user.
I am based on the principle that spam crawlers does not run JavaScript, which may not be true for all of them. Still this will deny some crawlers that may be considered good, such as Mechanize.
This technique can be easily ported to other backend languages, such as PHP, ASP, C#, Java, since it only requires a parameter filter on POSTs and PUTs
If the attacker focus your website, this will be easily broken.
If the user has JavaScript disabled, he can’t post, but this is a normal drawback on some captchas.
* the part of the error message including ‘cookies’ is just a disguise =)

Adding dynamic ajax data to DataTable, a jQuery Table Plugin

via Axiom Zen

Recently on PedeDelivery I’ve been working with some loads of data to display on admin panels, and it can be pretty boring to make custom searches and sorts on the server side (as well as expensive up from some rates).

In order to counter this issues, I am using the DataTable jQuery plugin, which is able to deal with some huge amount of data, and is extremely featured and at the same time, customizable.

On a client that have to deal with server updating data IRT, reloading all the rows is very undesirable to happen all over again on a AJAX callback. So instead, lets just require from the server new content, in JSON format.

pd web app

The PROBLEM here are the arguments .fnAddData() is able to take: a Array OR a Array of Arrays. But I need to add class to the TD Element and still bind with some events! For this purpose I made the function tidy_up_row(), see below.

oh, cut the crap! Snippet/Solution

Data request is made periodically to the server, that returns a JSON array with all the new content (that’s server responsibility I am using Rails for the task).

  // Initialize with all ids from the server in a hash
  $orders = ( { 10 : true,
                     11: true
                     // and so on
                   } )

  function periodic_update_DataTable(){
    j.ajax({

        url: "painel_restaurante/sync_data",
        success:
          function(data, status, xhr){
            add_multiple_rows( data );
          },
        complete:
          function(a,b,c){
            setTimeout( periodic_update_DataTable, 30*1000)
          },
        dataType: 'json'
      }
    )
    return true;
  }

  function add_multiple_rows( arr_arr ){

    var added = new Array();
    var count = 0;

    for (var i=0; i < arr_arr.length; i++) {
      var id = arr_arr[i].pop();

      // only add order if not found in table
      if( !$orders[id] ){

        // see http://www.datatables.net/api
        var added_aoData =  j('.data_table').dataTable().fnAddData( arr_arr[i] )[0];

        var row = j('.data_table').dataTable().fnSettings().aoData[ added_aoData ].nTr;

        tidy_up_row(row, id);

        $orders[id] = true

        count++;
      }

    };

    console.log( 'Added rows', count );

  }

  function tidy_up_row(row, id){
    var jrow = j(row).attr('data-order-id', id);

    jrow.children('td:eq(0)').addClass('order_cell1').click( click_order_detail );
    jrow.children('td:eq(1)').addClass('order_cell2').click( click_order_detail );
    jrow.children('td:last').addClass('order_actions');
  }

j(document).ready(function(){

    // Initialize the table just the way you do normally. (in my case I18n to Portuguese-BR)
    j('table.data_table').dataTable({ // http://datatables.net/examples/basic_init/filter_only.html
      "aaSorting": [[ 0, "desc" ]],
      "iDisplayLength": 50,
      "bAutoWidth": true,
      "oLanguage": {
        "sLengthMenu": "_MENU_ entradas por página",
        "sZeroRecords": "Vazio!",
        "sInfo": "Exibindo de _START_ até _END_. Total de: _TOTAL_ entradas",
        "sInfoEmpty": "Nenhuma entrada",
        "sInfoFiltered": "(total de _MAX_ entradas)",
        'sSearch' : "Busca"
      }
    });

   setInterval(function(){ j("td.order_cell1").updatePrettyTime(); }, 30*1000);

  // Other inits ...
  })

Discussion

It is very important to notice that I haven’t done anything new, its just that, this exact mode of use I haven’t found anywhere else.

I see no downsides on this method, just go ftw! 🙂

LazyLoad on JavaScript Load only once

What? And what’s the point?

On some web pages, the focus is displaying information fast, but also being aware that some actions that rely on a kinda heavy framework such as jQuery/Prototype/Dojo (Ajax+Animations+etc..) may still be done in page by a minority of users.

I believe the use cases of this technique are sparse, but would be around pages focusing Mobile Phones, where the processing and bandwidth are limited and pages that should prioritize very fast loadings.

To solve several browsers implementation for calling for JS after the page is fully loaded I used this library (LazyLoad 2.0) by a great programmer, a non-evil-doer. With this great lib is pretty easy to call a JS (or css for that matter) but is complicated to know when the Framework  you required is already loaded ( in the case that multiple acions may trigger the load)

For this case I developed a sort of proxy-function that should do the job in the following way:

        function load_and_run( callback, arg ){
		if( typeof($) == 'function' ){
			// This means jQuery is loaded
			callback( (typeof( arg ) != 'undefined' ? arg : true))
		}else{
			LazyLoad.js('/javascripts/jquery-1.4.2.js', function(arg){
				j=$;
			        //as here you can use LazyLoad to require some plugin
				callback( (typeof( arg ) != 'undefined' ? arg : true) );
			}, arg)

		}
	}

The secret sauce there is that I simply verify if the $ ( signature of jQuery, Prototype and others) is already defined, and for that to work I must proxy other actions by load_and_run, as can be seen here:

var spinner = new Image();	spinner.name = "madd-spinner";	spinner.src = "/images/ajax-loader.gif";
	
	function up_request( id, node ){
		load_and_run( up_request_callback )
		node.parentNode.appendChild( spinner )
	}
	function up_request_callback( id ){
		j.ajax({
					url: '/ajax/up',
					data:{definition_id:id},
					//etc...
					})

Remember to always give some feedback about the loading process for your user, in my case, I use a spinner gif preloaded.

Lesson learned from building www.gasa.jaeh.net, can be seen on any definition page