Wednesday, October 27, 2010

Emacs 22.1 uses xml instead of xmlstarlet

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=447378

The diff is pretty much:

diff -Naur emacs-22.1/lisp/progmodes/flymake.el emacs-22.1-flymake-xmlstarlet/lisp/progmodes/flymake.el
--- emacs-22.1/lisp/progmodes/flymake.el 2007-03-31 17:10:12.000000000 +0300
+++ emacs-22.1-flymake-xmlstarlet/lisp/progmodes/flymake.el 2007-10-20 17:20:04.794349104 +0300
@@ -1756,7 +1756,7 @@
 
 ;;;; xml-specific init-cleanup routines
 (defun flymake-xml-init ()
-  (list "xml" (list "val" (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace))))
+  (list "xmlstarlet" (list "val" (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace))))
 
 (provide 'flymake)
 

One alternative instead of directly patching the flymake.el file is:

sudo apt-get install xmlstarlet
sudo ln -s /usr/bin/xmlstarlet /usr/bin/xml

Tuesday, October 26, 2010

USPhoneNumberWidget

This discussion on Stack Overflow focused on how to build a multi <input> phone number fields in Django. The use case is a US Phone number (i.e. 650-555-1212) in which you want to create separate input fields for each part of the phone number. The proposed solutions are discussed in this thread:

http://stackoverflow.com/questions/1777435/django-multiwidget-phone-number-field

Django provides a widget class called MultiValueWidget.py in django.forms.widgets MultiValueWidget.py for this very purpose. We can look at SplitDateTimeWidget in this file for an example on how to use this class. Basically it comes down to implementing a decompress() method, which will take the string-encoded value and return back a list.
def decompress(self, value):
        if value:
            return value.split('-')
        return [None,None,None]
The above implementation implies that our data will be stored in the database with dash-separated fields. We also need to implement the value_from_datadict() method, which is what is used to return the value of the widget.

Normally, the MultiValueWidget class will retrieve each individual text field and concatenate the result back into a list as follows:
def value_from_datadict(self, data, files, name):
        return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]

We override this function and return back the data in hyphenated form:
def value_from_datadict(self, data, files, name):
        values = super(USPhoneNumberMultiWidget, self).value_from_datadict(data, files, name)
        return u'%s-%s-%s' % values
Footnote: The value_from_datadict() is used to retrieve the data submitted in the form and aggregating the data. The original method simply returns a list, but since we are not using MultiValueField objects in conjunction with MultiWidget, then we must go one extra step and return the data back into the hyphenated string form.

If we were to use MultiValueField objects with MultiWidgets, then the compress() method would be used when a form's clean() function is invoked. This would in turn would convert the list into the native Python object. Since we aren't using MultiValueField, then this clean() function is not invoked and we therefore must return the data directly. Hence, we have the " return u'%s-%s-%s' % values" inside our value_from_datadict() code.

django.forms.forms.py:
def _clean_fields(self):
        for name, field in self.fields.items():
            # value_from_datadict() gets the data from the data dictionaries.                                                                                                                                                                
            # Each widget type knows how to retrieve its own data, because some                                                                                                                                                              
            # widgets split data over several HTML fields.                                                                                                                                                                                   
            value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
            try:
                if isinstance(field, FileField):
                    initial = self.initial.get(name, field.initial)
                    value = field.clean(value, initial)
                else:
                    value = field.clean(value)
                self.cleaned_data[name] = value
                if hasattr(self, 'clean_%s' % name):

Monday, October 25, 2010

Using jQuery's pagination

Try out the demo of this jQuery pagination code:

http://d-scribe.de/webtools/jquery-pagination/demo/demo_options.htm

It works pretty well for static/fixed content, but how about for Ajax-based content? What about situations when you want to add elements to the document so that the total number of entries can be changed?

First, the great part of this plug-in is that you can use the pageSelectCallBack() to do your Ajax query. The demo_ajax.htm that comes with this plug-in shows you the basic approach. The challenge of course comes if you have a situation when you want to add/update existing entries and have the changes reflected in the pagination.

In order to accomplish this last part, several changes had to be made to the jquery.pagination.js code. There are actually several helper functions that get attached to the HTML DIV element, including selectPage(), prevPage(), nextPage().

1) I noticed first that the selectPage() was originally:
containers.each(function () {
        // Attach control functions to the DOM element   
       this.selectPage = function (page_id) { pageSelected(page_id);}

But this line appears to be a bug since the PageSelected() takes in an event object, and sets the current page using the event target and retrieves the page ID from there.
function pageSelected(evt) {
            var links, current_page = $(evt.target).data('page_id');
            containers.data('current_page', current_page);
            links = renderer.getLinks(current_page, pageSelected);
            containers.empty();

The way to fix it would be to invoke the callback function directly, similar to how it's done in other parts of the code:
this.selectPage = function (page_id) { opts.callback(page_id, containers); }

2) The other aspect is that we'll add helper functions that will enable us to update the rendering links (updateLinks()), as well as set and retrieve the total number of entries. Since the 'renderer' vars is a variable local to the scope, this addition was one way to get access to the values without modifying other parts of the existing code:
this.updateLinks = function() {
                var current_page = containers.data('current_page');
                links = renderer.getLinks(current_page, pageSelected);
                containers.empty();
                links.appendTo(containers);
            }
            this.setTotalPages = function(num) {
                renderer.pc.maxentries = num;
            }

            this.getTotalPages = function(num) {
                return renderer.pc.maxentries;
            }

3) Finally, inside our callback function, we invoke the getTotalPages(), setTotalPages(), and updateLinks() after the Ajax response, assuming that the JSON response returns back a 'total_entries' variable that denotes the total # of entries. Note that since jq is a jQuery object and we need to access the HTML DIV element (and not the jQuery object), we have to use j[0] to get access. (This approach of course limits us to have one pagination plug-in per page, but it can be modified to use each() if necessary).

function pageSelectCallback(page_index, jq) {
    var url = "/get_data";

    if (init === true) {
        $.ajax({
            url: url,
            dataType: 'json',
            data: {
                'start_page': page_index
            },
            success: function (response) {
                if (jq[0].getTotalPages() !== response.total_entries) { // Update the total count                                                                                                                                          
                        jq[0].setTotalPages(response.total_entries);
                        jq[0].updateLinks();

So our diff for the jQuery pagination plug-in looks like:

containers.each(function () {
        // Attach control functions to the DOM element
-       this.selectPage = function (page_id) { pageSelected(page_id);}
-       this.prevPage = function () {
-           var current_page = containers.data('current_page');
-           if (current_page > 0) {
-               pageSelected(current_page - 1);
-               return true;
-           }
-           else {
-               return false;
-           }
-       }
-       this.nextPage = function () {
-           var current_page = containers.data('current_page');
-           if(current_page < numPages()-1) {
-               pageSelected(current_page+1);
-               return true;
-           }
-           else {
-               return false;
-           }
-       }
+           this.selectPage = function (page_id) { opts.callback(page_id, containers); }
+           this.prevPage = function () {
+               var current_page = containers.data('current_page');
+               if (current_page > 0) {
+                   pageSelected(current_page - 1);
+                   return true;
+               }
+               else {
+                   return false;
+               }
+           }
+           this.nextPage = function () {
+               var current_page = containers.data('current_page');
+               if(current_page < numPages()-1) {
+                   pageSelected(current_page+1);
+                   return true;
+               }
+               else {
+                   return false;
+               }
+           }
+            this.updateLinks = function() {
+               var current_page = containers.data('current_page');
+               links = renderer.getLinks(current_page, pageSelected);
+               containers.empty();
+               links.appendTo(containers);
+            }
+            this.setTotalPages = function(num) {
+                renderer.pc.maxentries = num;
+            }
+
+            this.getTotalPages = function(num) {
+                return renderer.pc.maxentries;
+            }
        });
        // When all initialisation is done, draw the links
-       links = renderer.getLinks(current_page, pageSelected);
-       containers.empty();
-       links.appendTo(containers);
+        containers.each(function() { this.updateLinks(); });
+
        // call callback function
        opts.callback(current_page, containers);
-
+
 }

Saturday, October 23, 2010

Inside Facebook's JavaScript SDK code...

Over the past 2 weeks, I've been curious to figure out how Facebook's JavaScript SDK is implemented. The documentation and source code is discussed at http://github.com/facebook/connect-js/. The JavaScript library that you are supposed to add is located at: http://connect.facebook.net/en_US/all.js.

1. First, you can use Rhino and JS Beautifier (http://jsbeautifier.org/) to de-minify the http://connect.facebook.net/en_US/all.js file. Using the uncompressed version allows you not only review the source code but also enables you to set breakpoints and add alert statements to get a better understanding how the JavaScript code works.

2. Once you de-minify the code, you'll notice that Facebook's JavaScript SDK attempts to modularize the different aspects of its code. There are several different modules included with the all.js file. Among the notable modules and their accompanying utils inside this file are:

FB - getLoginStatus(), getSession(), login(), logout()
Array (array utils) - indexOf, merge, filter, keys, map, forEach
QS (query string utils) - encode, decode
Content (DOM-related utils) - append, appendHidden, insertIFrame, postTarget
Flash (Flash-related utils) - init, hasMinVersion, onReady
JSON - stringify, parse, flatten
ApiServer - graph, rest, oauthRequest, jsonp, flash
EventProvider - subscribers, subscribe, unsubscribe, monitor, clear, fire
Intl (international utils) - _endsInPunct, _tx, tx
String - trim, format, escapeHTML, quote
Dom - containsCss, addCss, removeCss, getStyle, setStyle, addScript, addCssRulse, getBrowserType, getViewPortInfo, ready
Dialog - _findRoot, _showLoader, _hideLoader, _makeActive, _lowerActive, _removeStacked, create, show, remove
XD (cross-domain utils) - init, resolveRelation, handler, recv, PostMessage.init, PostMessage.onMessage, Flash.init, Flash.onMessage, Fragment.checkandDispatch
Arbiter - inform
UiServer - genericTransform, prepareCall, getDisplayMode, getXdRelation, popup, hidden, iframe, async, _insertIframe, _triggerDefault, _popupMonitor, _xdChannelHandler, _xdNextHandler, _xdRecv, _xdResult
Auth - setSession, xdHandler, xdResponseWrapper
UIServer.Methods - permissions.request, auth.logout, auth.status
Canvas - setSize, setAutoResize

Inside all.js, you'll notice inside the source code the extensive use of FB.provide(), which is essentially a fancy classical object-oriented way to attach these modules to the FB object. For instance, FB.provide('Array', { ... }) is simply a way to add the Array object and its accompanying functions (i.e. FB.Array.indexOf()). (You can convince yourself by reviewing the provide() and create() functions inside the file to see how things work):
create: function(c, h) {
    var e = window.FB,
        d = c ? c.split('.') : [],
        a = d.length;
    for (var b = 0; b < a; b++) {
      var g = d[b];
      var f = e[g];
      if (!f) {
        f = (h && b + 1 == a) ? h : {};
        e[g] = f;
      }
      e = f;
    }
    return e;
  },

 provide: function(c, b, a) {
    return FB.copy(typeof c == 'string' ? FB.create(c) : c, b, a);
  },
3. When you call FB.init(), several things happen:
 a. The code verifies that you have provide an API key parameter (i.e. FB.init ( {apiKey : '12345'});).
 b. The function init() tries to load the fbs_ cookie that relates to this API key, setting FB.Cookie._enabled to be true, attempts to find a cookie that matches fbs_ + API_KEY for the domain, and sets the FB._session according to the cookie.
 c. The code then invokes getLoginStatus(), which attempts to run the auth.status function.
FB.ui({
      method: 'auth.status',
      display: 'hidden'
    }, c);
d. Inside the auth.status function, you will see a few notable things:
 1. A cross-domain handler is used (FB.Auth.xdHandler). These handlers will be used later to deal with the results rom invoking extern/login_status.php.
2. Three different callbacks are added: no_session, no_user, ok_session. Each of these callbacks translate to results of notConnected, unknown, and connected, respectively.
3. The callbacks each return a function called xdResponseWrapper() that is used to parse the JSON response from the result and used to update the FB._session object.
'auth.status': {
    url: 'extern/login_status.php',
    transform: function(a) {
      var b = a.cb,
          c = a.id,
          d = FB.Auth.xdHandler;
      delete a.cb;
      FB.copy(a.params, {
        no_session: d(b, c, 'parent', false, 'notConnected'),
        no_user: d(b, c, 'parent', false, 'unknown'),
        ok_session: d(b, c, 'parent', false, 'connected'),
        session_version: 3,
        extern: FB._inCanvas ? 0 : 2
      });
      return a;
In fact, if you were to step through this code, you could see that there are three callbacks:
no_user="http://static.ak.fbcdn.net/connect/xd_proxy.php#cb=f146913dd972a&origin=http%3A%2F%2Fdev.myhost.com%2Ff14444845a8136&relation=parent&transport=postmessage&frame=f87b5165d6fd26"
 
no_session="http://static.ak.fbcdn.net/connect/xd_proxy.php#cb=f3424882dbe5a&origin=http%3A%2F%2Fdev.myhost.com%2Ff14444845a8136&relation=parent&transport=postmessage&frame=f87b5165d6fd26"

no_user="http://static.ak.fbcdn.net/connect/xd_proxy.php#cb=f3424882dbe5c&origin=http%3A%2F%2Fdev.myhost.com%2Ff14444845a8136&relation=parent&transport=postmessage&frame=f87b5165d6fd26"
Each of these callbacks (cb=xxxxxxx) refer to a separate JavaScript function that will be invoked depending on the result returned by the cross-domain auth.status request. The all.js will insert a hidden iframe with a pointer to these callbacks. If we use the Firebug console and search for $('iframe') objects, you would see something similar:
<iframe scrolling="no" id="f1970fe755e1e58" name="f1a7f022a28bd14" style="border: medium none; overflow: hidden;" class="FB_UI_Hidden" src="http://www.facebook.com/extern/login_status.php?access_token=false&api_key=022046fa222ebeac8bdc99ec4ebdf8b2&display=hidden&extern=2&locale=en_US&method=auth.status&next=http%3A%2F%2Fstatic.ak.fbcdn.net%2Fconnect%2Fxd_proxy.php%23cb%3Df191b56da21a52a%26origin%3Dhttp%253A%252F%252Fdev.myhost.com%252Ff14444845a8136%26relation%3Dopener%26transport%3Dpostmessage%26frame%3Df87b5165d6fd26%26result%3D%2522xxRESULTTOKENxx%2522&no_session=http%3A%2F%2Fstatic.ak.fbcdn.net%2Fconnect%2Fxd_proxy.php%23cb%3Df3424882dbe5a%26origin%3Dhttp%253A%252F%252Fdev.myhost.com%252Ff14444845a8136%26relation%3Dparent%26transport%3Dpostmessage%26frame%3Df87b5165d6fd26&no_user=http%3A%2F%2Fstatic.ak.fbcdn.net%2Fconnect%2Fxd_proxy.php%23cb%3Df146913dd972a%26origin%3Dhttp%253A%252F%252Fdev.myhost.com%252Ff14444845a8136%26relation%3Dparent%26transport%3Dpostmessage%26frame%3Df87b5165d6fd26&ok_session=http%3A%2F%2Fstatic.ak.fbcdn.net%2Fconnect%2Fxd_proxy.php%23cb%3Df1ce4645681670e%26origin%3Dhttp%253A%252F%252Fdev.myhost.com%252Ff14444845a8136%26relation%3Dparent%26transport%3Dpostmessage%26frame%3Df87b5165d6fd26&sdk=joey&session_version=3"></iframe>
If you were to copy the entire src tag, do a wget "" -O /tmp/bla, you would see that the code returned would have an XD Proxy code that will execute doFragmentSend() when loaded. In other words, the IFrame will load this JavaScript code from www.facebook.com. What actually happens is that this iframe will redirect to xd_proxy.php, which will provide the frame of the callback to invoke in the cb= code. The redirect URL looks similar to the following:
http://static.ak.fbcdn.net/connect/xd_proxy.php#cb=f28ec7bcadf45c&origin=http%3A%2F%2Fdev.myhost.com%2Ffeb2a54d47bbae&relation=parent&transport=postmessage&frame=f2b29b433ebc468
Inside the xd_proxy.php code, these are the critical lines for the cross-domain request. Since params.relation is equal to 'parent' (relation=parent in the URL string), the resolveRelation() will return back window.parent and invoke the window.parent.postMessage() function that was added by the all.js file.
// either send the message via postMessage, or via Flash
  if (params.transport == 'postmessage') {
    resolveRelation(params.relation).postMessage(fragment, params.origin);
When the postMessage() gets invoked, an event listener is created for receiving the related event.data, which is then used to decode the URL query string and extract the cb= callback. The source code. This callback function is then execute (assuming the callback exists):
recv: function(b) {
    if (typeof b == 'string') b = FB.QS.decode(b);
    var a = FB.XD._callbacks[b.cb];
    if (!FB.XD._forever[b.cb]) delete FB.XD._callbacks[b.cb];
    a && a(b);
  },
  PostMessage: {
    init: function() {
      var a = FB.XD.PostMessage.onMessage;
      window.addEventListener ? window.addEventListener('message', a, false) : window.attachEvent('onmessage', a);
    },
    onMessage: function(event) {
      FB.XD.recv(event.data);
    }
  },
One side effect is that if the no_user or no_session callback is invoked, then the function that tries to parse the response and invoke the FB.Auth.setSession() will fail.
xdResponseWrapper: function(a, c, b) {
    return function(d) {
      try {
        b = FB.JSON.parse(d.session);
      } catch (f) {}
      if (b) c = 'connected';
      var e = FB.Auth.setSession(b || null, c);
      e.perms = d && d.perms || null;
      a && a(e);
    };

Wednesday, October 20, 2010

How the jQuery.active code works..

If you look online to find the best way to check if there are any active jQuery Ajax requests for Selenium, you may find the following line that can be invoked by your code:
browser.wait_for_condition("selenium.browserbot.getCurrentWindow().jQuery.active === 0;", '30000')
If you look inside the jQuery code, here is how the active flag works. Basically the global: flag is a default setting to enable global event handlers for ajaxStart and ajaxStop. If the jQuery.active counter is 0, then we fire ajaxStart. If the jQuery.active is equal to 0 after decrementing, then we fire the ajaxStop event:
ajaxSettings: {
        url: location.href,
        global: true,
        type: "GET",
        contentType: "application/x-www-form-urlencoded",
        processData: true,
        async: true,

    etag: {},

    ajax: function( origSettings ) {
        var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);

      // Watch for a new set of requests                                                                                                      
        if ( s.global && ! jQuery.active++ ) {
            jQuery.event.trigger( "ajaxStart" );
        }

       // Handle the global AJAX counter                                                                                                   
            if ( s.global && ! --jQuery.active ) {
                jQuery.event.trigger( "ajaxStop" );
            }

Here is the documentation describes the global variable in http://api.jquery.com/jQuery.ajax/
globalBoolean
Default: true
Whether to trigger global Ajax event handlers for this request. The default is true. Set to false to prevent the global handlers like ajaxStart or ajaxStop from being triggered. This can be used to control various Ajax Events.
In other words, you still need to put JavaScript-based wait_for_condition() calls if you are doing things like waiting for dialog boxes to render. For instance, if you are using a JQuery UI dialog box, you could do something like the following:
selenium.wait_for_condition("selenium.browserbot.getCurrentWindow().document.getElementsByClassName('ui-dialog')[0].style.display != 'none'", 10000)
This function will wait for the presence of the dialog box. We rely on the display: style property of the overlay to determine if the dialog is rendered properly. We assume in the example for now there is only one ui-dialog class. Using the overlay that gets rendered is how JQuery hides/shows the dialog box.

Using Selenium Tests with Django/Python, reverse SSH tunnels

Using Firefox/Internet Explorer with Selenium behind a firewall accessing a Slicehost/Amazon EC2 instance

Here is a HOWTO to get things working so that you can do Selenium on your own client browser and be running your code/dev server off a Slicehost/Amazon EC2 instance. You need to have an SSH client, Java, and the Selenium RC JAR file.

You also need to keep in mind that the more tests you have, the slower things go. Selenium RC executes each test one-by-one. Selenium Grid is meant to run multiple Selenium tests in parallel. Sauce Labs can be configured to parallelize these tests, but you can use this setup procedure to do your own sanity checks.

If you are using Windows to do testing, you may need to setup your PATH and profiles to get Firefox working. You may find it easier to run Firefox/Chrome tests on MacOS and Ubuntu-based machines than on Windows.

1. Download Selenium-RC from http://seleniumhq.org/download or http://selenium.googlecode.com/files/selenium-remote-control-1.0.3.zip download. You will also need to download Java from http://www.java.com.

2. Unzip the selenium-remote-control-1.0.3.zip file and extract the selenium.py file from selenium-python-client-driver-1.0.1. You may wish to review the selenium.py file to see what type of Selenium commands you can issue.

3. Setup a reverse tunnel on your client side (not SSH'd into your Slicehost). If you don't have an SSH public key, you will be prompted for one. Run the ssh command on your local machine:

ssh -nNT -R 9999:127.0.0.1:4444 username@dev.myhost.com

This SSH command is essentially tunneling the port 9999 on dev.myhost.com to your local host 4444. You can change port 9999 to whatever local port on dev.myhost.com. You can also change 127.0.0.1 to any other notes running the Selenium RC server on your local network.

3. Inside your Python code, you will need to import the selenium.py and then connect to the appropriate site hosting your dev server instance. The dev server needs to be accessible from your browser.

import selenium

# *iehta = Internet Explorer proxy mode, *firefox = Firefox
    selenium_browser = selenium("localhost", 9999, "*iehta", "http://dev.myhost.com")
#   selenium_browser = selenium("localhost", 9999, "*firefox", "http://dev.myhost.com")

    selenium_browser.start()
    selenium_browser.open("http://www.myhost.com")
    return selenium_browser

Things to note:

a. The example above shows port 9999. You may wish to change your reverse tunnel port.
b. The *iehta stands for Internet Explorer Proxy mode. You can also use *iexplore, *firefox but on PC's you will need to do setup PATH and setup work for Firefox.

5. Internet Explorer also needs to be changed to disable some security settings that cause issues:

a. You may also need to go into Internet Explorer -> Tools -> Internet Options -> Advanced and disable "Check for publisher's certificate revocation" and "Check for server certificate revocation*". You may also need to disable "Warn about certificate address mismatch."

b. Inside Tools->Internet Options->Privacy, you should turn off Pop-up Blocker.

c. Inside Tools->Internet Options->Security,you should turn off Protected Mode.

d. Restart IE8

Keep in mind these security changes will make your machine more susceptible to viruses/spyware, so buyer beware.

6. If you are using Windows, go to DOS but you must run in Administrator mode. At the command-line, run the Selenium server:

java -jar selenium-server.jar

Also, you can telnet to localhost 4444 and telnet to the port that you tunneled on dev.myhost.com to verify the reverse tunnel is operating correctly.

Tuesday, October 12, 2010

Chrome and JQuery UI min-height issue

The JQuery UI library attempts to determine whether the browser can support the min-height propery by injecting a <div> tag into the body and checking the offsetHeight property, which represents the total height of the element (including the padding & margin). If the offsetHeight is equal to 100, then the $.support.minHeight is also set to true.

jQuery UI (v.1.8.5)
// support
$(function() {
 var div = document.createElement( "div" ),
  body = document.body;

 $.extend( div.style, {
  minHeight: "100px",
  height: "auto",
  padding: 0,
  borderWidth: 0
 });

 $.support.minHeight = body.appendChild( div ).offsetHeight === 100;
 // set display to none to avoid a layout bug in IE
 // http://dev.jquery.com/ticket/4014
 body.removeChild( div ).style.display = "none";
});

The problem with this approach is that if you use Chrome and zoom-out (57%, 69%, 83%), the offsetHeight will be 98, 99, and 99 instead, therefore causing the $.support.minHeight to not equal to 100.

This issue can ultimately affect the use of the dialog box. If the browser cannot support the minimum height, it will resort to Math.max() operations to determine what the height of the dialog content should be. Since nonContentHeight can easily equal to 100px, you end up needing to set a content height of a minimum of 150px.

The problem of course is that your dialog box cannot grow/shrink according to the size of your content using the height: auto property.

this.element
   .css(options.height === 'auto' ? {
     minHeight: Math.max(options.minHeight - nonContentHeight, 0),
     height: $.support.minHeight ? 'auto' :
      Math.max(options.minHeight - nonContentHeight, 0)
    } : {
     minHeight: 0,
     height: Math.max(options.height - nonContentHeight, 0)    
   })
   .show();


I checked through the Chromium source code (http://dev.chromium.org/Home) and can confirm that HTML elements are scaled according to some type of zoom factor:

WebKit/WebCore/dom/Element.cpp
int Element::offsetHeight()
{
    document()->updateLayoutIgnorePendingStylesheets();
    if (RenderBoxModelObject* rend = renderBoxModelObject())
        return adjustForAbsoluteZoom(rend->offsetHeight(), rend);
    return 0;
}
.
.

inline int adjustForAbsoluteZoom(int value, const RenderStyle* style)
{
    double zoomFactor = style->effectiveZoom();
    if (zoomFactor == 1)
        return value;
    // Needed because computeLengthInt truncates (rather than rounds) when scaling up.
    if (zoomFactor > 1)
        value++;

    return roundForImpreciseConversion(value / zoomFactor);
}



template inline T roundForImpreciseConversion(double value)
{
    // Dimension calculations are imprecise, often resulting in values of e.g.
    // 44.99998.  We need to go ahead and round if we're really close to the
    // next integer value.
    value += (value < 0) ? -0.01 : +0.01;
    return ((value > max) || (value < min)) ? 0 : static_cast(value);
}

src/third_party/WebKit/WebCore/rendering/style/RenderStyle.h:
  static float initialZoom() { return 1.0f; }


m_style->setEffectiveZoom(m_parentStyle ? m_parentStyle->effectiveZoom() : RenderStyle::initialZoom());


  float m_effectiveZoom;

m_effectiveZoom(RenderStyle::initialZoom())



class RenderStyle: public RefCounted {
.
.
float effectiveZoom() const { return rareInheritedData->m_effectiveZoom; }

float zoom() const { return visual->m_zoom; }
void setZoom(float f) { SET_VAR(visual, m_zoom, f); setEffectiveZoom(effectiveZoom() * zoom()); }
}


The PageZoom class (found in src/chrome/common/page_zoom.h) seems to trigger different page zoom levels:

class PageZoom {
 public:
  // This enum is the parameter to various text/page zoom commands so we know
  // what the specific zoom command is.
  enum Function {
    ZOOM_OUT = -1,
    RESET    = 0,
    ZOOM_IN  = 1,
  };

 private:
  DISALLOW_IMPLICIT_CONSTRUCTORS(PageZoom);
};

#endif  // CHROME_COMMON_PAGE_ZOOM_H_

Inside browser.cc, the ZOOM commands are declared:
void Browser::Zoom(PageZoom::Function zoom_function) {

// Zoom
  command_updater_.UpdateCommandEnabled(IDC_ZOOM_MENU, true);
  command_updater_.UpdateCommandEnabled(IDC_ZOOM_PLUS, true);
  command_updater_.UpdateCommandEnabled(IDC_ZOOM_NORMAL, true);
  command_updater_.UpdateCommandEnabled(IDC_ZOOM_MINUS, true);

}

WebKit seems to adjust the zoom level here:
chromium/src/chrome/renderer/render_view.cc
void RenderView::OnZoom(PageZoom::Function function) {
  if (!webview())  // Not sure if this can happen, but no harm in being safe.
    return;

  webview()->hidePopups();

  double old_zoom_level = webview()->zoomLevel();
  double zoom_level;
  if (function == PageZoom::RESET) {
    zoom_level = 0;
  } else if (static_cast(old_zoom_level) == old_zoom_level) {
    // Previous zoom level is a whole number, so just increment/decrement.
    zoom_level = old_zoom_level + function;
  } else {
    // Either the user hit the zoom factor limit and thus the zoom level is now
    // not a whole number, or a plugin changed it to a custom value.  We want
    // to go to the next whole number so that the user can always get back to
    // 100% with the keyboard/menu.
    if ((old_zoom_level > 1 && function > 0) ||
        (old_zoom_level < 1 && function < 0)) {
      zoom_level = static_cast(old_zoom_level + function);
    } else {
      // We're going towards 100%, so first go to the next whole number.
      zoom_level = static_cast(old_zoom_level);
    }
  }

So far I've noticed that the height of the <div> scales properly, but the offsetHeight still reports 1-2px less than the desired minimum height. Oh, here's the first set of patches that implemented full page zoom on WebKit:

https://bug-14998-attachments.webkit.org/attachment.cgi?id=19878
https://bugs.webkit.org/show_bug.cgi?id=14998

Update: The problem possibly related to using integers for zooming/rendering?

https://bugs.webkit.org/show_bug.cgi?id=60318

Array.prototype.slice.call(arguments)

The Array.prototype.slice.call() function is used to "coerce" JavaScript arguments (which are not really arrays) into JavaScript arrays..

I've seen examples such as:

[].slice.call(arguments, 1);

http://www.danwebb.net/2006/11/7/a-low-down-dirty-goblin-of-a-hack

The '1' argument means to slice the arguments list from 1 to the end.

The criticism about this approach is that [] creates a new Array object in memory. It is therefore more preferable to do:

Array.prototype.slice.call(arguments, 1);

Remember that all functions have a call and apply method(). So we're invoking the slice method by using call() in this case:
http://ryanmorr.com/archives/scope-context-in-javascript

call and apply

If you want to know how all the mainstream libraries do it, well it’s simple; call and apply. These two very simple methods inherent to all functions allow you to execute any function in any desired context. Before I continue, it is important to know that everything in JavaScript inherently has its own context, including DOM nodes, objects, arrays, strings, functions, etc. Meaning, any function can be executed in the context of any object using call and apply. This may seem insignificant, but understand it is this implementation that serves as an underlying bridge between the object-oriented and functional methodologies in the JavaScript language.

Making the Array-like Objects become Arrays

Well, that heading is a misnomer. If we want those array-like objects to behave like arrays, we are going to need a new array.

view plaincopy to clipboardprint?
var testFunction = function() {  
  // Create a new array from the contents of arguments  
  var args = Array.prototype.slice.call(arguments);  
  
  var a = args.shift();  
  console.log("The first argument is: %s", a);  
  
  // send the remaining arguments to some other function  
  someOtherFunction(args);  
};  
Clearly, the magic is all in Array.prototype.slice.call(arguments). So let me break it down per piece.

Array - this is the name of the base object that we want
prototype -this can be thought of as the namespace for the instance methods of an array
slice - this extracts a section of an array and returns a new array, and without a beginning and ending index, it simply returns a copy of the array
call - this is a very useful function, it allows you to call a function from one object and use it in the context of another

Monday, October 11, 2010

css-mode.el missing in Emacs v22.1.1

In order to get css-mode working on Emacs v22.1.1, you can download the latest version of:

ftp://mirrors.kernel.org/gnu/emacs/emacs-23.2.tar.gz

Unpackage the .tar.gz file and extract the liisp/textmodes/css-mode.el. Copy it to your ~/.emacs.d directory:

Add this line to your .emacs file:
(autoload 'css-mode "css-mode")

We're assuming the load-path gets added:

(add-to-list 'load-path "~/.emacs.d")

Fixing microphone input on Ubuntu 10.04

HP Pavilion Elite HPE running Ubuntu 10.04.

Sound works, but no microphone. I made these changes:

sudo gedit /etc/modprobe.d/alsa-base.conf:
options snd-hda-intel model=snd-via82xx position_fix=1

1. Click on System->Preferences->Sound

2. Change to use Microphone Input #2.

Seems to fix the issue.

Wednesday, October 6, 2010

Django and the none()

The Django manager has a none() method that can retury an empty queryset:

http://thecatatemysourcecode.posterous.com/django-empty-queryset


django.db.models.manager.py:
def none(self):
        return self.get_empty_query_set()

def get_empty_query_set(self):
        return EmptyQuerySet(self.model)

django.db.models.query.py:
class EmptyQuerySet(QuerySet):
    def __init__(self, model=None, query=None):
        super(EmptyQuerySet, self).__init__(model, query)
        self._result_cache = []

    def __and__(self, other):
        return self._clone()

    def __or__(self, other):
        return other._clone()

    def count(self):
        return 0

    def delete(self):
        pass

    def _clone(self, klass=None, setup=False, **kwargs):
        c = super(EmptyQuerySet, self)._clone(klass, **kwargs)
        c._result_cache = []
        return c
I tried it out too:
>>> from apapa.events.models import Event

>>> Event.objects
>>> 

>>> Event.objects.none()
>>> []

>>> Event.objects.filter(id=100000000) (for a non-existent Event)
>>> []
The QuerySet class also has a __nonzero__() operator that is used when there is an if statement used, which makes the logic perform exactly the same if the queryset were actually a list.
__nonzero__(self)

Called to implement truth value testing, and the built-in operation bool(); should return False or True, or their integer equivalents 0 or 1. When this method is not defined, __len__ is called, if it is defined. If a class defines neither __len__ nor __nonzero__, all its instances are considered true.
django.db.models.query.py:
def __nonzero__(self):
        if self._result_cache is not None:
            return bool(self._result_cache)
        try:
            iter(self).next()
        except StopIteration:
            return False
        return True

Ubuntu Lucid and Thinkpad Sleep

I noticed that when trying to shut off my Thinkpad, the moon light continued to blink but the machine never entered into suspend state.

The problem appears to be related to an SD card mounted inside the card slot. If there is an SD card, then the Thinkpad will not be able to sleep.

The first solution is not to have the SD card. Another solution (untested) is to create a script to umount the SD card when the laptop enters Suspend or Hibernate mode:
http://www.thinkwiki.org/wiki/Installing_Ubuntu_9.10_(Karmic_Koala)_on_a_ThinkPad_T61

#!/bin/sh
PATH=/sbin:/usr/sbin:/bin:/usr/bin
case "${1}" in
        suspend|hibernate) 
                for i in `ls /media`; do
                   /usr/bin/gvfs-mount -u "/media/$i"
                done
                ;;
        resume|thaw)
  # nothing
                ;;
esac

Sunday, October 3, 2010

Squid/proxy server and .htaccess

sudo apt-get install
htpasswd -c /etc/squid/squid_passwd

vi /etc/squid/squid.conf:

auth_param basic program /usr/lib/squid/ncsa_auth /etc/squid/squid_passwd
acl ncsa_users proxy_auth REQUIRED
http_access allow ncsa_users


Go into your web browser and change your proxy settings now. You'll also want to supply the username and password to sign-in to the Squid server.