jQuery extension method to parse a query string

by timvasil 3/21/2013 9:49:00 PM

This code automatically trims a leading "?" (if present) and accomodates multiple params with the same key by stuffing them into an array.  (You can also find it on Gist.)

(function($) {
    var re = /([^&=]+)=?([^&]*)/g;
    var decode = function(str) {
        return decodeURIComponent(str.replace(/\+/g, ' '));
    };
    $.parseParams = function(query) {
        var params = {}, e;
        if (query) {
            if (query.substr(0, 1) == '?') {
                query = query.substr(1);
            }

            while (e = re.exec(query)) {
                var k = decode(e[1]);
                var v = decode(e[2]);
                if (params[k] !== undefined) {
                    if (!$.isArray(params[k])) {
                        params[k] = [params[k]];
                    }
                    params[k].push(v);
                } else {
                    params[k] = v;
                }
            }
        }
        return params;
    };

Example usage:


  $.parseParams(document.location.search)
 

Tags:

JavaScript | jQuery

Pagination JavaScript

by timvasil 3/20/2013 8:25:00 PM

Before I settled on an "infinite scrolling" solution to a pagination problem, I was using Twitter Bootstrap's "pagination" CSS class and a bunch of page links, including the usual next/previous.  I wrote the script to meet these requirements:

  1. First and previous page links must always be available
  2. Next and last page links must always be available
  3. The current page must be highlighted and non-clickable
  4. Up to X additional links must be visible for interstitial pages before and after the current page.  
  5. Each link must consume the same amount of horizontal space so next/previous/first/last links don't change position.  This prevents accidental clicks if the user were to click multiple times and the elements jumped around.

Here are some examples of how this looks for various pages:

This is the code:

var html = [];
var maxBlocks = 11;  // specify the max <li> elements you want rendered
var currentPage = 1; // specify current page here
var numPages = Math.ceil(totalItems / pageSize);

if (numPages > 0) {
    addPageLink = function(page, label, tooltip) {
        var cls = (page == currentPage || page === null) ? 'disabled' : '';
        if (label == currentPage) {
            cls += ' active';
        }
        html.push('<li title="', tooltip, '" data-page="', page, '" class="', cls, 
             '"><a href="#">', label, '</a></li>');
    }
    
    html.push('<ul>');
    addPageLink(Math.max(1, currentPage - 1), '&laquo;', 'Previous page');
    addPageLink(1, 1, 'First page');

    var maxPivotPages = Math.round((maxBlocks - 5) / 2);
    var minPage = Math.max(2, currentPage - maxPivotPages);
    var maxPage = Math.min(numPages - 1, 
                           currentPage + maxPivotPages * 2 - (currentPage - minPage));
    minPage = Math.max(2, minPage - (maxPivotPages * 2 - (maxPage - minPage)));

    for (var i = minPage; i <= maxPage; i++) {
        var isMore = (i == minPage && i != 2) || (i == maxPage && i != numPages - 1);
        if (isMore) {
            addPageLink(null, '&hellip;');
        } else {
            addPageLink(i, i, 'Page ' + i);
        }
    }
            
    addPageLink(numPages, numPages, 'Last page');
    addPageLink(Math.min(numPages, currentPage + 1), '&raquo;', 'Next page');
    html.push('</ul>');
}

$('.pagination').html(html.join(''));

And here are the Bootstrap CSS tweaks to get fixed width and the highlighted state (in Less syntax):

.pagination {
    li a {
        min-width: 20px;
    }
    
    li.active {
        a, a:hover, a:active {
            background-color: #005580;
            color: #fff;
        }
    }
}

Tags:

CSS | JavaScript | jQuery | Twitter Bootstrap

Options for a multi-app large-screen kiosk

by timvasil 3/19/2013 6:05:00 PM

I've been looking for a way to build a large-screen kiosk capable of running multiple apps and websites, but still run in a 'locked down" mode so kiosk users can't escape the sandbox and do something malicious.  The iPad now has a kiosk mode, but it limits you to a single app.  Android has kiosk apps, like SureLock, that do support multiple apps and websites, however there's no Android device quite large enough for what I'm trying to do (the 24" ViewSonic is the largest I've seen).  That leaves a custom solution:  probably built on top of Windows so Windows apps + Android apps (in an emulator like BlueStacks).

Here's a summary of the options I've evaluated to date to get a ~40" multi-app exhibit/kiosk experience:

 

Option 1 

Option 2 

Option 3

Concept

10 iPads + Mac Mini

Android all-in-one device

Large touchscreen + PC

Screen size

9.7" x 10 devices

24" x 2 devices

40" x 1 screen

Relative size

Max dimensions
(w × h)

40" × 26"

21" × 28"

40" × 26"

Multi-touch capabilities

11 touch points
(per device)

2 touch points

2 to 32 touch points
(varies by model)

Apps supported

Web sites + iPad apps
(max 1 app per device)

Web sites + Android apps

  

Web sites + some Android apps + some Windows apps

Restricted web site browsing

Yes

Yes

Yes

Lock portions of applications

No

Yes

Yes

Trailer-capable

Limited
(backgrounds only)

Limited
(backgrounds and logo)

Yes

Usage data collection

No

No

Yes

Remote admin

Limited (iTunes)

Yes

Yes

Risks

1.    Lead time for vendor to frame iPads

 

1.    Hardware not shipping until April (22" version available now)

1.    Multi-touch not currently available for Android apps

2.    Requires unproven custom software

Hardware

Touchscreens

$5,300
($3,500 with 10 iPad Minis)

$600

$2,500

PC

$650

$0

$650

Software & labor 

Kiosk software

$100

$60

75 hours
(custom development)

Configuration & testing

20 hours

20 hours

20 hours

Remote management

50 hours

20 hours

0 hours

Metric collection & reporting

40 hours
(requires “jailbreaking” iPads)

20 hours

7 hours

 

Can you think of better configurations?  Please let me know!

Tags:

Hardware | Windows | Android | iOS

ViewSonic's 24" Android tablet

by timvasil 3/19/2013 5:53:00 PM

Now here's a device you're not going to misplace:  the 24" Android tablet from ViewSonic (VSD220).  It hasn't hit the market yet, but its underpowered 22" sibling has, and the reviews go something like this:  "Cool, but sluggish, and generally poor/blurry web browsing experience."

I called ViewSonic to get the scoop on whether the device supported a 90 rotation so I could run it in portrait mode.  After some checking, the support guy informed me that, sadly, no, rotation is not supported.  I'm surprised there's nothing else like it on the market right now:  this sort of device seems idea for kiosks and exhibits.

Tags:

Hardware

jQuery Sparklines + ExtJS 4

by timvasil 3/18/2013 1:11:00 AM

I wrote an Ext JS column subclass to render jQuery Sparklines with Sencha Ext JS 4 within an Ext JS grid. 

The code is on GitHub: https://github.com/timvasil/Sparkline

The demo is on jsFiddle: http://jsfiddle.net/timvasil/2gVUh/1/

Features include:

  • Compound sparklines
  • Single sparkline config for entire column
  • Separate sparkline configs for each cell in a column
  • Asynchronous sparkline rendering  (in configurable increments) to minimize lag
  • Optional override to minimize flicker on updates
  • Automatic sparkline resizing when columns are resized (for relative-width sparklines, e.g. "50%")

Tags:

JavaScript | jQuery | ExtJS

Seeing stars: simple star rating CSS

by timvasil 3/16/2013 8:52:00 AM

Taking a cue from Twitter Bootstrap icons, I cooked up the following CSS to drop in a 5-point star rating label with quarter point increments using a single HTML tag and no JavaScript. 

# starsMarkupPreview
0 stars  <i class="star"></i>  
0.25 stars  <i class="star star-qtr"></i>  
0.5 stars  <i class="star star-half"></i>  
0.75 stars  <i class="star star-3qr"></i>  
1 star  <i class="star star-1"></i>  
2 stars  <i class="star star-2"></i>  
3 stars  <i class="star star-3"></i>  
4 stars  <i class="star star-4"></i>  
4.5 stars  <i class="star star-4 star-half"></i>  
4.75 stars  <i class="star star-4 star-3qtr"></i>  
5 stars  <i class="star star-5"></i>  

This is the CSS:

i.star {
	background-image: url(../img/star-sprite.png);
	display: inline-block;
	height: 16px;
	width: 80px;
	background-position-x: -80px;
	background-position-y: -48px;
}

i.star-1    { background-position-x: -64px; }
i.star-2    { background-position-x: -48px; }
i.star-3    { background-position-x: -32px; }
i.star-4    { background-position-x: -16px; }
i.star-5    { background-position-x: 0; }
i.star-qtr  { background-position-y: -32px; }
i.star-half { background-position-y: -16px; }
i.star-3qtr { background-position-y: 0; }

Here's the image spite that goes along with it, in the spirit of the Amazon star sprite:

As I was working on implementing interactivity with these stars, so users could offer their own ratings, I realized there were a number of jQuery plugins out there that did something similar—even providing a read-only mode to accomplish something similar to what I did, including half-, quarter-, and even third-star ratings. RateIt seems to be one of the most robust ones to date.

The stars that ship with that plugin seem a rough around the edges—lacking semi-transparency for a smooth appearance—and were a bit too large, so I created a png alternative:

Before:After:

Tags:

CSS

Sectionize: IEnumerable extension method to group items

by timvasil 3/10/2013 1:57:00 PM

One pattern I've found myself repeating, especially when rendering HTML, is grouping items of an enum into "sections," where each section might require special handling -- like "<ul>" and "</ul>" tags. The trouble with doing this "manually" is that doing so yields redundant code: you have to handle section breaks within a loop, and then also terminate the last section at the end of the loop.

I wrote an extension method to IEnumerable<T> Sectionize, to encapsulate this logic. This results in cleaner, non-redundant code. To use it, specify a delegate to handle the start and end of each section, and also provide a test delegate to determine whether two adjacent items in the enum are in the same section. This approach takes advantage of some nice features of the C# language: extension methods, lambda expressions, and dynamic iterators.

public static class SectionedEnumerator { /// <summary> /// Provides a way to enumerate items by "section", where header and footer delegates /// are called before and after each section, respectively. /// </summary> /// <typeparam name="T">The type of item to enumerate</typeparam> /// <param name="items">The items to enumerate</param> /// <param name="isSameSection">Tests whether the items are in the same section.</param> /// <param name="doHeader">Callback to handle the start of a section (optional)</param> /// <param name="doFooter">Callback to handle the end of a section (optional)</param> /// <returns>Enumerable over same items, but with section handling</returns> public static IEnumerable<T> Sectionize<T>(this IEnumerable<T> items, Func<T, T, bool> isSameSection, Action<T> doHeader, Action<T> doFooter) { T lastItem = default(T); bool startedSection = false; foreach (T item in items) { if (!startedSection || !isSameSection(item, lastItem)) { if (startedSection) { if (doFooter != null) { // End last section doFooter(lastItem); } } else { startedSection = true; } if (doHeader != null) { // Start new section doHeader(item); } } yield return item; lastItem = item; } if (startedSection && doFooter != null) { // End final section doFooter(lastItem); } } }

Since this extension method returns an Enumerable<T> itself, you can chain calls to subdivide sections into subsections easily. In this example, I'm grouping a list of accounts by account category (e.g. retirement accounts) and account types (e.g. 401(k), IRA, etc.):

foreach (AccountSummary a in Accounts .Sectionize(IsSameType, null, RenderTypeFooter) .Sectionize(IsSameCategory, null, RenderCategoryFooter)) { // Render item here }

If you do chain Sectionize calls, keep in mind the calls must proceed from innermost section to outermost.

Tags:

.NET Framework | C#

Configuring Python with IIS 7.0

by timvasil 3/5/2012 11:06:00 PM

Recently I thought I'd give Python a try for web development.  Configuring it with 64-bit Windows and IIS 7.0 took a bit of trial and error, though, and I didn't find these steps comprehensively laid out anywhere on the web.  So here goes:

  1. Download the 32-bit version of Python 2.6 here.  To work with the Python ISAPI filter, it's important you choose the 32-bit variant of the 2.6 version.  I accepted all defaults during the installation.
  2. Download Python ISAPI Extension for IIS 2.6 (pyisapie) here.  I unzipped it into a subfolder of C:\Python2.6
  3. Create an environment variable called PYTHONHOME and set it to where you installed Python, e.g. C:\Python2.6.
  4. Restart Windows.  Since IIS inherits its environment block from services.exe, which cannot be restarted, you need to reboot so the ISAPI filter sees the PYTHONHOME variable.
  5. Configure IIS to use the ISAPI extension:
    1. Create a 32-bit application pool (Advanced Settings > Enable 32-Bit Applications = True)
    2. Create a web site (or application) and configure it to use the application pool you just created
    3. Add a "Script Map" handler mapping with a request path of "*.py", an executable location of the Python ISAPI DLL (e.g. C:\Python26\PyISAPIe-1.1.0-rc4-Py2.6\PyISAPIe.dll), and any name you'd like.  (Accepting Request Restriction defaults is fine.)
  6. In the home directory of your website, create a Python script to test your handiwork, e.g. a file named page.py with this code:
     
    from Http import *
    def Request():
        Header("Content-type: text/html")
        Write("Hello, World!")
      
  7. Visit the URL (e.g. http://website/page.py); if all is well, the message "Hello, world!" appears!

Tags:

IIS | ISAPI | Python

Creating a "favicon" for your website or web app

by timvasil 5/17/2011 11:18:00 PM

Dynamic Drive has a great tool for converting images to "favicon" icon files that appear in the tab/address bar of browsers. Simply upload the desired image (such as a PNG with alpha channel), and you can see the results in a simulated address bar and download a Windows-compliant .ico file.  Nice!

You can find it here: http://tools.dynamicdrive.com/favicon/.

Here's a preview of the interface with an icon I've created for use with collabormate.com:

Tags:

Graphic design

CSS Reset

by timvasil 5/16/2011 9:03:00 PM

Here are a few simple CSS rules to "reset" various browsers' settings to a common baseline.  I've adapted this from what I spotted on Kyle Neath's blog here: http://warpspire.com/posts/css-frameworks/, who did a nice job at concise definitions (compared with what I've seen from the YUI toolkit and elsewhere).

* { padding:0; margin:0; }

h1, h2, h3, h4, h5, h6, p, pre, blockquote, label, ul, ol, dl, fieldset, address { margin:1em 0; }

li, dd { margin-left:5%; }

fieldset { padding: .5em; }

select option{ padding:0 5px; }

.hide { display:none; }

.left { float:left; }

.right { float:right; }

.clear { clear:both; height:1px; font-size:1px; line-height:1px; }

a img { border:none; }

Tags:

CSS

Search

Calendar

«  June 2013  »
SuMoTuWeThFrSa
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

View posts in large calendar

Recent comments

Archive