Tag error:   ->  Textpattern Notice: Page template default does not contain a txp:article tag  on line 464
Netxus Foundries: DHTML

Nothing really new since there is already a firefox extension which does just the same but I also wanted support other browsers.

So this bookmarklet toggles the cookie Xdebug uses to flag if it should debug the running script or not.

Toggle Xdebug

Drag it to your bookmarks (or favourites or whatever is called in your browser) toolbar so it’s easy to access. Assign it a keyboard shortcut if your browser has that function.

By default it’ll prompt for the idekey to be used by Xdebug, it’s a good idea to modify the idekey variable at the start of the bookmarklet’s source with your idekey or with ‘default’ if you’re not using a DBGp proxy.

I’m working on a little thing which I don’t want to speak about just now, lets see if I can get it to do what I want first before making it public. However for that project I needed to pack several small rectangles of varying dimensions into a bigger one without them overlapping.

At first it seem like an easy task but the more I thought about it the more difficult it was. After a bit of research I found out that until today this seems to be a non resolved problem, there are algorithms which offer a pretty decent approximation to the optimal result though. The problem is commonly known as the Bin Packing problem and in this case I needed to work with two dimensional objects (rectangles) so it gets a lot more complicated.

After more googling I found an article by Jim Scott with pseudo code to solve a similar problem, the article explains on how to pack several lightmaps into a single texture. It was the easiest to understand algorithm I had found so I went ahead and implemented it in PHP. I had it in a few minutes but I wanted to fine tune the results by feeding it the rectangles by different sorting criteria, since it seems to be a good way to achieve the best results. Well, I got tired to slowly generating the image in PHP to check the results with every minor variation so I implemented a JavaScript version which allows to see the result in real time.

Here you can see the algorithm implementation or download it

javascript:
  1. /*
  2. Script: RectanglePacker.js
  3.   An algorithm implementation in JavaScript for rectangle packing.
  4. Author:
  5.   Ivan Montes <drslump@drslump.biz>, <http://blog.netxus.es>
  6. License:
  7.   LGPL - Lesser General Public License
  8. Credits:
  9.   - Algorithm based on <http://www.blackpawn.com/texts/lightmaps/default.html>
  10. */
  11.  
  12. /*
  13.   Class: NETXUS.RectanglePacker
  14.   A class that finds an 'efficient' position for a rectangle inside another rectangle
  15.   without overlapping the space already taken.
  16.  
  17.   Algorithm based on <http://www.blackpawn.com/texts/lightmaps/default.html>
  18.  
  19.   It uses a binary tree to partition the space of the parent rectangle and allocate the
  20.   passed rectangles by dividing the partitions into filled and empty.
  21. */
  22.  
  23.  
  24. // Create a NETXUS namespace object if it doesn't exists
  25. if (typeof NETXUS === 'undefined')
  26.   var NETXUS = function() {};   
  27.  
  28.  
  29. /* 
  30.   Constructor: NETXUS.RectanglePacker
  31.   Initializes the object with the given maximum dimensions
  32.  
  33.   Parameters:
  34.  
  35.     width - The containing rectangle maximum width as integer
  36.     height - The containing rectangle maximum height as integer
  37.    
  38. */ 
  39. NETXUS.RectanglePacker = function ( width, height ) {
  40.  
  41.   this.root = {};
  42.  
  43.   // initialize
  44.   this.reset( width, height )
  45. }
  46.  
  47.  
  48. /*
  49.   Resets the object to its initial state by initializing the internal variables
  50.   Parameters:
  51.  
  52.     width - The containing rectangle maximum width as integer
  53.     height - The containing rectangle maximum height as integer
  54. */
  55. NETXUS.RectanglePacker.prototype.reset = function ( width, height ) {
  56.   this.root.x = 0;
  57.   this.root.y = 0;
  58.   this.root.w = width;
  59.   this.root.h = height;
  60.   delete this.root.lft;
  61.   delete this.root.rgt;
  62.  
  63.   this.usedWidth = 0;
  64.   this.usedHeight = 0
  65. }
  66.  
  67.  
  68. /*
  69.   Returns the actual used dimensions of the containing rectangle.
  70.  
  71.   Returns:
  72.  
  73.     A object composed of the properties: 'w' for width and 'h' for height.
  74. */
  75. NETXUS.RectanglePacker.prototype.getDimensions = function () {
  76.   return { w: this.usedWidth, h: this.usedHeight };
  77. }
  78.  
  79.  
  80. /*
  81.   Finds a suitable place for the given rectangle
  82.  
  83.   Parameters:
  84.  
  85.     w - The rectangle width as integer.
  86.     h - The rectangle height as integer.
  87.    
  88.   Returns:
  89.  
  90.     If there is room for the rectangle then returns the coordinates as an object
  91.     composed of 'x' and 'y' properties.
  92.     If it doesn't fit returns null
  93. */   
  94. NETXUS.RectanglePacker.prototype.findCoords = function ( w, h ) {
  95.  
  96.   // private function to traverse the node tree by recursion
  97.   function recursiveFindCoords ( node, w, h ) {
  98.  
  99.     // private function to clone a node coords and size
  100.     function cloneNode ( node ) {
  101.       return {
  102.         x: node.x,
  103.         y: node.y,
  104.         w: node.w,
  105.         h: node.h
  106.       };
  107.     }   
  108.    
  109.     // if we are not at a leaf then go deeper
  110.     if ( node.lft ) {
  111.       // check first the left branch if not found then go by the right
  112.       var coords = recursiveFindCoords( node.lft, w, h );
  113.       return coords ? coords : recursiveFindCoords( node.rgt, w, h );
  114.     }
  115.     else
  116.     {
  117.       // if already used or it's too big then return
  118.       if ( node.used || w > node.w || h > node.h )
  119.         return null;
  120.        
  121.       // if it fits perfectly then use this gap
  122.       if ( w == node.w && h == node.h ) {
  123.         node.used = true;
  124.         return { x: node.x, y: node.y };
  125.       }
  126.      
  127.       // initialize the left and right leafs by clonning the current one
  128.       node.lft = cloneNode( node );
  129.       node.rgt = cloneNode( node );
  130.      
  131.       // checks if we partition in vertical or horizontal
  132.       if ( node.w - w > node.h - h ) {
  133.         node.lft.w = w;
  134.         node.rgt.x = node.x + w;
  135.         node.rgt.w = node.w - w; 
  136.       } else {
  137.         node.lft.h = h;
  138.         node.rgt.y = node.y + h;
  139.         node.rgt.h = node.h - h;             
  140.       }
  141.      
  142.       return recursiveFindCoords( node.lft, w, h );   
  143.     }
  144.   }
  145.    
  146.   // perform the search
  147.   var coords = recursiveFindCoords( this.root, w, h );
  148.   // if fitted then recalculate the used dimensions
  149.   if (coords) {
  150.     if ( this.usedWidth < coords.x + w )
  151.       this.usedWidth = coords.x + w;
  152.     if ( this.usedHeight < coords.y + h )
  153.       this.usedHeight = coords.y + h;
  154.   }
  155.   return coords;
  156. }

This algorithm can be used for a wide variety of problems, from packaging small sprites in a big image to allocate tasks in complex projects (use the width for the team developers and the height for the time).

A simple usage example could be as follows:


var coords, packer = new NETXUS.RectanglePacker( 512, 512 );
for (var i=0; i<images.length; i++) { coords = packer.findCoords( images[i].width, images[i].height ); alert( 'Image of ' + images[i].width + 'x' + images[i].height + ' allocated at ' + coords.x + 'x' + coords.y );
}

I’ve put up a demo so you can try it and see the results with different parameters. Tip: the best results seems to be with the blocks sorted by the magic method and reversed.

Jun 15, 12:57 AM 2 comments

For secret reasons I need to usually convert hex strings to decimal notation. This can be easily done with a scientific calculator, like the one supplied with Windows, but it’s slow and I’m usually in a hurry to convert them :)

So I took a look at GreaseMonkey, which is an extension for Firefox which allows to modify a web page once rendered with a simple javascript file.

To install it you’ll first need to have GreaseMonkey installed and activated (a little monkey face on Firefox’s status bar), then click on this link satkeystringconversor.user.js

Here is the highlighted source code so you can review it easily. It’s a pretty simple script which traverses the DOM performing regular expression tests on text nodes and converting the matched strings into span elements with an onclick behaviour.

javascript:
  1. // ==UserScript==
  2. // @name           Sat key string conversor
  3. // @namespace      http://blog.netxus.es
  4. // @description    Converts satelite key strings from hex to dec or vice versa
  5. // @include        http://www.satscreen.ws/forum/*
  6. // @include      https://www.blogger.com/*
  7. // @include      http://floresd.blogspot.com/*
  8. // @include      http://galeon.com/tusflores/*
  9. // ==/UserScript==
  10. function HEXDEC ( node ) {
  11.  
  12.   function hex2dec(hex) {
  13.     var dec = [],
  14.       re = /([0-9A-Za-z]{2})s*/g,
  15.       m,
  16.       conv;
  17.     while (m = re.exec(hex)) {
  18.       conv = parseInt(m[1], 16);
  19.       dec.push( (conv < 100 ? ( conv < 10 ? '00' : '0' ) : '') + conv );
  20.     }
  21.     return dec.join(' ');
  22.   }
  23.  
  24.   function dec2hex(dec) {
  25.     var hex = [],
  26.       re = /([0-9]{3})s*/g,
  27.       m,
  28.       conv;
  29.     while (m = re.exec(dec)) {
  30.       conv = (m[1]-0).toString(16).toUpperCase();
  31.       hex.push( conv.length < 2 ? '0'+conv : conv );
  32.     }
  33.     return hex.join(' ');   
  34.   }
  35.  
  36.   function swapHexDec( e ){
  37.     var target;
  38.     if (!e) var e = window.event;
  39.     if (e.target) target = e.target
  40.     else if (e.srcElement) target = e.srcElement
  41.     if (target.nodeType === 3) target = target.parentNode;
  42.     var tmp = target.firstChild.nodeValue;
  43.     target.firstChild.nodeValue = target.getAttribute('hexdec');
  44.     target.setAttribute('hexdec', tmp);
  45.     if (target.className === 'hexdec')
  46.       target.className = 'dechex';
  47.     else
  48.       target.className = 'hexdec';
  49.   }
  50.  
  51.  
  52.   // if no node is specified then start processing on the page body
  53.   node = node || document.body;
  54.    
  55.   var skipRe = /^(script|style|textarea)$/i;
  56.   var re = /((?:[0-9A-Ha-h]{2}s*){6,})|((?:[0-9]{3}s*){6,})/g;
  57.   // get all nodes from dom
  58.   var nodes = node.all || node.getElementsByTagName('*');
  59.   var i, cnode, span;
  60.  
  61.   // loop thru all the nodes
  62.   for (i=0; i<nodes.length; i++) {
  63.     // skip if it has no child nodes, it's already processed or the tag is not valid
  64.     if (!nodes[i].childNodes.length ||
  65.       nodes[i].hasAttribute('hexdec') ||
  66.       skipRe.test(nodes[i].tagName)
  67.     )
  68.       continue;
  69.    
  70.     // loop thru all children
  71.     cnode = nodes[i].childNodes[0];
  72.     while (cnode) {
  73.       // we just care about text nodes
  74.       if (cnode.nodeType === 3) {
  75.         // loop thru all regex matches
  76.         while (m=re.exec(cnode.nodeValue)) {
  77.           // if there is text before the match add it on its own node
  78.           if (m.index > 0)
  79.             cnode.parentNode.insertBefore( 
  80.               document.createTextNode( cnode.nodeValue.substr(0, m.index) ), cnode
  81.             );
  82.           // create a new span node for the key string
  83.           span = document.createElement('SPAN');
  84.           // if the first capture parenthesis is found then it's in hex format
  85.           if (m[1]) {
  86.             span.className = 'hexdec';
  87.             span.setAttribute( 'hexdec', hex2dec(m[0]) );
  88.           // otherwise it is in decimal format
  89.           } else {
  90.             span.className = 'dechex';
  91.             span.setAttribute( 'hexdec', dec2hex(m[0]) );
  92.           }
  93.           span.setAttribute( 'title', 'Click to swap betwen hex and decimal notations' );
  94.           span.appendChild( document.createTextNode( m[0] ) );
  95.           //span.onclick = swapHexDec;
  96.           span.addEventListener( 'click', swapHexDec, true );
  97.           cnode.parentNode.insertBefore( span, cnode );
  98.          
  99.           // remove the already processed text from the node
  100.           cnode.nodeValue = cnode.nodeValue.substring( m.index + m[0].length );
  101.           // reset the regex text pointer
  102.           re.lastIndex = 0;
  103.         }
  104.       }
  105.       // point to next children or null
  106.       cnode = cnode.nextSibling;
  107.     }
  108.   }
  109. }
  110.  
  111. window.addEventListener( 'load', function () { HEXDEC() }, true );
  112.  
  113. function addGlobalStyle(css) {
  114.     var head, style;
  115.     head = document.getElementsByTagName('head')[0];
  116.     if (!head) { return; }
  117.     style = document.createElement('style');
  118.     style.type = 'text/css';
  119.     style.innerHTML = css;
  120.     head.appendChild(style);
  121. }
  122. addGlobalStyle( '\
  123.   .hexdec, .dechex { \
  124.     cursor: hand;\
  125.     cursor: pointer !important;\
  126.     border-bottom: 1px dotted #888;\
  127.     padding-left: 32px;\
  128.     background: url(http://pollinimini.net/hex.png) no-repeat left;\
  129.   }\n\
  130.   .dechex {\
  131.     background: url(http://pollinimini.net/dec.png) no-repeat left;\
  132.   }' );

By default the following sites are enabled (you can configure it for whatever sites you need):

  • http://www.satscreen.ws/forum/
  • http://floresd.blogspot.com/
  • http://galeon.com/tusflores/
  • https://www.blogger.com/

It uses a neutral blue coloring for the styling which shouldn’t break most web designs. The images are served from my own server, so they aren’t very reliable, don’t be afraid if one day they stop showing :)

Jun 11, 02:33 PM 1 comment

I just stumbled upon a short yet excellent post on Dean Edwards blog. It shows how to use IE proprietary javascript conditional comments to detect if we’re running on IE in a secure and very short way.

javascript:
  1. var isMSIE = /*@cc_on!@*/false;

Further down in the post’s comments someone named Lucky propose the use of a variant to check against IE 5.5+ and IE7.

javascript:
  1. var ieScript/*@cc_on=@_jscript_version@*/;
  2. if (ieScript) // IE
  3. if (ieScript>=5.5) // IE 5.5 +
  4. if (ieScript==5.7) // IE 7.0 Vista

Moreover Dean also shows in one of the comments how to check for the rendering path used by IE (quirks VS strict modes)

javascript:
  1. var ieStrict = document.compatMode == 'CSS1Compat';

While is not very wise to abuse the sniffing of browsers vendors it’s also true that sometimes it’s required to split the code path in order to support the incompatibilities among browser vendors and versions.

I’ve been working a lot lately with regular expressions in Javascript. Being a really powerful tool it’s quite easy to get shoot on your own feet if you are not careful.

After pushing the Javascript built in regular expression engine to its limits I found it lacking some modern features available in most recent versions of regex engines used in many languages like .Net, C++ Boost, Perl, Ruby or PHP. The feature I miss the most is the ability to use look behind assertions, with JS just supporting look ahead ones. Another feature I miss are named groups and then one very useful new feature is the /x modifier (also known as ignore whitespace) which allows regular expression literals to span over multiple lines, ignoring white space, new lines and embedded comments!

See the following example regex to match an url (I’ve break it so it doesn’t mess with the site layout):


/\b(https?|ftp):\/\/([-A-Z0-9.]+)
(\/[-A-Z0-9+&@#/%=~_|!:,.;]*)?
(\?[-A-Z0-9+&@#/%=~_|!:,.;]*)?/i

Writting it is not difficult but modifiing it or even understanding it becomes an issue. Using the Perl /x modifier it would look like this:


m/\b # match at word break (https?|ftp):// # protocol: http, https or ftp ([-A-Z0-9.]+) # domain (/[-A-Z0-9+&@#/%=~_|!:,.;]*)? # file path - optional (\?[-A-Z0-9+&@#/%=~_|!:,.;]*)? # query string - optional
/ix;

Much easier to understand, isn’t it? At least I really think so. To imitate this behaviour in Javascript I’ve written this simple function which just concatenates the regular expressions passed as arguments and returns a new one. This allows to break a complex regex into smaller pieces and comment them.

javascript:
  1. function xRegExp() {
  2.     var cur, re;
  3.     var arr = [];
  4.     var n = arguments.length;
  5.  
  6.     // check if the last argument contains the regexp modifiers
  7.     if (/^[gmi]+$/.test(arguments[n-1]))
  8.         n--;
  9.  
  10.     // go thru all the passed regexes and concatenate them
  11.     for (var i=0; i<n; i++) {
  12.         if (typeof arguments[i] === 'string') {
  13.             arr.push( arguments[i] );
  14.         } else {
  15.             // when we cast a regexp object to a string
  16.             // the / delims and modifiers are included
  17.             cur = String(arguments[i]);
  18.             arr.push( cur.substring(cur.indexOf('/')+1, cur.lastIndexOf('/')) );
  19.         }
  20.     }
  21.        
  22.     // create a new empty regexp
  23.     re = new RegExp();
  24.     // compile the concatenated regex to speed it up
  25.     re.compile( arr.join(''), arguments[n] );
  26.     return re;
  27. }

Please note that the same thing can be done by concatenating strings manually, however regexes syntax is terse enough without having to double escape all the tokens. That’s why I preffer to write them out in their literal form (between slashes) instead of using quotes.

Using the given function the above example would look like this. Note that you can use the regexp literal notation or just pass a simple string. The last argument can optionally pass the modifiers for the regular expression.


var re = new xRegExp( /\b/, //match at word break '(https?|ftp)://', //protocol: http, https and ftp '([-A-Z0-9.]+)', //domain '(/[-A-Z0-9+&@#/%=~_|!:,.;]*)?', //file path (optional) /(\?[-A-Z0-9+&@#/%=~_|!:,.;]*)?/, //query string (optional) 'i' //Modifiers: case-insensitive
);

Mar 20, 01:04 PM 5 comments