Yacine's

JMVC + JQM improved dispatch

In my previous example, I used an eval to load a page controller from the jQuery.fn  name space. Here is a much better version of that code which has the added benefit of allowing access to the instance of my controller:


If ( typeof MyApp.Pages[pageName] === 'function') {
    // don't really need the element passed here per this example's controller, will check if needed.
    pageInstance  = new MyApp.Pages[pageName]($('body'));
    pageInstance.view.done( function ( htmlOutput ) {
        $('body').append(htmlOutput);
        $('#' + pageName).page();
        $.mobile.changePage( $('#' + pageName) );
    }

On the controller side, the init method becomes much simpler than I had originally written:


/**
 * @class MyApp.Pages.Frontpage
 */
$.Controller('MyApp.Pages.Frontpage',
    /** @Static */
    {
        defaults : {
            itemsPerPage : 20,
            currentPage : 1
        },
        pluginName: 'frontpage'
    },
    /** @Prototype */
    {
        /**
         * Downloads and renders a full page of blog posts
         */
        init : function() {
            this.view = $.View("my_app/pages/views/frontpage.ejs", {
                items: MyApp.Models.BlogPost.findAll({
                    start : (this.options.currentPage-1)
                            * this.options.itemsPerPage,
                    count : this.options.itemsPerPage
                })
            });
        }
    }
); // End of controller

My full bootstrap method now looks like this:


function() {
    // intercept changePage events to inject our controllers as needed (dispatch)
    $(document).bind( "pagebeforechange", function( e, data ) {
        // we only handle url changes
        if ( typeof data.toPage === "string" ) {
            // We are being asked to load a page by URL
            var u = $.mobile.path.parseUrl( data.toPage ),
                re = /^\#([a-zA-Z]+)$/; // TODO $.route

            if (u.hash.search(re) !== -1 && $(u.hash).length == 0) {
                var pageName = re.exec(u.hash);
                pageName = pageName[1];
                if(pageName === 'splah') {
                    return false;
                }
                // ucwords - ignore as it will be replaced by using $.route
                controllerName = pageName.replace(/^([a-z])|\s+([a-z])/g, function (str) {
                    return str.toUpperCase();
                });
                if (typeof MyApp.Pages[controllerName] === 'function') {
                    var $instance = new MyApp.Pages[controllerName]($('body'));
                    $instance.view.done(function (htmlOutput){
                        // add the rendered output to the body (could also be done in the controllers instead)
                        $('body').append(htmlOutput);
                        $('#' + pageName).page(); // jQuery Mobile magic decoration
                        // passing an object skips the dispatch and just shows the page
                        $.mobile.changePage($('#' + pageName));
                    });
                    e.preventDefault();
                }
            }
        }
    });
    $('#splash').fadeIn(500);
    setTimeout(function () {
        $.mobile.changePage('#frontpage');
    }, 500);
});

I’ll flesh out the full updated integration when I get home, but i am now close to my ideal setup and my full app is working as expected from start to finish. Among my recent changes are the use of partials, routes, pagination, API service auth (with caveats) and of course testing and documentation.

Share

PhoneGap + JavascriptMVC production build

Once you build your app and produce production.js and production.css (hopefully through an ant script like mine), you may notice that your app does not run once deployed with PhoneGap. This is because it seems the recommended way of bootstrapping your app in JMVC uses the script’s URI to specify the app name: “../steal/steal.production.js?myappname” which PhoneGap does not appear to support.

The simple workaround is to resort to this notation instead:


<script src="../steal/steal.production.js"></script>
<script>
    steal('myappname/production.js').then('myappname/myappname.js');
</script>

Now, your deployed assets will not contain myappname.js since you already built it into production.js. Once you load production.js you have essentially loaded all the javascript you told steal.build to package for you. Unfortunately, you need to remember that the build refers to your files from the perspective of the folder directly above your app, which means you need to load ‘myappname/myappname.js’ instead of ‘./myappname.js’.

These are minor issues and once you figure out the gotchas, it should be smooth sailing from there. I have divided my html into 2 files, one for development, and one for production. My Ant script now picks the right html to include as PhoneGap’s index.html depending on what I am building.

Share

Javascript MVC + PhoneGap + jQm Build with Ant

In an effort to streamline the build of mobile applications, I made an ant build file to automate some of the tasks you would normally have to do to package your JMVC webapp and deploy it into an android project.

Here is the interesting parts of my build.xml file:


    <target name="build" description="Builds the app for production"  depends="">
        <exec executable="./js" dir="${src}/.." vmlauncher="no">
           <arg line="${src}/scripts/build.js" />
        </exec>
        <copy todir="${build}/steal">
            <fileset dir="${src}/../steal"/>
        </copy>
        <copy todir="${build}/${appName}/images">
            <fileset dir="${src}/images"/>
        </copy>
        <copy file="${src}/${appName}.html" tofile="${build}/${appName}/index.html"/>
        <replace file="${build}/${appName}/index.html" token="steal/steal.js" value="steal/steal.production.js" />
        <move file="${src}/production.css" tofile="${build}/${appName}/production.css"/>
        <move file="${src}/production.js" tofile="${build}/${appName}/production.js"/>
    </target>

I also have the clean phonegap android projects inside a folder called platforms/Android under my JMVC app folder. This project was updated to generate an ant build.xml file using this command from within the project folder:


android update project -p .

Finally I have a task that makes a copy of that Android project into my build folder copies the JMVC build into its assets/www folder and starts building the mobile app:


    <target name="android" description="Invokes a build on the android platform" depends="build">
        <copy todir="${build}/Android">
            <fileset dir="${src}/platforms/Android"/>
        </copy>
        <copy todir="${build}/Android/assets/www/steal">
            <fileset dir="${build}/steal"/>
        </copy>
        <copy todir="${build}/Android/assets/www/${appName}">
            <fileset dir="${build}/${appName}"/>
        </copy>
        <ant antfile="${build}/Android/build.xml" dir="${build}/Android" target="debug">
        </ant>
    </target>

This is not rocket science, but I thought it may be helpful to someone just starting out with a similar project.

Share

PhoneGap + JavascriptMVC + jQuery Mobile

I started a new mobile app project and decided to go with HTML5 wrapped with PhoneGap. The reasons are many and this post isn’t about why, but how.

Before you start, here are the libraries/frameworks I am using:
jQuery Mobile – a “Touch-Optimized Web Framework for Smartphones & Tablets”
Javascript MVC – a Javascript MVC Framework (and more)
PhoneGap – “HTML5 app platform”/embedded cross platform browser

I will be posting code snippets as I go along that may be helpful to someone just starting out with a similar project.

Here is my directory structure so far:

  • blocks
    • controllers
    • views
    • blocks.js
  • pages
    • controllers
    • views
  • models
    • models.js
    • blog_post.js
  • css
  • images
  • platforms // platform projects (iOS XCode project, Android eclipse project, etc.)
  • myapp.js
  • myapp.html
  • build.xml

I also chose to go with EJS for the templating system which allows my view to look very similar to a PHP/Zend Framework view:


<%== blocks('header') %>
<div data-role="content" id="frontpageContent">
    <ul data-role="listview" data-theme="c">
        <% if (items) for(var i=0; i < items.length; i++) { %>
            <li data-icon="tcc2-arrow-right">
                <a href="#">
                    <img src="<%= items[i].media.m %>"/>
                    <h3><%= items[i].title %></h3>
                </a>
            </li>
        <% } %>
    </ul>
</div>
<%== blocks('footer') %>

The first piece of code that makes life a little easier on large projects is my blocks view helper. This way I can modularize and reuse:


    $.EJS.Helpers.prototype.blocks = function (blockName) {
        if (typeof MyApp.Blocks[blockName] === 'function') {
            blockInstance = new MyApp.Blocks[blockName]($('<div/>'));
            return blockInstance.view;
        }
        throw "No such block controller MyApp.Blocks." + blockName;
    }

You could also just call

<%== $.View('someview.ejs') %>
if you have no need for a controller. Block controllers look like this:

$.Controller('MyApp.Blocks.Header',
    /** @Static */
    {
        defaults: {},
        pluginName: 'header'
    },
    /** @Prototype */
    {
        init : function(){
            this.view = $.View("my_app/blocks/views/header.ejs",{});
        }
    }
); // Header controller

Now on to more substantial jQuery Mobile related code. We need jqm navigation to tie into our MVC controllers and vice versa. In other words, when we navigate to #frontpage, we want a new page injected into our dom using the frontpage controller, which in turns will render a view (frontpage.ejs, or init.ejs if you stick to the generated naming).

The answer to this problem comes straight from the jQm manual:
From myapp.js (or whatever you called your app)


steal(
    'phonegap-1.3.0.js',
    'jquery/jquery.js',
    './css/jquery.mobile.structure-1.0.min.css' // jQuery Mobile structure CSS
    )
.then(
    './css/global.css', // Our global css style and jqm theme
    'jquery/controller',
    'jquery/view/ejs'
    )
.then(
    './models/models.js', // all models
    './pages/pages.js', // all pages
    './blocks/blocks.js', // all blocks and Blocks view helper
     function () { // Init some jqm settings and fadeIn splash page
        $(document).bind("mobileinit", function(){
            $.mobile.touchOverflowEnabled = true;
            $.mobile.allowCrossDomainPages = true;
        });
    })
.then('//library/jquery.mobile-1.0.min.js') // jQuery Mobile library
.then(
    // Run the frontpage controller
    function() {
        $('#splash').fadeIn(500);
        // intercept changePage events to inject our controllers as needed (dispatch)
        $(document).bind( "pagebeforechange", function( e, data ) {
            // we only handle url changes
            if ( typeof data.toPage === "string" ) {
                // We are being asked to load a page by URL
                var u = $.mobile.path.parseUrl( data.toPage ),
                    re = /^\#([a-zA-Z]+)$/;

                if (u.hash.search(re) !== -1 ) {
                    var $page = re.exec(u.hash);
                    $page = $page[1];
                    console.log('Loading page ' + $page);
                    // We're being asked to display the items for a specific page
                    // Call our internal controller
                    if (typeof MyApp.Pages[$page] === 'function') {
                        var controller = new MyApp.Pages[$page]( $('body') );
                        controller.view.done(function (htmlOutput) {
                            $(htmlOutput).appendTo( $('body') ).page();
                            $.mobile.changePage( $(htmlOutput) );
                        });
                    }
                }
            }
        });
        setTimeout(function () {
            $.mobile.changePage('#frontpage');
        }, 500);
    });

This is of course a very early draft, but I hope it is enough to fill in the gaps between the docs of JMVC and jQm to get someone started. There is much you need to add to make this nicer. For example you could have an routes object defining each hash and params and which controller to send them to, then use that during the event above (look at jquery page params for adding hash params to jQuery Mobile).

The company’s strategy seems to be working and
chanel espadrilles guide people to buy Timberland boots as a new prepare for winter

money and self
valentinoChinese Gold Moves Set Stage for Price Spike
Share

Macbook battery woes

My Macbook’s battery is finally dead, which means the book shuts down immediately if I unplug it from power. I accidentally pulled on the power cable yesterday, and as a result my usually rock solid Leopard OS started showing me instability of applications, and eventually the dreaded screen of death.

Luckily, I have time machine running and backing up everything. I rebooted on the OSX install disk, ran a repair disk through the Disk Utility, then restored a couple of the Application Support folders under my user folder that I found to have been corrupted (mostly stuff I had running at the time of the crash: Firefox, Mail, VLC, iTunes, etc.)

I am glad time machine worked as advertised. My last backup was done at noon, and the crash at 2PM so I haven’t lost anything.

Share

Why The FCC Wants To Smash Open The iPhone

Apparently, the FCC is asking Apple, AT&T and Google some questions as part of an investigation into the recent rejection by Apple of the Google Voice app. The FCC wants to know if AT&T had a hand in that rejection, and why Apple rejected it. See the full story at the Washington Post below:

Why The FCC Wants To Smash Open The iPhone.

Share

Google Voice app GV Mobile ported to jailbroken iPhones, web app version in the works

The google voice app was rejected by Apple’s app store. It quickly went underground as a result driving more people like me to jailbreak their iPhones. <rant> I don’t understand this childish paranoia on the part of Apple and AT&T. I am pretty much disillusioned with the whole Appstore approval process. The Skype app for example was castrated to work only on Wifi. After I gave so much of my hard earned money to ATT and Apple, I need them both to take a humility pill and stop playing these games with app developers. </rant>

Google Voice app GV Mobile ported to jailbroken iPhones, web app version in the works.

Share

Is Digg down?

I can’t seem to get anything out of digg.com for almost 10 minutes. Life goes on and all, but I am wondering if its just me or if other people out there are having the same problem?

Share

Thruthful feature list

Found this on NewEgg: “LCD Features: Glare”. Wow, I’ve always wanted that feature!!!

NewEgg MSI X320

Share

Cheap IT to start a business (Part 2)

Friends ask me all the time: how much would it cost to start a business from an IT perspective, as in get a website, get email, phone numbers, an ERP system, a CRM system, some basic automation, etc. This makes non-techies very nervous when just starting out on the cheap. Word on the street is that these things cost millions. Interestingly enough, most people don’t know that they can do most of that expensive stuff for next to nothing, and a little elbow grease.

This article is not meant as an in depth how-to but more as an example of what you can get done with a little knowledge when just starting out.

Click here if you’ve missed Part 1 of this article

Read the full article »

Share

Cheap IT to start a business (Part 1)

Friends ask me all the time: how much would it cost to start a business from an IT perspective, as in get a website, get email, phone numbers, an ERP system, a CRM system, some basic automation, etc. This makes non-techies very nervous when just starting out on the cheap. Word on the street is that these things cost millions. Interestingly enough, most people don’t know that they can do most of that expensive stuff for next to nothing, and a little elbow grease.

This article is not meant as an in depth how-to but more as an example of what you can get done with a little knowledge when just starting out.

Read the full article »

Share

Burney Falls Video

This is another video I took of Burney Falls. Taken with my Panasonic Lumix DMC-LX2 at 1280×720, 15fps.

Get the Flash Player to see this content.

Share

Santa Cruz Natural Bridges

Nice day by the tide pools in Santa Cruz’s natural bridges beach. Took this with my Panasonic Lumix DMC-LX2 at 1280×720 (original looks better, flash encoding still looks ok though)

Get the Flash Player to see this content.

Share

San Mateo Kite Surfing

A Video taken from my iPhone of Kite Surfers in San Mateo, off of 3rd Av.

Get the Flash Player to see this content.

Share

Burney falls

This is a picture taken at burney falls. It was gorgeous and a great stop.

Share