Wednesday, December 22, 2010

Animated scrolling background with parallax using jQuery

I wanted to distinguish the author comments on my blog in an interesting way. Here's what I came up with:


Click the image to see it live

Notice in the live view that there is a parallax effect: some of the clouds appear to float by closer than others. This is accomplished using two images with one on top of the other. The images used are these:
Background:
Transparent overlay:

Implementation

Two css classes are used, one for each image:
.byAuthor
{
color: Black;
background-image: url("http://lh4.ggpht.com/abbrev_path/sky.png");
padding: 5px;
}

.byAuthorOverlay
{
background-image: url("http://lh4.ggpht.com/abbrev_path/sky2.png");
}

Eventually our comments will take the form of two nested divs, each given one of the classes defined above with the overlay on top. For example:
<div class="byAuthor">
<div class="byAuthorOverlay">
Here is the text of the comment
</div>
</div>

Next comes the javascript. The setInterval() method is used to call a function which changes the Y offset of the background image. This is done twice: once for each image.
function scrollBackground(options) {
options.step = options.step || 1; //step property defaults to 1
options.top = options.top === undefined || Math.abs(options.top) >= options.bgImgHeight ? 0 : options.top - options.step;
options.$elements.css("background-position", "0 " + options.top + "px");
}

var mainBg = { speed: 30, bgImgHeight: 400 };
setInterval(function () { scrollBackground(mainBg); }, mainBg.speed);

var overlayBg = { speed: 50, bgImgHeight: 400 };
setInterval(function () { scrollBackground(overlayBg); }, overlayBg.speed);

scrollBackground() takes an object named "options" as an argument. Here is the definition of this object:
options = {
delta: 1, //Y delta - number of pixels by which to change the background-position each time
speed: 30, //time interval between animation steps (milliseconds)(lower is faster)
bgImgHeight: 400px, //height of the image being scrolled
$elements: $(".byAuthor") //a jquery set of elements to apply css changes to
}

setInterval() causes scrollBackground() to be called every X milliseconds. scrollBackground() calculates a new Y value for the background image and applies it. Notice that the "speed" values for each of the images is different. This causes the overlay image to scroll a little faster than the background image providing the parallax effect.

Applying to blogger

Getting this to work on blogger requires understanding of jQuery and the DOM. Because templates differ, you'll need to examine how blogger displays your comments, taking note of the DOM elements surrounding them. In my case, the DOM looks like this (trimmed for brevity):
<dt class="comment-author blog-author">Joel said... </dt>
<dd class="comment-body">
<p>
Comment Text
</p>
</dd>

I want to enclose the comment text within the two nested divs mentioned above. This is simple using jQuery:
    $(".blog-author").next().find("p").wrap('<div class="byAuthorOverlay" />');
$(".byAuthorOverlay").wrap('<div class="byAuthor" />');

That results in the following DOM layout:
<dt class="comment-author blog-author">Joel said... </dt>
<dd class="comment-body">
<div class="byAuthor" >
<div class="byAuthorOverlay" >
<p>
Comment Text
</p>
</div>
</div>
</dd>

And now, with both setInterval() functions executing at the prescribed intervals, the background images for the new divs appear to scroll upward.

Thursday, October 21, 2010

Python Import Search Order/Precedence

The Problem


I recently ran into an interesting (and very frustrating error). The error was:
ImportError: No module named apps.core.models
This error was happening to me in a django project. The package structure was as follows:

foo/
apps/
core/
models.py
auth/
foo/
models.py
views.py


In views.py I had the import statement:
from foo.apps.core.models import AModel

This was very confusing to me. Even worse was that the exact same statement worked just fine from the apps.auth.foo.models module.

The Explanation


The problem here has to do with how python handles searching for modules. You can read about it here.
To summarize python searches (in this order):
  1. The folder of the current module.
  2. Each folder listed in the PYTHONPATH.
  3. System specific folders ("set path", "export path", etc).

The Solution


The real problem I was running into was that the current folder had a foo module. So the full name that it was actually looking in was foo.apps.auth.foo.apps.core.models. To solve this I just needed to either change the package names (which really may be the best idea), or change the import statements to use a relative import path.

Something like this:
from ..core.models import AModel


Happy pythoning!

Wednesday, September 01, 2010

Using Mercurial Subrepositories

If you have any class libraries whose usage spans multiple projects, you will want to include them as subrepositories in your Mercurial repository. This allows you to have the library stored in its own repository rather than copying it into each individual project that requires it. This way, changes to the library can be reflected in all projects using the library. This article shows how to accomplish that task.

Let's say you have a class library named ClassLibrary1 which contains methods useful to several projects. Here is the code for ClassLibrary1
    public class Class1
    {
        public static string MyFunction()
        {
            return "ClassLibrary1 Version 1.0";
        }
    }

The library resides in a mercurial repository located at
\\hgStore\c\hgMain\Libraries\ClassLibrary1

After creating a new project, we set up the mercurial repository on it.
F:\My Projects\CoolApp>hg init

F:\My Projects\CoolApp>hg add
adding src\CoolApp.sln
adding src\CoolApp\CoolApp.csproj
adding src\CoolApp\Form1.Designer.cs
adding src\CoolApp\Form1.cs
adding src\CoolApp\Program.cs
adding src\CoolApp\Properties\AssemblyInfo.cs
adding src\CoolApp\Properties\Resources.Designer.cs
adding src\CoolApp\Properties\Resources.resx
adding src\CoolApp\Properties\Settings.Designer.cs
adding src\CoolApp\Properties\Settings.settings

F:\My Projects\CoolApp>

Now we clone the library into our CoolApp project repository.
F:\My Projects\CoolApp>hg clone \\hgStore\c\hgMain\Libraries\ClassLibrary1 src\ClassLibrary1
updating to branch default
9 files updated, 0 files merged, 0 files removed, 0 files unresolved

The class library is now located in the project directory structure where we want it. So now we can mark that directory as being a subrepository. We do that by creating a special file called ".hgsub".
F:\My Projects\CoolApp>echo src\ClassLibrary1 = \\hgStore\c\hgMain\Libraries\ClassLibrary1> .hgsub

In this case "src\ClassLibrary" is the path in our working directory, and of course "\\hgStore\c\hgMain\Libraries\ClassLibrary1" is the path to pull from.

Now add our special .hgsub file to version control:
F:\My Projects\CoolApp>hg add .hgsub

And commit:
F:\My Projects\CoolApp>hg commit --message="Set up subrepository for ClassLibrary1"
committing subrepository src\ClassLibrary1

F:\My Projects\CoolApp>

We're all set. Now if we clone our CoolApp project, it will include the ClassLibrary1 dependency.
F:\My Projects\>hg clone CoolApp CoolApp2
updating to branch default
pulling subrepo src\ClassLibrary1 from \\hgStore\c\hgMain\Libraries\ClassLibrary1
requesting all changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 14 changes to 9 files
12 files updated, 0 files merged, 0 files removed, 0 files unresolved

An important consideration when working with subrepositories is whether reverting to a specified revision of the project will also revert the library to the revision at the specified time.

Let's change the output of ClassLibrary1 and commit our changes.

    public class Class1

    {

        public static string MyFunction()

        {

            return "ClassLibrary1 Version 1.1"; //updated version

        }

    }


F:\My Projects\CoolApp2>hg commit --message="Changed output of ClassLibrary1"
committing subrepository src\ClassLibrary1

F:\My Projects\CoolApp2>hg push
pushing to f:\My Projects\CoolApp
pushing subrepo src\ClassLibrary1 to \\hgStore\c\hgMain\Libraries\ClassLibrary1
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files

Uh-oh. Someone thinks they found a bug in Version 1.0 of CoolApp. In order to duplicate the bug so we can understand it, we need to revert to the reported version. If our subrepository doesn't also revert to the state of ClassLibrary1 at the time of Version 1.0 we're going to have a mess on our hands. Let's try it and see what happens:
F:\My Projects\CoolApp2>hg log
changeset: 2:ef31b83178d1
tag: tip
user: Joel
date: Wed Sep 01 12:30:07 2010 -0600
summary: Changed output of ClassLibrary1

changeset: 1:a577f539790e
user: Joel
date: Wed Sep 01 12:21:30 2010 -0600
summary: Set up subrepository for ClassLibrary1

changeset: 0:d12383009bd2
user: Joel
date: Wed Sep 01 12:19:09 2010 -0600
summary: Initial revision


a577f539790e is the revision we want to work with.
F:\My Projects\CoolApp2>hg update a577f539790e
1 files updated, 0 files merged, 0 files removed, 0 files unresolved


Let's take a look at the code for ClassLibrary1

    public class Class1

    {

        public static string MyFunction()

        {

            return "ClassLibrary1 Version 1.0";

        }

    }


Perfect! It's just as we hoped.

Learn more about subrepositories in Mercurial
Have I missed something? Feedback is greatly appreciated.

Tuesday, May 18, 2010

Handy Linq/DataSet operations

Create a list from a field in the DataSet: (Query syntax)

List<uint> myList = (from row in aDataSet.Tables[0].AsEnumerable()
                     select row.Field<uint>("AColumnName")).ToList();

or: (Method Syntax)

List<uint> myList = aDataSet.Tables[0].AsEnumerable().Select(dataRow => dataRow.Field<uint>("AColumnName")).ToList();

or more generically...

IEnumerable<uint> myList = aDataSet.Tables[0].AsEnumerable().Select(dataRow => dataRow.Field<uint>("AColumnName"));



Create a Dictionary from two fields in the DataSet:

Dictionary<uint, uint> myDictionary = aDataSet.Tables[0].AsEnumerable()
    .ToDictionary(k => k.Field<uint>("KeyColumn"), v => v.Field<uint>("ValueColumn"));

Tuesday, February 23, 2010

Control.Invoke using Lambdas

Instead of using anonymous methods or delegates, you can now use a simple lambda expression. The default Control.Invoke() wont work so the best thing to do is write your own extension method. An example follows:

Extension Method


public static class ControlExtensions

{

    public static void Invoke(this Control Control, Action Action)

    {

        Control.Invoke(Action);

    }

}



Usage


this.Invoke(() => Text = "Refreshed");