Friday, December 31, 2010

Fixing WymEditor from stripping out YouTube embedded links

JavaScript/jQuery doesn't come with a routine to remove elements from an array, so we create one as follows. This code was inspired from the MooTools v1.3 code:
Array.prototype.remove = function(item)
{   
    for(var i = this.length; i--;) {
        if(this[i] === item) this.splice(i,1);
    }
    return this;
};
https://github.com/rogerhu/wymeditor/commit/3cf8fe07f52547056918be6dd8e69fb08da2c60f

We'll later use this remove() function to remove <param> tag from being considered as a block tag, and insert param/embed tags an inline_tags.
var XhtmlSaxListener = WYMeditor.XhtmlSaxListener;
        WYMeditor.XhtmlSaxListener = function () {
            var listener = XhtmlSaxListener.call(this);
            listener.block_tags.remove('param');
            listener.inline_tags.push('param');
            listener.inline_tags.push('embed');
            return listener;
        };
If we really wanted to be consistent, we could also use the following statements with the WymEditor patch, we can do the following:
WYMeditor.XhtmlValidator['_tags']["embed"] = {
            "attributes":[
                "allowscriptaccess",
                "allowfullscreen",
                "height",
                "src",
                "type",
                "wmode",
                "width"
            ],
            "inside":"object"
        };
WYMeditor.XhtmlValidator['_tags']['param'] = {
            "attributes" : ["type", "value", "name"],
            "required" : "name",
            "inside" : "object"
        };

Copying a bunch of images defined in a CSS

Suppose we want to verify that all the images defined in a CSS file exist. We could also want to copy all of the images to a remote server dir. Using grep and awk, we can dump out a command line that will print an scp command that can then be piped to be executed.

grep -oi "../images/.[^)]*" /tmp/bla.css | awk '{print "scp " $0 " user@myhost.com:mydir"}' | sh

The -o flag in grep will output only the matching field. Since non-greedy expressions can only be represented with the [^)]* field (Python/Perl allow for the .* syntax to denote non-greedy), we express this grep command match everything up to the closing parenthesis.

Sunday, December 19, 2010

Creating slideshow picture/audio clips

Picasa for Windows 7/Vista allows you the ability to create slideshows from images, as well as including audio clips that can be added to the presentation.   You can use Ubuntu's ffmpeg command to extract the audio, but there are several steps that also need to be done to equip your machine to do so.

1. First, you should verify that your Ubuntu installation has the 'multiverse' repository added. On Slicehost instances only the 'universe' install, so trying to proceed to the next steps will fail since there are package dependencies that are not included with just the basic universe repository.

deb http://archive.ubuntu.com/ubuntu/ lucid main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ lucid main restricted universe multiv
erse

deb http://archive.ubuntu.com/ubuntu/ lucid-updates main restricted universe mu
ltiverse
deb-src http://archive.ubuntu.com/ubuntu/ lucid-updates main restricted univers
e multiverse

deb http://security.ubuntu.com/ubuntu lucid-security main restricted universe m
ultiverse
deb-src http://security.ubuntu.com/ubuntu lucid-security main restricted univer
se multiverse

2. Next, you need to install the Medibuntu instructions. One way is to install 'ffmpeg' manually but the quicker way is to install the Medibuntu packages. The instructions are listed here:

http://ubuntuforums.org/showthread.php?t=1117283

You can also execute this command which will use wget to add the Medibuntu to sourceslist, and the Medibuntu package repository, and install the medibuntu-keyring package:
sudo wget http://www.medibuntu.org/sources.list.d/`lsb_release -cs`.list --output-document=/etc/apt/sources.list.d/medibuntu.list && sudo apt-get -q update && sudo apt-get --yes -q --allow-unauthenticated install medibuntu-keyring && sudo apt-get -q update
3. You should be able to run this command. Again, if you're missing the multiverse repository, the libavcodec-extra-52 will complain that it is missing a few dependencies (i.e. libmp3lame0) and fail to install.
apt-get install ffmpeg libavcodec-extra-52
4. The final step is to use ffmpeg to extract audio clips:
ffmpeg -i -vn

The -vn flag will remove the video and output an MP3 file with the extracted audio. From there, you can add the MP3 to your movie clip. Remember, only Picasa for Windows supports this feature. Picasa for Ubuntu will only allow you to create an .AVI file without any MP3 soundtracks.

Saturday, December 18, 2010

Converting JPG to GIFs with ls and awk...

ls *.jpg | awk -F "." '{print "convert " $1".jpg " $1".gif"}' | sh

Friday, December 17, 2010

The trouble with contenteditable and WYSWYG editors..

If you want to implement some type of WYSWYG editors, there are several options.  You can try out CkEditor (http://ckeditor.com/), which seems to be a re-write of the popular FCkEditor.  It's still 300kB gzip'd, and it has a lot of plug-ins.  You can also TinyMCE and WymEditor, and neither seem optimal since they add so many extra HTML tags and add to the code base.  There's WysiHat, which is used in Basecamp and developed by 37signals.com, but primarily leverages Prototype so JQuery/MooTools frameworks are out.

Enter the 'contenteditable' attribute, which lets you turn any DOM element into an editable field.  You can bold, highlight, italicize and the browser automatically adds the appropriate HTML tag.  The challenge of course is that different browsers add different tags when the Enter button is pressed.  The approach listed here is to intercept the Enter key and insert <br> tags to avoid cross-browser issues:

http://stackoverflow.com/questions/2735672/how-to-change-behavior-of-contenteditable-blocks-after-on-enter-pressed-in-variou

There isn't an answer on Stack Overflow threads about what to do with the browser to get the cursor position to be move to be situated after "<br>" tags are inserted.  I looked through the CKEditor source code and there's a specific plug-in called _source/plugins/enterkey/plugin.js that tries to deal with the issue. It looks like what ends up happening is that not only is a <br><br/> is added but also an extra bogus <br> is added, in addition to a <span>&nbsp;</span> gets inserted.  The code gets even more convoluted when you start working with IE and Opera browsers, so this general approach just seems a bit difficult in geneal.

The solution may be to post-replace these tags instead:

http://stackoverflow.com/questions/3455931/extracting-text-from-a-contenteditable-div

collapse() in Ran


A good explanation of JavaScript DOM ranges...

http://www.wrox.com/WileyCDA/Section/JavaScript-DOM-Ranges-Page-3.id-292303.html

Collapsing a DOM Range

To empty a range, (that is, to have it select no part of the document), you collapse it. Collapsing a range resembles the behavior of a text box. When you have text in a text box, you can highlight an entire word using the mouse. However, if you left-click the mouse again, the selection is removed and the cursor is located between two letters. When you collapse a range, you are setting its locations between parts of a document, either at the beginning of the range selection or at the end. Figure 6 illustrates what happens when a range is collapsed.
JavaScript DOM Ranges : Figure 6
Figure 6You can collapse a range by using the collapse() method, which accepts a single argument: a Boolean value indicating which end of the range to collapse to. If the argument is true, then the range is collapsed to its starting point; if false, the range is collapsed to its ending point. To determine if a range is collapsed, you can use the collapsed property:
oRange.collapse(true);      //collapse to the starting point
alert(oRange.collapsed);    //outputs "true"
How to do range highlighting too:

http://efreedom.com/Question/1-3771824/Select-Range-Contenteditable-Div

Wednesday, December 15, 2010

The best du replacement...

sudo apt-get install ncdu

ncdu <dir>

It provides a high-level summary of all the dirs, and allows you to drill-in/drill-out to see which subdirs consume the most space.

Friday, December 10, 2010

What the r' in re.match() means..

re.search(r"\b(two)", "one two three).groups()
instead of:
re.search("\b(two)", "one two three).groups()

Regular expressions use the backslash character ('\') to indicate special forms or to allow special characters to be used without invoking their special meaning. This collides with Python’s usage of the same character for the same purpose in string literals; for example, to match a literal backslash, one might have to write '\\\\' as the pattern string, because the regular expression must be \\, and each backslash must be expressed as \\ inside a regular Python string literal.

The solution is to use Python’s raw string notation for regular expression patterns; backslashes are not handled in any special way in a string literal prefixed with 'r'. So r"\n" is a two-character string containing '\' and 'n', while "\n" is a one-character string containing a newline. Usually patterns will be expressed in Python code using this raw string notation.

Saturday, November 27, 2010

Turning off numlock on a Kinesis...

Hit Keypad + "7":

Or follow the long Kinesis instructions:

http://www.kinesis-ergo.com/tech_support/trouble.htm

With a Kinesis contoured keyboard, press the keypad button to turn on the embedded keypad. To change the state of Num lock, press the number row "7" key (Num Lk is printed on the front face). Normally the keyboard will beep twice and the keyboard light next to the "1" will illuminate when Num lock is turned on. The keyboard will beep once and the light will go out when Num lock is turned off.

Monday, November 22, 2010

Getting a Brother MFC-8480 to scan in Ubuntu 10.04

This Brother multi-function printer/scanner/fax actually has support for scanning documents in Ubuntu. The directions, which are somewhat scattered, are posted here:

http://welcome.solutions.brother.com/bsc/public_s/id/linux/en/download_scn.html

1. This Brother printer is classified as the brscan3 model, so we can download it from here:
http://welcome.solutions.brother.com/bsc/public_s/id/linux/en/download_scn.html#brscan3

2. sudo dpkg -i /tmp/brscan3-0.2.11-2.amd64.deb

3. sudo brsaneconfig3 -a name=MyScanner model=BR8480TDN IP=10.0.1.11

4. sudo apt-get install sane xsane

5. Click on Graphics->Simple Scan. You can start scanning by clicking the "Start Scan" button.

To verify your drivers are installed properly:

dpkg -l | grep
sudo brsaneconfig3 -q

Wednesday, November 17, 2010

jQuery extend() removes undefined values...

If you pass in an undefined value into the data for the $.ajax call(), this undefined field will be removed during the $.ajax() call when extending the variables:

var params = { width:1680, height:1050, picture: undefined };

$.ajax({url: 'www.myurl.com', data: params});


This will invoke the $.ajax function, which then invokes the extend() function:
   ajax: function( origSettings ) {
        var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);

The extend() function tries to remove out the undefined/null values:
jQuery.extend = jQuery.fn.extend = function() {
       // Only deal with non-null/undefined values                                                                                                                                                                                          
        if ( (options = arguments[ i ]) != null ) {
            // Extend the base object                                                                                                                                                                                                        
          
                // Don't bring in undefined values                                                                                                                                                                                           
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;

Python 2.x and Unicode

Suppose you tried the following:
>>> a = u"Hey\u2019t"
>>> b = a.encode('utf-8')
>>> b.encode('utf-8')
Traceback (most recent call last):
  File "", line 1, in 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 3: ordinal not in range(128)
The encode('utf-8') takes a Python unicode object and converts it into a Python ASCII string object. When you try to encode a Python string object into UTF-8, Python throws an error above.

The best slide talk that discusses these issues can be found here: http://farmdev.com/talks/unicode/

Python 2.x has these problems in general because it has created separate typed objects, u' for unicode, and ' for string (ASCII), both derived from the basestring type. Python 3.0 solves this issue by unifying the string object.

Wednesday, November 10, 2010

nth-child vs. eq CSS selectors

The eq() and nth-child() CSS selectors are not the same..

http://api.jquery.com/nth-child-selector/

The :nth-child(n) pseudo-class is easily confused with :eq(n), even though the two can result in dramatically different matched elements. With :nth-child(n), all children are counted, regardless of what they are, and the specified element is selected only if it matches the selector attached to the pseudo-class. With :eq(n) only the selector attached to the pseudo-class is counted, not limited to children of any other element, and the (n+1)th one (n is 0-based) is selected.

Or how someone else explains it....
http://forum.jquery.com/topic/eq-0-vs-nth-child-1-with-find-inconsitency

:eq() selects an element based on it's index in the array of selected elements.
:nth-child() selects all elements that are the nth child of their parent.

Sunday, November 7, 2010

WymEditor strips out <embed> tags

WymEditor seems to strip out <embed>/<object> tags so does not seem to have the ability to embed YouTube clips. The way to fix it is to patch the latest WymEditor release, or download the GitHub files. Here are two blog posts that talk about the issue:

http://simonwoodside.com/weblog/2009/2/8/how_to_make_wym_editor/
http://meridimus.com/post/167515648/wymeditor-flash

I took the diff of these changes and patched the 0.5rc2 release accordingly:

1757d1756
<       "param",
2124a2124,2150
>     "param":
>     {
>       "attributes":
>       [
>         "type",
>         "value",
>   "name"
>       ],
>       "required":[
>       "name"
>       ],
>       "inside":"object"
>     },
>  "embed":
>     {
>       "attributes":
>       [
>         "width",
>         "height",
>         "allowfullscreen",
>         "allowscriptaccess",
>         "wmode",
>         "type",
>         "src"
>       ],
>    "inside":"object"
>     },
2166,2178d2191
<     "param":
<     {
<       "attributes":
<       {
<         "0":"type",
<         "valuetype":/^(data|ref|object)$/,
<         "1":"valuetype",
<         "2":"value"
<       },
<       "required":[
<       "name"
<       ]
<     },
3381c3394
<     "object", "ol", "optgroup", "option", "p", "param", "pre", "q",
---
>     "ol", "optgroup", "option", "p", "pre", "q",
3384c3397
<     "thead", "title", "tr", "tt", "ul", "var", "extends"];
---
>     "thead", "title", "tr", "tt", "ul", "var", "extends", "object"];
3387c3400
<     this.inline_tags = ["br", "hr", "img", "input"];
---
>     this.inline_tags = ["br", "hr", "img", "input", "param", "embed"];

This issue has been filed as a ticket on the trac.wymeditor.org. If you want to create an account, you have to visit http://trac.wymeditor/trac/register. The link that's made available on the web site doesn't quite work. You can download the patched file here:

http://trac.wymeditor.org/trac/ticket/221

Update: There is already a plug-in that adds these tags into the code:

https://github.com/wymeditor/wymeditor/blob/master/src/wymeditor/plugins/embed/jquery.wymeditor.embed.js

Fixing brightness controls on the T510 laptop

Ubuntu v10.04 (Lucid) with the Thinkpad T510 Nvidia Quadro 3100M has issues with trying to adjust the brightness controls. Apparently the issue is related to X11. To the issue, you need to edit /etc/x11/xorg.conf, and restart X:

http://www.nvnews.net/vbulletin/showthread.php?p=2204839#post2204839
Section "Screen"
 Identifier "Default Screen"
 DefaultDepth 24
EndSection

Section "Module"
 Load "glx"
EndSection

Section "Device"
 Identifier "Default Device"
 Driver "nvidia"
 Option "NoLogo" "True"
 Option  "RegistryDWords" "EnableBrightnessControl=1"
EndSection


The brightness controls will then work with this extra tweak!

Saturday, November 6, 2010

Updating entries from a SQL join

Suppose I want to update onl the entries from an INNER JOIN. Well apparently in SQL I can do the following:

UPDATE TABLE1 AS A LEFT JOIN TABLE2 AS B ON A.F1=B.F1 SET A.X = 1, B.Y = 2

According to the MySQL docs, the "table_references" can refer to any type of SELECT syntax we want to use. We can set any value referring to the two entries by giving each of the tables being joined an alias (in the example above, we refer to TABLE1 as 'A' and TABLE as 'B')

http://dev.mysql.com/doc/refman/5.0/en/update.html

UPDATE [LOW_PRIORITY] [IGNORE] table_references
    SET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ...
    [WHERE where_condition]

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.

Wednesday, October 13, 2010

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.

Wednesday, September 29, 2010

Issues with Sprint U301 USB modem (especially on Ubuntu Lucid)

These link say it all:
http://kerneltrap.org/mailarchive/linux-usb/2010/2/19/6257991

I'm doing some reliability testing to see what happens to the device if it loses
service.  With a connection up, I place the device inside a metal enclosure to
simulate the loss of network connectivity.

The device behaves as I would expect for the first ten to fifteen minutes (I
have not timed it).  The PPP connection takes a few minutes to timeout and die.
wvdial tries to dial a bit more and then quits.

The problems come after the device has sat idle for a few more minutes.  It
subsequently goes offline.  I am unable to revive it except by physically
disconnecting it from the USB port and reinserting it.
http://www.mail-archive.com/networkmanager-list@gnome.org/msg15988.html

It may be time to switch back to the U598 modem.

Friday, September 24, 2010

Thinkpad T510 laptops mouse/touchpad issues with Ubuntu v10.04

There have been 2 problems that I've observed with using Ubuntu Lucid v10.04 with the Thinkpad T510. The first is that the Synaptics touchpad seems to cause accidental scrolls and button clicks. The second is that the middle trackpoint doesn't work out of the box.

For the first issue, this link first clue me into the possible issue:
http://www.paralaptop.com/tag/ibm-thinkpad-t510-mouse-problems
Actually there is simple solution to fix the problem of T500 touchpad messing up while typing. What you need is just pressing Fn + F8 keys on your T500 laptop. There you should see any setting you can use to manage the system. Looking for the touchpad option and choose to only enable the touchpoint and disable the touchpad. This method is works fine whether on Lenovo T500 or T400 laptop.

Hitting Fn + F8 must be done after each reboot, so one way would be to install the gpointing-device-settings package (sudo apt-get install gpointing-device-settings) and then selecting the "Touchpad off" option:

http://www.andrewferrier.com/blog/2010/06/04/disabling-synaptics-touchpad-with-ubuntu-10-04/

For the record, I tried only to disable the vertical/horizontal scrolling checkboxes to no avail. It seems that the touchpad should be disabled if you're using the pointer device.

Another thing: the middle button does not work for scrolling without extra changes:

http://psung.blogspot.com/2010/04/thinkpad-trackpoint-scrolling-in-ubuntu.html

Create a new file /usr/lib/X11/xorg.conf.d/20-thinkpad.conf with the following contents:

Section "InputClass"
    Identifier "Trackpoint Wheel Emulation"
    MatchProduct "TrackPoint"
    MatchDevicePath "/dev/input/event*"
    Driver "evdev"
    Option "EmulateWheel" "true"
    Option "EmulateWheelButton" "2"
    Option "Emulate3Buttons" "false"
    Option "XAxisMapping" "6 7"
    Option "YAxisMapping" "4 5"
EndSection
Then restart X11 (or it may be easier to reboot).

Polycom VoiceStation 500 and Android headsets...a solution...

http://www.amazon.com/Voicestation-Analog-Conference-Phone-Bluetooth/dp/B000GGMLNM/ref=sr_1_1?s=electronics&ie=UTF8&qid=1285368276&sr=1-1

We called Polycom support and it appears to be a known technical issue with the Android stack. You're supposed to click on the Mode button for 3 seconds on the Polycom to pairing mode, which will show the device on the Android phone. However, all you see is the "Pairing but not connected" when trying to connect to the Polycom VoiceStation 500 unit. The issue is observed for both Android v1.6 (HTC Magic phones) and Motorola Droid phones running Android v2.2

Polycom says it is an issue with Google's Bluetooth stack. The latest v2.2 release does not seem to solve this compatibility issue, and there enough reports that the Android Bluetooth stack (BlueZ) has issues pairing with car stereos, keyboards, etc.

For Android-based headsets, you can use the 3.5mm to 2.5mm cable that comes with the Polycom VoiceStation 500. However, the cable that comes with this one has two types of a cable: 1) a 2.5mm to 2.5mm cable (which doesn't work on Droid 3.5mm headset), and 2) a 2.5mm to Y-pronged 3.5mm for computer microphone and output.

If you use the 2nd cable included (and use the green-labeled connection for headset), you can at least use the Polycom for voice conference calls while relying on the Droid headset for microphone input. We're in the process of ordering a 2.5mm female to 3.5mm adapter to see if we can use both the Polycom's microphone/speaker.

At least this solution allows Droid-based phones to use the Polycom, though you're certainly paying a premium for Bluetooth support that doesn't work. The Polycom unit seems to work fine though with iPhone's.

In case you want to learn more about how the Android Bluetooth stack works, here is a slide presentation that I found:
http://www.slideshare.net/erinyueh/android-bluetooth-introduction

Thursday, September 23, 2010

Switched to using http://deluxetemplates.net

I've switched to using the templates at: http://www.deluxetemplates.net/
You have to unzip the XML template and then upload the file according to these instructions:

http://www.deluxetemplates.net/1999/04/how-to-install-blogger-xml-template.html

How to install a Blogger Template

Follow Most Simple Guide to Install your Favorite Blogger Template.
1. Click in Download button and save file in PC [hard drive].

2. File that you have downloaded is .ZIP file so need to be extracted. Extract / Unzip it.

3. Log in Blogger dashboard

4. Click Design link in dashboard panel.

5. Now click in Edit HTML link in tab bar.

6. Click Browse button and look for folder that is created after extracted file that you have downloaded.

7. Folder has a XML file and TXT file or Internet shortcut, you select only XML file.

8. Click Upload button.

9. Confirm and Save.

One thing I had to do is reinstall Prettify based on this Stack Overflow thread:
http://stackoverflow.com/questions/1852537/how-to-use-prettify-with-blogger-blogspot

<link href='http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css' rel='stylesheet' type='text/css'/> 
<script src='http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js' type='text/javascript'></script>
After "</head>" replace "<body>" with "<body onload='prettyPrint()'>"

One more thing: some designs may turn off the navbar at the top. Some people find it annoying, but I found it very useful. You will need to go into the Design->Edit HTML and look for the CSS navbar-iframe style. Here is the code commented out:

/*#navbar-iframe {
display: none !important;
}*/

OpenOffice and Windows PowerPoint PPT/PPTX files in Ubuntu 10.04

One of the recent issues I encountered was opening PowerPoint documents in OpenOffice. I noticed font-rendering issues. It made me realize that the Ubuntu 10.04 install doesn't come with the Vista/Windows 7 fonts.

I followed these instructions:
http://embraceubuntu.com/2007/09/16/installing-vista-fonts-in-ubuntu/

sudo apt-get install cabextract
sudo apt-get install curl
sudo apt-get install wget
I then downloaded this installer script:
wget http://plasmasturm.org/code/vistafonts-installer/vistafonts-installer
The installer script will download the Microsoft PowerPoint Viewer (25MB fil) and extract the various fonts (Calibri, Cambria, Candara, etc.).
mkdir ~/.fonts
chmod a+x ~/vista-fonts-installer
to make the file/script executable.
Then run the script using:
$ ./vista-fonts-installer

Notice inside the vista-fonts-installer.sh, there is this line:
fc-cache -fv ~/.fonts
The installer script wants you to download your fonts into ~/.fonts. If you intend to share the instance with multiple users, it may be better to move them into /usr/share/fonts/vista or /usr/share/fonts/windows7. You should run "fc-cache -fv /usr/share/fonts/[dir]" if you move your font files.

My script therefore got changed to:

#!/bin/sh
# Copyright (c) 2007 Aristotle Pagaltzis
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

set -e

exists() { which "$1" &> /dev/null ; }

if ! [ -d /usr/share/fonts/windows7 ] ; then
 exec 2>&1
 echo 'There is no .fonts directory in your home.'
 echo 'Is fontconfig set up for privately installed fonts?'
 exit 1
fi

# split up to keep the download command short
DL_HOST=download.microsoft.com
DL_PATH=download/f/5/a/f5a3df76-d856-4a61-a6bd-722f52a5be26
ARCHIVE=PowerPointViewer.exe
URL="http://$DL_HOST/$DL_PATH/$ARCHIVE"


if ! [ -e "$ARCHIVE" ] ; then
 if   exists curl  ; then curl -O "$URL"
 elif exists wget  ; then wget    "$URL"
 elif exists fetch ; then fetch   "$URL"
 fi
fi

TMPDIR=`mktemp -d`
trap 'rm -rf "$TMPDIR"' EXIT INT QUIT TERM

cabextract -L -F ppviewer.cab -d "$TMPDIR" "$ARCHIVE"

cabextract -L -F '*.TT[FC]' -d /usr/share/fonts/windows7 "$TMPDIR/ppviewer.cab"

( cd /usr/share/fonts/windows7 && mv cambria.ttc cambria.ttf && chmod 644 \
 calibri{,b,i,z}.ttf cambria{,b,i,z}.ttf candara{,b,i,z}.ttf \
 consola{,b,i,z}.ttf constan{,b,i,z}.ttf corbel{,b,i,z}.ttf )

fc-cache -fv /usr/share/fonts/windows7


The other thing you may need to do is logout and log back in. This will let you insure that the fonts are loaded properly.

Installing Java on Ubuntu 10.04

sudo apt-get install python-software-properties
sudo add-apt-repository 'deb http://archive.canonical.com/ lucid partner'
sudo apt-get update
sudo apt-get install sun-java6-jre sun-java6-plugin sun-java6-fonts

If you want JDK for development, you should also do:

sudo apt-get install sun-java6-jdk

Wednesday, September 22, 2010

JSONP and cross-domain Ajax calls

I was looking at this demo and trying to figure out how the cross-domain Ajax works:

http://www.prettyklicks.com/demo/fbjson.php

Some background info is here:
http://www.ibm.com/developerworks/library/wa-aj-jsonp1/?ca=dgr-jw64JSONP jQuery&S_TACT=105AGY46&S_CMP=grsitejw64

Here's the nitty/gritty - it boils down to avoid using the XMLHttpRequest object, injecting a <script src> tag that retrieves a JSON-encoded response but wrapped in a callback function, and then creating the callback function in the native browser that then gets invoked.

Both client and server need to be equipped to handle this technique. On the client side, JQuery does all of this magic under the covers with its JSONP implementation. On the server side, the server needs to wrap a JSON data if a callback= parameter is specified.

The JavaScript code has this line:
var url = "http://graph.facebook.com/prettyklicks/feed?limit=5&callback=?";
But the actual URL call translates to:
http://graph.facebook.com/prettyklicks/feed?limit=5&callback=jsonp1285193963567
It turns out there's a bit of jQuery and server-side magic going on. On the server side, It seems that when this callback= function will wrap a function that can then be executed by the DOM tree, similar to what's being done here:

http://code.google.com/p/django-rest-interface/issues/detail?id=39

So the server will return a result such as the following (note the jsonp1285197073066() function):
jsonp1285197073066({
   "data": [
      {
         "id": "45075726743_434059296743",
         "from": {
            "name": "Jason John Adams",
            "id": "1205845313"
         },
         "to": {
            "data": [
               {
                  "name": "Pretty Klicks",
                  "category": "Artist",
                  "id": "45075726743"
               }
            ]
         },
.
.
.
});
On the client-side, the JQuery's ajax() function has some checking for whether the Ajax() call actually seems to have a regexp check to see if there is a & or ? appended at the end of the URL call for JSON Ajax calls. It uses this regexp to replace the ? with a generated function name to call.
jsre = /=\?(&|$)/,
This section seems to rename the callback to ?jsonp=jsc (i.e. jsonp1285193963567).
var jsc = now(),
.
.
// Build temporary JSONP function
  if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
   jsonp = s.jsonpCallback || ("jsonp" + jsc++);



   // Replace the =? sequence both in the query string and the data
   if ( s.data ) {
    s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
   }


This section then creates a function based on the callback name (jsonp):

s.url = s.url.replace(jsre, "=" + jsonp + "$1");

   // We need to make sure
   // that a JSONP style response is executed properly
   s.dataType = "script";

   // Handle JSONP-style loading
   window[ jsonp ] = window[ jsonp ] || function( tmp ) {
    data = tmp;
    success();
    complete();
    // Garbage collect
    window[ jsonp ] = undefined;

    try {
     delete window[ jsonp ];
    } catch(e) {}

    if ( head ) {
     head.removeChild( script );
    }
   };
  }
So what's happening is that jQuery creates a function that will take as an argument tmp, which corresponds to the data from the JSON response. So when the script finishes loading, it will be executed and the Ajax callback is called.
function success() {
   // If a local callback was specified, fire it and pass it the data
   if ( s.success ) {
    s.success.call( callbackContext, data, status, xhr );
   }

   // Fire the global callback
   if ( s.global ) {
    trigger( "ajaxSuccess", [xhr, s] );
   }
  }
The last part of the equation is that script tags are inserted in the HEAD document with the src= set to the external URL. By doing is this way, the result will be evaluated by the function that we previously added to the window object.
// If we're requesting a remote document
  // and trying to load JSON or Script with a GET
  if ( s.dataType === "script" && type === "GET" && remote ) {
   var head = document.getElementsByTagName("head")[0] || document.documentElement;
   var script = document.createElement("script");
   script.src = s.url;
   if ( s.scriptCharset ) {
    script.charset = s.scriptCharset;
   }

   // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
   // This arises when a base node is used (#2709 and #4378).
   head.insertBefore( script, head.firstChild );

   // We handle everything using the script element injection
   return undefined;
  }
This success function can then be used by the $.getJSON call from http://www.prettyklicks.com/demo/fbjson.php to retrieve and process the data:

function(json){
      var html = "
    "; //loop through and within data array's retrieve the message variable. $.each(json.data,function(i,fb){ html += "
  • " + fb.message + "
  • "; }); html += "
";

Friday, September 17, 2010

Finding the encoding of a MySQL table

Inside a MySQL database, you can type the following:

sql> show variables like "character_set%"
    -> ;
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | latin1                     | 
| character_set_connection | latin1                     | 
| character_set_database   | utf8                       | 
| character_set_filesystem | binary                     | 
| character_set_results    | latin1                     | 
| character_set_server     | latin1                     | 
| character_set_system     | utf8                       | 
| character_sets_dir       | /usr/share/mysql/charsets/ | 
+--------------------------+----------------------------+
8 rows in set (0.00 sec)

Thanks to https://wincent.com/wiki/Finding_out_the_encoding_of_a_MySQL_database

http://blog.awarelabs.com/2008/django-tips-utf-8-ascii-encoding-errors-urllib2-and-mysql/

Django uses a default character set of UTF-8, so when it connects to the MySQL database, it uses UTF-8. If you use mysql to interface, you will notice that the character_set_results are set to latin1 (otherwise known as ASCII).

Thursday, September 16, 2010

Design for Non Designers - Jason Putorti's talk

Jason Putorti - http://jasonputorti.com/

Slides from the talk: http://www.slideshare.net/novaurora/10-things-ceos-need-to-know-about-design

Annotated notes:

- Jason left Mint.com after it was bought by Intuit.
- He was offered a job at Bessemer Ventures.
He consults with about 3 dozen companies in the portfolio.

1. Design can change businesses

- The whole premise of Mint.com: “We want your bank passwords. Trust me.”
- The visual design affects creditability in a very emotional way.
- A Craigslist design was not going to work.
- Notice on the site: Safe & Secure is dead last.
- Don't think of an elephant.
- No motivation to sign-up.
- Overwhelm the customer with benefits.

- Zappos.com
- Shoes is a commodity business. Focused on crafting customer experience.
- Often times Zappos upgrades your shipping to 2-day for free (wow!).

- Picture of the first MP3 player (not Apple obviously - Creative Labs?)
- Whole process: rip, burn, copy, very cumbersome.
- Lots of friction
- This product had 1st mover advantage.
- However, Apple was the 1st with product/market fit
- ITunes: designed around the music experience

- I'm on Twitter. vs. You should follow me on twitter here.-> 173% increase in clips
No big design/code changes: just a quick text change to compel the user to
take action.

- ABTest.com
- Use this site test 2 different versions of the same page (layout changes)

2. Design is more than pretty pictures.

- What is design? Design thinking: essential ability to combine empathy, creativity, and rationality to meet user needs & drive business success.

User experience:
How your product feels
Refers to Jesse James Garrett's book: Elements of User Experience (http://www.jjg.net/elements/)
Going through signup: completing a task in the product all the way to the error message in the product.
Make error messages things that users can take action on!

Customer experience:
It represents the sum of all the interactions a customer has with a company.
You should assign a value for each of them to better quantify.

Signup flow:
Completing a product.
Error message in the product
Vieiwng your home page
Reading a marketing email
Dealing with customer service

Your brand is how customers feel about you.
Shows AT&T logo
(You can hear people groan – translates to people associating the company with dropped calls)
Steve Ballmer picture

3. Great design: talks benefits, not features.

Four examples:
Mint.com: Understand your money vs. providing 20 different color configurations
“Remove your personal data” vs. “computer fuzzy-matching algorithm
iPod Ad: “All kinds of fun”
No GB storage spcs, video clip of just people having fun
CampignMonitor.com
The site has clear calls to action.
It has a clear visual heirarchy.
At the bottom there are creditability logos (Ebay, Intel, etc.)

4. Great design: thinks in flows, not screens.

1.User enters email address/login.
2.User enters pw.
3.User clicks Login button
4.System validates login

Submit button: just don't let them submit. Hide submit button and validate while the user's typing. You may consider disabling the submit button.

5. Great design: doesn't make the user think
Low-hanging fruit
Banana: if users can't find the banana, they are going to leave.
Yahoo example:
Focused on making the site a portal, put too much stuff because click impressions and banner ads made money. Thought about business objective, not the user objective.
Google
They put search right in the middle!

Take note of task flows in your product.
The truth is that people really don't want to use your software. Make it easy.

Put your site on an interaction scale.
Obvious <-----------> Requires Thought

Measure it this way.

Make it obvious what's clickable.
If you can't, make it discoverable.
Mint.com example: Hover over the pie charts, then click. The user has to discover it, but only has to learn once.

People don't read, they scan.
Newspaper have figured this out. They have a visual heirarchy that allows people to drill into stories in which they're interested.

Minimize noise
American Airlines site: it's an example of marketing departments gone wild.
Need someone to say no.
Bright colors, too much text.

- Omit needless words.

Users pick the 1st reasonable option..
- Example: Intacct.com: I am....

Too many options leads to paralysis.
Intacct.com offers 3 ways to do a demo:
Schedule a Live Demo
On-demand Demo
Intact Test Drive

American Airlines site:
Put a person on the page → go straight to their eyes.

Want to get famous? make fun of a site. He shows an example of a mockup of
what AA's site could look like redesigned.

6. A great design process starts with a great story

- Does everyone on your team know what the experience will be like interacting with your offering five years from now on?

How would it work if it were magic? OpenTable futuristic example: turning it into a concierge service.

7. Uses design as a lever.

The best marketing tool you can have is a well-designed application.
Business development:
Assign your revenue with valuable and legitimate users tasks.
Mint.com does this well to save you money.

Negative interactions drive negative social media.
Easy tools of mass command: easy to kill your brand.
No amount of money can buy the media to fix a boring product.
Have a brand czar.

8. A great design process gets people out of the office.

Have your teams spend at least 2 hours watching people experience your product or service.
Ex: logging people out because they were constantly going to take care of other things.


9. A great design process...has a bible.

A startup needs a brand czar.
Large company should have a style guide.
As opposed to each department have its own customer-facing guidess.


10. A great design process...repeats and refines.
In the last six weeks, have you held a celebration of a recently introduced design problem?

Q&A:

Jason: considers himself the defender of user experience. Tensions with the Mint.com biz dev: wanted to do a whole bunch of pop-ups of financial products. Rather than doing it this way, he put it in the activity stream. This way, it did not tick people off yet still achieved the comapny's goals.

Role of Product Manager: gather intelligence like the CIA.
Loudest users don't always represent the majority.
Mint.com: at one point wanted to consider managing transactions, but you introduce a whole tons of complexity (i.e. reconciling inconsistencies of your balance).

Wednesday, September 15, 2010

Using ObjectDoesNotExist to catch DoesNotExist errors

The Django documentation discusses the DoesNotExist error as follows:

http://docs.djangoproject.com/en/dev/ref/models/querysets/#id5
get() raises a DoesNotExist exception if an object wasn't found for the given parameters. This exception is also an attribute of the model class. Example:

Entry.objects.get(id='foo') # raises Entry.DoesNotExist

The DoesNotExist exception inherits from django.core.exceptions.ObjectDoesNotExist, so you can target multiple DoesNotExist exceptions.
How does it actually work? The Stack Overflow thread has this discussion:
http://stackoverflow.com/questions/2143506/django-where-does-doesnotexist-come-from

If you dig through the django.db.models.base.py file, you see these lines:
class ModelBase(type):

     super_new = super(ModelBase, cls).__new__
     new_class = super_new(cls, name, bases, {'__module__': module})

     if not abstract:
            new_class.add_to_class('DoesNotExist',
                    subclass_exception('DoesNotExist', ObjectDoesNotExist, module))
            new_class.add_to_class('MultipleObjectsReturned',
                    subclass_exception('MultipleObjectsReturned', MultipleObjectsReturned, module))

.
.
.
   def add_to_class(cls, name, value):
        if hasattr(value, 'contribute_to_class'):
            value.contribute_to_class(cls, name)
        else:
            setattr(cls, name, value)


You then have to wade through the bottom of the file to see what subclass_exception() actually does:
if sys.version_info < (2, 5):
    # Prior to Python 2.5, Exception was an old-style class                                                                                                                                                                                  
    def subclass_exception(name, parent, unused):
        return types.ClassType(name, (parent,), {})
else:
    def subclass_exception(name, parent, module):
        return type(name, (parent,), {'__module__': module})
It seems that Django creates a DoesNotExist exception for the class itself dynamically. The parent object is ObjectDoesNotExist, so you can use that parent to be able to catch exceptions thrown by multiple classes.

Tuesday, September 14, 2010

Emacs and PyFlakes

If you happen to be running Ubuntu v8.04, doing an apt-get install Pyflakes will install version 0.20. The Ubuntu packages confirmed this finding for me:

https://launchpad.net/ubuntu/+source/pyflakes/0.2.1+svn14502-1ubuntu1/+build/545566

Doing a Google search, you will see reports that Emacs hangs when using triple quotes ('''). You end up seeing really high CPU load and the only way to solve the issue is by killing the process. In fact, there was a $500 bounty to solve this issue 2 years ago:

http://www.plope.com/bounty_solved
I have solved our problem with triple-quotes.

 The problem is that Emacs expects PyFlakes to only output error
 messages, but on a syntax error, PyFlakes prints out an error message,
 then the *entire* contents of the module that it cannot import, and
 *finally* a line that contains a number of spaces equal to the offset
 into the file of the syntax error (in the case of my real-world file,
 the triple-quote was 3,896 characters into the file, so PyFlake's line 
 of spaces was that long as well).  The offending code is in the
 "pyflakes" command-line program and looks like this:

    print >> sys.stderr, 'could not compile %r:%d:' % (filename, lineno)
    print >> sys.stderr, line
    print >> sys.stderr, " " * (offset-2), "^"

 By removing or commenting out those last two lines, so that PyFlakes 
 only outputs its error message, you will stop flooding the Emacs
 regular-expression engine with data.  It's actually the long line of
 spaces that causes the problem, and it's one regular expression that's
 really sensitive to it (this is from the Emacs 22 flymake.el):

     ;; ant/javac
     (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)\:\\([0-9]+\\)\:[ \t\n]*\\(.+\\)"
      2 4 nil 5))

 For some reason (I'm not an RE engine guru), something about the way
 it's matching spaces takes exponential time when given several thousand
 spaces.  Go figure.  Anyway, I see no point in throwing anything but 
 error messages at Flymake, so I comment out both the printing of the
 module and the spaces from PyFlakes.


The problem was corrected in PyFlakes v0.3.0 according to the release notes:
http://divmod.org/trac/browser/tags/releases/Pyflakes-0.4.0/NEWS.txt
0.3.0 (2009-01-30):
20   - Display more informative SyntaxError messages.
21   - Don't hang flymake with unmatched triple quotes (only report a single
22     line of source for a multiline syntax error).
23   - Recognize __builtins__ as a defined name.
24   - Improve pyflakes support for python versions 2.3-2.5
25   - Support for if-else expressions and with statements.
26   - Warn instead of error on non-existant file paths.
27   - Check for __future__ imports after other statements.
28   - Add reporting for some types of import shadowing.
29   - Improve reporting of unbound locals
The trick to fixing this issue on Ubuntu v8.04? Use pip install pyflakes, which will download the 0.4.0 version.

sudo apt-get remove pyflakes
sudo pip install pyflakes

VMWare Image for CDH3

I made this posting 2 months ago (http://getsatisfaction.com/cloudera/topics/vmware_image_for_cdh3) and apparently Cloudera has yet to release a fix for this VMWare:

In any case, here is the same posting:

VMWare image for CDH3 -- eth0 does not show up as an installed driver. The previous cloudera-training-0.3.3 VmWare image did not have this issue, but cloudera-training-0.3.4
does seem to be missing the appropriate driver. By default I can't access the Internet.

I managed to bypass this issue by activating eth1 by editing /etc/network/interfaces:
sudo bash 
vi /etc/network/interfaces: 

auto eth1 
iface eth1 inet dhcp 
/etc/init.d/networking restart 
Also, I tried to install VMWare Tools to see if the eth0 issue would go away, but after rebooting, the problem still persisted. I certainly didn't see this issue on the previous VmWare image.

In any case, here are instructions to install VMWare Tools:
sudo apt-get install linux-headers-`uname -r` (this is necessary to get the right headers) 
Click on install VMWare Tools 
mount /cdrom 
cd /cdrom 
Copy VMWare tool source code to /tmp and extract the files (the actual filename will depend on your VMWare version [which the VMWare installer would have reported in case of a mismatch]):
cp VMWareTools*.tar.gz /tmp 
cd /tmp 
tar -xzvf VMWareTools*.tar.gz 
cd vmware-tools-distrib/ 
sudo ./vmware-install.pl

Changing the behavior of Emacs' list-buffers mode

I encountered the same issue with Emacs.  The issue is that when you hit Ctrl-X, Ctrl-B to see all of your buffers with the window split in 2, the buffer list shows up on the other side.  It's hugely annoying since you lose the previous buffer screen.

http://stackoverflow.com/questions/1231188/emacs-list-buffers-behavior

In GNU emacs, every time I hit Ctrl-x Ctrl-b to see all of my buffers, the window is split to show the buffer list, or if I have my window already split in 2 (for instance, I will have a shell running in the lower window), the buffer list appears in the other window.

My desired behavior is for the buffer list to appear in my active window so that I can select the buffer I want and continue to working in the same window, rather than having to Ctrl-x Ctrl-o to the other buffer, selecting the buffer (with enter) and editing that buffer in the other window... I've googled for it but it doesn't seem to be a common desire? I wonder if anyone has an elispy (or other) solution?

Simplest solution?

Edit your .emacs:

(global-set-key "\C-x\C-b" 'buffer-menu)