Mostrando entradas con la etiqueta javascript. Mostrar todas las entradas
Mostrando entradas con la etiqueta javascript. Mostrar todas las entradas

miércoles, 22 de junio de 2016

writing selenium in javascript node with webdriverio - instructions for impatients

The following is what you need to do in order to write a javascript program that will command your browser through selenium using webdriver.io node library.

 First is about installing and running the selenium server. Second is a little webdriverio example. 

Installing the selenium server

The following instructions are give in a shell script that was tested on mac:
#install java

# download selenium server: 

curl -O http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.0.jar

#download googlechrome selenium drivers: 

curl -O http://chromedriver.storage.googleapis.com/2.22/chromedriver_mac32.zip
unzip chromedriver_mac32.zip


#Execute selenium server: 

java -Dwebdriver.chrome.driver=./chromedriver -jar selenium-server-standalone-2.53.0.jar


The Program

From here the instructions are the same as in http://webdriver.io/guide.html - just execute ```npm install webdriverio``` in your node project and execute the following file. In this simple program that will navigate to google and when is ready print the document title in the console:
var webdriverio = require('webdriverio');
var options = {
    desiredCapabilities: {
        browserName: 'chrome'
    }
};
webdriverio
    .remote(options)
    .init()
    .url('http://www.google.com')
    .getTitle().then(function(title) {
        console.log('Title was: ' + title);
    })
    .end();

Appendix: Technologies used

  • http://www.seleniumhq.org/ - the infrastructure that talks to browsers (java) 
  • http://webdriver.io/guide.html - the selenium javascript api 
  • http://chromedriver.storage.googleapis.com/index.html - selenium driver to manage chrome browser.
  • java and node

jueves, 19 de mayo de 2016

Minify JavaScript code with esprima and escodegen


var esprima = require('esprima'), 
escodegen = require('escodegen');

function doUglify(s)
{
 try
 {
  var ast = esprima.parse(s);
  s = escodegen.generate(ast, {format: escodegen.FORMAT_MINIFY}) || s;
 }
 catch(ex)
 {
  console.log(ex);
 }
 return s;
}

martes, 6 de octubre de 2015

Queue based on Q promises

So just that,I'm working with promises in JavaScript and at certain point I needed a queue for executing asynchronous tasks. My promises are implemented by q library and my particular requirements are as follows. I implemented this web service client without knowing that no concurrent operations are allowed. In some cases, if you request two operations at the same moment the server will return an error. In my case, an obvious quick solution was to en-queue the method that makes the requests in my client. Something like make it synchronous (similar as java methods can be declared as 'synchronous' when multi threads access it).

I research a little and there are plenty alternatives, most appreciated was the queue utility of async, a library that also use. Nevertheless instead learning how to use it, and because I know this problem can be solved w promises wanted to try something else

I very liked the solution proposed here because is simple so I adapted to use Q promises instead jQuery's. This is the result (working example in node, but should work also in the browser)

var Q = require('q')


var Queue = function () 
{
    var lastPromise = null;

    this.add = function (obj, method, args, context) 
    {
        var methodDeferred = Q.defer(); 
        var queueDeferred = this.setup();
        if(context === undefined) { context = obj; }

        // execute next queue method
        queueDeferred.done(function () 
        {
            // call actual method and wrap output in deferred
            setTimeout(function () 
            {
                obj[method].apply(context, args);
                methodDeferred.resolve();
            }, 100);
        });
        lastPromise = methodDeferred.promise;
    };

    this.setup = function () 
    {
        var queueDeferred = Q.defer(); 

        // when the previous method returns, resolve this one
        if(!lastPromise)
        {
            lastPromise = Q.defer(); 
            lastPromise.resolve();
            lastPromise = lastPromise.promise;
        }
        lastPromise.done(function () 
        {
            queueDeferred.resolve();
        });

        return queueDeferred.promise;
    }
};



var queue = new Queue();
queue.add(console, 'log', ['one']);
queue.add(console, 'log', ['two']);
queue.add(console, 'log', ['three']);
setTimeout(function ()
{
    queue.add(console, 'log', ['four']);
}, 2000);

martes, 24 de marzo de 2015

console.timing() - measure multiple calls of a code block

The following is an utility, similar to console.time / console.timeEnd to measure how long a code block takes to execute but it will take in account sevaral times and report the total calling time and average. Very useful wile profiling code blocks execution time.


console.timing = function(key)
{
 this.timings = this.timings || {}; 
 this.timings[key] = this.timings[key] || []; 
 this.timings[key].push(performance.now()); 
};

console.timingEnd = function(key)
{
 var t0 = this.timings[key][this.timings[key].length-1]; 
 this.timings[key][this.timings[key].length-1] = performance.now() - t0;
}; 

console.timingReport = function(key)
{
 var times = this.timings[key]; 
 var sum = 0;
 for (var i = 0; i < times.length; i++) 
 {
  sum += times[i]; 
 }
 var average = sum / times.length;
 console.log(key + ': times: ' + times.length + ', sum: ' + sum + ', average: ' + average);  
};

console.timingReset = function(key)
{
 this.timings[key] = []; 
}; 

sábado, 24 de mayo de 2014

Calculate average in javascript

Currently I'm doing a lot of performance improvements in one of our pages in often I need to calculate the average of the numbers in an array. For this I simply write something like the following in a javascript terminal:
(function(a){var c = 0; for(var i in a){c+=a[i]}; return c/a.length})([11.368,  10.065, 4.403])

viernes, 23 de mayo de 2014

How to measure web page load visually using window.performance API

Navigation Timing API, http://www.w3.org/TR/navigation-timing/ provides detailed  measurements about of current web page performance in terms of load time. It support server, requests and browser loading and rendering information. This standar is supported at least by chrome, firefox and IE>=9

The project https://github.com/kaaes/timing provides a visual report for seeing this information. For make it work, in your web page just add the following lines:

var profiler_js = 'https://raw.githubusercontent.com/kaaes/timing/gh-pages/profiler.js'; 
var d = document;
var h = d.getElementsByTagName('head')[0];
var s = d.createElement('script');
var t = new Date();  
s.type='text/javascript';
s.src=profiler_js+'?'+t.getTime();  
s.onload = c;
s.onreadystatechange = function(){
 if(this.readyState == 'loaded'){
  c();
 }
};  
h.appendChild(s);
function c() {
 window.__profiler = window.__profiler || function() {
  var p = new __Profiler();
  p.init();
 }  
 window.__profiler();
 __profiler.scriptLoaded = true;
}

IMHO this is the best way of measure some browser internal stuff like DNS fetching, rendering, html markup parsing, etc.

Also another way of running this profile tools but less intrusive, this is not downloading any extra file is the following:
Include the minified inline version of profile.js:
function __Profiler(){this.totalTime=0;this.barHeight=18;this.timeLabelWidth=50;this.nameLabelWidth=160;this.textSpace=this.timeLabelWidth+this.nameLabelWidth;this.spacing=1.2;this.unit=1;this.fontStyle="11.5px Arial";this.containerPadding=20;this.container=null;this.customElement=false;this.timingData=[];this.sections=[]}__Profiler.prototype.eventsOrder=["navigationStart","redirectStart","redirectStart","redirectEnd","fetchStart","domainLookupStart","domainLookupEnd","connectStart","secureConnectionStart","connectEnd","requestStart","responseStart","responseEnd","unloadEventStart","unloadEventEnd","domLoading","domInteractive","msFirstPaint","domContentLoadedEventStart","domContentLoadedEventEnd","domContentLoaded","domComplete","loadEventStart","loadEventEnd"];__Profiler.prototype.cssReset="font-size:12px;line-height:1em;z-index:99999;text-align:left;font-family:Calibri,'Lucida Grande',Arial,sans-serif;text-shadow:none;box-shadow:none;display:inline-block;color:#444;font-weight:normal;border:none;margin:0;padding:0;background:none;";__Profiler.prototype.elementCss="position:fixed;margin:0 auto;top:0;left:0;right:0;border-bottom:solid 1px #EFCEA1;box-shadow:0 2px 5px rgba(0,0,0,.1);";__Profiler.prototype.containerCss="background:#FFFDF2;background:rgba(255,253,242,.99);padding:20px;display:block;";__Profiler.prototype.headerCss="font-size:16px;font-weight:normal;margin:0 0 1em 0;width:auto";__Profiler.prototype.buttonCss="float:right;background:none;border-radius:5px;padding:3px 10px;font-size:12px;line-height:130%;width:auto;margin:-7px -10px 0 0;cursor:pointer";__Profiler.prototype.infoLinkCss="color:#1D85B8;margin:1em 0 0 0;";__Profiler.prototype._getPerfObjKeys=function(obj){var keys=Object.keys(obj);return keys.length?keys:Object.keys(Object.getPrototypeOf(obj))};__Profiler.prototype._setUnit=function(canvas){this.unit=(canvas.width-this.textSpace)/this.totalTime};__Profiler.prototype._getSections=function(){return Array.prototype.indexOf?[{name:"network",color:[224,84,63],firstEventIndex:this.eventsOrder.indexOf("navigationStart"),lastEventIndex:this.eventsOrder.indexOf("connectEnd"),startTime:0,endTime:0},{name:"server",color:[255,188,0],firstEventIndex:this.eventsOrder.indexOf("requestStart"),lastEventIndex:this.eventsOrder.indexOf("responseEnd"),startTime:0,endTime:0},{name:"browser",color:[16,173,171],firstEventIndex:this.eventsOrder.indexOf("unloadEventStart"),lastEventIndex:this.eventsOrder.indexOf("loadEventEnd"),startTime:0,endTime:0}]:[]};__Profiler.prototype._createContainer=function(){var container=document.createElement("div");var header=this._createHeader();var button=this._createCloseButton();button.onclick=function(e){button.onclick=null;container.parentNode.removeChild(container)};container.style.cssText=this.cssReset+this.containerCss;if(!this.customElement){container.style.cssText+=this.elementCss}header.appendChild(button);container.appendChild(header);return container};__Profiler.prototype._createHeader=function(){var c=document.createElement("div");var h=document.createElement("h1");var sectionStr="/ ";for(var i=0,l=this.sections.length;i<l;i++){sectionStr+='<span style="color:rgb('+this.sections[i].color.join(",")+')">'+this.sections[i].name+"</span> / "}h.innerHTML="Page Load Time Breakdown "+sectionStr;h.style.cssText=this.cssReset+this.headerCss;c.appendChild(h);return c};__Profiler.prototype._createCloseButton=function(){var b=document.createElement("button");b.innerHTML="close this box ×";b.style.cssText=this.cssReset+this.buttonCss;return b};__Profiler.prototype._createInfoLink=function(){var a=document.createElement("a");a.href="http://kaaes.github.com/timing/info.html";a.target="_blank";a.innerHTML="What does that mean?";a.style.cssText=this.cssReset+this.infoLinkCss;return a};__Profiler.prototype._createNotSupportedInfo=function(){var p=document.createElement("p");p.innerHTML="Navigation Timing API is not supported by your browser";return p};__Profiler.prototype._createChart=function(){var chartContainer=document.createElement("div");var canvas=document.createElement("canvas");canvas.width=this.container.clientWidth-this.containerPadding*2;var infoLink=this._createInfoLink();this._drawChart(canvas);chartContainer.appendChild(canvas);chartContainer.appendChild(infoLink);return chartContainer};__Profiler.prototype._prepareDraw=function(canvas,mode,eventData){var sectionData=this.sections[eventData.sectionIndex];var barOptions={color:sectionData.color,sectionTimeBounds:[sectionData.startTime,sectionData.endTime],eventTimeBounds:[eventData.time,eventData.timeEnd],label:eventData.label};return this._drawBar(mode,canvas,canvas.width,barOptions)};__Profiler.prototype._drawBar=function(mode,canvas,barWidth,options){var start;var stop;var width;var timeLabel;var metrics;var color=options.color;var sectionStart=options.sectionTimeBounds[0];var sectionStop=options.sectionTimeBounds[1];var nameLabel=options.label;var context=canvas.getContext("2d");if(mode==="block"){start=options.eventTimeBounds[0];stop=options.eventTimeBounds[1];timeLabel=start+"-"+stop}else{start=options.eventTimeBounds[0];timeLabel=start}timeLabel+="ms";metrics=context.measureText(timeLabel);if(metrics.width>this.timeLabelWidth){this.timeLabelWidth=metrics.width+10;this.textSpace=this.timeLabelWidth+this.nameLabelWidth;this._setUnit(canvas)}return function(context){if(mode==="block"){width=Math.round((stop-start)*this.unit);width=width===0?1:width}else{width=1}context.strokeStyle="rgba("+color[0]+","+color[1]+","+color[2]+",.3)";context.lineWidth=1;context.fillStyle="rgba(255,255,255,0)";context.fillRect(0,0,barWidth-this.textSpace,this.barHeight);context.fillStyle="rgba("+color[0]+","+color[1]+","+color[2]+",.05)";context.fillRect(0,0,barWidth-this.textSpace,this.barHeight);context.shadowColor="white";context.fillStyle="rgba("+color[0]+","+color[1]+","+color[2]+",.2)";context.fillRect(Math.round(this.unit*sectionStart),2,Math.round(this.unit*(sectionStop-sectionStart)),this.barHeight-4);context.fillStyle="rgb("+color[0]+","+color[1]+","+color[2]+")";context.fillRect(Math.round(this.unit*start),2,width,this.barHeight-4);context.fillText(timeLabel,barWidth-this.textSpace+10,2*this.barHeight/3);context.fillText(nameLabel,barWidth-this.textSpace+this.timeLabelWidth+15,2*this.barHeight/3)}};__Profiler.prototype._drawChart=function(canvas){var time;var eventName;var options;var skipEvents=[];var drawFns=[];var context=canvas.getContext("2d");context.font=this.fontStyle;this._setUnit(canvas);for(var i=0,l=this.eventsOrder.length;i<l;i++){var evt=this.eventsOrder[i];if(!this.timingData.hasOwnProperty(evt)){continue}var item=this.timingData[evt];var startIndex=evt.indexOf("Start");var isBlockStart=startIndex>-1;var hasBlockEnd=false;if(isBlockStart){eventName=evt.substr(0,startIndex);hasBlockEnd=this.eventsOrder.indexOf(eventName+"End")>-1}if(isBlockStart&&hasBlockEnd){item.label=eventName;item.timeEnd=this.timingData[eventName+"End"].time;drawFns.push(this._prepareDraw(canvas,"block",item));skipEvents.push(eventName+"End")}else{if(skipEvents.indexOf(evt)<0){item.label=evt;drawFns.push(this._prepareDraw(canvas,"point",item))}}}canvas.height=this.spacing*this.barHeight*drawFns.length;context.font=this.fontStyle;var step=Math.round(this.barHeight*this.spacing);drawFns.forEach(function(draw){draw.call(this,context);context.translate(0,step)},this)};__Profiler.prototype._matchEventsWithSections=function(){var data=this.timingData;var sections=this.sections;for(var i=0,len=sections.length;i<len;i++){var firstEventIndex=sections[i].firstEventIndex;var lastEventIndex=sections[i].lastEventIndex;var sectionOrder=this.eventsOrder.slice(firstEventIndex,lastEventIndex+1);var sectionEvents=sectionOrder.filter(function(el){return data.hasOwnProperty(el)});sectionEvents.sort(function(a,b){return data[a].time-data[b].time});firstEventIndex=sectionEvents[0];lastEventIndex=sectionEvents[sectionEvents.length-1];sections[i].startTime=data[firstEventIndex].time;sections[i].endTime=data[lastEventIndex].time;for(var j=0,flen=sectionEvents.length;j<flen;j++){var item=sectionEvents[j];if(data[item]){data[item].sectionIndex=i}}}};__Profiler.prototype._getData=function(){if(!window.performance){return}var data=window.performance;var timingData=data.timing;var eventNames=this._getPerfObjKeys(timingData);var events={};var startTime=timingData.navigationStart||0;var eventTime=0;var totalTime=0;for(var i=0,l=eventNames.length;i<l;i++){var evt=timingData[eventNames[i]];if(evt&&evt>0){eventTime=evt-startTime;events[eventNames[i]]={time:eventTime};if(eventTime>totalTime){totalTime=eventTime}}}this.totalTime=totalTime;return events};__Profiler.prototype._init=function(){this.timingData=this._getData();this.sections=this._getSections();this.container=this._createContainer();if(this.customElement){this.customElement.appendChild(this.container)}else{document.body.appendChild(this.container)}var content;if(this.timingData&&this.sections.length){this._matchEventsWithSections();content=this._createChart()}else{content=this._createNotSupportedInfo()}this.container.appendChild(content)};__Profiler.prototype.init=function(element,timeout){if(element instanceof HTMLElement){this.customElement=element}if(timeout&&parseInt(timeout,10)>0){var self=this;setTimeout(function(){self._init()},timeout)}else{this._init()}};


And now just run it:
 window.__profiler = window.__profiler || function() {
  var p = new __Profiler();
  p.init();
 }  
 window.__profiler();


Enjoy!

miércoles, 29 de mayo de 2013

Get final HTML markup of a JavaScript application using rhino - envjs

This article briefly describes how to dump the final HTML markup of any web site using serverside tool rhino-envjs. We will work into the release root folder so "cd" to it.

step 1 - download latest envjs release (as this writing 1.2) and uncompress it.
step 2 - download ant and run "ant" on the previously uncompressed envjs folder. This should build envjs. step 3 - create the file test1.js with the following content: 
load('dist/env.rhino.js');
var url = "http://jquery.com"; 
window.location = url; 
print(document.innerHTML+"");
step 4 - in a shell enter the following command:
java -cp rhino/js.jar org.mozilla.javascript.tools.shell.Main \
  -opt -1 test1.js > test1.html

If everything was fine you can open the generated file test1.html with firefox and see the jquery generated markup, not a very visual replica but often useful for SEO and those. For each JavaScript error a Java exception will be printed on stderr.

In my case this is useful for 100% JavaScript web applications SEO support.

martes, 4 de diciembre de 2012

Guidelines for writing GWT Overlay types

Guidelines for writing GWT Overlay types

This post is related with an older post Eclipse templates for overlay types about the same topic. You can find eclipse java code templates for code proposed here in that section. Now, that I have my ideas more clear and tested this is a summary.

When writing overlay types we have to take in consideration two main ideas:

  • we are writing a Java class that will be used by Java users
  • we are overlaying a JavaScript object with an API designed to be used in JavaScript

The following presents some guidelines learned and adopted in my GWT libraries porting javascript libraries like YUI and raphaël to GWT using 100% overlay types for a zero overhead Java API implementation.

Constructor

Use a static method with a simple name like create() or newInstance() that return an empty object or an object with some desired initialization state. Example:

public class MyOverlay extends JavaScriptObject {
 protected MyOverlay(){}
 public static native final create()/*-{
  return {}; 
 }-*/;
}

Properties

In Java we use the Bean convention for accessing obejct attributes / properties with getters and setters methods:

public class MyOverlay extends JavaScriptObject {
 protected MyOverlay(){}
 public static native final create()/*-{
  return {}; 
 }-*/;
 public native final String getColor()/*-{
  return this.color; 
 }-*/;
 public native final void setColor(String color)/*-{
  this.color=color; 
 }-*/;
}
End User example code:
MyOverlay o = MyOverlay.create(); 
o.setColor("red"); 
aController.method1(o);

Nevertheless, we must remember that "we are overlaying a JavaScript object with an API designed to be used in JavaScript". In JavaScript we normally are able to define an entire object in a single statement. For accomplish this in Java we need our setters methods to return the this object so we can perform method chaning. Also, but not so important, se omit the prefixes get/set for there methods

public class MyOverlay extends JavaScriptObject {
 protected MyOverlay(){}
 public static native final create()/*-{
  return {}; 
 }-*/;
 public native final String color()/*-{
  return this["color"]; 
 }-*/;
 public native final MyOverlay color(String color)/*-{
  this["color"]=color; 
  return this; 
 }-*/;
}

End User example code:

aController.method1(MyOverlay.create().color("red")); 

Much more similar to its JavaScript common counterpart aController.method1({color: "red"}), don't you think ?

Some notes:

  • We use setters and getters without the prefixes "get" or "set", so our method names are the same as the property name. This make it easy to use IDE tools like eclipse Java Code templates for generating the getter/setter code automatically.
  • In JSNI code we use string literals this["color"] instead variable names like this.color so the closure compiler (available optionally in GWT > 2.5) don't mess up JavaScript Object property names.

For those Java convention lovers I think it wouldnt cost too much to have "normal" java bean getters and setters AND this kind of shorter accessor methods for those who like to define object more "a la" javascript.

About Performance

Unfortunately the proposed code style here adds some overhead to the JavaScript code output produced by the GWT compiler. In the following example, I have created 2 JSOs, one called NormalBean that follows the Java Bean getter/setter conventions and other one called JsFriendlyBean that uses the solution proposed here with setters that return 'this' for method chaining. Then I use them both and compared the resulting JavaScript output code by GWT compiler using -style PRETTY and -optimize 9 compiler options. This is the result:

Java Sources:

NormalBean normalBean = NormalBean.create();
normalBean.setColor("turttlered1");
normalBean.setAge(13);

JsFriendlyBeam jsFriendlyBeam = JsFriendlyBeam.create()
 .color("turttlered2").age(14); 

Window.alert(normalBean + " - " + jsFriendlyBeam);

JavaScript output:

normalBean = {};
normalBean['color'] = 'turttlered1';
normalBean['age'] = 13;
jsFriendlyBeam = $age($color({}, 'turttlered2'), 14);
$wnd.alert(normalBean + ' - ' + jsFriendlyBeam);
...
function $color(this$static, val){
  this$static['color'] = val;
  return this$static;
}
function $age(this$static, val){
  this$static['age'] = val;
  return this$static;
}

As you can see, the setters of the NormalBean are inlined in JavaScript with zero-overhead while the setters of JsFriendlyBean are not. In the later, JavaScript functions $age() and $color() are created and called and that adds little but some overhead.

I cannot find a workaround for this. Tried with single JSNI statements like the followings but the result is the same:

public native final JsFriendlyBeam age(int val) /*-{
 return (this["age"]=val, this); 
}-*/;
and
public native final JsFriendlyBeam age(int val) /*-{
 return (this["age"] = val || true) ? this : null;
}-*/;

If anybody has an idea about how this setters can be written so they are inlined in JavaScritp output, pleas share it with me :)

domingo, 10 de junio de 2012

JavaScript function inBetween

JavaScript function inBetween - be notified when a javascript function is called N times in between a time lapsus of T milliseconds.

This idea borned when reading the work of Ben Alman for function throttle : http://benalman.com/projects/jquery-throttle-debounce-plugin/

In that case the problem is to execute a function no more than once every T milliseconds. This is very helpful when you want to control how much an event handler is called back. I put a common example for events that are called intensively, like mousemove, or drag in my raphaël tutorial: http://cancerbero.vacau.com/raphaeltut/index.html#sec-events-function-throttle   There I explain very usefull cases of function throttle.

The thing is that function throttle made me think on the opposite problem: being notified when a function is called N times in a time lapsus of T milliseconds. This can be useful for registering double, triple, cuadruple clicks, and in general nth-clicks events.I call this, function inBetween (don't have a better name, suggestions accepted). See for example, how I would use inBetween with jquery for neing notified when a triple click occurs in between 1000 ms:

function click3Hendler(evt) {
    alert(this.clickCount+" clicks detected. last click xcoord: "+evt.clientX);
};
$("#someElement").click(inBetween(3, 1000, cb, {clickCount: 3}));

This is a complete working example: http://jsfiddle.net/RP7mW/11/.

Also there is a section in my raphaël tutorial for registering nth-click listeners to raphaël shapes using inBetween(). Link: http://cancerbero.vacau.com/raphaeltut/index.html#sec-events-inbetween

And this is the inBetween function code:
        
/**
 * inBetween resolves the problem of being notified when a function is called N times in in between a time lapsus of T ms.
 * @param n the amount of times.
 * @param t the time lapsus in ms
 * @param callback - the function to be called when the returned fcuntion is called at least n times in a lapsus of n ms.
 * @return a new function - when that function is called n times in a lapsus
 of t ms, then callback function will be called using the context object as
 the callback context.
 */
function inBetween(n, t, callback, context) {    
    var sb = [];
    sb.push("var that = arguments.callee; ")
    sb.push("var thisTime = new Date().getTime(); ")
    sb.push("var arr = that['ARR'];");
    sb.push("if(!arr){");
    sb.push("    arr = []; ");
    sb.push("    for(var i = 0; i < that['N']; i++) arr.push(thisTime); ");
    sb.push("    that['ARR'] = arr;");
    sb.push("    that['COUNT']=0");
    sb.push("}");
    
    sb.push("that['COUNT']++; ");;
    sb.push("arr.push(thisTime);");
    sb.push("var lastTime = arr.shift();");
        
    sb.push("if(that['COUNT'] >= that['N']) {");
    sb.push("    that['COUNT']=1; ");
    sb.push("    for(var i = 0; i < that['N']; i++) arr[i] = thisTime; ");
    sb.push("    if(thisTime-lastTime < that['T']) ");          
    sb.push("        that['CB'].apply(that['CTX'], arguments); ");
    sb.push("}");
        
    var fn = new Function(sb.join(""));    
    fn['N']=n;
    fn['T']=t;
    fn['CB']=callback;
    fn['CTX']=context;
    return fn;        
}; 

Hope this can be of some use for those who need some advance event - function stuff like this.

viernes, 9 de marzo de 2012

RaphaelMarkup - XML syntax for great drawing Javascript API

My new pet project is http://code.google.com/p/raphaelmarkup/, let me present it to you:


Some links:

feature examples: http://cancerbero.vacau.com/raphaelmarkup/src/doc/examples.html

different ways of including raphael XML in HTML documents:
http://cancerbero.vacau.com/raphaelmarkup/src/doc/usage.html

documented raphael XML document definition in XML Schema:
http://cancerbero.vacau.com/raphaelmarkup/src/raphael.xsd




resume:

Write and validate your raphael drawings in xml, stylize with CSS like
syntax. Access any raphael paper instances in an XML DOM using jquery.
Use CSS 3 selectors for locating your shapes in the raphael DOM.

long description:

raphaël javascript library has a portable and nice API for drawing
vector graphics with javascript. This project, RaphaelMarkup, try to
bring declarative parts of raphaël API to XML so you can draw with XML
and CSS. Some important things that raphaelmarkup supports:

* RaphaelMarkup define a XML document type for writing raphaël in
XML. A XML Schema document definition is provided so users can
validate their xml drawings.
* It contains a javascript renderer for easily render xml drawings
in your HTML documents.
Provides several ways of including XML drawings in HTML documents,
like embedding code, ajax, XSL stylesheets, etc.
* Creates an XML DOM that can be manipulated programatically with
tools like jquery. and then easily update the drawing with the changes
in the DOM
* CSS like syntax for raphaël shapes attributes. Raphael elements
support id and class attributes, and CSS3 selectors are supported.
Note that this is not CSS, some cascading rules are different and
raphael attributes are not valid CSS.
* Easy scripting for rendered documents.
* Utilities for easy export any raphael paper to XML drawing code.

RaphaelMarkup depends on raphael and jquery libraries. jquery is used
for XML parsing and XML DOM Manipulation.

The project just started, many things still need revision and some
parts of raphaeljs api still need works. Expect improvements soon! I'm
announcing it here for feedback so please if you have any suggestions.
Expect project inprovements soon!

miércoles, 23 de noviembre de 2011

Load javascript or css dynamically in javascript

Loading Javascript or CSS dynamically in a DOM with pure JavasScript

If for some reason you need to programatically load some javascript or css stylesheet in the document, here is some code that helped me and seems to work fine in most browsers.

/**
 * loads javascript file or css stylesheet in current document. Usage Examples:
 * loadFile("www.server.com/file1.js", "js", function(){ alert("file1.js
 * loaded!"); });
 * 
 * @param name :
 *            the url of the resource to load. I think there is no cross site
 *            problems here.
 * @param type :
 *            "js" or "css". if "js" then a new javascript script will be loaded
 * @param listener
 *            an optional function that will be called when loading of the
 *            resource is ready.
 * @author: sgurin
 */
var _loadF = function(name, type, listener) {
 var el = null;
 if (type == "js") {
  el = document.createElement("script");
  el.setAttribute("type", "text/javascript");
  if (listener) {
   el.onreadystatechange = function() {
    if (this.readyState == 'complete')
     listener();
   };
   el.onload = listener;
  }
  el.setAttribute("src", name);
 } else if (type == "css") {
  el = document.createElement("link");
  el.setAttribute("rel", "stylesheet");
  el.setAttribute("type", "text/css");
  el.setAttribute("href", name);
 }
 document.body.appendChild(el);
}

An example usage:

_loadF("http://code.jquery.com/jquery-2.0.0.min.js", "js", function(){
 //jquery loaded to current DOM window and ready to use
 $("p").click(function(e){
  alert("p clicked"); 
 });
}); 

Example where this can be useful

Some exentric programmers / web application need a javascript application to be loaded into the current document. This is often accomplished setting current web address to a "javascript: " prefixed address that execute some javascript code in the context of the current document or using firebug / chrome development console.

In this modality, the given javascript code will execute in the context of the current document, being able to show new visual components or even modify the current document. Here it is nice to have a short function that is capable of loading both javascript or css into the current DOM so we can use these libraries in this kind of programs.

Other example, paste the folowing into firebug/chrome devel console:

var _loadJs = function(name, listener) {
 var el = document.createElement("script");
 el.setAttribute("type", "text/javascript");
 if (listener) {
  el.onreadystatechange = function() {
   if (this.readyState == 'complete')
    listener();
  };
  el.onload = listener;
 }
 el.setAttribute("src", name);
 document.getElementsByTagName("head")[0].appendChild(el);
}; 
_loadJs("http://code.jquery.com/jquery-1.9.1.min.js", function(){
 alert("this document has "+$("p").size()+" paragraphs. "); 
}); 

viernes, 27 de agosto de 2010

Script for dynamic JavaScript and CSS loading

With the following script, you can easily load javascripts and css files dynamically and asynchronously of course:


/**javascript and css loader
* @author: sgurin
*/
(function(){

var _loadF = function(name, type, listener){
var el=null;
if (type=="js"){
el=document.createElement("script");
el.setAttribute("type","text/javascript");
if(listener) {
el.onreadystatechange = function () {
if (this.readyState == 'complete') listener();
};
el.onload=listener;
}
el.setAttribute("src", name);
}
else if (type=="css"){
el=document.createElement("link");
el.setAttribute("rel", "stylesheet");
el.setAttribute("type", "text/css");
el.setAttribute("href", name);
}
document.body.appendChild(el);
};
/**public global function
* @param css - array of css url to load. can be null
* @param js - array of javascript scripts urls to load. can be null
* @param listener function that will be called when ALL js and css files are loaded.
*/
LOADFILE = function (css, js, listener) {
css=css==null?[]:css;
js=js==null?[]:js;
var subListener=function(){
LOADFILE.counter--;
if(LOADFILE.counter==0)
listener();
};
for(var i=0; i<css.length; i++) {
_loadF(css[i], "css", subListener);
}
for(var i=0; i<js.length; i++) {
LOADFILE.counter++;
_loadF(js[i], "js", subListener);
}
};
LOADFILE.counter=0;//file load counter

})();


For example, you can dynamically load all your javascript application's javascript and css files like this:


LOADFILE(
["http://yui.yahooapis.com/2.8.1/build/slider/assets/skins/sam/slider.css",
"http://yui.yahooapis.com/2.8.1/build/fonts/fonts-min.css",
"http://yui.yahooapis.com/2.8.1/build/colorpicker/assets/skins/sam/colorpicker.css"],

["http://yui.yahooapis.com/2.8.1/build/yahoo-dom-event/yahoo-dom-event.js",
"http://yui.yahooapis.com/2.8.1/build/animation/animation-min.js",
"http://yui.yahooapis.com/2.8.1/build/dragdrop/dragdrop-min.js",
"http://yui.yahooapis.com/2.8.1/build/slider/slider-min.js",
"http://yui.yahooapis.com/2.8.1/build/element/element-min.js",
"http://yui.yahooapis.com/2.8.1/build/colorpicker/colorpicker-min.js"],

function(){
//my javascript application's code
var Event = YAHOO.util.Event,
picker;
Event.onDOMReady(function() {
YAHOO.log("Creating Color Picker.", "info", "example");
picker = new YAHOO.widget.ColorPicker("container", {
showhsvcontrols: true,
showhexcontrols: true,
images: {
PICKER_THUMB: "assets/picker_thumb.png",
HUE_THUMB: "assets/hue_thumb.png"
}
});
YAHOO.log("Finished creating Color Picker.", "info", "example");
var onRgbChange = function(o) {
YAHOO.log("The new color value is " + o.newValue, "info", "example");
}
picker.on("rgbChange", onRgbChange);
Event.on("reset", "click", function(e) {
picker.setValue([255, 255, 255], false);
});
Event.on("gethex", "click", function(e) {
alert("Current hex value: " + picker.get("hex"), "info", "example");
});
});
});


Hope this help somebody