Wednesday, August 1, 2012

Install PostgreSQL for Django and Virtualenv on Ubuntu

Install PostgreSQL:

sudo apt-get install postgresql

Later we need to install psycopg2 using pip. To resolve some dependency issues, we need to install libpq-dev and python-dev. For more details, please see here.

sudo apt-get install libpq-dev python-dev

Set password for the "postgres" user.

sudo passwd postgres

Then enter password.

Create a Django dev user:

sudo -u postgres createuser -P django_dev

Enter password.

Add postgres to sudoers:

sudo vi /etc/sudoers

Find the following line:

root ALL=(ALL:ALL) ALL 

Below this line, add the following:

postgres ALL=(ALL) ALL

Now switch to the "postgres" user

su postgres

Enter PostgreSQL shell:

psql postgres

Create a DB:

CREATE DATABASE django_db OWNER django_dev ENCODING 'UTF8';

Type \q to quit the PostgreSQL shell.

Edit the permission file:

sudo vi /etc/postgresql/9.1/main/pg_hba.conf

Your PostgreSQL version number might be different. Replace 9.1 with the correct version number.

Change:

local    all    postgres    peer 
local    all    all    peer

To:

local    all    postgres    md5
local    all    all    md5

And add the following line:

local    django_db    django_dev    md5

Now, restart PostgreSQL server:

sudo /etc/init.d/postgresql restart

Get into virtual env and install psycopg2:

source ~/your_virtual_env/bin/activate
pip install psycopg2

Go to your django project and edit the settings.py file, change the following settings:

'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'django_db', 
'USER': 'django_dev', 
'PASSWORD': 'your_password', 

Finally, at the directory where manage.py is, type the following command to synchronize DB:

python manage.py syncdb


References:

- How to install PostgreSQL on Ubuntu for Django

- PostgreSQL Ubuntu Installation

- PostgreSQL Fatal Authentication Failure

- Add User to "sudoers" File

- PostgreSQL Server Guide

Tuesday, June 26, 2012

CSS Opacity, IE Filter, Binary and Script Behaviors

(This article is about using IE specific "filter" to mimic the standard CSS attribute "opacity", this mimicry's consequence when the Binary and Script Behaviors setting is disabled in IE8 and IE7, and the workaround.)

CSS is not rocket science, but nonetheless difficult due to the infamous browser-compatibility issue. Any experienced web developer will tell you that CSS is a mess. The CSS standard doesn't look very complicate on paper, however, in reality, there are always some corner cases that break your UI in certain browsers (most likely IE). Some of such corner cases are well documented and mind-shared in the web developer community, however, some of them are not so well known, lurking there to ambush you.

I got ambushed today by such corner case. This corner case is so nasty that it cannot be reproduced in most browser settings. Here is the story:

Opacity is a standard CSS attribute invented to control transparency of HTML elements. However, not so surprisingly, IE under version 9 doesn't support "opacity". To fix that, most articles returned by Google search will recommend adding "filter" -- a IE specific attribute to mimic "opacity".

#trans {
  opacity: 0.4;
  filter: alpha(opacity=40); /* For IE8 and earlier */
}

Snippet 1

The above snippet I partially copied from w3schools.com (link) works in most browser settings. Actually, it works so well that most web developers won't have to worry about it in their lifetime.

My application is a third-party web app. It works in a tight security environment in which IT built up a fortress to fend off any possibly suspicious activity. One of such suspicious activities is using ActiveX.

"""
The Microsoft Windows Server 2003 Internet Explorer Enhanced Security Configuration component (also known as Microsoft Internet Explorer hardening) reduces a server’s vulnerability to attacks from Web content by applying more restrictive Internet Explorer security settings that disable scripts, ActiveX components, and file downloads for resources in the Internet security zone.
"""
(If you really want to read the full explanation, here is the original article)

This IE security setting is called "Binary and Script Behaviors" under the "ActiveX controls and plug-ins" category.


Figure 1

This setting is disabled in my case, which means using ActiveX is not an option. Now, you must wonder why this has anything to do with CSS opacity?

The "filter" attribute actually relies on ActiveX in order to mimic "opacity" in IE8 and below (discussed in stackoverflow). When the "Binary and Script Behaviors" setting is disabled, ActiveX became unavailable and the filter trick stopped working.

In Snippet 1, the filter is set to "alpha(opacity=40)", but when the "Binary and Script Behaviors" setting is disabled, filter will be ignored; and if background color is black, you will see a solid black instead of a semi-transparent black.


Figure 2

To reproduce this issue in IE8, first, you need to remove your app from the Trusted Sites (Figure 3). Then, disable "Binary and Script Behaviors" (See Figure 1).


Figure 3

The workaround is to set the "background-image" attribute to a semi-transparent PNG only for IE 7 to 8.

#trans {
  opacity: 0.4;
  /* filter: alpha(opacity=40); <-- Remove filter */
}
body.ie7 #trans, 
body.ie8 #trans {
  background: transparent url(/images/opacity40_black.png);
}
Snippet 2
The above CSS works with the following conditional body tags for marking IE versions.
<!--[if lt IE 7 ]><body class="ie6"><![endif]-->
<!--[if IE 7 ]><body class="ie7"><![endif]-->
<!--[if IE 8 ]><body class="ie8"><![endif]-->
<!--[if IE 9 ]><body class="ie9"><![endif]-->
<!--[if gt IE 9 ]><body class="ie10"><![endif]-->
<!--[if !IE]>--><body class="not-ie"><!--<![endif]-->
Snippet 3
To create a semi-transparent PNG, there are millions of PNG editing tools out there, but I found this free online generator is sufficient: Transparent PNG generator.

Friday, May 25, 2012

JavaScript Function to Compare Versions

Useful to compare version numbers, e.g., Flash Player versions.

Only work with digital versions.

Live demo: jsfiddle example

function compVersions(strV1, strV2) {
  var nRes = 0
    , parts1 = strV1.split('.')
    , parts2 = strV2.split('.')
    , nLen = Math.max(parts1.length, parts2.length);

  for (var i = 0; i < nLen; i++) {
    var nP1 = (i < parts1.length) ? parseInt(parts1[i], 10) : 0
      , nP2 = (i < parts2.length) ? parseInt(parts2[i], 10) : 0;

    if (isNaN(nP1)) { nP1 = 0; }
    if (isNaN(nP2)) { nP2 = 0; }

    if (nP1 != nP2) {
      nRes = (nP1 > nP2) ? 1 : -1;
      break;
    }
  }

  return nRes;
};

alert(compVersions('10', '10.0')); // 0
alert(compVersions('10.1', '10.01.0')); // 0
alert(compVersions('10.0.1', '10.0')); // 1
alert(compVersions('10.0.1', '10.1')); // -1

Tuesday, April 3, 2012

CSS3 Transition: Slideup Box (Take 2)

Demo and source code

This post is to propose a better solution for creating an expandable/slideup box. My previous implementation has a flaw -- the fixed "max-height" truncate part of the content when its height grows. This solution will resolve this issue.

First, we have the following markup.

<article>
  <h2>Click me to expand</h2>
  <div class="content_w">
    <div class="content">
      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do 
      eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut 
      enim ad minim veniam, quis nostrud exercitation ullamco laboris 
      nisi ut aliquip ex ea commodo consequat...
    </div>
  </div>
</article>

When users click inside an <article>, the content area will slide down (expand) or slide up (collapse). The title (<h2>) will always be visible.

The idea is to wrap the content inside a wrapper (div.content_w). The wrapper will hide any overflown content. We will change the height of the wrapper to create a slide up/down effect.

article .content_w {
  overflow: hidden;
  height: 0;
}
article .content_w.transition {
  -webkit-transition: height 0.5s;
     -moz-transition: height 0.5s;
       -o-transition: height 0.5s;
          transition: height 0.5s;
}

The wrapper needs to have "overflow: hidden" in order to clip the overflown content. We set "height: 0" to collapse the box initially.

The transition will take effect on the wrapper's height. When the box needs to be collapsed, we set the wrapper's height to 0. CSS3 transition will smoothly slide up the box. When expanding (sliding down), we set the wrapper's height back to the height of its enclosed content.

$('article').on('click', function() {
  slide($('.content', this)); 
});

function slide(content) {
  var wrapper = content.parent();
  var contentHeight = content.outerHeight(true);
  var wrapperHeight = wrapper.height();

  wrapper.toggleClass('open');
  if (wrapper.hasClass('open')) {
    setTimeout(function() {
      wrapper.addClass('transition').css('height', contentHeight);
    }, 10);
  }
  else {
    setTimeout(function() {
      wrapper.css('height', wrapperHeight);
      setTimeout(function() {
        wrapper.addClass('transition').css('height', 0);
      }, 10);
    }, 10);
  }

  wrapper.one('transitionEnd webkitTransitionEnd transitionend oTransitionEnd msTransitionEnd', function() {
    if(wrapper.hasClass('open')) {
      wrapper.removeClass('transition').css('height', 'auto');
    }
  });
}

The trick is that we don't want to keep a fixed height on the wrapper when it finishes expanding. A fixed height will clip its content when it grows, or leave unnecessary space at the bottom when the content shrinks. To fix that, we need to set height back to "auto" in order to "relax" the height. However, setting "height: auto" on HTML elements with CSS3 transition will make the transition have no effect. We have to remove transitions before setting "height: auto".

Demo and source code

Tested in Chrome, Safari, FireFox, and Opera Mobile Emulator


Saturday, March 31, 2012

Sliding / Expandable / Collapsible Box with max-height CSS Transition

Note


An improved solution can be found in this post.








Source code and demo

One common CSS3 Transition is to slide up (collapse) and slide down (expand) a box by manipulating its "height" attribute, e.g. changing 400px to 0. However, when either height is set to "auto", the transition won't work anymore. This topic has been discussed here.

The solution is to change the "max-height" instead of "height". Max-height is a CSS attribute supported in almost all modern browsers (see compatibility chart here). It defines the maximum height of an element. We can use it to "shrink" a box by setting max-height to 0, or expand a box by restoring its original height. In order to restore the original height, we need to retain the computed height of the box content.

Adam at stackoverflow.com provided a solution inspired by this same idea. I simplified the solution by removing some of the JavaScript code.

HTML markup


Here I create an item (div.item) with a title (<h2>) and content area (div.content). I want to make the content area slide down (expand) or slide up (collapse) once the item is clicked. The title will always be visible.

<div class="item">
    <!-- Title -->
    <h2>Click me to expand</h2> 

    <!-- Content wrapper -->
    <div class="content_w"> 

        <!-- Content -->
        <div class="content"> 
            Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
            sed do eiusmod tempor incididunt ut labore et dolore 
            magna aliqua. Ut enim ad minim veniam, quis nostrud 
            exercitation ullamco laboris nisi ut aliquip ...
        </div>
    </div>
</div>

I put the content div inside a wrapper (div.content_w). Instead of changing the max-height of the real content div, we change the wrapper's. This way, we can achieve the sliding up/down effect while still retaining the computed height of the real content div.

Style sheet


Here is the style sheet. Please notice that the max-height and transition are set on the content wrapper rather than the content itself. The content will remain unchanged no matter if the wrapper is collapsed or expanded. The content wrapper needs to have "overflow: hidden" in order to hide its contained content when the wrappers's height becomes less than the content's.

.item {
    width: 400px;
}

/* Content wrapper */
.content_w {
    overflow: hidden;
    max-height: 0;
    -webkit-transition: max-height 0.5s;
       -moz-transition: max-height 0.5s;
         -o-transition: max-height 0.5s;
            transition: max-height 0.5s;
}

JavaScript


With a little help from jQuery, I toggle the max-height between 0 and the content height based on the "open" class which I use simply to mark the expanded and collapsed state.

(function($) {

  // max-height transition. 
  // Inspired by http://jsfiddle.net/adambiggs/MAbD3/
  function toggleContent($contentWrapper) {
    // Get the computed height of the content
    var contentHeight = $('.content', $contentWrapper).outerHeight(true);

    // Add or remove class "open"
    $contentWrapper.toggleClass('open');

    // Set max-height
    if ($contentWrapper.hasClass('open')) {
      $contentWrapper.css('max-height', contentHeight);
    }
    else {
      $contentWrapper.css('max-height', 0);
    }
  }

  // Listen to click events on the item element 
  $('.item').on('click', function(e) {
    e.preventDefault();

    toggleContent($('.content_w', this)); 
  });

})(jQuery);​

One thing to notice is that I didn't use jQuery to do the transition. The transition is done by CSS. jQuery is used only for selecting DOM elements, marking elements, and applying CSS styles. You can replace jQuery with any of your favorite JavaScript libraries.

Limitations


The max-height is set to the content height when it is expanded. So if the content changes or re-flows later, some content will be clipped. The extra code that Adam put there is to prevent this by setting max-height to a really big number at the end of the expand transition. However, if you need a simple slideup box whose content and layout won't change after expansion, then this solution should work fine for you.

An improved solution can be found in this post.

Saturday, March 24, 2012

Bring GVIM menu back in Ubuntu 11.10

GVIM menu bar disappeared in Ubuntu 11.10 with Unity if gvim is launched from command line. This post is a compilation of things that I tried to fix the issue.

First, you might need to remove Vim from the .gnome2 dir:

rm ~/.gnome2/Vim

Then, add the following line to ~/.bashrc

alias gvi='gvim -f'

Restart terminal. Type gvi, and you should see the gvim with menu in the unity top bar. People report that the "-f" should fix the menu problem.

If you don't want to block a terminal when opening gvim, replace the above alias line with this:

gvi() {
  gvim -f $@ &
}

Next time, when you type gvi, the gvim process will be put in background.

If you want to keep gvim running even after its terminal is closed, try this:

gvi() {
  gvim -f $@ &
  disown
}

References:


Friday, March 2, 2012

Don't use # to create empty links

An empty link is a <a> tag that doesn’t link to anywhere. Oftentimes, we have these links where we want to ignore the href attribute and customize the behavior for the click events. To make an empty link, usually, we put a “#” in the href attribute, like this:

<a href=”#”>link</a>

This approach is simple, however, the “#” href introduced two issues:

1. It creates an entry in browser history whenever users click on the link.

2. (This is caused by the first issue) If the link is in the middle of a page, after clicking the link, users will be brought back to the top of the page.

A # in a href attribute points to an anchor. If the anchor name is empty, it refers to the top of the current page itself. When user clicks on such # link, browsers treat # as a normal navigation, and put it to the history stack. When users click on the back button, they expect a previous page, however, what they will see is the current page again, because the # is at the top of the history stack, and it points to the current page. If users click the # link multiple times, a same amount of # entries will be put to the history stack, and users have to click back button several times in order to go back to the real previous page. This behavior certainly introduces confusions to users.

Since browser treats # as a normal navigation, and # refers to the top of the current page, when users click on the # links, browser will display the current page with the vertical scroll bar being reset to the top. User will lose their scrolling positions. This will be a big problem for long pages.

The correct way to create an empty link is:

<a href=”javascript:void(0);”>link</a>

This will make browser ignore the href attribute, and won’t introduce a new entry in browser history.

Wednesday, February 22, 2012

JavaScript Array Cheatsheet

var a = []; // An empty array
var b = [1, 2, 3];

b.length // Note: length is NOT a method
// 3

// Appends elements to an array, and returns the new length
a.push(4); 
// 1
// a = [4]
a.push(5, 6);
// 3
// a = [4, 5, 6]

// Merge two arrays. 
// This method will NOT affect the original arrays
b.concat(a);
// [1, 2, 3, 4, 5, 6]
// a = [4, 5, 6]
// b = [1, 2, 3]
var c = b.concat(a, [7], [8, 9, 10]);
// a = [4, 5, 6]
// b = [1, 2, 3]
// c = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// Removes the last element, and returns that element
c.pop(); 
// 10
// c = [1, 2, 3, 4, 5, 6, 7, 8, 9]

// Removes the first element, and returns that element
c.shift(); 
// 1
// b = [2, 3, 4, 5, 6, 7, 8, 9]

// Add elements to the beginning of the array, 
// and returns the new length
c.unshift(1); 
// 9
// c = [1, 2, 3, 4, 5, 6, 7, 8, 9]
c.unshift(-1, 0);
// 11
// c = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

// Array can have mixed types of elements
c = [1, 2, '3', 'a', 'b', true]; 

The versatile splice method adds and/or deletes elements to/from an array, and returns the deleted elements.

array.splice( index, count, element1, ..., elementN )

var c = [1, 2, '3', 'a', 'b', true]; 

// Deletes 1 element starting at the element of index 2 (0-based), 
// and returns the deleted elements
c.splice(2, 1); 
// ['3']
// c = [1, 2, 'a', 'b', true]

// Deletes 2 elements starting at the element of index 1 (0-based), 
// and returns the deleted elements
c.splice(1, 2); 
// [2, 'a']
// c = [1, 'b', true]

// Deletes 1 elements starting at the element of index 1 (0-based), 
// Inserts 'A' and 'B', 
// and returns the deleted elements
c.splice(1, 1, 'A', 'B')
// ['b']
// c = [1, 'A', 'B', true]

// Inserts 'X' at index 1
c.splice(1, 0, 'X');
// [] Didn't delete anything
// c = [1, 'X', 'A', 'B', true]