/*global window: false, $: false, pageTracker: false, kanso: true */

/**
 * Code required to bootstrap the browser CommonJS environment.
 */

(function (exports) {

    exports.moduleCache = {};

    exports.normalizePath = function (p) {
        var path = [];
        var parts = p.split('/');
        for (var i = 0; i < parts.length; i += 1) {
            if (parts[i] === '..') {
                path.pop();
            }
            else if (parts[i] !== '.') {
                path.push(parts[i]);
            }
        }
        return path.join('/');
    };

    exports.dirname = function (p) {
        if (p === '/') {
            return p;
        }
        var parts = p.split('/');
        parts.pop();
        if (parts.length === 1 && parts[0] === '') {
            return '/';
        }
        return parts.join('/');
    };

    exports.createRequire = function (current) {
        return function (target) {
            var path;
            if (target.charAt(0) === '.') {
                var dir = exports.dirname(current);
                path = exports.normalizePath(dir + '/' + target);
            }
            else {
                path = exports.normalizePath(target);
            }
            var m = kanso.moduleCache[path];
            if (!m) {
                throw new Error('No such module: ' + path);
            }
            if (!m.loaded) {
                m.exports = {};
                m.id = path;
                // TODO: property not provided by couchdb, but is by node:
                //m.require = exports.createRequire(path);
                // TODO: property not provided by couchdb, but is by node:
                //m.filename = '';
                // TODO: module properties provided by couchdb, but not by kanso
                // * current
                // * parent
                // set this to true *before* calling m.load so circular
                // requires don't blow the call stack
                m.loaded = true;
                //m.load(m, m.exports, m.require);
                m.load(m, m.exports, exports.createRequire(path));
            }
            return m.exports;
        };
    };

    if (typeof require === 'undefined') {
        // make require available globally, unless already in a commonjs
        // environment
        this.require = exports.createRequire('');
    }

}((typeof exports === 'undefined') ? this.kanso = {}: module.exports));


/**
 * CommonJS modules are wrapped and appended to this file.
 */
/********** lib/app **********/

kanso.moduleCache["lib/app"] = {load: (function (module, exports, require) {

module.exports = {
    app_settings: require('./app_settings'),
    rewrites: require('./rewrites'),
    lists: require('./lists'),
    shows: require('./shows'),
    views: require('./views'),
    validate_doc_update: require('./validate')
};




})};

/********** lib/app_settings **********/

kanso.moduleCache["lib/app_settings"] = {load: (function (module, exports, require) {

module.exports = {
    use_garden_market_logo : true,
    market_name: "App Garden",
    use_google_analytics : true,
    google_analytics_code : "UA-31457059-1",
    admin_review_apps: false
};

})};

/********** lib/dashboards **********/

kanso.moduleCache["lib/dashboards"] = {load: (function (module, exports, require) {

var url = require('url'),
    path = require('path'),
    cookies = require('cookies'),
    _ = require('underscore')._;


var  dutils = {
    getBaseURL : function(req) {
        return '.';
    }
}

exports.default_url = 'http://localhost:5984/dashboard/_design/dashboard/_rewrite/';
exports._dashboard_urls = [];


exports.getURLs = function () {
    return _.uniq(exports._dashboard_urls);
};

exports.detectLocal = function () {
    if (_.indexOf(exports._dashboard_urls, exports.default_url) === -1) {
        $.ajax({
            url: exports.default_url + '_info?callback=?',
            dataType: 'json',
            jsonp: true,
            success: function (data) {
                if (data.dashboard) {
                    exports.add(exports.default_url);
                }
            }
        });
    }
};

exports.readCookie = function () {
    var value = cookies.readCookie('_dashboard_urls');
    if (value) {
        var durls = JSON.parse(unescape(value));
        exports._dashboard_urls = _.uniq(exports._dashboard_urls.concat(durls));
    }
};

exports.updateCookie = function () {
    cookies.setBrowserCookie(null, {
        name: '_dashboard_urls',
        value: JSON.stringify(exports._dashboard_urls),
        path: '/'
    });
};

exports.add = function (url) {
    exports._dashboard_urls.unshift(url);
    exports._dashboard_urls = _.uniq(exports._dashboard_urls);
    exports.updateCookie();
};

exports.init = function () {
    exports.readCookie();
    exports.detectLocal();
    // detect 'dashboard' param in URL
    var parts = url.parse(window.location.toString(), true);
    if (parts.query && parts.query.dashboard) {
        exports.add(parts.query.dashboard);
    }
};

exports.installURL = function (dashboard_url, app_url) {
    var parts = url.parse(dashboard_url, true);
    parts.pathname = path.join(parts.pathname, 'install');
    parts.query.app_url = app_url;
    return url.format(parts);
};

exports.friendlyName = function(dashboard_url) {
    var details = url.parse(dashboard_url);
    if (details.hostname === '0.0.0.0' || details.hostname === '127.0.0.1' || details.hostname === 'localhost') {
        return 'Your Computer';
    }
    return details.hostname;
}


exports.moveToTop = function (url) {
    exports._dashboard_urls = _.without(exports._dashboard_urls, url)
    exports.add(url);
};

exports.checkDashboardForKnownProviders = function(dashboard_url, known_providers) {
    if (!known_providers) return null;
    var d_host = url.parse(dashboard_url).host;
    var provider = null;
    _.each(known_providers, function(p){
        if (!p.rootURL) return;
        var parts = url.parse(p.rootURL);
        if (d_host.indexOf(parts.host) > 0) provider = p;
    });
    return provider;
}

})};

/********** lib/lists **********/

kanso.moduleCache["lib/lists"] = {load: (function (module, exports, require) {

var templates = require('handlebars').templates,
    utils = require('./utils'),
    shows = require('./shows'),
    ui = require('./ui'),
    jsonp = require('jsonp'),
    datelib = require('datelib'),
    flattr = require('flattr'),
    _ = require('underscore')._,
    url = require('url');


exports.home = function (head, req) {




    start({code: 200, headers: {'Content-Type': 'text/html'}});



        var row, rows = [];
        while (row = getRow()) {
            var promo_images = row.value.config.promo_images || {};
            row.promo_image = promo_images.small;
            row.short_description = utils.truncateParagraph(
                row.value.config.description,
                120
            );
            row.title = utils.app_title(row.value.config);
            rows.push(row);
        }

        utils.show( {
            title: 'App Garden',
            app_settings : this.app_settings,
            baseURL : '',
            db_name : req.info.db_name,
            content: templates['home.html']({
                app_settings : this.app_settings,
                rows: rows,
                baseURL : ''
            })
        });
};

exports.category_page = function (head, req) {
    start({code: 200, headers: {'Content-Type': 'text/html'}});

    var row, rows = [];
    while (row = getRow()) {
        var promo_images = row.value.config.promo_images || {};
        row.promo_image = promo_images.small;
        row.short_description = utils.truncateParagraph(
            row.value.config.description,
            120
        );
        row.title = utils.app_title(row.value.config);
        rows.push(row);
    }

    utils.show({
        title: req.query.category,
        baseURL : '../',
        app_settings : this.app_settings,
        content: templates['category_page.html']({
            rows: rows,
            app_settings : this.app_settings,
            title: utils.toSentenceCase(req.query.category),
            category: req.query.category,
            baseURL : '../'
        })
    });

};


exports.app_details = function (head, req) {
    start({code: 200, headers: {'Content-Type': 'text/html'}});

    var row, rows = [];
    while (row = getRow()) {
        rows.push(row);
    }

    var id = rows[0].id;
    var meta = rows[0].value;




    if (!rows.length) {
        return shows.not_found(null, req);
    }
    var ldesc = meta.config.long_description;
    var title = utils.app_title(meta.config);
    var flattr_link = null;
    if (flattr.hasFlattr(meta)) {
        var flattr_details = flattr.createFlattrDetailsFromKanso(meta, utils.flattr_url(req, id));
        flattr_link = flattr.generateFlatterLinkHtml(flattr_details);
    }

    var cfg = meta.config;
    var screenshots = cfg.screenshots.map(function (s) {
        return {src: '../_db/' + id + '/' + s};
    });
    screenshots[0].active = true;

    var onload = '';
    if (meta.config.url) {
        var details = url.parse(meta.config.url);
        if (details.hostname === 'github.com') {
            var git_commit = '';
            if (meta.git && meta.git.commit) {
                git_commit = meta.git.commit;
            }

            onload = 'ui.check_github("' +  details.pathname.substr(1) + '", "'+ git_commit +'")';
        }
    }

    utils.show ({
        title: 'App: ' + title,
        baseURL : '../',
        app_settings : this.app_settings,
        onload : onload,
        content: templates['app_details.html']({
            baseURL : '../',
            app_settings : this.app_settings,
            meta: meta,
            id: id,
            title: title,
            long_description_paragraphs: ldesc.split('\n\n'),
            screenshots : screenshots,
            screenshots_many : (screenshots.length > 1),
            icon_96: meta.config.icons['96'],
            updated: datelib.prettify(meta.push_time),
            install_script_url: utils.install_script_url(req, id),
            flattr_link : flattr_link
        })
    });

};

exports.app_details_install = function (head, req) {
    start({code: 200, headers: {'Content-Type': 'text/html'}});

    var row, rows = [];
    while (row = getRow()) {
        rows.push(row);
    }

    var id = rows[0].id;
    var meta = rows[0].value;


    if (!rows.length) {
        return shows.not_found(null, req);
    }

    var title = utils.app_title(meta.config);


    utils.show ({
        title: 'App: ' + title,
        baseURL : '../../',
        app_settings : this.app_settings,
        onload : 'ui.showInstallChoices();',
        content: templates['app_details_install.html']({
            baseURL : '../../',
            app_settings : this.app_settings,
            meta: meta,
            id: id,
            title: title,
            icon_96: meta.config.icons['96'],
            updated: datelib.prettify(meta.push_time),
            install_script_url: utils.install_script_url(req, id)
        })
    });


};
exports.app_details_install_couch = function (head, req) {
    start({code: 200, headers: {'Content-Type': 'text/html'}});

    var row, rows = [];
    while (row = getRow()) {
        rows.push(row);
    }

    var id = rows[0].id;
    var meta = rows[0].value;


    if (!rows.length) {
        return shows.not_found(null, req);
    }

    var title = utils.app_title(meta.config);


    utils.show({
        title: 'App: ' + title,
        app_settings : this.app_settings,
        baseURL : '../../',
        onload: 'ui.couch_install_page()',
        content: templates['app_details_manual_install.html']({
            meta: meta,
            app_settings : this.app_settings,
            id: id,
            title: title,
            baseURL : '../../',
            icon_96: meta.config.icons['96'],
            updated: datelib.prettify(meta.push_time),
            install_script_url: utils.install_script_url(req, id)
        })
    });

};
exports.user_page = function (head, req) {
    start({code: 200, headers: {'Content-Type': 'text/html'}});

    var row, rows = [];
    while (row = getRow()) {
        var promo_images = row.value.config.promo_images || {};
        row.promo_image = promo_images.small;
        row.short_description = utils.truncateParagraph(
            row.value.config.description,
            120
        );
        row.title = utils.app_title(row.value.config);
        rows.push(row);
    }
    utils.show({
        title: req.query.user,
        app_settings : this.app_settings,
        baseURL : '../',
        content: templates['user_page.html']({
            rows: rows,
            app_settings : this.app_settings,
            title: utils.toSentenceCase(req.query.user),
            user: req.query.user,
            baseURL : '../'
        })
    });

};

exports.app_versions = function(head, req) {
    var row = [];
    var result = {};
    while (row = getRow()) {
        var version;
        if (row.value.config && row.value.config.version) {
            result[row.id] = row.value.config.version;
        }
        else if (row.value.version) {
            result[row.id] = row.value.version;
        }
    }
    var result =  jsonp.response(req.query.callback, result);
    start({code: result.code, headers: result.headers});
    send(result.body);
}


exports.intersection = function (head, req) {

    var extraKeys = [];
    if (req.query.key) {
        extraKeys.push(req.query.key);
    }
    if (req.query.extra_keys) {
        var split = req.query.extra_keys.split(' ');
        extraKeys = extraKeys.concat(split);
    }

    extraKeys = _.uniq(_.map(extraKeys, function(key) {return key.toLowerCase()}));

    var realJson = true;
    if (req.query.streamJson) {
        realJson = false;
    }

    start({'headers' : {'Content-Type' : 'application/json'}});
    if (realJson) send('[\n');
    var count = 0;
    var row;
    while ((row = getRow())) {

        var doc_intersection = _.intersection(row.value.keywords, extraKeys);
        if (doc_intersection.length == extraKeys.length) {
            var pre = '';
            if (count++ > 0 && realJson) pre = ',';
            send(pre + JSON.stringify(row.value) + '\n');
        }
    }
    if (realJson) send(']');
}

})};

/********** lib/popup **********/

kanso.moduleCache["lib/popup"] = {load: (function (module, exports, require) {

var templates = require('handlebars').templates;


var id_counter = 0;

exports.resize = function (el, options) {
    $('.note', el).css({
        width: options.width + 'px',
        height: (options.height - 20) + 'px',
        marginLeft: (0 - options.width/2) + 'px',
        marginTop: (0 - options.height/2 + 20) + 'px'
    });
    $('.note-top', el).css({
        width: (options.width - 19) + 'px',
        marginLeft: (0 - options.width/2) + 'px',
        marginTop: (0 - options.height/2) + 'px'
    });
    $('.note-corner', el).css({
        marginLeft: (options.width/2 - 19) + 'px',
        marginTop: (0 - options.height/2 + 1) + 'px'
    });
    $('.note-corner-border', el).css({
        marginLeft: (options.width/2 - 18) + 'px',
        marginTop: (0 - options.height/2) + 'px'
    });
    $('.note-inner', el).css({
        height: (options.height - 20) + 'px'
    });
    $('.note-actions', el).css({
        width: (options.width - 20) + 'px'
    });
};

exports.open = function (req, options) {
    if (!options) {
        throw new Error("missing options");
    }
    if (!options.width) {
        throw new Error("missing options.width");
    }
    if (!options.height) {
        throw new Error("missing options.height");
    }
    var el = $(templates.render('popup.html', req, {
        options: options
    }));
    el.attr('id', 'popup' + id_counter);
    id_counter++;
    $('.closebtn', el).click(function (ev) {
        ev.preventDefault();
        exports.close(el);
        return false;
    });
    exports.resize(el, options);
    $('.note-inner', el).html(options.content || '');
    $('body').append(el);
    return el;
};

exports.close = function (el) {
    el.remove();
};

exports.closeAll = function () {
    $('.note-container').remove();
};


})};

/********** lib/rewrites **********/

kanso.moduleCache["lib/rewrites"] = {load: (function (module, exports, require) {

module.exports = [
    // fake the db-level api for pushing design docs,
    // stripping the leading '_design/' part of the id
    {from: '/upload/_design/:name', to: '../../:name'},
    {from: '/upload/:name', to: '../../:name'},
    {from: '/upload', to: '../..'},

    {from: '/', to: '_list/home/apps'},
    {from: '/all', to: '_list/category_page/apps', query: {category: 'all'}},
    {
        from: '/category/:category',
        to: '_list/category_page/apps_by_category', query: {
            startkey: [':category'],
            endkey: [':category', {}],
            reduce: 'false',
            sort: 'alphabetical'
        }
    },
    {
        from: '/user/:user',
        to: '_list/user_page/apps_by_user', query: {
            startkey: [':user'],
            endkey: [':user', {}],
            sort: 'alphabetical'
        }
    },

    {
        from: '/_db/_design/garden/*',
        to : '*'
    },


    {from: '/upload_app', to: '_show/upload_app'},
    {from: '/details/:name', to: '_list/app_details/apps', query: {
        key: [':name']
    }},
    {from: '/details/:name/install', to: '_list/app_details_install/apps', query: {
        key: [':name']
    }},
    {from: '/details/:name/couch_install', to: '_list/app_details_install_couch/apps', query: {
        key: [':name']
    }},
    {from: '/details/:name/install.sh', to: '_show/install_script/:name'},
    {from: '/details/:name/json', to: '_show/kanso_details/:name'},
    {from: '/details/:name/ddoc', to: '../../:name'},
    {from: '/search', to: '_show/search' },
    {from: '/_search/:key', to: '_list/intersection/keyword_search',  query: { reduce: 'false', key: ':key' } },
    {from: '/_search/:key/:extra_keys', to: '_list/intersection/keyword_search',  query: { reduce: 'false', key: ':key', extra_keys : ':extra_keys' } },
    {from: '/static/*', to: 'static/*'},
    {from: '/modules.js', to: 'modules.js'},
    {from: '/_db/*', to : '../../*'},
    {from: '/_db', to : '../../'},
    {from: '*', to: '_show/not_found'}
];


})};

/********** lib/showdown-wiki **********/

kanso.moduleCache["lib/showdown-wiki"] = {load: (function (module, exports, require) {

/******************************
 * NOTE:
 *
 * This has been edited to avoid embedded HTML and add support for fenced code
 * blocks, wiki links etc
 *
 *****************************/

var duality_utils = require('duality/utils'),
    sanitize = require('sanitize'),
    _ = require('underscore')._;


//
// showdown.js -- A javascript port of Markdown.
//
// Copyright (c) 2007 John Fraser.
//
// Original Markdown Copyright (c) 2004-2005 John Gruber
//   <http://daringfireball.net/projects/markdown/>
//
// Redistributable under a BSD-style open source license.
// See license.txt for more information.
//
// The full source distribution is at:
//
//              A A L
//              T C A
//              T K B
//
//   <http://www.attacklab.net/>
//

//
// Wherever possible, Showdown is a straight, line-by-line port
// of the Perl version of Markdown.
//
// This is not a normal parser design; it's basically just a
// series of string substitutions.  It's hard to read and
// maintain this way,  but keeping Showdown close to the original
// design makes it easier to port new features.
//
// More importantly, Showdown behaves like markdown.pl in most
// edge cases.  So web applications can do client-side preview
// in Javascript, and then build identical HTML on the server.
//
// This port needs the new RegExp functionality of ECMA 262,
// 3rd Edition (i.e. Javascript 1.5).  Most modern web browsers
// should do fine.  Even with the new regular expression features,
// We do a lot of work to emulate Perl's regex functionality.
// The tricky changes in this file mostly have the "attacklab:"
// label.  Major or self-explanatory changes don't.
//
// Smart diff tools like Araxis Merge will be able to match up
// this file with markdown.pl in a useful way.  A little tweaking
// helps: in a copy of markdown.pl, replace "#" with "//" and
// replace "$text" with "text".  Be sure to ignore whitespace
// and line endings.
//


//
// Showdown usage:
//
//   var text = "Markdown *rocks*.";
//
//   var converter = new Showdown.converter();
//   var html = converter.makeHtml(text);
//
//   alert(html);
//
// Note: move the sample code to the bottom of this
// file before uncommenting it.
//


//
// Showdown namespace
//
var Showdown = {};

//
// converter
//
// Wraps all "globals" so that the only thing
// exposed is makeHtml().
//
Showdown.converter = function() {

//
// Globals:
//

// Global hashes, used by various utility routines
var g_urls;
var g_titles;
var g_html_blocks;

// Used to track when we're inside an ordered or unordered list
// (see _ProcessListItems() for details):
var g_list_level = 0;


this.makeHtml = function(text) {
//
// Main function. The order in which other subs are called here is
// essential. Link and image substitutions need to happen before
// _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>
// and <img> tags get encoded.
//

    // Clear the global hashes. If we don't clear these, you get conflicts
    // from other articles when generating a page which contains more than
    // one article (e.g. an index page that shows the N most recent
    // articles):
    g_urls = new Array();
    g_titles = new Array();
    g_html_blocks = new Array();

    // attacklab: Replace ~ with ~T
    // This lets us use tilde as an escape char to avoid md5 hashes
    // The choice of character is arbitray; anything that isn't
    // magic in Markdown will work.
    text = text.replace(/~/g,"~T");

    // attacklab: Replace $ with ~D
    // RegExp interprets $ as a special character
    // when it's in a replacement string
    text = text.replace(/\$/g,"~D");

    // Standardize line endings
    text = text.replace(/\r\n/g,"\n"); // DOS to Unix
    text = text.replace(/\r/g,"\n"); // Mac to Unix

    // Make sure text begins and ends with a couple of newlines:
    text = "\n\n" + text + "\n\n";

    // Convert all tabs to spaces.
    text = _Detab(text);

    // Strip any lines consisting only of spaces and tabs.
    // This makes subsequent regexen easier to write, because we can
    // match consecutive blank lines with /\n+/ instead of something
    // contorted like /[ \t]*\n+/ .
    text = text.replace(/^[ \t]+$/mg,"");

    // Turn block-level HTML blocks into hash entries
    text = _HashHTMLBlocks(text);

    // Strip link definitions, store in hashes.
    text = _StripLinkDefinitions(text);

    text = _RunBlockGamut(text);

    text = _UnescapeSpecialChars(text);

    // attacklab: Restore dollar signs
    text = text.replace(/~D/g,"$$");

    // attacklab: Restore tildes
    text = text.replace(/~T/g,"~");

    // ** GFM **  Auto-link URLs and emails
    text = text.replace(/https?\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!]/g, function(wholeMatch,matchIndex){
        var left = text.slice(0, matchIndex), right = text.slice(matchIndex)
        if (left.match(/<[^>]+$/) && right.match(/^[^>]*>/)) {return wholeMatch}
        //href = wholeMatch.replace(/^http:\/\/github.com\//, "https://github.com/")
        //return "<a href='" + href + "'>" + wholeMatch + "</a>";
        return "<a href='" + wholeMatch + "'>" + wholeMatch + "</a>";
    });
    text = text.replace(/[a-z0-9_\-+=.]+@[a-z0-9\-]+(\.[a-z0-9-]+)+/ig, function(wholeMatch){return "<a href='mailto:" + wholeMatch + "'>" + wholeMatch + "</a>";});

    return text;
}


var _StripLinkDefinitions = function(text) {
//
// Strips link definitions from text, stores the URLs and titles in
// hash references.
//

    // Link defs are in the form: ^[id]: url "optional title"

    /*
        var text = text.replace(/
                ^[ ]{0,3}\[(.+)\]:  // id = $1  attacklab: g_tab_width - 1
                  [ \t]*
                  \n?               // maybe *one* newline
                  [ \t]*
                <?(\S+?)>?          // url = $2
                  [ \t]*
                  \n?               // maybe one newline
                  [ \t]*
                (?:
                  (\n*)             // any lines skipped = $3 attacklab: lookbehind removed
                  ["(]
                  (.+?)             // title = $4
                  [")]
                  [ \t]*
                )?                  // title is optional
                (?:\n+|$)
              /gm,
              function(){...});
    */
    var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,
        function (wholeMatch,m1,m2,m3,m4) {
            m1 = m1.toLowerCase();
            g_urls[m1] = _EncodeAmpsAndAngles(m2);  // Link IDs are case-insensitive
            if (m3) {
                // Oops, found blank lines, so it's not a title.
                // Put back the parenthetical statement we stole.
                return m3+m4;
            } else if (m4) {
                g_titles[m1] = m4.replace(/"/g,"&quot;");
            }
            
            // Completely remove the definition from the text
            return "";
        }
    );

    return text;
}


var _HashHTMLBlocks = function(text) {
    // attacklab: Double up blank lines to reduce lookaround
    text = text.replace(/\n/g,"\n\n");

    // Hashify HTML blocks:
    // We only want to do this for block-level HTML tags, such as headers,
    // lists, and tables. That's because we still want to wrap <p>s around
    // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
    // phrase emphasis, and spans. The list of tags we're looking for is
    // hard-coded:
    var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"
    var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math"

    // First, look for nested blocks, e.g.:
    //   <div>
    //     <div>
    //     tags for inner block must be indented.
    //     </div>
    //   </div>
    //
    // The outermost tags must start at the left margin for this to match, and
    // the inner nested divs must be indented.
    // We need to do this before the next, more liberal match, because the next
    // match will start at the first `<div>` and stop at the first `</div>`.

    // attacklab: This regex can be expensive when it fails.
    /*
        var text = text.replace(/
        (                       // save in $1
            ^                   // start of line  (with /m)
            <($block_tags_a)    // start tag = $2
            \b                  // word break
                                // attacklab: hack around khtml/pcre bug...
            [^\r]*?\n           // any number of lines, minimally matching
            </\2>               // the matching end tag
            [ \t]*              // trailing spaces/tabs
            (?=\n+)             // followed by a newline
        )                       // attacklab: there are sentinel newlines at end of document
        /gm,function(){...}};
    */
    text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,hashElement);

    //
    // Now match more liberally, simply from `\n<tag>` to `</tag>\n`
    //

    /*
        var text = text.replace(/
        (                       // save in $1
            ^                   // start of line  (with /m)
            <($block_tags_b)    // start tag = $2
            \b                  // word break
                                // attacklab: hack around khtml/pcre bug...
            [^\r]*?             // any number of lines, minimally matching
            .*</\2>             // the matching end tag
            [ \t]*              // trailing spaces/tabs
            (?=\n+)             // followed by a newline
        )                       // attacklab: there are sentinel newlines at end of document
        /gm,function(){...}};
    */
    text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement);

    // Special case just for <hr />. It was easier to make a special case than
    // to make the other regex more complicated.  

    /*
        text = text.replace(/
        (                       // save in $1
            \n\n                // Starting after a blank line
            [ ]{0,3}
            (<(hr)              // start tag = $2
            \b                  // word break
            ([^<>])*?           // 
            \/?>)               // the matching end tag
            [ \t]*
            (?=\n{2,})          // followed by a blank line
        )
        /g,hashElement);
    */
    text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);

    // Special case for standalone HTML comments:

    /*
        text = text.replace(/
        (                       // save in $1
            \n\n                // Starting after a blank line
            [ ]{0,3}            // attacklab: g_tab_width - 1
            <!
            (--[^\r]*?--\s*)+
            >
            [ \t]*
            (?=\n{2,})          // followed by a blank line
        )
        /g,hashElement);
    */
    text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);

    // PHP and ASP-style processor instructions (<?...?> and <%...%>)

    /*
        text = text.replace(/
        (?:
            \n\n                // Starting after a blank line
        )
        (                       // save in $1
            [ ]{0,3}            // attacklab: g_tab_width - 1
            (?:
                <([?%])         // $2
                [^\r]*?
                \2>
            )
            [ \t]*
            (?=\n{2,})          // followed by a blank line
        )
        /g,hashElement);
    */
    text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);

    // attacklab: Undo double lines (see comment at top of this function)
    text = text.replace(/\n\n/g,"\n");
    return text;
}

var hashElement = function(wholeMatch,m1) {
    var blockText = m1;

    // Undo double lines
    blockText = blockText.replace(/\n\n/g,"\n");
    blockText = blockText.replace(/^\n/,"");
    
    // strip trailing blank lines
    blockText = blockText.replace(/\n+$/g,"");
    
    // Replace the element text with a marker ("~KxK" where x is its key)
    blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";
    
    return blockText;
};

var _RunBlockGamut = function(text) {
//
// These are all the transformations that form block-level
// tags like paragraphs, headers, and list items.
//
    text = _DoHeaders(text);

    // Do Horizontal Rules:
    var key = hashBlock("<hr />");
    text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);
    text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);
    text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);

    text = _DoLists(text);
    text = _DoCodeBlocks(text);
    text = _DoBlockQuotes(text);

    // We already ran _HashHTMLBlocks() before, in Markdown(), but that
    // was to escape raw HTML in the original Markdown source. This time,
    // we're escaping the markup we've just created, so that we don't wrap
    // <p> tags around block-level tags.
    text = _HashHTMLBlocks(text);
    text = _FormParagraphs(text);

    return text;
}


var _RunSpanGamut = function(text) {
//
// These are all the transformations that occur *within* block-level
// tags like paragraphs, headers, and list items.
//

    text = _DoCodeSpans(text);
    text = _EscapeSpecialCharsWithinTagAttributes(text);
    text = _EncodeBackslashEscapes(text);

    // Process anchor and image tags. Images must come first,
    // because ![foo][f] looks like an anchor.
    text = _DoImages(text);
    text = _DoAnchors(text);

    // Make links out of things like `<http://example.com/>`
    // Must come after _DoAnchors(), because you can use < and >
    // delimiters in inline links like [this](<url>).
    text = _DoAutoLinks(text);
    text = _EncodeAmpsAndAngles(text);
    text = _DoItalicsAndBold(text);

    // Do hard breaks:
    text = text.replace(/  +\n/g," <br />\n");

    return text;
}

var _EscapeSpecialCharsWithinTagAttributes = function(text) {
//
// Within tags -- meaning between < and > -- encode [\ ` * _] so they
// don't conflict with their use in Markdown for code, italics and strong.
//

    // Build a regex to find HTML tags and comments.  See Friedl's 
    // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
    var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;

    text = text.replace(regex, function(wholeMatch) {
        var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");
        tag = escapeCharacters(tag,"\\`*_");
        return tag;
    });

    return text;
}

var _DoAnchors = function(text) {
//
// Turn Markdown link shortcuts into XHTML <a> tags.
//
    //
    // First, handle reference-style links: [link text] [id]
    //

    /*
        text = text.replace(/
        (                           // wrap whole match in $1
            \[
            (
                (?:
                    \[[^\]]*\]      // allow brackets nested one level
                    |
                    [^\[]           // or anything else
                )*
            )
            \]

            [ ]?                    // one optional space
            (?:\n[ ]*)?             // one optional newline followed by spaces

            \[
            (.*?)                   // id = $3
            \]
        )()()()()                   // pad remaining backreferences
        /g,_DoAnchors_callback);
    */
    text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);

    //
    // Next, inline-style links: [link text](url "optional title")
    //

    /*
        text = text.replace(/
            (                       // wrap whole match in $1
                \[
                (
                    (?:
                        \[[^\]]*\]  // allow brackets nested one level
                    |
                    [^\[\]]         // or anything else
                )
            )
            \]
            \(                      // literal paren
            [ \t]*
            ()                      // no id, so leave $3 empty
            <?(.*?)>?               // href = $4
            [ \t]*
            (                       // $5
                (['"])              // quote char = $6
                (.*?)               // Title = $7
                \6                  // matching quote
                [ \t]*              // ignore any spaces/tabs between closing quote and )
            )?                      // title is optional
            \)
        )
        /g,writeAnchorTag);
    */
    text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);

    //
    // Last, handle reference-style shortcuts: [link text]
    // These must come last in case you've also got [link test][1]
    // or [link test](/foo)
    //

    /*
        text = text.replace(/
        (                           // wrap whole match in $1
            \[
            ([^\[\]]+)              // link text = $2; can't contain '[' or ']'
            \]
        )()()()()()                 // pad rest of backreferences
        /g, writeAnchorTag);
    */
    text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);

    return text;
}

var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
    if (m7 == undefined) m7 = "";
    var whole_match = m1;
    var link_text   = m2;
    var link_id  = m3.toLowerCase();
    var url     = m4;
    var title   = m7;
    
    if (url == "") {
        if (link_id == "") {
            // lower-case and turn embedded newlines into spaces
            link_id = link_text.toLowerCase().replace(/ ?\n/g," ");
        }
        url = "#"+link_id;
        
        if (g_urls[link_id] != undefined) {
            url = g_urls[link_id];
            if (g_titles[link_id] != undefined) {
                title = g_titles[link_id];
            }
        }
        else {
            if (whole_match.search(/\(\s*\)$/m)>-1) {
                // Special case for explicit empty url
                url = "";
            } else {
                return whole_match;
            }
        }
    }   
    
    url = escapeCharacters(url,"*_");
    var result = "<a href=\"" + url + "\"";
    
    if (title != "") {
        title = title.replace(/"/g,"&quot;");
        title = escapeCharacters(title,"*_");
        result +=  " title=\"" + title + "\"";
    }
    
    result += ">" + link_text + "</a>";
    
    return result;
}


var _DoImages = function(text) {
//
// Turn Markdown image shortcuts into <img> tags.
//

    //
    // First, handle reference-style labeled images: ![alt text][id]
    //

    /*
        text = text.replace(/
        (                       // wrap whole match in $1
            !\[
            (.*?)               // alt text = $2
            \]

            [ ]?                // one optional space
            (?:\n[ ]*)?         // one optional newline followed by spaces

            \[
            (.*?)               // id = $3
            \]
        )()()()()               // pad rest of backreferences
        /g,writeImageTag);
    */
    text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);

    //
    // Next, handle inline images:  ![alt text](url "optional title")
    // Don't forget: encode * and _

    /*
        text = text.replace(/
        (                       // wrap whole match in $1
            !\[
            (.*?)               // alt text = $2
            \]
            \s?                 // One optional whitespace character
            \(                  // literal paren
            [ \t]*
            ()                  // no id, so leave $3 empty
            <?(\S+?)>?          // src url = $4
            [ \t]*
            (                   // $5
                (['"])          // quote char = $6
                (.*?)           // title = $7
                \6              // matching quote
                [ \t]*
            )?                  // title is optional
        \)
        )
        /g,writeImageTag);
    */
    text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);

    return text;
}

var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
    var whole_match = m1;
    var alt_text   = m2;
    var link_id  = m3.toLowerCase();
    var url     = m4;
    var title   = m7;

    if (!title) title = "";
    
    if (url == "") {
        if (link_id == "") {
            // lower-case and turn embedded newlines into spaces
            link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");
        }
        url = "#"+link_id;
        
        if (g_urls[link_id] != undefined) {
            url = g_urls[link_id];
            if (g_titles[link_id] != undefined) {
                title = g_titles[link_id];
            }
        }
        else {
            return whole_match;
        }
    }   
    
    alt_text = alt_text.replace(/"/g,"&quot;");
    url = escapeCharacters(url,"*_");
    var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";

    // attacklab: Markdown.pl adds empty title attributes to images.
    // Replicate this bug.

    //if (title != "") {
        title = title.replace(/"/g,"&quot;");
        title = escapeCharacters(title,"*_");
        result +=  " title=\"" + title + "\"";
    //}
    
    result += " />";
    
    return result;
}


var _DoHeaders = function(text) {

    // Setext-style headers:
    //  Header 1
    //  ========
    //  
    //  Header 2
    //  --------
    //
    text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
        function(wholeMatch,m1){return hashBlock('<h1 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h1>");});

    text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
        function(matchFound,m1){return hashBlock('<h2 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h2>");});

    // atx-style headers:
    //  # Header 1
    //  ## Header 2
    //  ## Header 2 with closing hashes ##
    //  ...
    //  ###### Header 6
    //

    /*
        text = text.replace(/
            ^(\#{1,6})              // $1 = string of #'s
            [ \t]*
            (.+?)                   // $2 = Header text
            [ \t]*
            \#*                     // optional closing #'s (not counted)
            \n+
        /gm, function() {...});
    */

    text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
        function(wholeMatch,m1,m2) {
            var h_level = m1.length;
            return hashBlock("<h" + h_level + ' id="' + headerId(m2) + '">' + _RunSpanGamut(m2) + "</h" + h_level + ">");
        });

    function headerId(m) {
        return m.replace(/[^\w]/g, '').toLowerCase();
    }
    return text;
}

// This declaration keeps Dojo compressor from outputting garbage:
var _ProcessListItems;

var _DoLists = function(text) {
//
// Form HTML ordered (numbered) and unordered (bulleted) lists.
//

    // attacklab: add sentinel to hack around khtml/safari bug:
    // http://bugs.webkit.org/show_bug.cgi?id=11231
    text += "~0";

    // Re-usable pattern to match any entirel ul or ol list:

    /*
        var whole_list = /
        (                                   // $1 = whole list
            (                               // $2
                [ ]{0,3}                    // attacklab: g_tab_width - 1
                ([*+-]|\d+[.])              // $3 = first list item marker
                [ \t]+
            )
            [^\r]+?
            (                               // $4
                ~0                          // sentinel for workaround; should be $
            |
                \n{2,}
                (?=\S)
                (?!                         // Negative lookahead for another list item marker
                    [ \t]*
                    (?:[*+-]|\d+[.])[ \t]+
                )
            )
        )/g
    */
    var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;

    if (g_list_level) {
        text = text.replace(whole_list,function(wholeMatch,m1,m2) {
            var list = m1;
            var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";

            // Turn double returns into triple returns, so that we can make a
            // paragraph for the last item in a list, if necessary:
            list = list.replace(/\n{2,}/g,"\n\n\n");;
            var result = _ProcessListItems(list);
    
            // Trim any trailing whitespace, to put the closing `</$list_type>`
            // up on the preceding line, to get it past the current stupid
            // HTML block parser. This is a hack to work around the terrible
            // hack that is the HTML block parser.
            result = result.replace(/\s+$/,"");
            result = "<"+list_type+">" + result + "</"+list_type+">\n";
            return result;
        });
    } else {
        whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
        text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
            var runup = m1;
            var list = m2;

            var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
            // Turn double returns into triple returns, so that we can make a
            // paragraph for the last item in a list, if necessary:
            var list = list.replace(/\n{2,}/g,"\n\n\n");;
            var result = _ProcessListItems(list);
            result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";   
            return result;
        });
    }

    // attacklab: strip sentinel
    text = text.replace(/~0/,"");

    return text;
}

_ProcessListItems = function(list_str) {
//
//  Process the contents of a single ordered or unordered list, splitting it
//  into individual list items.
//
    // The $g_list_level global keeps track of when we're inside a list.
    // Each time we enter a list, we increment it; when we leave a list,
    // we decrement. If it's zero, we're not in a list anymore.
    //
    // We do this because when we're not inside a list, we want to treat
    // something like this:
    //
    //    I recommend upgrading to version
    //    8. Oops, now this line is treated
    //    as a sub-list.
    //
    // As a single paragraph, despite the fact that the second line starts
    // with a digit-period-space sequence.
    //
    // Whereas when we're inside a list (or sub-list), that line will be
    // treated as the start of a sub-list. What a kludge, huh? This is
    // an aspect of Markdown's syntax that's hard to parse perfectly
    // without resorting to mind-reading. Perhaps the solution is to
    // change the syntax rules such that sub-lists must start with a
    // starting cardinal number; e.g. "1." or "a.".

    g_list_level++;

    // trim trailing blank lines:
    list_str = list_str.replace(/\n{2,}$/,"\n");

    // attacklab: add sentinel to emulate \z
    list_str += "~0";

    /*
        list_str = list_str.replace(/
            (\n)?                           // leading line = $1
            (^[ \t]*)                       // leading whitespace = $2
            ([*+-]|\d+[.]) [ \t]+           // list marker = $3
            ([^\r]+?                        // list item text   = $4
            (\n{1,2}))
            (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
        /gm, function(){...});
    */
    list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
        function(wholeMatch,m1,m2,m3,m4){
            var item = m4;
            var leading_line = m1;
            var leading_space = m2;

            if (leading_line || (item.search(/\n{2,}/)>-1)) {
                item = _RunBlockGamut(_Outdent(item));
            }
            else {
                // Recursion for sub-lists:
                item = _DoLists(_Outdent(item));
                item = item.replace(/\n$/,""); // chomp(item)
                item = _RunSpanGamut(item);
            }

            return  "<li>" + item + "</li>\n";
        }
    );

    // attacklab: strip sentinel
    list_str = list_str.replace(/~0/g,"");

    g_list_level--;
    return list_str;
}


var _DoCodeBlocks = function(text) {
//
//  Process Markdown `<pre><code>` blocks.
//  

    /*
        text = text.replace(text,
            /(?:\n\n|^)
            (                               // $1 = the code block -- one or more lines, starting with a space/tab
                (?:
                    (?:[ ]{4}|\t)           // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
                    .*\n+
                )+
            )
            (\n*[ ]{0,3}[^ \t\n]|(?=~0))    // attacklab: g_tab_width
        /g,function(){...});
    */

    // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
    text += "~0";
    
    text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
        function(wholeMatch,m1,m2) {
            var codeblock = m1;
            var nextChar = m2;
        
            codeblock = _EncodeCode( _Outdent(codeblock));
            codeblock = _Detab(codeblock);
            codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
            codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace

            codeblock = "<pre><code>" + codeblock + "\n</code></pre>";

            return hashBlock(codeblock) + nextChar;
        }
    );

    // attacklab: strip sentinel
    text = text.replace(/~0/,"");

    return text;
}

var hashBlock = function(text) {
    text = text.replace(/(^\n+|\n+$)/g,"");
    return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
}


var _DoCodeSpans = function(text) {
//
//   *  Backtick quotes are used for <code></code> spans.
// 
//   *  You can use multiple backticks as the delimiters if you want to
//   include literal backticks in the code span. So, this input:
//   
//       Just type ``foo `bar` baz`` at the prompt.
//   
//     Will translate to:
//   
//       <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
//   
//  There's no arbitrary limit to the number of backticks you
//  can use as delimters. If you need three consecutive backticks
//  in your code, use four for delimiters, etc.
//
//  *  You can use spaces to get literal backticks at the edges:
//   
//       ... type `` `bar` `` ...
//   
//     Turns to:
//   
//       ... type <code>`bar`</code> ...
//

    /*
        text = text.replace(/
            (^|[^\\])                   // Character before opening ` can't be a backslash
            (`+)                        // $2 = Opening run of `
            (                           // $3 = The code block
                [^\r]*?
                [^`]                    // attacklab: work around lack of lookbehind
            )
            \2                          // Matching closer
            (?!`)
        /gm, function(){...});
    */

    text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
        function(wholeMatch,m1,m2,m3,m4) {
            var c = m3;
            c = c.replace(/^([ \t]*)/g,""); // leading whitespace
            c = c.replace(/[ \t]*$/g,"");   // trailing whitespace
            c = _EncodeCode(c);
            return m1+"<code>"+c+"</code>";
        });

    return text;
}


// THIS HAS BEEN EDITED FOR THE WIKI
var _EncodeCode = function(text) {
//
// Encode/escape certain characters inside Markdown code runs.
// The point is that in code, these characters are literals,
// and lose their special Markdown meanings.
//
    // Encode all ampersands; HTML entities are not
    // entities within a Markdown code span.
    //text = text.replace(/&/g,"&amp;");

    // Do the angle bracket song and dance:
    //text = text.replace(/</g,"&lt;");
    //text = text.replace(/>/g,"&gt;");

    // Now, escape characters that are magic in Markdown:
    text = escapeCharacters(text,"\*_{}[]\\",false);

// jj the line above breaks this:
//---

//* Item

//   1. Subitem

//            special char: *
//---

    return text;
}


var _DoItalicsAndBold = function(text) {

    // <strong> must go first:
    text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,
        "<strong>$2</strong>");

    text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
        "<em>$2</em>");

    return text;
}


var _DoBlockQuotes = function(text) {

    /*
        text = text.replace(/
        (                               // Wrap whole match in $1
            (
                ^[ \t]*>[ \t]?          // '>' at the start of a line
                .+\n                    // rest of the first line
                (.+\n)*                 // subsequent consecutive lines
                \n*                     // blanks
            )+
        )
        /gm, function(){...});
    */

    text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
        function(wholeMatch,m1) {
            var bq = m1;

            // attacklab: hack around Konqueror 3.5.4 bug:
            // "----------bug".replace(/^-/g,"") == "bug"

            bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0");   // trim one level of quoting

            // attacklab: clean up hack
            bq = bq.replace(/~0/g,"");

            bq = bq.replace(/^[ \t]+$/gm,"");       // trim whitespace-only lines
            bq = _RunBlockGamut(bq);                // recurse
            
            bq = bq.replace(/(^|\n)/g,"$1  ");
            // These leading spaces screw with <pre> content, so we need to fix that:
            bq = bq.replace(
                    /(\s*<pre>[^\r]+?<\/pre>)/gm,
                function(wholeMatch,m1) {
                    var pre = m1;
                    // attacklab: hack around Konqueror 3.5.4 bug:
                    pre = pre.replace(/^  /mg,"~0");
                    pre = pre.replace(/~0/g,"");
                    return pre;
                });
            
            return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");
        });
    return text;
}


var _FormParagraphs = function(text) {
//
//  Params:
//    $text - string to process with html <p> tags
//

    // Strip leading and trailing lines:
    text = text.replace(/^\n+/g,"");
    text = text.replace(/\n+$/g,"");

    var grafs = text.split(/\n{2,}/g);
    var grafsOut = new Array();

    //
    // Wrap <p> tags.
    //
    var end = grafs.length;
    for (var i=0; i<end; i++) {
        var str = grafs[i];

        // if this is an HTML marker, copy it
        if (str.search(/~K(\d+)K/g) >= 0) {
            grafsOut.push(str);
        }
        else if (str.search(/\S/) >= 0) {
            str = _RunSpanGamut(str);
            str = str.replace(/^([ \t]*)/g,"<p>");
            str += "</p>"
            grafsOut.push(str);
        }

    }

    //
    // Unhashify HTML blocks
    //
    end = grafsOut.length;
    for (var i=0; i<end; i++) {
        // if this is a marker for an html block...
        while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
            var blockText = g_html_blocks[RegExp.$1];
            blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
            grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
        }
    }

    return grafsOut.join("\n\n");
}


var _EncodeAmpsAndAngles = function(text) {
// Smart processing for ampersands and angle brackets that need to be encoded.
    
    // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
    //   http://bumppo.net/projects/amputator/
    //text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");
    
    // Encode naked <'s
    //text = text.replace(/<(?![a-z\/?\$!])/gi,"&lt;");
    
    return text;
}


var _EncodeBackslashEscapes = function(text) {
//
//   Parameter:  String.
//   Returns:   The string, with after processing the following backslash
//             escape sequences.
//

    // attacklab: The polite way to do this is with the new
    // escapeCharacters() function:
    //
    //  text = escapeCharacters(text,"\\",true);
    //  text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
    //
    // ...but we're sidestepping its use of the (slow) RegExp constructor
    // as an optimization for Firefox.  This function gets called a LOT.

    text = text.replace(/\\(\\)/g,escapeCharacters_callback);
    text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
    return text;
}


var _DoAutoLinks = function(text) {

    text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"<a href=\"$1\">$1</a>");

    // Email addresses: <address@domain.foo>

    /*
        text = text.replace(/
            <
            (?:mailto:)?
            (
                [-.\w]+
                \@
                [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
            )
            >
        /gi, _DoAutoLinks_callback());
    */
    text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
        function(wholeMatch,m1) {
            return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
        }
    );

    return text;
}


var _EncodeEmailAddress = function(addr) {
//
//  Input: an email address, e.g. "foo@example.com"
//
//  Output: the email address as a mailto link, with each character
//  of the address encoded as either a decimal or hex entity, in
//  the hopes of foiling most address harvesting spam bots. E.g.:
//
//  <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
//     x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
//     &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
//
//  Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
//  mailing list: <http://tinyurl.com/yu7ue>
//

    // attacklab: why can't javascript speak hex?
    function char2hex(ch) {
        var hexDigits = '0123456789ABCDEF';
        var dec = ch.charCodeAt(0);
        return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
    }

    var encode = [
        function(ch){return "&#"+ch.charCodeAt(0)+";";},
        function(ch){return "&#x"+char2hex(ch)+";";},
        function(ch){return ch;}
    ];

    addr = "mailto:" + addr;

    addr = addr.replace(/./g, function(ch) {
        if (ch == "@") {
            // this *must* be encoded. I insist.
            ch = encode[Math.floor(Math.random()*2)](ch);
        } else if (ch !=":") {
            // leave ':' alone (to spot mailto: later)
            var r = Math.random();
            // roughly 10% raw, 45% hex, 45% dec
            ch =  (
                    r > .9  ?   encode[2](ch)   :
                    r > .45 ?   encode[1](ch)   :
                                encode[0](ch)
                );
        }
        return ch;
    });

    addr = "<a href=\"" + addr + "\">" + addr + "</a>";
    addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part

    return addr;
}


var _UnescapeSpecialChars = function(text) {
//
// Swap back in all the special characters we've hidden.
//
    text = text.replace(/~E(\d+)E/g,
        function(wholeMatch,m1) {
            var charCodeToReplace = parseInt(m1);
            return String.fromCharCode(charCodeToReplace);
        }
    );
    return text;
}


var _Outdent = function(text) {
//
// Remove one level of line-leading tabs or spaces
//

    // attacklab: hack around Konqueror 3.5.4 bug:
    // "----------bug".replace(/^-/g,"") == "bug"

    text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width

    // attacklab: clean up hack
    text = text.replace(/~0/g,"")

    return text;
}

var _Detab = function(text) {
// attacklab: Detab's completely rewritten for speed.
// In perl we could fix it by anchoring the regexp with \G.
// In javascript we're less fortunate.

    // expand first n-1 tabs
    text = text.replace(/\t(?=\t)/g,"    "); // attacklab: g_tab_width

    // replace the nth with two sentinels
    text = text.replace(/\t/g,"~A~B");

    // use the sentinel to anchor our regex so it doesn't explode
    text = text.replace(/~B(.+?)~A/g,
        function(wholeMatch,m1,m2) {
            var leadingText = m1;
            var numSpaces = 4 - leadingText.length % 4;  // attacklab: g_tab_width

            // there *must* be a better way to do this:
            for (var i=0; i<numSpaces; i++) leadingText+=" ";

            return leadingText;
        }
    );

    // clean up sentinels
    text = text.replace(/~A/g,"    ");  // attacklab: g_tab_width
    text = text.replace(/~B/g,"");

    return text;
}


//
//  attacklab: Utility functions
//


var escapeCharacters = function(text, charsToEscape, afterBackslash) {
    // First we have to escape the escape characters so that
    // we can build a character class out of them
    var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g,"\\$1") + "])";

    if (afterBackslash) {
        regexString = "\\\\" + regexString;
    }

    var regex = new RegExp(regexString,"g");
    text = text.replace(regex,escapeCharacters_callback);

    return text;
}


var escapeCharacters_callback = function(wholeMatch,m1) {
    var charCodeToEscape = m1.charCodeAt(0);
    return "~E"+charCodeToEscape+"E";
}

} // end of Showdown.converter


exports.Showdown = Showdown;


// escape characters special to markdown, replacing _ with \_ for example
exports.escapeMarkdown = function (str) {
    return str.replace(/([\*_{}\[\]\\])/g, function (whole_match, m1) {
        return '\\' + m1;
    });
};

/**
 * Converts a page body from markdown text to HTML. Internal links are converted
 * to markdown-style links first. Any HTML elements are sanitized before going
 * through the markdown converter to avoid script injections.
 *
 * @name convert(str)
 * @param {String} str - The markdown text to convert to HTML
 * @returns {String}
 * @api public
 */

exports.convert = function (str) {

    // preprocess internal wiki links
    var baseURL = duality_utils.getBaseURL();

    // links with custom text
    var match, re = new RegExp('\\[([^\\]]+)\\]\\[\\[([^\\]]+)\\]\\]');
    while (match = re.exec(str)) {
        var before = str.substr(0, match.index)
        var after = str.substr(match.index + match[0].length)
        var target = match[2].split('#');
        var pageid = encodeURIComponent(target[0]);
        var anchor = target.length > 1 ?
            '#' + encodeURIComponent(target.slice(1).join('#')):
            '';
        str = before +
            '[' + exports.escapeMarkdown(sanitize.h(match[1])) + ']' +
            '(' + baseURL + '/' + pageid + anchor + ')' +
            after;
    }
    // links without custom text
    var match, re = new RegExp('\\[\\[([^\\]]+)\\]\\]');
    while (match = re.exec(str)) {
        var before = str.substr(0, match.index)
        var after = str.substr(match.index + match[0].length)
        var text = match[1].replace(/_/g, ' ');
        var target = match[1].split('#');
        var pageid = encodeURIComponent(target[0]);
        var anchor = target.length > 1 ?
            '#' + encodeURIComponent(target.slice(1).join('#')):
            '';
        str = before +
            '[' + exports.escapeMarkdown(sanitize.h(text)) + ']' +
            '(' + baseURL + '/' + pageid + anchor + ')' +
            after;
    }

    // escape html
    str = sanitize.h(str);

    // fix blockquotes
    str = _.map(str.split('\n'), function (l) {
        var m = /(^(?:&gt;\s)+)/.exec(l);
        if (m) {
            return l.substr(0, m.index) +
                   m[1].replace(/&gt;/g, '>') +
                   l.substr(m.index + m[1].length);
        }
        return l;
    }).join('\n');

    // process fenced code blocks
    var re1 = /```([A-Za-z]+)\s*([\s\S]+?)```/; // with syntax highlighting
    var re2 = /```\s*([\s\S]+?)```/; // without syntax highlighting
    var block;
    while (block = re1.exec(str) || re2.exec(str)) {
        var pre;
        if (block.length === 3) {
            // we have a code format
            pre = '<pre><code class="' + block[1] + '">' +
                block[2] + '</code></pre>';
        }
        else {
            // no syntax highlighting, use basic pre tag
            pre = '<pre><code>' + block[1] + '</code></pre>';
        }
        str = str.substr(0, block.index) +
              pre +
              str.substr(block.index + block[0].length);
    }

    var c = new Showdown.converter();
    return c.makeHtml(str);
};


})};

/********** lib/shows **********/

kanso.moduleCache["lib/shows"] = {load: (function (module, exports, require) {

/**
 * Show functions to be exported from the design doc.
 */
var _ = require('underscore')._;


var templates = require('handlebars').templates,
    jsonp = require('jsonp'),
    utils = require('./utils'),
    ui = require('./ui');


exports.not_found = function (doc, req) {
    return utils.base_template({
        code: 404,
        app_settings : this.app_settings,
        title: 'Not found',
        content: templates['404.html']({})
    });
};

exports.install_script = function (doc, req) {
    return utils.base_template({
        code: 200,
        app_settings : this.app_settings,
        headers: {'Content-Type': 'text/plain'},
        body: templates['install.sh']({
            app: req.query.name,
            app_settings : this.app_settings,
            app_url: utils.app_url(req, req.query.name),
            open_path: utils.open_path(doc)
        })
    });
};

exports.upload_app = function (doc, req) {

    return utils.base_template({
        code: 200,
        baseURL : './',
        app_settings : this.app_settings,
        title: 'Upload your app',
        onload : 'ui.upload_page()',
        content: templates['upload_app.html']({
            upload_url: utils.upload_url(req),
            app_settings : this.app_settings,
            baseURL : './'
        })
    });
};

exports.search = function(doc, req) {
    return utils.base_template({
        code: 200,
        baseURL : './',
        app_settings : this.app_settings,
        title: 'Search Results',
        onload : 'ui.perform_search("'+ req.query.q +'")',
        content: templates['search.html']({
            baseURL : './',
            app_settings : this.app_settings,
            terms : req.query.q
        })
    });
}



exports.kanso_details = function(doc, req) {

    // check for _rewrite, we assume we are on a vhost. Make the terrible assumption
    // that the vhost is the db name

    var db_path = req.info.db_name + '/_db';
    if(_.indexOf(req.requested_path, '_rewrite') >= 0) {
        // we are on the rewrite
        db_path = req.info.db_name + '/_design/market/_rewrite/_db';
    }

    var meta = doc.couchapp || doc.kanso;

    return jsonp.response(req.query.callback, {
            kanso: meta,
            open_path: utils.open_path(doc),
            style : 'design-doc',  /* option for db to replicate whole db */
            db_src : utils.app_db(req, db_path),
            doc_id : doc._id,
            icon_url : utils.app_db(req, db_path) + '/' + doc._id + '/' +  meta.config.icons['96'],
            user_url : utils.user_url(req, meta.pushed_by),
            user : meta.pushed_by,
            db_path : db_path,
            requested_path :req.requested_path
    });
}


})};

/********** lib/ui **********/

kanso.moduleCache["lib/ui"] = {load: (function (module, exports, require) {

var templates = require('handlebars').templates,
    utils = require('./utils'),
    popup = require('./popup'),
    dashboards = require('./dashboards'),
    db = require('db'),
    url = require('url'),
    path = require('path'),
    _ = require('underscore')._;


var  dutils = {
    getBaseURL : function(req) {
        return '.';
    }
}


exports.init = function (){
    dashboards.init();
    //kind of broad...
    $('.carousel').carousel({
      interval: false
    });
    exports.addCategories();
}

exports.preloadImage = function (src, callback) {
    var img = $('<img>').attr({ src: src });
    img.load(function () { callback(null, this); });
    img.error(callback);
    return img;
};

exports.loadScreenshots = function (id, meta, req) {
    var cfg = meta.config;
    var screenshots = cfg.screenshots.map(function (s) {
        return {src: dutils.getBaseURL(req) + '/_db/' + id + '/' + s};
    });

    // fade in loading after delay set on css transition
    $('#screenshot_container .loading').css({opacity: 1});

    // preload first image
    exports.preloadImage(screenshots[0].src, function (err, img) {
        if (err) {
            return console.error(err);
        }
        $('#screenshot_container').html(
            templates.render('screenshots.html', req, {
                screenshots: screenshots
            })
        );
        screenshots = _.map(screenshots, function (s, i) {
            function updateTop(img) {
                var h = img.height;
                if (h === 0) {
                    // probably not loaded yet
                    $(img).parents('li').css({marginTop: 0});
                }
                else {
                    s.top = Math.max(400 - h, 0) / 2;
                    $(img).parents('li').css({marginTop: s.top});
                }
            }
            var img = $('#screenshots li img').get(i);
            $(img).load(function () {
                updateTop(this);
            });
            updateTop(img);
            s.left = $('#screenshots ul').position().left - (i * 680);
            return s;
        });

        var curr = 0;

        // show or hide next and prev buttons depending on position
        function toggleBtns() {
            if (screenshots.length <= 1) {
                $('#screenshots .prev').hide();
                $('#screenshots .next').hide();
            }
            if (curr === 0) {
                $('#screenshots .prev').hide();
                $('#screenshots .next').show();
            }
            else if (curr === screenshots.length - 1) {
                $('#screenshots .prev').show();
                $('#screenshots .next').hide();
            }
            else {
                $('#screenshots .prev').show();
                $('#screenshots .next').show();
            }
        }

        // update active screenshot nav position
        function updateNav() {
            $('#screenshot_nav li').removeClass('active');
            var li = $('#screenshot_nav li')[curr];
            $(li).addClass('active');
        }

        function update(val) {
            if (val !== undefined) {
                curr = val;
            }
            $('#screenshots ul').css({
                left: screenshots[curr].left + 'px'
            });
            toggleBtns();
            updateNav();
        }
        update();

        $('#screenshot_nav li').each(function (i) {
            $(this).click(function () {
                update(i);
            });
        });

        $('#screenshot_container .next').click(function () {
            if (curr < screenshots.length - 1) {
                update(curr + 1);
            }
        });
        $('#screenshot_container .prev').click(function () {
            if (curr > 0) {
                update(curr - 1);
            }
        });
    });
};


exports.addCategories = function () {
    var current_category = $('#category-page').data('category');
    var appdb = db.use(utils.getDBURL(window.location));
    var q = {
        reduce: true,
        group_level: 1
    };
    appdb.getView('market', 'apps_by_category', q, function (err, data) {
        var total_apps = 0, categories = [];
        _.each(data.rows, function (r) {
            if (r.key === 'total') {
                total_apps = r.value;
            }
            else {
                categories.push({
                    key: r.key[0],
                    name: utils.toSentenceCase(r.key[0]),
                    count: r.value
                });
            }
        });

        var baseURL = utils.getBaseURL(window.location);
        if (baseURL[baseURL.length - 1] !== '/') baseURL += '/';


        $('#categories').replaceWith(
            templates['category_list.html']({
                categories: categories,
                total_apps: total_apps,
                current_category: current_category,
                baseURL : baseURL
            })
        );

    });
};

/**
 * Add syntax highlighting to the page using highlight.js (hljs)
 */

exports.syntaxHighlight = function () {
    $('pre > code').each(function () {
        if (this.className) {
            // has a code class
            $(this).html(hljs.highlight(this.className, $(this).text()).value);
        }
    });
};


exports.showInstallPopup = function (req, id) {
    var loc = window.location.toString();
    var dashboard_urls = _.map(dashboards.getURLs(), function (url, i) {
        return {id: i, url: url};
    });

    var content = templates.render('install_popup_content.html', req, {
        create_dashboard_url: 'http://garden20.com/?app_url=' + escape(loc)
    });
    popup.closeAll();
    var el = popup.open(req, {
        content: content,
        width: 900,
        height: 300
    });
    if (dashboard_urls.length > 0) {
            exports.createDefaultInstructions(el, req, id);
    } else {
        exports.showOtherOptionsPopup(req, id);
    }
};

exports.showOtherOptionsPopup = function (req, id) {
    var loc = window.location.toString();
    var dashboard_urls = _.map(dashboards.getURLs(), function (url, i) {
        return {id: i, url: url};
    });

    var content = templates.render('install_popup_content.html', req, {
        create_dashboard_url: 'http://garden20.com/?app_url=' + escape(loc)
    });
    popup.closeAll();
    var el = popup.open(req, {
        content: content,
        width: 900,
        height: 300
    });
    if (dashboard_urls.length > 0) {
        $('.note-actions', el).prepend(
            '<a href="#" class="btn backbtn">&lt; Back</a>'
        );
    }

    $('#manual_install img').click(function (ev) {
        $('.note-actions .backbtn').remove();
        exports.createManualInstructions(el, req, id);
    });
    $('#install_to_dashboard img').click(function (ev) {
        $('.note-actions .backbtn').remove();
        exports.createDashboardInstructions(el, req, id);
    });
    $('#create_dashboard').click(function (ev) {
    });
    $('.note-actions .backbtn', el).click(function (ev) {
        ev.preventDefault();
        $('.note-actions .backbtn').remove();
        exports.createDefaultInstructions(el, req, id);
        return false;
    });


}



exports.createManualInstructions = function (el, req, id) {
    $('.note-inner', el).html(
        templates.render('install_popup_manual.html', req, {
            install_script_url: utils.install_script_url(req, id)
        })
    );
    $('.note-actions', el).prepend(
        '<a href="#" class="btn backbtn">&lt; Back</a>' +
        '<a href="#" class="btn primary">Done</a>'
    );
    $('.note-actions .primary', el).click(function (ev) {
        ev.preventDefault();
        popup.close(el);
        return false;
    });
    $('.note-actions .backbtn', el).click(function (ev) {
        ev.preventDefault();
        exports.showOtherOptionsPopup(el, req, id);
        return false;
    });
};

exports.createDefaultInstructions = function (el, req, id) {
    var dashboard_urls = _.map(dashboards.getURLs(), function (url, i) {
        return {id: i, url: url};
    });

    $('.note-inner', el).html(
        templates.render('install_popup_default.html', req, {
            dashboard_urls: dashboard_urls
        })
    );
    $('.note-actions', el).prepend(
        '<a href="#" class="btn backbtn">Other Options</a>' +
        '<a href="#" class="btn primary">Install</a>'
    );
    $('.note-actions .primary', el).click(function (ev) {
        ev.preventDefault();
        var durl = dashboard_urls[0].url;
        window.open(dashboards.installURL(durl, window.location.toString()));
        popup.close(el);
        return false;
    });
    $('.note-actions .backbtn', el).click(function (ev) {
        ev.preventDefault();
        exports.showOtherOptionsPopup(req, id);
        return false;
    });
};


exports.createDashboardInstructions = function (el, req, id) {
    $('.note-inner', el).html(
        templates.render('install_popup_dashboard.html', req, {
            dashboard_urls: _.map(dashboards.getURLs(), function (url, i) {
                return {id: i, url: url};
            })
        })
    );
    $('.note-actions', el).prepend(
        '<a href="#" class="btn backbtn">&lt; Back</a>' +
        '<a href="#" class="btn primary">Install</a>'
    );
    $('.note-actions .primary', el).click(function (ev) {
        ev.preventDefault();
        var durl;
        if ($('#dashboard_custom').get(0).checked) {
            durl = $('#dashboard_custom_text').val();
            dashboards.add(durl);
        }
        else {
            durl = $('input[name="dashboard_url"]:checked').val();
            dashboards.moveToTop(durl);
        }
        window.open(dashboards.installURL(durl, window.location.toString()));
        popup.close(el);
        return false;
    });
    $('.note-actions .backbtn', el).click(function (ev) {
        ev.preventDefault();
        exports.showOtherOptionsPopup(req, id);
        return false;
    });
};

exports.showInstallChoices = function(req) {

    var loc = window.location.toString();
    var app_url = loc.substring(0, loc.length -  8); // remove the trailing /install
    var g20 = 'http://garden20.com/?app_url=' + escape(app_url);

    var baseURL = '../..';
    var appdb = db.use('../../_db');

    var default_url = g20;
    var dashboard_urls = [];

    appdb.getView('market', 'hosting', { include_docs: true }, function (err, data) {
        dashboard_urls = dashboard_urls.concat(_.map(data.rows, function (row, i) {
                return {
                    id: i,
                    url: row.doc.url + '?app_url=' + escape(app_url),
                    name: row.doc.name,
                    img : baseURL + '/static/img/garden20.png',
                    type: 'Hosted'
                };
        }));
        dashboard_urls = dashboard_urls.concat(_.map(dashboards.getURLs(), function (url, i) {
            var provider = dashboards.checkDashboardForKnownProviders(url, dashboard_urls);
            var dash =  {
                id: i,
                url:  dashboards.installURL(url, app_url),
                name: dashboards.friendlyName(url),
                img: baseURL + '/static/img/install_to_dashboard.png',
                type: 'Garden'
            };

            if (i == 0) {
                dash.primary = true;
                default_url = dash.url;
            }
            //else dash.advanced = true;

            if (provider) {
                dash.type = 'Hosted';
                dash.img  = provider.img;

            }
            return dash;
        }));
        dashboard_urls.push({
            id: 0,
            url : g20,
            name: 'Garden20',
            rootURL : 'http://garden20.com',
            img : baseURL + '/static/img/garden20.png',
            type: 'Hosted',
            primary : (default_url == g20)
        });

        dashboard_urls.push({
            id: 'other',
            url: 'other',
            name: 'Enter URL...',
            img : baseURL + '/static/img/install_to_dashboard.png',
            type: 'Dashboard',
            other: true,
            advanced : true
        });


//        dashboard_urls.push({
//            id: 'couch',
//            url: 'couch_install',
//            name: 'CouchDB manual install',
//            img : baseURL + '/static/img/couch.png',
//            type: 'Couch',
//            advanced : true
//        });

        $('.install_where tbody').html(
            templates['own_garden_option.html']({
                dashboard_urls: dashboard_urls
            })
        );

        $('.advanced-row').hide();
        $('a.next').attr('href', default_url);


        var selectedUrl = function(){
            var radio = $('input[type="radio"]:checked');
            var tr = radio.closest('tr');
            var url = radio.val();
            if (url == 'other') {
                var durl = $('#other-url').css('box-shadow', 'none').css('border', '0').val();
                url = dashboards.installURL(durl, app_url);

            }
            return url;
        }


        $('.install_where table tr').click(function(){
            $('input[type="radio"]', this).attr('checked','checked');
            $('a.next').attr('href', selectedUrl());
        });

        $('input[type="radio"]').click(function(){
            $('a.next').attr('href', selectedUrl());
        });

        $('.advanced-toggle').click(function(){
            var $me = $(this);
            if ($me.hasClass('showing')){
                $me.removeClass('showing').text('Show Advanced Locations');
                $('.advanced-row').hide();
            } else {
                $me.addClass('showing').text('Hide Advanced Locations');
                $('.advanced-row').show();
            }
            return false;
        });


        $('a.next').click(function(){

           var radio = $('input[type="radio"]:checked');
           var tr = radio.closest('tr');
           var url = radio.val();
           if (url == 'other') {
               var durl = $('#other-url').css('box-shadow', 'none').css('border', '0').val();
               dashboards.add(durl);
           }
           $('.please-wait').show();

        });

    });






}

exports.check_github = function(project, git_commit) {
    $.getJSON('https://api.github.com/repos/' + project + '?callback=?', function(resp){
       if (resp.data.message == "Not Found") return;
       if (git_commit && git_commit !== "") {
           resp.data.git_commit_small = git_commit.substring(0, 6);
           resp.data.git_commit = git_commit;
       }
       $('.info_table').append(templates['github_info.html'](resp.data));
    });
}

exports.upload_page = function() {
    var cur = url.parse(window.location);
    var target = url.resolve(cur, './upload');
    $('.upload_url').text('kanso push ' + target);
}

exports.couch_install_page = function(){
    var cur = url.parse(window.location);
    var target = url.resolve(cur, './install.sh');
    $('.upload_url').text('curl ' + target + ' | bash');
    $('.read').attr('href', target);
}


exports.perform_search = function(text) {
    var terms = text.toLowerCase().split(' ');

    terms = _.filter(terms, function (term) {
        return (term.length > 1);
    });
    var url = build_search_url(terms);
    $.get(url, function(data){
       $('.count').text('About ' + data.length + ' results.');
       $('.results').html(templates['search_results.html']({results: data}));
    });
}





function build_search_url(terms) {
    var url = "_search/" + _.first(terms);
    if (terms.length > 1) {
        var first = true;
        _.each(_.rest(terms), function(term){
            var prepend = '/';
            if (!first)  prepend = '+';
            url += prepend + term;
            first = false;
        })
    }
    return url;
}



})};

/********** lib/utils **********/

kanso.moduleCache["lib/utils"] = {load: (function (module, exports, require) {

var templates = require('handlebars').templates,
    url = require('url'),
    db = require('db');


var dutils = {
    getBaseURL : function(req) {
        return '.';
    }
}


exports.show = function(ctx) {
    send(templates['base.html'](ctx));
}

exports.base_template = function(ctx) {
    return templates['base.html'](ctx);
}

exports.upload_url = function (req) {
    var baseURL = dutils.getBaseURL(req);
    if (req.client) {
        return url.format({
            host: window.location.host,
            protocol: window.location.protocol,
            pathname: baseURL + '/upload',
            auth: req.userCtx.name
        });
    }
    else {
        return url.format({
            host: req.headers['Host'],
            protocol: 'http',
            pathname: baseURL + '/upload',
            auth: req.userCtx.name
        });
    }
};


exports.app_db = function(req, path) {
    var proto = 'http';
    // If couchdb is behind a proxy use that protocol.
    if (req.headers['X-Forwarded-Proto']) {
        proto = req.headers['X-Forwarded-Proto'];
    }
    return url.format({
        host: req.headers['Host'],
        protocol: proto,
        pathname: path
    });
}


exports.app_url = function (req, id) {
    var baseURL = dutils.getBaseURL(req);
    if (req.client) {
        return url.format({
            host: window.location.host,
            protocol: window.location.protocol,
            pathname: baseURL + '/_db/' + db.encode(id),
            query: 'attachments=true'
        });
    }
    else {
        return url.format({
            host: req.headers['Host'],
            protocol: 'http',
            pathname: baseURL + '/_db/' + db.encode(id),
            query: 'attachments=true'
        });
    }
};

exports.user_url = function(req, user) {
    var baseURL = dutils.getBaseURL(req);
    return url.format({
        host: req.headers['Host'],
        protocol: 'http',
        pathname: baseURL + '/user/' + db.encode(user)
    });

}

exports.install_script_url = function (req, id) {
    var baseURL = dutils.getBaseURL(req);
    if (req.client) {
        return url.format({
            host: window.location.host,
            protocol: window.location.protocol,
            pathname: baseURL + '/details/' + db.encode(id) + '/install.sh'
        });
    }
    else {
        return url.format({
            host: req.headers['Host'],
            protocol: 'http',
            pathname: baseURL + '/details/' + db.encode(id) + '/install.sh'
        });
    }
};

exports.flattr_url = function(req, id) {
    var baseURL = dutils.getBaseURL(req);
    log(req.headers['Host']);
    return url.format({
        host: req.headers['Host'],
        protocol: 'http',
        pathname: baseURL + '/details/' + db.encode(id)
    });
}


exports.open_path = function (doc) {
    var meta = doc.couchapp || doc.kanso;
    if (meta.index) {
        return doc.kanso.index;
    }
    if (meta.baseURL) {
        return doc.kanso.baseURL + '/';
    }
    if (doc.rewrites && doc.rewrites.length) {
        return '/_rewrite/';
    }
    if (doc._attachments && doc._attachments['index.html']) {
        return '/index.html';
    }
    if (doc._attachments && doc._attachments['index.htm']) {
        return '/index.htm';
    }
    return '';
};

exports.app_title = function (cfg) {
    var title = cfg.display_name || cfg.name;
    return title.substr(0, 1).toUpperCase() + title.substr(1);
};

exports.truncateParagraph = function (str, max_chars) {
    if (str.length > max_chars) {
        return str.substr(0, max_chars).replace(/\s[^\s]*$/, '\u2026');
    }
    return str;
};

/**
 * Uppercase first letter
 */

exports.toSentenceCase = function (str) {
    return str.substr(0, 1).toUpperCase() + str.substr(1);
};

exports.getBaseURL = function(location) {
    var loc = url.parse(location);
    if (loc.pathname.indexOf('_rewrite/') >= 0 ) {
        return loc.pathname.substring(0, (loc.pathname.indexOf('_rewrite/')  + 9 ) );
    } else {
        if (loc.pathname.indexOf('/') >= 0) {
            // 'http://something.com/db/'
            var result = loc.pathname;
            if (result[0] === '/') result = result.substring(1);
            if (loc.pathname.indexOf('/') >= 0) {
                result = result.substring(0, (result.indexOf('/') ));
            }


            return '/' + result;
        } else {
            return loc.pathname + '';
        }
    }

}


exports.getDBURL = function(location){
    var baseUrl = exports.getBaseURL(location);
    console.log(baseUrl);
   return  baseUrl + '/_db';
}


})};

/********** lib/validate **********/

kanso.moduleCache["lib/validate"] = {load: (function (module, exports, require) {

module.exports = function (newDoc, oldDoc, userCtx) {

    // deleting
    if (oldDoc && newDoc._deleted) {
        for (var i = 0; i < userCtx.roles.length; i++) {
            if (userCtx.roles[i] === '_admin') {
                // admins can delete any app
                return;
            }
        }
        var old_meta = oldDoc.couchapp || oldDoc.kanso;
        if (userCtx.name !== old_meta.pushed_by) {
            throw {
                unauthorized: 'Only the uploader can delete an existing app'
            };
        }
        // don't need to further validate a deleted document!
        return;
    }
    var manifest = newDoc.couchapp || newDoc.kanso;
    // updating
    if (oldDoc && !newDoc._deleted) {
        if (manifest.pushed_by !== manifest.pushed_by) {
            throw {
                unauthorized: 'Only the uploader can update an existing app'
            };
        }
    }

    // TODO: check for other doc types here like comments etc
    if (!newDoc.type && !manifest) {
        throw {
            forbidden: 'Document is missing "kanso" or "couchapp" property\n' +
                       'Adding apps requires at least kanso 0.1.4 or erica 2.0'
        };
    }

    if (manifest) {
        if (!userCtx.name) {
            throw {unauthorized: 'You must be logged in to upload an app'};
        }
        if (!manifest.pushed_by) {
            throw {forbidden: 'Manifest missing pushed_by property'};
        }
        if (!manifest.push_time) {
            throw {forbidden: 'Manifest missing push_time property'};
        }
        if (!manifest.build_time) {
            throw {forbidden: 'Manifest missing build_time property'};
        }
        if (!manifest.config) {
            throw {forbidden: 'Manifest missing config property'};
        }
        if (!manifest.config.name) {
            throw {forbidden: 'Manifest missing config.name property'};
        }
        if (!manifest.config.description) {
            throw {forbidden: 'Manifest missing config.description property'};
        }
        if (!manifest.config.long_description) {
            throw {forbidden: 'Manifest missing config.long_description property'};
        }
        if (!manifest.config.version) {
            throw {forbidden: 'Manifest missing config.version property'};
        }
        if (!manifest.config.screenshots) {
            throw {forbidden: 'Manifest missing config.screenshots property'};
        }
        if (!manifest.config.screenshots.length) {
            throw {forbidden: 'You must provide at least one screenshot'};
        }
        if (!manifest.config.promo_images) {
            throw {forbidden: 'Missing config.promo_images property'};
        }
        if (!manifest.config.icons) {
            throw {forbidden: 'Missing config.icons property'};
        }
        if (!manifest.config.icons['16']) {
            throw {forbidden: 'Missing 16x16 icon'};
        }
        if (!manifest.config.icons['48']) {
            throw {forbidden: 'Missing 48x48 icon'};
        }
        if (!manifest.config.icons['96']) {
            throw {forbidden: 'Missing 96x96 icon'};
        }
        if (!manifest.config.icons['128']) {
            throw {forbidden: 'Missing 128x128 icon'};
        }
        if (!manifest.config.promo_images.small) {
            throw {forbidden: 'You must provide at least a small promo image'};
        }
        if (!manifest.config.categories) {
            throw {forbidden: 'Missing property config.categories'};
        }
        if (!manifest.config.categories.length) {
            throw {forbidden: 'You must provide at least one category'};
        }

    }

};


})};

/********** lib/views **********/

kanso.moduleCache["lib/views"] = {load: (function (module, exports, require) {

exports.apps = {
    map: function (doc) {
        var meta = doc.couchapp || doc.kanso;
        if (!/^_design\//.test(doc._id) && meta) {
            emit([doc._id], meta);
        }
    }
};

exports.apps_by_category = {
    map: function (doc) {
        var meta = doc.couchapp || doc.kanso;
        if (!/^_design\//.test(doc._id) && meta) {
            var cfg = meta.config;
            if (cfg.categories && cfg.categories.length) {
                for (var i = 0; i < cfg.categories.length; i++) {
                    emit([cfg.categories[i]], meta);
                }
            }
            else {
                emit(['uncategorized'], meta);
            }
            emit('total', 1);
        }
    },
    reduce: function (keys, values, rereduce) {
        if (rereduce) {
            return sum(values);
        }
        return values.length;
    }
};

exports.apps_by_user = {
    map: function (doc) {
        var meta = doc.couchapp || doc.kanso;
        if (!/^_design\//.test(doc._id) && meta) {
            emit([meta.pushed_by], meta);
        }
    }
};

exports.hosting = {
    map : function(doc) {
        if (doc.type && doc.type === 'hosting') {
            emit(doc.name, null);
        }
    }
}

exports.keyword_search = {
    map : function(doc) {
        var meta = doc.couchapp || doc.kanso;
        if (!meta || !meta.config) return;
        var Tokenizer = require('views/lib/tokenizer');
        var _ = require('views/lib/underscore')._;

        var filter_junk = function(tokens) {
            var map = [];
            for (i in tokens) {
                var t = tokens[i];
                t = t.toLowerCase();
                // filter some junk
                if ((t.length !== 1) && (t.indexOf('_') < 0)) {
                    map.push(t);
                }
            }
            return map;
        }


        var tokenizer = new Tokenizer();
        var config = meta.config;
        var id_tokens = tokenizer.tokenize(config.name);
        var id_map = filter_junk(id_tokens);

        var desc_tokens = tokenizer.tokenize(config.description);
        var desc_map = filter_junk(desc_tokens);


        var long_description_tokens = tokenizer.tokenize(config.long_description);
        var l_desc_map = filter_junk(long_description_tokens);

        var keywords = _.union(id_map, desc_map, l_desc_map);

        var icon = config.icons["96"];
        var by = meta.pushed_by;


        var title = config.display_name || config.name;
        title = title.substr(0, 1).toUpperCase() + title.substr(1);

        _.each(keywords, function(token){
            emit(token, {
                _id : doc._id,
                name : config.name,
                display_name : title,
                description: config.description,
                keywords : keywords,
                icon_96 : icon,
                by : by
            });
        });



    }
}


})};

/********** views/lib/porter_stemmer **********/

kanso.moduleCache["views/lib/porter_stemmer"] = {load: (function (module, exports, require) {

/*
Copyright (c) 2011, Chris Umbel

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.
*/



// denote groups of consecutive consonants with a C and consecutive vowels
// with a V.
function categorizeGroups(token) {
    return token.replace(/[^aeiou]+/g, 'C').replace(/[aeiouy]+/g, 'V');
}

// denote single consonants with a C and single vowels with a V
function categorizeChars(token) {
    return token.replace(/[^aeiou]/g, 'C').replace(/[aeiouy]/g, 'V');
}

// calculate the "measure" M of a word. M is the count of VC sequences dropping
// an initial C if it exists and a trailing V if it exists.
function measure(token) {
    if(!token)
	return -1;

    return categorizeGroups(token).replace(/^C/, '').replace(/V$/, '').length / 2;
}

// determine if a token end with a double consonant i.e. happ
function endsWithDoublCons(token) {
    return token.match(/([^aeiou])\1$/);
}

// replace a pattern in a word. if a replacement occurs an optional callback
// can be called to post-process the result. if no match is made NULL is
// returned.
function attemptReplace(token, pattern, replacement, callback) {
    var result = null;

    if((typeof pattern == 'string') && token.substr(0 - pattern.length) == pattern)
        result = token.replace(new RegExp(pattern + '$'), replacement);
    else if((pattern instanceof RegExp) && token.match(pattern))
        result = token.replace(pattern, replacement);

    if(result && callback)
        return callback(result);
    else
        return result;
}

// attempt to replace a list of patterns/replacements on a token for a minimum
// measure M.
function attemptReplacePatterns(token, replacements, measureThreshold) {
    var replacement = null;

    for(var i = 0; i < replacements.length; i++) {
	if(measureThreshold == null || measure(attemptReplace(token, replacements[i][0], '')) > measureThreshold)
	    replacement = attemptReplace(token, replacements[i][0], replacements[i][1]);

	if(replacement)
	    break;
    }

    return replacement;
}

// replace a list of patterns/replacements on a word. if no match is made return
// the original token.
function replacePatterns(token, replacements, measureThreshold) {
    var result = attemptReplacePatterns(token, replacements, measureThreshold);
    token = result == null ? token : result;

    return token;
}

// step 1a as defined for the porter stemmer algorithm.
function step1a(token) {
    if(token.match(/(ss|i)es$/))
        return token.replace(/(ss|i)es$/, '$1');

    if(token.substr(-1) == 's' && token.substr(-2, 1) != 's')
        return token.replace(/s?$/, '');

    return token;
}

// step 1b as defined for the porter stemmer algorithm.
function step1b(token) {
    if(token.substr(-3) == 'eed') {
	if(measure(token.substr(0, token.length - 3)) > 0)
            return token.replace(/eed$/, 'ee');
    } else {
	var result = attemptReplace(token, /ed|ing$/, '', function(token) {
	    if(categorizeGroups(token).indexOf('V') > 0) {
		var result = attemptReplacePatterns(token, [['at', 'ate'],  ['bl', 'ble'], ['iz', 'ize']]);

		if(result)
		    return result;
		else {
		    if(endsWithDoublCons(token) && token.match(/[^lsz]$/))
			return token.replace(/([^aeiou])\1$/, '$1');

		    if(measure(token) == 1 && categorizeChars(token).substr(-3) == 'CVC' && token.match(/[^wxy]$/))
			return token + 'e';
		}

		return token;
	    }

	    return null;
	});

	if(result)
	    return result;
    }

    return token;
}

// step 1c as defined for the porter stemmer algorithm.
function step1c(token) {
    if(categorizeGroups(token).substr(-2, 1) == 'V') {
        if(token.substr(-1) == 'y')
            return token.replace(/y$/, 'i');
    }

    return token;
}

// step 2 as defined for the porter stemmer algorithm.
function step2(token) {
    return replacePatterns(token, [['ational', 'ate'], ['tional', 'tion'], ['enci', 'ence'], ['anci', 'ance'],
        ['izer', 'ize'], ['abli', 'able'], ['alli', 'al'], ['entli', 'ent'], ['eli', 'e'],
        ['ousli', 'ous'], ['ization', 'ize'], ['ation', 'ate'], ['ator', 'ate'],['alism', 'al'],
        ['iveness', 'ive'], ['fulness', 'ful'], ['ousness', 'ous'], ['aliti', 'al'],
        ['iviti', 'ive'], ['biliti', 'ble']], 0);
}

// step 3 as defined for the porter stemmer algorithm.
function step3(token) {
    return replacePatterns(token, [['icate', 'ic'], ['ative', ''], ['alize', 'al'],
				   ['iciti', 'ic'], ['ical', 'ic'], ['ful', ''], ['ness', '']], 0);
}

// step 4 as defined for the porter stemmer algorithm.
function step4(token) {
    return replacePatterns(token, [['al', ''], ['ance', ''], ['ence', ''], ['er', ''],
        ['ic', ''], ['able', ''], ['ible', ''], ['ant', ''],
        ['ement', ''], ['ment', ''], ['ent', ''], [/([st])ion/, '$1'], ['ou', ''], ['ism', ''],
        ['ate', ''], ['iti', ''], ['ous', ''], ['ive', ''],
        ['ize', '']], 1);
}

// step 5a as defined for the porter stemmer algorithm.
function step5a(token) {
    var m = measure(token);

    if((m > 1 && token.substr(-1) == 'e') || (m == 1 && !(categorizeChars(token).substr(-4, 3) == 'CVC' && token.match(/[^wxy].$/))))
        return token.replace(/e$/, '');

    return token;
}

// step 5b as defined for the porter stemmer algorithm.
function step5b(token) {
    if(measure(token) > 1) {
        if(endsWithDoublCons(token) && token.substr(-2) == 'll')
           return token.replace(/ll$/, 'l');
    }

    return token;
}



// perform full stemming algorithm on a single word
exports.stem = function(token) {
    return step5b(step5a(step4(step3(step2(step1c(step1b(step1a(token.toLowerCase())))))))).toString();
};



})};

/********** views/lib/underscore **********/

kanso.moduleCache["views/lib/underscore"] = {load: (function (module, exports, require) {

//     Underscore.js 1.2.0
//     (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
//     Underscore is freely distributable under the MIT license.
//     Portions of Underscore are inspired or borrowed from Prototype,
//     Oliver Steele's Functional, and John Resig's Micro-Templating.
//     For all details and documentation:
//     http://documentcloud.github.com/underscore

(function() {

  // Baseline setup
  // --------------

  // Establish the root object, `window` in the browser, or `global` on the server.
  var root = this;

  // Save the previous value of the `_` variable.
  var previousUnderscore = root._;

  // Establish the object that gets returned to break out of a loop iteration.
  var breaker = {};

  // Save bytes in the minified (but not gzipped) version:
  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;

  // Create quick reference variables for speed access to core prototypes.
  var slice            = ArrayProto.slice,
      unshift          = ArrayProto.unshift,
      toString         = ObjProto.toString,
      hasOwnProperty   = ObjProto.hasOwnProperty;

  // All **ECMAScript 5** native function implementations that we hope to use
  // are declared here.
  var
    nativeForEach      = ArrayProto.forEach,
    nativeMap          = ArrayProto.map,
    nativeReduce       = ArrayProto.reduce,
    nativeReduceRight  = ArrayProto.reduceRight,
    nativeFilter       = ArrayProto.filter,
    nativeEvery        = ArrayProto.every,
    nativeSome         = ArrayProto.some,
    nativeIndexOf      = ArrayProto.indexOf,
    nativeLastIndexOf  = ArrayProto.lastIndexOf,
    nativeIsArray      = Array.isArray,
    nativeKeys         = Object.keys,
    nativeBind         = FuncProto.bind;

  // Create a safe reference to the Underscore object for use below.
  var _ = function(obj) { return new wrapper(obj); };

  // Export the Underscore object for **CommonJS**, with backwards-compatibility
  // for the old `require()` API. If we're not in CommonJS, add `_` to the
  // global object.
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = _;
    _._ = _;
  } else {
    // Exported as a string, for Closure Compiler "advanced" mode.
    root['_'] = _;
  }

  // Current version.
  _.VERSION = '1.2.0';

  // Collection Functions
  // --------------------

  // The cornerstone, an `each` implementation, aka `forEach`.
  // Handles objects with the built-in `forEach`, arrays, and raw objects.
  // Delegates to **ECMAScript 5**'s native `forEach` if available.
  var each = _.each = _.forEach = function(obj, iterator, context) {
    if (obj == null) return;
    if (nativeForEach && obj.forEach === nativeForEach) {
      obj.forEach(iterator, context);
    } else if (obj.length === +obj.length) {
      for (var i = 0, l = obj.length; i < l; i++) {
        if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
      }
    } else {
      for (var key in obj) {
        if (hasOwnProperty.call(obj, key)) {
          if (iterator.call(context, obj[key], key, obj) === breaker) return;
        }
      }
    }
  };

  // Return the results of applying the iterator to each element.
  // Delegates to **ECMAScript 5**'s native `map` if available.
  _.map = function(obj, iterator, context) {
    var results = [];
    if (obj == null) return results;
    if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
    each(obj, function(value, index, list) {
      results[results.length] = iterator.call(context, value, index, list);
    });
    return results;
  };

  // **Reduce** builds up a single result from a list of values, aka `inject`,
  // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
  _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
    var initial = memo !== void 0;
    if (obj == null) obj = [];
    if (nativeReduce && obj.reduce === nativeReduce) {
      if (context) iterator = _.bind(iterator, context);
      return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
    }
    each(obj, function(value, index, list) {
      if (!initial) {
        memo = value;
        initial = true;
      } else {
        memo = iterator.call(context, memo, value, index, list);
      }
    });
    if (!initial) throw new TypeError("Reduce of empty array with no initial value");
    return memo;
  };

  // The right-associative version of reduce, also known as `foldr`.
  // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
  _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
    if (obj == null) obj = [];
    if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
      if (context) iterator = _.bind(iterator, context);
      return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
    }
    var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse();
    return _.reduce(reversed, iterator, memo, context);
  };

  // Return the first value which passes a truth test. Aliased as `detect`.
  _.find = _.detect = function(obj, iterator, context) {
    var result;
    any(obj, function(value, index, list) {
      if (iterator.call(context, value, index, list)) {
        result = value;
        return true;
      }
    });
    return result;
  };

  // Return all the elements that pass a truth test.
  // Delegates to **ECMAScript 5**'s native `filter` if available.
  // Aliased as `select`.
  _.filter = _.select = function(obj, iterator, context) {
    var results = [];
    if (obj == null) return results;
    if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
    each(obj, function(value, index, list) {
      if (iterator.call(context, value, index, list)) results[results.length] = value;
    });
    return results;
  };

  // Return all the elements for which a truth test fails.
  _.reject = function(obj, iterator, context) {
    var results = [];
    if (obj == null) return results;
    each(obj, function(value, index, list) {
      if (!iterator.call(context, value, index, list)) results[results.length] = value;
    });
    return results;
  };

  // Determine whether all of the elements match a truth test.
  // Delegates to **ECMAScript 5**'s native `every` if available.
  // Aliased as `all`.
  _.every = _.all = function(obj, iterator, context) {
    var result = true;
    if (obj == null) return result;
    if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
    each(obj, function(value, index, list) {
      if (!(result = result && iterator.call(context, value, index, list))) return breaker;
    });
    return result;
  };

  // Determine if at least one element in the object matches a truth test.
  // Delegates to **ECMAScript 5**'s native `some` if available.
  // Aliased as `any`.
  var any = _.some = _.any = function(obj, iterator, context) {
    iterator = iterator || _.identity;
    var result = false;
    if (obj == null) return result;
    if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
    each(obj, function(value, index, list) {
      if (result |= iterator.call(context, value, index, list)) return breaker;
    });
    return !!result;
  };

  // Determine if a given value is included in the array or object using `===`.
  // Aliased as `contains`.
  _.include = _.contains = function(obj, target) {
    var found = false;
    if (obj == null) return found;
    if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
    any(obj, function(value) {
      if (found = value === target) return true;
    });
    return found;
  };

  // Invoke a method (with arguments) on every item in a collection.
  _.invoke = function(obj, method) {
    var args = slice.call(arguments, 2);
    return _.map(obj, function(value) {
      return (method.call ? method || value : value[method]).apply(value, args);
    });
  };

  // Convenience version of a common use case of `map`: fetching a property.
  _.pluck = function(obj, key) {
    return _.map(obj, function(value){ return value[key]; });
  };

  // Return the maximum element or (element-based computation).
  _.max = function(obj, iterator, context) {
    if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
    if (!iterator && _.isEmpty(obj)) return -Infinity;
    var result = {computed : -Infinity};
    each(obj, function(value, index, list) {
      var computed = iterator ? iterator.call(context, value, index, list) : value;
      computed >= result.computed && (result = {value : value, computed : computed});
    });
    return result.value;
  };

  // Return the minimum element (or element-based computation).
  _.min = function(obj, iterator, context) {
    if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
    if (!iterator && _.isEmpty(obj)) return Infinity;
    var result = {computed : Infinity};
    each(obj, function(value, index, list) {
      var computed = iterator ? iterator.call(context, value, index, list) : value;
      computed < result.computed && (result = {value : value, computed : computed});
    });
    return result.value;
  };

  // Shuffle an array.
  _.shuffle = function(obj) {
    var shuffled = [], rand;
    each(obj, function(value, index, list) {
      if (index == 0) {
        shuffled[0] = value;
      } else {
        rand = Math.floor(Math.random() * (index + 1));
        shuffled[index] = shuffled[rand];
        shuffled[rand] = value;
      }
    });
    return shuffled;
  };

  // Sort the object's values by a criterion produced by an iterator.
  _.sortBy = function(obj, iterator, context) {
    return _.pluck(_.map(obj, function(value, index, list) {
      return {
        value : value,
        criteria : iterator.call(context, value, index, list)
      };
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }), 'value');
  };

  // Groups the object's values by a criterion produced by an iterator
  _.groupBy = function(obj, iterator) {
    var result = {};
    each(obj, function(value, index) {
      var key = iterator(value, index);
      (result[key] || (result[key] = [])).push(value);
    });
    return result;
  };

  // Use a comparator function to figure out at what index an object should
  // be inserted so as to maintain order. Uses binary search.
  _.sortedIndex = function(array, obj, iterator) {
    iterator || (iterator = _.identity);
    var low = 0, high = array.length;
    while (low < high) {
      var mid = (low + high) >> 1;
      iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
    }
    return low;
  };

  // Safely convert anything iterable into a real, live array.
  _.toArray = function(iterable) {
    if (!iterable)                return [];
    if (iterable.toArray)         return iterable.toArray();
    if (_.isArray(iterable))      return slice.call(iterable);
    if (_.isArguments(iterable))  return slice.call(iterable);
    return _.values(iterable);
  };

  // Return the number of elements in an object.
  _.size = function(obj) {
    return _.toArray(obj).length;
  };

  // Array Functions
  // ---------------

  // Get the first element of an array. Passing **n** will return the first N
  // values in the array. Aliased as `head`. The **guard** check allows it to work
  // with `_.map`.
  _.first = _.head = function(array, n, guard) {
    return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
  };

  // Returns everything but the last entry of the array. Especcialy useful on
  // the arguments object. Passing **n** will return all the values in
  // the array, excluding the last N. The **guard** check allows it to work with
  // `_.map`.
  _.initial = function(array, n, guard) {
    return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
  };

  // Get the last element of an array. Passing **n** will return the last N
  // values in the array. The **guard** check allows it to work with `_.map`.
  _.last = function(array, n, guard) {
    return (n != null) && !guard ? slice.call(array, array.length - n) : array[array.length - 1];
  };

  // Returns everything but the first entry of the array. Aliased as `tail`.
  // Especially useful on the arguments object. Passing an **index** will return
  // the rest of the values in the array from that index onward. The **guard**
  // check allows it to work with `_.map`.
  _.rest = _.tail = function(array, index, guard) {
    return slice.call(array, (index == null) || guard ? 1 : index);
  };

  // Trim out all falsy values from an array.
  _.compact = function(array) {
    return _.filter(array, function(value){ return !!value; });
  };

  // Return a completely flattened version of an array.
  _.flatten = function(array) {
    return _.reduce(array, function(memo, value) {
      if (_.isArray(value)) return memo.concat(_.flatten(value));
      memo[memo.length] = value;
      return memo;
    }, []);
  };

  // Return a version of the array that does not contain the specified value(s).
  _.without = function(array) {
    return _.difference(array, slice.call(arguments, 1));
  };

  // Produce a duplicate-free version of the array. If the array has already
  // been sorted, you have the option of using a faster algorithm.
  // Aliased as `unique`.
  _.uniq = _.unique = function(array, isSorted, iterator) {
    var initial = iterator ? _.map(array, iterator) : array;
    var result = [];
    _.reduce(initial, function(memo, el, i) {
      if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
        memo[memo.length] = el;
        result[result.length] = array[i];
      }
      return memo;
    }, []);
    return result;
  };

  // Produce an array that contains the union: each distinct element from all of
  // the passed-in arrays.
  _.union = function() {
    return _.uniq(_.flatten(arguments));
  };

  // Produce an array that contains every item shared between all the
  // passed-in arrays. (Aliased as "intersect" for back-compat.)
  _.intersection = _.intersect = function(array) {
    var rest = slice.call(arguments, 1);
    return _.filter(_.uniq(array), function(item) {
      return _.every(rest, function(other) {
        return _.indexOf(other, item) >= 0;
      });
    });
  };

  // Take the difference between one array and another.
  // Only the elements present in just the first array will remain.
  _.difference = function(array, other) {
    return _.filter(array, function(value){ return !_.include(other, value); });
  };

  // Zip together multiple lists into a single array -- elements that share
  // an index go together.
  _.zip = function() {
    var args = slice.call(arguments);
    var length = _.max(_.pluck(args, 'length'));
    var results = new Array(length);
    for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
    return results;
  };

  // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
  // we need this function. Return the position of the first occurrence of an
  // item in an array, or -1 if the item is not included in the array.
  // Delegates to **ECMAScript 5**'s native `indexOf` if available.
  // If the array is large and already in sort order, pass `true`
  // for **isSorted** to use binary search.
  _.indexOf = function(array, item, isSorted) {
    if (array == null) return -1;
    var i, l;
    if (isSorted) {
      i = _.sortedIndex(array, item);
      return array[i] === item ? i : -1;
    }
    if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
    for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
    return -1;
  };

  // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
  _.lastIndexOf = function(array, item) {
    if (array == null) return -1;
    if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
    var i = array.length;
    while (i--) if (array[i] === item) return i;
    return -1;
  };

  // Generate an integer Array containing an arithmetic progression. A port of
  // the native Python `range()` function. See
  // [the Python documentation](http://docs.python.org/library/functions.html#range).
  _.range = function(start, stop, step) {
    if (arguments.length <= 1) {
      stop = start || 0;
      start = 0;
    }
    step = arguments[2] || 1;

    var len = Math.max(Math.ceil((stop - start) / step), 0);
    var idx = 0;
    var range = new Array(len);

    while(idx < len) {
      range[idx++] = start;
      start += step;
    }

    return range;
  };

  // Function (ahem) Functions
  // ------------------

  // Create a function bound to a given object (assigning `this`, and arguments,
  // optionally). Binding with arguments is also known as `curry`.
  // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
  // We check for `func.bind` first, to fail fast when `func` is undefined.
  _.bind = function(func, obj) {
    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
    var args = slice.call(arguments, 2);
    return function() {
      return func.apply(obj, args.concat(slice.call(arguments)));
    };
  };

  // Bind all of an object's methods to that object. Useful for ensuring that
  // all callbacks defined on an object belong to it.
  _.bindAll = function(obj) {
    var funcs = slice.call(arguments, 1);
    if (funcs.length == 0) funcs = _.functions(obj);
    each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
    return obj;
  };

  // Memoize an expensive function by storing its results.
  _.memoize = function(func, hasher) {
    var memo = {};
    hasher || (hasher = _.identity);
    return function() {
      var key = hasher.apply(this, arguments);
      return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
    };
  };

  // Delays a function for the given number of milliseconds, and then calls
  // it with the arguments supplied.
  _.delay = function(func, wait) {
    var args = slice.call(arguments, 2);
    return setTimeout(function(){ return func.apply(func, args); }, wait);
  };

  // Defers a function, scheduling it to run after the current call stack has
  // cleared.
  _.defer = function(func) {
    return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
  };

  // Internal function used to implement `_.throttle` and `_.debounce`.
  var limit = function(func, wait, debounce) {
    var timeout;
    return function() {
      var context = this, args = arguments;
      var throttler = function() {
        timeout = null;
        func.apply(context, args);
      };
      if (debounce) clearTimeout(timeout);
      if (debounce || !timeout) timeout = setTimeout(throttler, wait);
    };
  };

  // Returns a function, that, when invoked, will only be triggered at most once
  // during a given window of time.
  _.throttle = function(func, wait) {
    return limit(func, wait, false);
  };

  // Returns a function, that, as long as it continues to be invoked, will not
  // be triggered. The function will be called after it stops being called for
  // N milliseconds.
  _.debounce = function(func, wait) {
    return limit(func, wait, true);
  };

  // Returns a function that will be executed at most one time, no matter how
  // often you call it. Useful for lazy initialization.
  _.once = function(func) {
    var ran = false, memo;
    return function() {
      if (ran) return memo;
      ran = true;
      return memo = func.apply(this, arguments);
    };
  };

  // Returns the first function passed as an argument to the second,
  // allowing you to adjust arguments, run code before and after, and
  // conditionally execute the original function.
  _.wrap = function(func, wrapper) {
    return function() {
      var args = [func].concat(slice.call(arguments));
      return wrapper.apply(this, args);
    };
  };

  // Returns a function that is the composition of a list of functions, each
  // consuming the return value of the function that follows.
  _.compose = function() {
    var funcs = slice.call(arguments);
    return function() {
      var args = slice.call(arguments);
      for (var i = funcs.length - 1; i >= 0; i--) {
        args = [funcs[i].apply(this, args)];
      }
      return args[0];
    };
  };

  // Returns a function that will only be executed after being called N times.
  _.after = function(times, func) {
    return function() {
      if (--times < 1) { return func.apply(this, arguments); }
    };
  };

  // Object Functions
  // ----------------

  // Retrieve the names of an object's properties.
  // Delegates to **ECMAScript 5**'s native `Object.keys`
  _.keys = nativeKeys || function(obj) {
    if (obj !== Object(obj)) throw new TypeError('Invalid object');
    var keys = [];
    for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key;
    return keys;
  };

  // Retrieve the values of an object's properties.
  _.values = function(obj) {
    return _.map(obj, _.identity);
  };

  // Return a sorted list of the function names available on the object.
  // Aliased as `methods`
  _.functions = _.methods = function(obj) {
    var names = [];
    for (var key in obj) {
      if (_.isFunction(obj[key])) names.push(key);
    }
    return names.sort();
  };

  // Extend a given object with all the properties in passed-in object(s).
  _.extend = function(obj) {
    each(slice.call(arguments, 1), function(source) {
      for (var prop in source) {
        if (source[prop] !== void 0) obj[prop] = source[prop];
      }
    });
    return obj;
  };

  // Fill in a given object with default properties.
  _.defaults = function(obj) {
    each(slice.call(arguments, 1), function(source) {
      for (var prop in source) {
        if (obj[prop] == null) obj[prop] = source[prop];
      }
    });
    return obj;
  };

  // Create a (shallow-cloned) duplicate of an object.
  _.clone = function(obj) {
    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
  };

  // Invokes interceptor with the obj, and then returns obj.
  // The primary purpose of this method is to "tap into" a method chain, in
  // order to perform operations on intermediate results within the chain.
  _.tap = function(obj, interceptor) {
    interceptor(obj);
    return obj;
  };

  // Internal recursive comparison function.
  function eq(a, b, stack) {
    // Identical objects are equal. `0 === -0`, but they aren't identical.
    // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
    if (a === b) return a !== 0 || 1 / a == 1 / b;
    // A strict comparison is necessary because `null == undefined`.
    if (a == null) return a === b;
    // Compare object types.
    var typeA = typeof a;
    if (typeA != typeof b) return false;
    // Optimization; ensure that both values are truthy or falsy.
    if (!a != !b) return false;
    // `NaN` values are equal.
    if (_.isNaN(a)) return _.isNaN(b);
    // Compare string objects by value.
    var isStringA = _.isString(a), isStringB = _.isString(b);
    if (isStringA || isStringB) return isStringA && isStringB && String(a) == String(b);
    // Compare number objects by value.
    var isNumberA = _.isNumber(a), isNumberB = _.isNumber(b);
    if (isNumberA || isNumberB) return isNumberA && isNumberB && +a == +b;
    // Compare boolean objects by value. The value of `true` is 1; the value of `false` is 0.
    var isBooleanA = _.isBoolean(a), isBooleanB = _.isBoolean(b);
    if (isBooleanA || isBooleanB) return isBooleanA && isBooleanB && +a == +b;
    // Compare dates by their millisecond values.
    var isDateA = _.isDate(a), isDateB = _.isDate(b);
    if (isDateA || isDateB) return isDateA && isDateB && a.getTime() == b.getTime();
    // Compare RegExps by their source patterns and flags.
    var isRegExpA = _.isRegExp(a), isRegExpB = _.isRegExp(b);
    if (isRegExpA || isRegExpB) {
      // Ensure commutative equality for RegExps.
      return isRegExpA && isRegExpB &&
             a.source == b.source &&
             a.global == b.global &&
             a.multiline == b.multiline &&
             a.ignoreCase == b.ignoreCase;
    }
    // Ensure that both values are objects.
    if (typeA != 'object') return false;
    // Unwrap any wrapped objects.
    if (a._chain) a = a._wrapped;
    if (b._chain) b = b._wrapped;
    // Invoke a custom `isEqual` method if one is provided.
    if (_.isFunction(a.isEqual)) return a.isEqual(b);
    // Assume equality for cyclic structures. The algorithm for detecting cyclic structures is
    // adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
    var length = stack.length;
    while (length--) {
      // Linear search. Performance is inversely proportional to the number of unique nested
      // structures.
      if (stack[length] == a) return true;
    }
    // Add the first object to the stack of traversed objects.
    stack.push(a);
    var size = 0, result = true;
    if (a.length === +a.length || b.length === +b.length) {
      // Compare object lengths to determine if a deep comparison is necessary.
      size = a.length;
      result = size == b.length;
      if (result) {
        // Deep compare array-like object contents, ignoring non-numeric properties.
        while (size--) {
          // Ensure commutative equality for sparse arrays.
          if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
        }
      }
    } else {
      // Deep compare objects.
      for (var key in a) {
        if (hasOwnProperty.call(a, key)) {
          // Count the expected number of properties.
          size++;
          // Deep compare each member.
          if (!(result = hasOwnProperty.call(b, key) && eq(a[key], b[key], stack))) break;
        }
      }
      // Ensure that both objects contain the same number of properties.
      if (result) {
        for (key in b) {
          if (hasOwnProperty.call(b, key) && !size--) break;
        }
        result = !size;
      }
    }
    // Remove the first object from the stack of traversed objects.
    stack.pop();
    return result;
  }

  // Perform a deep comparison to check if two objects are equal.
  _.isEqual = function(a, b) {
    return eq(a, b, []);
  };

  // Is a given array or object empty?
  _.isEmpty = function(obj) {
    if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
    for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
    return true;
  };

  // Is a given value a DOM element?
  _.isElement = function(obj) {
    return !!(obj && obj.nodeType == 1);
  };

  // Is a given value an array?
  // Delegates to ECMA5's native Array.isArray
  _.isArray = nativeIsArray || function(obj) {
    return toString.call(obj) === '[object Array]';
  };

  // Is a given variable an object?
  _.isObject = function(obj) {
    return obj === Object(obj);
  };

  // Is a given variable an arguments object?
  _.isArguments = function(obj) {
    return !!(obj && hasOwnProperty.call(obj, 'callee'));
  };

  // Is a given value a function?
  _.isFunction = function(obj) {
    return !!(obj && obj.constructor && obj.call && obj.apply);
  };

  // Is a given value a string?
  _.isString = function(obj) {
    return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
  };

  // Is a given value a number?
  _.isNumber = function(obj) {
    return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));
  };

  // Is the given value `NaN`? `NaN` happens to be the only value in JavaScript
  // that does not equal itself.
  _.isNaN = function(obj) {
    return obj !== obj;
  };

  // Is a given value a boolean?
  _.isBoolean = function(obj) {
    return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
  };

  // Is a given value a date?
  _.isDate = function(obj) {
    return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
  };

  // Is the given value a regular expression?
  _.isRegExp = function(obj) {
    return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
  };

  // Is a given value equal to null?
  _.isNull = function(obj) {
    return obj === null;
  };

  // Is a given variable undefined?
  _.isUndefined = function(obj) {
    return obj === void 0;
  };

  // Utility Functions
  // -----------------

  // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
  // previous owner. Returns a reference to the Underscore object.
  _.noConflict = function() {
    root._ = previousUnderscore;
    return this;
  };

  // Keep the identity function around for default iterators.
  _.identity = function(value) {
    return value;
  };

  // Run a function **n** times.
  _.times = function (n, iterator, context) {
    for (var i = 0; i < n; i++) iterator.call(context, i);
  };

  // Escape a string for HTML interpolation.
  _.escape = function(string) {
    return (''+string).replace(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
  };

  // Add your own custom functions to the Underscore object, ensuring that
  // they're correctly added to the OOP wrapper as well.
  _.mixin = function(obj) {
    each(_.functions(obj), function(name){
      addToWrapper(name, _[name] = obj[name]);
    });
  };

  // Generate a unique integer id (unique within the entire client session).
  // Useful for temporary DOM ids.
  var idCounter = 0;
  _.uniqueId = function(prefix) {
    var id = idCounter++;
    return prefix ? prefix + id : id;
  };

  // By default, Underscore uses ERB-style template delimiters, change the
  // following template settings to use alternative delimiters.
  _.templateSettings = {
    evaluate    : /<%([\s\S]+?)%>/g,
    interpolate : /<%=([\s\S]+?)%>/g,
    escape      : /<%-([\s\S]+?)%>/g
  };

  // JavaScript micro-templating, similar to John Resig's implementation.
  // Underscore templating handles arbitrary delimiters, preserves whitespace,
  // and correctly escapes quotes within interpolated code.
  _.template = function(str, data) {
    var c  = _.templateSettings;
    var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
      'with(obj||{}){__p.push(\'' +
      str.replace(/\\/g, '\\\\')
         .replace(/'/g, "\\'")
         .replace(c.escape, function(match, code) {
           return "',_.escape(" + code.replace(/\\'/g, "'") + "),'";
         })
         .replace(c.interpolate, function(match, code) {
           return "'," + code.replace(/\\'/g, "'") + ",'";
         })
         .replace(c.evaluate || null, function(match, code) {
           return "');" + code.replace(/\\'/g, "'")
                              .replace(/[\r\n\t]/g, ' ') + "__p.push('";
         })
         .replace(/\r/g, '\\r')
         .replace(/\n/g, '\\n')
         .replace(/\t/g, '\\t')
         + "');}return __p.join('');";
    var func = new Function('obj', tmpl);
    return data ? func(data) : func;
  };

  // The OOP Wrapper
  // ---------------

  // If Underscore is called as a function, it returns a wrapped object that
  // can be used OO-style. This wrapper holds altered versions of all the
  // underscore functions. Wrapped objects may be chained.
  var wrapper = function(obj) { this._wrapped = obj; };

  // Expose `wrapper.prototype` as `_.prototype`
  _.prototype = wrapper.prototype;

  // Helper function to continue chaining intermediate results.
  var result = function(obj, chain) {
    return chain ? _(obj).chain() : obj;
  };

  // A method to easily add functions to the OOP wrapper.
  var addToWrapper = function(name, func) {
    wrapper.prototype[name] = function() {
      var args = slice.call(arguments);
      unshift.call(args, this._wrapped);
      return result(func.apply(_, args), this._chain);
    };
  };

  // Add all of the Underscore functions to the wrapper object.
  _.mixin(_);

  // Add all mutator Array functions to the wrapper.
  each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
    var method = ArrayProto[name];
    wrapper.prototype[name] = function() {
      method.apply(this._wrapped, arguments);
      return result(this._wrapped, this._chain);
    };
  });

  // Add all accessor Array functions to the wrapper.
  each(['concat', 'join', 'slice'], function(name) {
    var method = ArrayProto[name];
    wrapper.prototype[name] = function() {
      return result(method.apply(this._wrapped, arguments), this._chain);
    };
  });

  // Start chaining a wrapped Underscore object.
  wrapper.prototype.chain = function() {
    this._chain = true;
    return this;
  };

  // Extracts the result from a wrapped and chained object.
  wrapper.prototype.value = function() {
    return this._wrapped;
  };

})();

})};

/********** views/lib/tokenizer **********/

kanso.moduleCache["views/lib/tokenizer"] = {load: (function (module, exports, require) {

var _ = require('views/lib/underscore')._;

var stemmer = require('views/lib/porter_stemmer');

var TreebankWordTokenizer = function() {
};

TreebankWordTokenizer.prototype.trim = function(array) {
    if(array[array.length - 1] == '')
        array.pop();

    if(array[0] == '')
        array.shift();

    return array;
};



var contractions2 = [
    /(.)('ll|'re|'ve|n't|'s|'m|'d)\b/ig,
    /\b(can)(not)\b/ig,
    /\b(D)('ye)\b/ig,
    /\b(Gim)(me)\b/ig,
    /\b(Gon)(na)\b/ig,
    /\b(Got)(ta)\b/ig,
    /\b(Lem)(me)\b/ig,
    /\b(Mor)('n)\b/ig,
    /\b(T)(is)\b/ig,
    /\b(T)(was)\b/ig,
    /\b(Wan)(na)\b/ig];

var contractions3 = [
    /\b(Whad)(dd)(ya)\b/ig,
    /\b(Wha)(t)(cha)\b/ig
];


// a list of commonly used words that have little meaning and can be excluded
// from analysis.
var stopwords = [
    'about', 'after', 'all', 'also', 'am', 'an', 'and', 'another', 'any', 'app', 'are', 'as', 'at', 'be',
    'because', 'been', 'before', 'being', 'between', 'both', 'but', 'by', 'came', 'can',
    'come', 'could', 'did', 'do', 'each', 'for', 'from', 'get', 'got', 'has', 'had',
    'he', 'have', 'her', 'here', 'him', 'himself', 'his', 'how', 'if', 'in', 'into',
    'is', 'it', 'like', 'make', 'many', 'me', 'might', 'more', 'most', 'much', 'must',
    'my', 'never', 'now', 'of', 'on', 'only', 'or', 'other', 'our', 'out', 'over',
    'said', 'same', 'see', 'should', 'since', 'some', 'still', 'such', 'take', 'than',
    'that', 'the', 'their', 'them', 'then', 'there', 'these', 'they', 'this', 'those',
    'through', 'to', 'too', 'under', 'up', 'very', 'was', 'way', 'we', 'well', 'were',
    'what', 'where', 'which', 'while', 'who', 'with', 'would', 'you', 'your',
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '$', '1',
    '2', '3', '4', '5', '6', '7', '8', '9', '0', '_'];



TreebankWordTokenizer.prototype.tokenize = function(text) {
    contractions2.forEach(function(regexp) {
	    text = text.replace(regexp,"$1 $2");
    });

    contractions3.forEach(function(regexp) {
	    text = text.replace(regexp,"$1 $2 $3");
    });

    // most punctuation
    text = text.replace(/([^\w\.\'\-\/\+\<\>,&])/g, " $1 ");



    // commas if followed by space
    text = text.replace(/(,\s)/g, " $1");

    // single quotes if followed by a space
    text = text.replace(/('\s)/g, " $1");

    // periods before newline or end of string
    text = text.replace(/\. *(\n|$)/g, " . ");

    // words with periods at the end
    text = text.replace(/\./g, '');

    // single quotes
    text = text.replace(/'/g, "");

    text = _.without(text.split(/\s+/), '');

    var result = [];
    text.forEach(function(token) {
        if(stopwords.indexOf(token) == -1)
            result.push(token);
            var stemmed = stemmer.stem(token);
            if (stemmed !== token) result.push(stemmed);
    });


    return  result;
};

module.exports = TreebankWordTokenizer;


})};

/********** db **********/

kanso.moduleCache["db"] = {load: (function (module, exports, require) {

/*global $: false */

/**
 * ## DB Module
 *
 * This contains the core functions for dealing with CouchDB. That includes
 * document CRUD operations, querying views and creating/deleting databases.
 *
 *
 * ### Events
 *
 * The db module is an EventEmitter. See the
 * [events package](http://kan.so/packages/details/events) for more information.
 *
 * #### unauthorized
 *
 * Emitted by the db module when a request results in a 401 Unauthorized
 * response. This is listened to used by the session module to help detect
 * session timeouts etc.
 *
 * ```javascript
 * var db = require("db");
 *
 * db.on('unauthorized', function (req) {
 *     // req is the ajax request object which returned 401
 * });
 * ```
 *
 * @module
 */


var events = require('events'),
    _ = require('underscore')._;


/**
 * Tests if running in the browser
 *
 * @returns {Boolean}
 */

function isBrowser() {
    return (typeof(window) !== 'undefined');
}


/**
 * This module is an EventEmitter, used for emitting 'unauthorized' events
 */

var exports = module.exports = new events.EventEmitter();


/**
 * Taken from jQuery 1.4.4 so we can support more recent versions of jQuery.
 */

var httpData = function (xhr, type, s) {
    var ct = xhr.getResponseHeader("content-type") || "",
        xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
        data = xml ? xhr.responseXML : xhr.responseText;

    if (xml && data.documentElement.nodeName === "parsererror") {
        $.error("parsererror");
    }
    if (s && s.dataFilter) {
        data = s.dataFilter(data, type);
    }
    if (typeof data === "string") {
        if (type === "json" || !type && ct.indexOf("json") >= 0) {
            data = $.parseJSON(data);
        }
        else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
            $.globalEval(data);
        }
    }
    return data;
};


/**
 * Returns a function for handling ajax responses from jquery and calls
 * the callback with the data or appropriate error.
 *
 * @param {Function} callback(err,response)
 * @api private
 */

function onComplete(options, callback) {
    return function (req) {
        var resp;
        var ctype = req.getResponseHeader('Content-Type');
        if (ctype === 'application/json' || ctype === 'text/json') {
            try {
                resp = httpData(req, "json");
            }
            catch (e) {
                return callback(e);
            }
        }
        else {
            if (options.expect_json) {
                try {
                    resp = httpData(req, "json");
                }
                catch (ex) {
                    return callback(
                        new Error('Expected JSON response, got ' + ctype)
                    );
                }
            }
            else {
                resp = req.responseText;
            }
        }
        if (req.status === 401) {
            exports.emit('unauthorized', req);
        }
        if (req.status === 200 || req.status === 201 || req.status === 202) {
            callback(null, resp);
        }
        else if (resp.error || resp.reason) {
            var err = new Error(resp.reason || resp.error);
            err.error = resp.error;
            err.reason = resp.reason;
            err.code = resp.code;
            err.status = req.status;
            callback(err);
        }
        else {
            // TODO: map status code to meaningful error message
            var err2 = new Error('Returned status code: ' + req.status);
            err2.status = req.status;
            callback(err2);
        }
    };
}


/**
 * Attempts to guess the database name and design doc id from the current URL,
 * or the loc paramter. Returns an object with 'db', 'design_doc' and 'root'
 * properties, or null for a URL not matching the expected format (perhaps
 * behing a vhost).
 *
 * You wouldn't normally use this function directly, but use `db.current()` to
 * return a DB object bound to the current database instead.
 *
 * @name guessCurrent([loc])
 * @param {String} loc - An alternative URL to use instead of window.location
 *     (optional)
 * @returns {Object|null} - An object with 'db', 'design_doc' and 'root'
 *     properties, or null for a URL not matching the
 *     expected format (perhaps behing a vhost)
 * @api public
 */

exports.guessCurrent = function (loc) {
    var loc = loc || window.location;

    /**
     * A database must be named with all lowercase letters (a-z), digits (0-9),
     * or any of the _$()+-/ characters and must end with a slash in the URL.
     * The name has to start with a lowercase letter (a-z).
     *
     * http://wiki.apache.org/couchdb/HTTP_database_API
     */

    var re = /\/([a-z][a-z0-9_\$\(\)\+-\/]*)\/_design\/([^\/]+)\//;
    var match = re.exec(loc.pathname);

    if (match) {
        return {
            db: match[1],
            design_doc: match[2],
            root: '/'
        }
    }
    return null;
};

/**
 * Converts an object to a string of properly escaped URL parameters.
 *
 * @name escapeUrlParams([obj])
 * @param {Object} obj - An object containing url parameters, with
 *       parameter names stored as property names (or keys).
 * @returns {String}
 * @api public
 */

exports.escapeUrlParams = function (obj) {
    var rv = [ ];
    for (var key in obj) {
        rv.push(
            encodeURIComponent(key) +
                '=' + encodeURIComponent(obj[key])
        );
    }
    return (rv.length > 0 ? ('?' + rv.join('&')) : '');
};

/**
 * Encodes a document id or view, list or show name. This also will make sure
 * the forward-slash is not escaped for documents with id's beginning with
 * "\_design/".
 *
 * @name encode(str)
 * @param {String} str - the name or id to escape
 * @returns {String}
 * @api public
 */

exports.encode = function (str) {
    return encodeURIComponent(str).replace(/^_design%2F/, '_design/');
};


/**
 * Properly encodes query parameters to CouchDB views etc. Handle complex
 * keys and other non-string parameters by passing through JSON.stringify.
 * Returns a shallow-copied clone of the original query after complex values
 * have been stringified.
 *
 * @name stringifyQuery(query)
 * @param {Object} query
 * @returns {Object}
 * @api public
 */

exports.stringifyQuery = function (query) {
    var q = {};
    for (var k in query) {
        if (typeof query[k] !== 'string') {
            q[k] = JSON.stringify(query[k]);
        }
        else {
            q[k] = query[k];
        }
    }
    return q;
};


/**
 * Make a request, with some default settings, proper callback
 * handling, and optional caching. Used behind-the-scenes by
 * most other DB module functions.
 *
 * @name request(options, callback)
 * @param {Object} options
 * @param {Function} callback(err,response)
 * @api public
 */

exports.request = function (options, callback) {
    options.complete = onComplete(options, callback);
    options.dataType = 'json';
    $.ajax(options);
};


/**
 * Creates a CouchDB database.
 *
 * If you're running behind a virtual host you'll need to set up
 * appropriate rewrites for a DELETE request to '/' either turning off safe
 * rewrites or setting up a new vhost entry.
 *
 * @name createDatabase(name, callback)
 * @param {String} name
 * @param {Function} callback(err,response)
 * @api public
 */

exports.createDatabase = function (name, callback) {
    var req = {
        type: 'PUT',
        url: '/' + exports.encode(name.replace(/^\/+/, ''))
    };
    exports.request(req, callback);
};

/**
 * Deletes a CouchDB database.
 *
 * If you're running behind a virtual host you'll need to set up
 * appropriate rewrites for a DELETE request to '/' either turning off safe
 * rewrites or setting up a new vhost entry.
 *
 * @name deleteDatabase(name, callback)
 * @param {String} name
 * @param {Function} callback(err,response)
 * @api public
 */

// TODO: detect when 'name' argument is a url and don't construct a url then
exports.deleteDatabase = function (name, callback) {
    var req = {
        type: 'DELETE',
        url: '/' + exports.encode(name.replace(/^\/+/, ''))
    };
    exports.request(req, callback);
};


/**
 * Lists all databses
 *
 * If you're running behind a virtual host you'll need to set up
 * appropriate rewrites for a DELETE request to '/' either turning off safe
 * rewrites or setting up a new vhost entry.
 *
 * @name allDbs(callback)
 * @param {Function} callback(err,response)
 * @api public
 */

exports.allDbs = function (callback) {
    var req = {
        type: 'GET',
        url: '/_all_dbs'
    };
    exports.request(req, callback);
};


/**
 * Returns a new UUID generated by CouchDB. Its possible to cache
 * multiple UUIDs for later use, to avoid making too many requests.
 *
 * @name newUUID(cacheNum, callback)
 * @param {Number} cacheNum (optional, default: 1)
 * @param {Function} callback(err,response)
 * @api public
 */

var uuidCache = [];

exports.newUUID = function (cacheNum, callback) {
    if (!callback) {
        callback = cacheNum;
        cacheNum = 1;
    }
    if (uuidCache.length) {
        return callback(null, uuidCache.shift());
    }
    var req = {
        url: '/_uuids',
        data: {count: cacheNum},
        expect_json: true
    };
    exports.request(req, function (err, resp) {
        if (err) {
            return callback(err);
        }
        uuidCache = resp.uuids;
        callback(null, uuidCache.shift());
    });
};


/**
 * DB object created by use(dbname) function
 */

function DB(url) {
    this.url = url;
    // add the module functions to the DB object
    for (var k in exports) {
        this[k] = exports[k];
    }
};


/**
 * Creates a new DB object with methods operating on the database at 'url'
 *
 * The DB object also exposes the same module-level methods (eg, createDatabase)
 * so it can be used in-place of the db exports object, for example:
 *
 * ```javascript
 * var db = require('db').use('mydb');
 *
 * db.createDatabase('example', function (err, resp) {
 *     // do something
 * });
 * ```
 *
 * @name use(url)
 * @param {String} url - The url to bind the new DB object to
 * @returns {DB}
 * @api public
 */

// TODO: handle full urls, not just db names
exports.use = function (url) {
    /* Force leading slash; make absolute path */
    return new DB(url);
};

/**
 * Attempts to guess the current DB name and return a DB object using that.
 * Should work reliably unless running behind a virtual host.
 *
 * Throws an error if the current database url cannot be detected.
 *
 * The DB object also exposes the same module-level methods (eg, createDatabase)
 * so it can be used in-place of the db exports object, for example:
 *
 * ```javascript
 * var db = require('db').current();
 *
 * db.createDatabase('example', function (err, resp) {
 *     // do something
 * });
 * ```
 *
 * @name current()
 * @returns {DB}
 * @api public
 */

exports.current = function () {
    // guess current db url etc
    var curr = exports.guessCurrent();
    if (!curr) {
        throw new Error(
            'Cannot guess current database URL, if running behind a virtual ' +
            'host you need to explicitly set the database URL using ' +
            'db.use(database_url) instead of db.current()'
        );
    }
    return exports.use(curr.db);
};


/**
 * Fetches a rewrite from the database the app is running on. Results
 * are passed to the callback, with the first argument of the callback
 * reserved for any exceptions that occurred (node.js style).
 *
 * @name DB.getRewrite(name, path, [q], callback)
 * @param {String} name - the name of the design doc
 * @param {String} path
 * @param {Object} q (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.getRewrite = function (name, path, /*optional*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }
    // prepend forward-slash if missing
    path = (path[0] === '/') ? path: '/' + path;

    try {
        var data = exports.stringifyQuery(q);
    }
    catch (e) {
        return callback(e);
    }
    var req = {
        url: this.url + '/_design/' + exports.encode(name) + '/_rewrite' + path,
        data: data
    };
    exports.request(req, callback);
};


/**
 * Queries all design documents in the database.
 *
 * @name DB.allDesignDocs([q], callback)
 * @param {Object} q - query parameters to pass to /_all_docs (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.allDesignDocs = function (/*optional*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }
    q.startkey = '"_design"';
    q.endkey = '"_design0"';
    this.allDocs(q, callback);
};


/**
 * Queries all documents in the database (include design docs).
 *
 * @name DB.allDocs([q], callback)
 * @param {Object} q - query parameters to pass to /_all_docs (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.allDocs = function (/*optional*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }
    try {
        var data = exports.stringifyQuery(q);
    }
    catch (e) {
        return callback(e);
    }
    var req = {
        url: this.url + '/_all_docs',
        data: data,
        expect_json: true
    };
    exports.request(req, callback);
};


/**
 * Fetches a document from the database the app is running on. Results are
 * passed to the callback, with the first argument of the callback reserved
 * for any exceptions that occurred (node.js style).
 *
 * @name DB.getDoc(id, [q], callback)
 * @param {String} id
 * @param {Object} q (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.getDoc = function (id, /*optional*/q, callback) {
    if (!id) {
        throw new Error('getDoc requires an id parameter to work properly');
    }
    if (!callback) {
        callback = q;
        q = {};
    }
    try {
        var data = exports.stringifyQuery(q);
    }
    catch (e) {
        return callback(e);
    }
    var req = {
        url: this.url + '/' + exports.encode(id),
        expect_json: true,
        data: data
    };
    exports.request(req, callback);
};


/**
 * Saves a document to the database the app is running on. Results are
 * passed to the callback, with the first argument of the callback reserved
 * for any exceptions that occurred (node.js style).
 *
 * @name DB.saveDoc(doc, callback)
 * @param {Object} doc
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.saveDoc = function (doc, callback) {
    var method, url = this.url;
    if (doc._id === undefined) {
        method = "POST";
    }
    else {
        method = "PUT";
        url += '/' + doc._id;
    }
    try {
        var data = JSON.stringify(doc);
    }
    catch (e) {
        return callback(e);
    }
    var req = {
        type: method,
        url: url,
        data: data,
        processData: false,
        contentType: 'application/json',
        expect_json: true
    };
    exports.request(req, callback);
};

/**
 * Deletes a document from the database the app is running on. Results are
 * passed to the callback, with the first argument of the callback reserved
 * for any exceptions that occurred (node.js style).
 *
 * @name DB.removeDoc(doc, callback)
 * @param {Object} doc
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.removeDoc = function (doc, callback) {
    if (!doc._id) {
        throw new Error('removeDoc requires an _id field in your document');
    }
    if (!doc._rev) {
        throw new Error('removeDoc requires a _rev field in your document');
    }
    var req = {
        type: 'DELETE',
        url: this.url + '/' + exports.encode(doc._id) +
             '?rev=' + exports.encode(doc._rev)
    };
    exports.request(req, callback);
};


/**
 * Fetches a view from the database the app is running on. Results are
 * passed to the callback, with the first argument of the callback reserved
 * for any exceptions that occurred (node.js style).
 *
 * @name DB.getView(name, view, [q], callback)
 * @param {String} name - name of the design doc to use
 * @param {String} view - name of the view
 * @param {Object} q (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.getView = function (name, view, /*opt*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }
    var viewname = exports.encode(view);
    try {
        var data = exports.stringifyQuery(q);
    }
    catch (e) {
        return callback(e);
    }
    var req = {
        url: (this.url +
            '/_design/' + exports.encode(name) +
            '/_view/' + viewname
        ),
        expect_json: true,
        data: data
    };
    exports.request(req, callback);
};


/**
 * Fetches a spatial view from the database the app is running on. Results are
 * passed to the callback, with the first argument of the callback reserved
 * for any exceptions that occurred (node.js style).
 *
 * __Parameters:__
 * * bbox - the bounding box filter e.g.: bbox: '0,0,180,90'
 * * plane_bounds - e.g.: plane_bounds: '-180,-90,180,90'
 * * stale - stale: 'ok' prevents the spatial index to be rebuilt
 * * count - count: true will only return the number of geometries
 *
 * @name DB.getSpatialView(name, view, q, callback)
 * @param {String} name - name of the design doc to use
 * @param {String} view - name of the view
 * @param {Object} q - query parameters (see options above)
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.getSpatialView = function (name, view, q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }
    var viewname = exports.encode(view);
    try {
        var data = exports.stringifyQuery(q);
    }
    catch (e) {
        return callback(e);
    }
    var req = {
        url: (this.url +
            '/_design/' + exports.encode(name) +
            '/_spatial/' + viewname
        ),
        expect_json: true,
        data: data
    };
    exports.request(req, callback);
};


/**
 * Transforms and fetches a view through a list from the database the app
 * is running on. Results are passed to the callback, with the first
 * argument of the callback reserved for any exceptions that occurred
 * (node.js style).
 *
 * @name DB.getList(name, list, view, [q], callback)
 * @param {String} name - name of the design doc to use
 * @param {String} list - name of the list function
 * @param {String} view - name of the view to apply the list function to
 * @param {Object} q (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

// TODO: run list function client-side?
DB.prototype.getList = function (name, list, view, /*optional*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }
    var listname = exports.encode(list);
    var viewname = exports.encode(view);
    try {
        var data = exports.stringifyQuery(q);
    }
    catch (e) {
        return callback(e);
    }
    var req = {
        url: this.url + '/_design/' + exports.encode(name) +
            '/_list/' + listname + '/' + viewname,
        data: data
    };
    exports.request(req, callback);
};

/**
 * Transforms and fetches a document through a show from the database the app
 * is running on. Results are passed to the callback, with the first
 * argument of the callback reserved for any exceptions that occurred
 * (node.js style).
 *
 * @name DB.getShow(name, show, docid, [q], callback)
 * @param {String} name - name of the design doc to use
 * @param {String} show - name of the show function
 * @param {String} docid - id of the document to apply the show function to
 * @param {Object} q (optional)
 * @param {Function} callback(err,response)
 * @api public
 */

// TODO: run show function client-side?
DB.prototype.getShow = function (name, show, docid, /*optional*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }
    try {
        var data = exports.stringifyQuery(q);
    }
    catch (e) {
        return callback(e);
    }
    var showname = exports.encode(show);
    var show_url = this.url + '/_design/' +
        exports.encode(name) + '/_show/' + exports.encode(showname);
    var req = {
        url: show_url + (docid ? '/' + exports.encode(docid): ''),
        data: data
    };
    exports.request(req, callback);
};


/**
 * Fetch a design document from CouchDB.
 *
 * @name DB.getDesignDoc(name, callback)
 * @param name The name of (i.e. path to) the design document without the
 *     preceeding "\_design/".
 * @param callback The callback to invoke when the request completes.
 * @api public
 */

DB.prototype.getDesignDoc = function (name, callback) {
    this.getDoc('_design/' + name, function (err, ddoc) {
        if (err) {
            return callback(err);
        }
        return callback(null, ddoc);
    });
};

/**
 * Gets information about the database.
 *
 * @name DB.info(callback)
 * @param {Function} callback(err,response)
 * @api public
 */

DB.prototype.info = function (callback) {
    var req = {
        url: this.url,
        expect_json: true,
    };
    exports.request(req, callback);
};


/**
 * Listen to the changes feed for a database.
 *
 * __Options:__
 * * _filter_ - the filter function to use
 * * _since_ - the update_seq to start listening from
 * * _heartbeat_ - the heartbeat time (defaults to 10 seconds)
 * * _include_docs_ - whether to include docs in the results
 *
 * Returning false from the callback will cancel the changes listener
 *
 * @name DB.changes([q], callback)
 * @param {Object} q (optional) query parameters (see options above)
 * @param {Function} callback(err,response)
 * @api public
 */

// TODO: change this to use an EventEmitter
DB.prototype.changes = function (/*optional*/q, callback) {
    if (!callback) {
        callback = q;
        q = {};
    }

    var that = this;

    q = q || {};
    q.feed = 'longpoll';
    q.heartbeat = q.heartbeat || 10000;

    function getChanges(since) {
        q.since = since;
        try {
            var data = exports.stringifyQuery(q);
        }
        catch (e) {
            return callback(e);
        }
        var req = {
            type: 'GET',
            expect_json: true,
            url: that.url + '/_changes',
            data: data
        };
        var cb = function (err, data) {
            var result = callback.apply(this, arguments);
            if (result !== false) {
                getChanges(data.last_seq);
            }
        }
        exports.request(req, cb);
    }

    // use setTimeout to pass control back to the browser briefly to
    // allow the loading spinner to stop on page load
    setTimeout(function () {
        if (q.hasOwnProperty('since')) {
            getChanges(q.since);
        }
        else {
            that.info(function (err, info) {
                if (err) {
                    return callback(err);
                }
                getChanges(info.update_seq);
            });
        }
    }, 0);
};


/**
 * Saves a list of documents, without using separate requests.
 * This function uses CouchDB's HTTP bulk document API (_bulk_docs).
 * The return value is an array of objects, each containing an 'id'
 * and a 'rev' field. The return value is passed to the callback you
 * provide via its second argument; the first argument of the callback
 * is reserved for any exceptions that occurred (node.js style).
 *
 * **Options:**
 * * *all_or\_nothing* - Require that all documents be saved
 *   successfully (or saved with a conflict); otherwise roll
 *   back the operation.
 *
 * @name DB.bulkSave(docs, [options], callback)
 * @param {Array} docs An array of documents; each document is an object
 * @param {Object} options (optional) Options for the bulk-save operation.
 * @param {Function} callback(err,response) - A function to accept results
 *          and/or errors. Document update conflicts are reported in the
 *          results array.
 * @api public
 */

DB.prototype.bulkSave = function (docs, /*optional*/ options, callback) {
    if (!_.isArray(docs)) {
        throw new Error(
            'bulkSave requires an array of documents to work properly'
        );
    }
    if (!callback) {
        callback = options;
        options = {};
    }
    options.docs = docs;
    try {
        var data = JSON.stringify(options);
    }
    catch (e) {
        return callback(e);
    }
    var req = {
        type: 'POST',
        url: this.url + '/_bulk_docs',
        data: data,
        processData: false,
        contentType: 'application/json',
        expect_json: true
    };
    exports.request(req, callback);
};


/**
 * Requests a list of documents, using only a single HTTP request.
 * This function uses CouchDB's HTTP bulk document API (_all_docs).
 * The return value is an array of objects, each of which is a document.
 * The return value is passed to the callback you provide via its second
 * argument; the first argument of the callback is reserved for any
 * exceptions that occurred (node.js style).
 *
 * @name DB.bulkGet(keys, [q], callback)
 * @param {Array} keys An array of documents identifiers (i.e. strings).
 * @param {Object} q (optional) Query parameters for the bulk-read operation.
 * @param {Function} callback(err,response) - A function to accept results
 *          and/or errors. Document update conflicts are reported in the
 *          results array.
 * @api public
 */

DB.prototype.bulkGet = function (keys, /*optional*/ q, callback) {
    if (keys && !_.isArray(keys)) {
        throw new Error(
            'bulkGet requires that _id values be supplied as a list'
        );
    }
    if (!callback) {
        callback = q;
        q = {};
    }

    /* Encode every query-string option:
        CouchDB requires that these be JSON, even though they
        will be URL-encoded as part of the request process. */

    try {
        for (var k in q) {
            q[k] = JSON.stringify(q[k]);
        }
    }
    catch (e) {
        return callback(e);
    }

    /* Make request:
        If we have a list of keys, use a post request containing
        a JSON-encoded list of keys. Otherwise, use a get request. */

    var req = {
        expect_json: true,
        url: this.url + '/_all_docs' + exports.escapeUrlParams(q)
    };
    if (keys) {
        try {
            var data = JSON.stringify({ keys: keys});
        }
        catch (e) {
            return callback(e);
        }
        req = _.extend(req, {
            type: 'POST',
            processData: false,
            contentType: 'application/json',
            data: data
        });
    } else {
        req = _.extend(req, {
            type: 'GET'
        });
    }

    exports.request(req, callback);
};


/**
 * DB methods can only be called client-side
 */

_.each(_.keys(DB.prototype), function (k) {
    var _fn = DB.prototype[k];
    DB.prototype[k] = function () {
        if (!isBrowser()) {
            throw new Error(k + ' cannot be called server-side');
        }
        return _fn.apply(this, arguments);
    };
});


})};

/********** events **********/

kanso.moduleCache["events"] = {load: (function (module, exports, require) {

/**
 * ## Events module
 *
 * This is a browser port of the node.js events module. Many objects and
 * modules emit events and these are instances of events.EventEmitter.
 *
 * You can access this module by doing: `require("events")`
 *
 * Functions can then be attached to objects, to be executed when an event is
 * emitted. These functions are called listeners.
 *
 * @module
 */


/**
 * To access the EventEmitter class, require('events').EventEmitter.
 *
 * When an EventEmitter instance experiences an error, the typical action is to
 * emit an 'error' event. Error events are treated as a special case. If there
 * is no listener for it, then the default action is for the error to throw.
 *
 * All EventEmitters emit the event 'newListener' when new listeners are added.
 *
 * @name events.EventEmitter
 * @api public
 *
 * ```javascript
 * var EventEmitter = require('events').EventEmitter;
 *
 * // create an event emitter
 * var emitter = new EventEmitter();
 * ```
 */

var EventEmitter = exports.EventEmitter = function () {};

var isArray = Array.isArray || function (obj) {
    return toString.call(obj) === '[object Array]';
};


/**
 * By default EventEmitters will print a warning if more than 10 listeners are
 * added for a particular event. This is a useful default which helps finding
 * memory leaks. Obviously not all Emitters should be limited to 10. This
 * function allows that to be increased. Set to zero for unlimited.
 *
 * @name emitter.setMaxListeners(n)
 * @param {Number} n - The maximum number of listeners
 * @api public
 */

// By default EventEmitters will print a warning if more than
// 10 listeners are added to it. This is a useful default which
// helps finding memory leaks.
//
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
var defaultMaxListeners = 10;
EventEmitter.prototype.setMaxListeners = function(n) {
  if (!this._events) this._events = {};
  this._events.maxListeners = n;
};


/**
 * Execute each of the listeners in order with the supplied arguments.
 *
 * @name emitter.emit(event, [arg1], [arg2], [...])
 * @param {String} event - The event name/id to fire
 * @api public
 */

EventEmitter.prototype.emit = function(type) {
  // If there is no 'error' event listener then throw.
  if (type === 'error') {
    if (!this._events || !this._events.error ||
        (isArray(this._events.error) && !this._events.error.length))
    {
      if (arguments[1] instanceof Error) {
        throw arguments[1]; // Unhandled 'error' event
      } else {
        throw new Error("Uncaught, unspecified 'error' event.");
      }
      return false;
    }
  }

  if (!this._events) return false;
  var handler = this._events[type];
  if (!handler) return false;

  if (typeof handler == 'function') {
    switch (arguments.length) {
      // fast cases
      case 1:
        handler.call(this);
        break;
      case 2:
        handler.call(this, arguments[1]);
        break;
      case 3:
        handler.call(this, arguments[1], arguments[2]);
        break;
      // slower
      default:
        var args = Array.prototype.slice.call(arguments, 1);
        handler.apply(this, args);
    }
    return true;

  } else if (isArray(handler)) {
    var args = Array.prototype.slice.call(arguments, 1);

    var listeners = handler.slice();
    for (var i = 0, l = listeners.length; i < l; i++) {
      listeners[i].apply(this, args);
    }
    return true;

  } else {
    return false;
  }
};


/**
 * Adds a listener to the end of the listeners array for the specified event.
 *
 * @name emitter.on(event, listener) | emitter.addListener(event, listener)
 * @param {String} event - The event name/id to listen for
 * @param {Function} listener - The function to bind to the event
 * @api public
 *
 * ```javascript
 * session.on('change', function (userCtx) {
 *     console.log('session changed!');
 * });
 * ```
 */

// EventEmitter is defined in src/node_events.cc
// EventEmitter.prototype.emit() is also defined there.
EventEmitter.prototype.addListener = function(type, listener) {
  if ('function' !== typeof listener) {
    throw new Error('addListener only takes instances of Function');
  }

  if (!this._events) this._events = {};

  // To avoid recursion in the case that type == "newListeners"! Before
  // adding it to the listeners, first emit "newListeners".
  this.emit('newListener', type, listener);

  if (!this._events[type]) {
    // Optimize the case of one listener. Don't need the extra array object.
    this._events[type] = listener;
  } else if (isArray(this._events[type])) {

    // Check for listener leak
    if (!this._events[type].warned) {
      var m;
      if (this._events.maxListeners !== undefined) {
        m = this._events.maxListeners;
      } else {
        m = defaultMaxListeners;
      }

      if (m && m > 0 && this._events[type].length > m) {
        this._events[type].warned = true;
        console.error('(node) warning: possible EventEmitter memory ' +
                      'leak detected. %d listeners added. ' +
                      'Use emitter.setMaxListeners() to increase limit.',
                      this._events[type].length);
        console.trace();
      }
    }

    // If we've already got an array, just append.
    this._events[type].push(listener);
  } else {
    // Adding the second element, need to change to array.
    this._events[type] = [this._events[type], listener];
  }

  return this;
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

/**
 * Adds a one time listener for the event. This listener is invoked only the
 * next time the event is fired, after which it is removed.
 *
 * @name emitter.once(event, listener)
 * @param {String} event- The event name/id to listen for
 * @param {Function} listener - The function to bind to the event
 * @api public
 *
 * ```javascript
 * db.once('unauthorized', function (req) {
 *     // this event listener will fire once, then be unbound
 * });
 * ```
 */

EventEmitter.prototype.once = function(type, listener) {
  var self = this;
  self.on(type, function g() {
    self.removeListener(type, g);
    listener.apply(this, arguments);
  });

  return this;
};

/**
 * Remove a listener from the listener array for the specified event. Caution:
 * changes array indices in the listener array behind the listener.
 *
 * @name emitter.removeListener(event, listener)
 * @param {String} event - The event name/id to remove the listener from
 * @param {Function} listener - The listener function to remove
 * @api public
 *
 * ```javascript
 * var callback = function (init) {
 *     console.log('duality app loaded');
 * };
 * devents.on('init', callback);
 * // ...
 * devents.removeListener('init', callback);
 * ```
 */

EventEmitter.prototype.removeListener = function(type, listener) {
  if ('function' !== typeof listener) {
    throw new Error('removeListener only takes instances of Function');
  }

  // does not use listeners(), so no side effect of creating _events[type]
  if (!this._events || !this._events[type]) return this;

  var list = this._events[type];

  if (isArray(list)) {
    var i = list.indexOf(listener);
    if (i < 0) return this;
    list.splice(i, 1);
    if (list.length == 0)
      delete this._events[type];
  } else if (this._events[type] === listener) {
    delete this._events[type];
  }

  return this;
};

/**
 * Removes all listeners, or those of the specified event.
 *
 * @name emitter.removeAllListeners([event])
 * @param {String} event - Event name/id to remove all listeners for (optional)
 * @api public
 */

EventEmitter.prototype.removeAllListeners = function(type) {
  // does not use listeners(), so no side effect of creating _events[type]
  if (type && this._events && this._events[type]) this._events[type] = null;
  return this;
};

/**
 * Returns an array of listeners for the specified event. This array can be
 * manipulated, e.g. to remove listeners.
 *
 * @name emitter.listeners(event)
 * @param {String} events - The event name/id to return listeners for
 * @api public
 *
 * ```javascript
 * session.on('change', function (stream) {
 *     console.log('session changed');
 * });
 * console.log(util.inspect(session.listeners('change'))); // [ [Function] ]
 * ```
 */

EventEmitter.prototype.listeners = function(type) {
  if (!this._events) this._events = {};
  if (!this._events[type]) this._events[type] = [];
  if (!isArray(this._events[type])) {
    this._events[type] = [this._events[type]];
  }
  return this._events[type];
};


/**
 * @name emitter Event: 'newListener'
 *
 * This event is emitted any time someone adds a new listener.
 *
 * ```javascript
 * emitter.on('newListener', function (event, listener) {
 *     // new listener added
 * });
 * ```
 */


})};

/********** session **********/

kanso.moduleCache["session"] = {load: (function (module, exports, require) {

/**
 * ## Session module
 *
 * This module contains functions related to session management. Logging in,
 * logging out and checking the current state of a user's session.
 *
 * Functions in this module follow the node.js callback style. The first
 * argument is an error object (if one occurred), the following arguments are
 * the results of the operation. The callback is always the last argument to a
 * function.
 *
 *
 * ### Events
 *
 * The session module is an EventEmitter. See the
 * [events package](http://kan.so/packages/details/events) for more information.
 *
 * #### change
 *
 * Emitted whenever a change to the user's session is detected, this
 * can occur as the result of a login/logout call or by getting the user's
 * session info (and it's changed).
 *
 * ```javascript
 * var session = require("session");
 *
 * session.on('change', function (userCtx) {
 *     // update session information, eg "Logged in as ..."
 * });
 * ```
 *
 * @module
 */


var events = require('events'),
    db = require('db');


/**
 * Quick utility function for testing if running in the browser, since
 * these functions won't run on CouchDB server-side
 */

function isBrowser() {
    return (typeof(window) !== 'undefined');
}

/**
 * When a db call results in an unauthorized response, the user's session is
 * checked to see if their session has timed out or they've logged out in
 * another screen.
 *
 * This check is throttled to once per second, to avoid flooding the server if
 * multiple requests are made with incorrect permissions.
 */

var last_session_check = 0;

db.on('unauthorized', function (req) {
    // db call returned 'Unauthorized', check the user's session if it's not
    // been checked on an 'Unauthorized' repsonse in the last second
    if (last_session_check < new Date().getTime() - 1000) {
        exports.info();
    }
});


/**
 * This module is an EventEmitter, used for handling 'change' events
 */

var exports = module.exports = new events.EventEmitter();


/**
 * Attempt to login using the username and password provided.
 *
 * @name login(username, password, callback)
 * @param {String} username - the username to login with
 * @param {String} password - the user's password (unhashed)
 * @param {Function} callback - function called with the result of the login
 *     attempt
 * @api public
 *
 * ```javascript
 * session.login('testuser', 'password', function (err, response) {
 *     if (err) // an error occurred logging in
 *     else     // success
 * });
 * ```
 */

exports.login = function (username, password, callback) {
    if (!isBrowser()) {
        throw new Error('login cannot be called server-side');
    }
    db.request({
        type: "POST",
        url: "/_session",
        data: {name: username, password: password}
    },
    function (err, resp) {
        if (resp && resp.ok) {
            // TODO: for some reason resp.name is set to null in the response
            // even though the roles are correct for the user! Look into this
            // and see if its a bug in couchdb, for now, just using the username
            // given to the login function instead, since we know the login
            // request was accepted.
            exports.userCtx = {name: username, roles: resp.roles};
            exports.session = {userCtx: exports.userCtx};
            exports.emit('change', exports.userCtx);
        }
        if (callback) {
            callback(err, resp);
        }
    });
};


/**
 * Logs out the current user.
 *
 * @name logout(callback)
 * @param {Function} callback - function called with the result of the logout
 *     attempt
 * @api public
 *
 * ```javascript
 * session.logout(function (err, response) {
 *     if (err) // an error occurred logging out
 *     else     // success
 * });
 * ```
 */

exports.logout = function (callback) {
    if (!isBrowser()) {
        throw new Error('logout cannot be called server-side');
    }
    db.request({
        type: "DELETE",
        url: "/_session", // don't need baseURL, /_session always available
        username: "_",
        password : "_"
    },
    function (err, resp) {
        if (resp && resp.ok) {
            exports.userCtx = {name: null, roles: []};
            exports.session = {userCtx: exports.userCtx};
            exports.emit('change', exports.userCtx);
        }
        if (callback) {
            callback(err, resp);
        }
    });
};


/**
 * Returns the current user's session information. The info object contains a
 * `userCtx` property and an `info` property. The first contains the name and
 * roles of the current user, the second contains information about the user
 * database and authentication handlers.
 *
 * @name info(callback)
 * @param {Function} callback - function called with the session information
 * @api public
 *
 * ```javascript
 * session.info(function (err, info) {
 *     if (err) // an error occurred getting session info
 *     else     // success
 * });
 * ```
 */

exports.info = function (callback) {
    if (!isBrowser()) {
        throw new Error('info cannot be called server-side');
    }
    db.request({
        type: "GET",
        url: "/_session"
    },
    function (err, resp) {
        var oldUserCtx = exports.userCtx;
        exports.session = resp;
        exports.userCtx = (resp && resp.userCtx) || {name: null, roles: []};
        // TODO: should this check for differences in more than just name?
        if (!oldUserCtx || oldUserCtx.name !== exports.userCtx.name) {
            exports.emit('change', exports.userCtx);
        }
        if (callback) {
            callback(err, resp);
        }
    });
};


})};

/********** handlebars **********/

kanso.moduleCache["handlebars"] = {load: (function (module, exports, require) {

// lib/handlebars/base.js
var Handlebars = {};

Handlebars.VERSION = "1.0.beta.2";

Handlebars.helpers  = {};
Handlebars.partials = {};

Handlebars.registerHelper = function(name, fn, inverse) {
  if(inverse) { fn.not = inverse; }
  this.helpers[name] = fn;
};

Handlebars.registerPartial = function(name, str) {
  this.partials[name] = str;
};

Handlebars.registerHelper('helperMissing', function(arg) {
  if(arguments.length === 2) {
    return undefined;
  } else {
    throw new Error("Could not find property '" + arg + "'");
  }
});

Handlebars.registerHelper('blockHelperMissing', function(context, options) {
  var inverse = options.inverse || function() {}, fn = options.fn;


  var ret = "";
  var type = Object.prototype.toString.call(context);

  if(type === "[object Function]") {
    context = context();
  }

  if(context === true) {
    return fn(this);
  } else if(context === false || context == null) {
    return inverse(this);
  } else if(type === "[object Array]") {
    if(context.length > 0) {
      for(var i=0, j=context.length; i<j; i++) {
        ret = ret + fn(context[i]);
      }
    } else {
      ret = inverse(this);
    }
    return ret;
  } else {
    return fn(context);
  }
});

Handlebars.registerHelper('each', function(context, options) {
  var fn = options.fn, inverse = options.inverse;
  var ret = "";

  if(context && context.length > 0) {
    for(var i=0, j=context.length; i<j; i++) {
      ret = ret + fn(context[i]);
    }
  } else {
    ret = inverse(this);
  }
  return ret;
});

Handlebars.registerHelper('if', function(context, options) {
  if(!context || Handlebars.Utils.isEmpty(context)) {
    return options.inverse(this);
  } else {
    return options.fn(this);
  }
});

Handlebars.registerHelper('unless', function(context, options) {
  var fn = options.fn, inverse = options.inverse;
  options.fn = inverse;
  options.inverse = fn;

  return Handlebars.helpers['if'].call(this, context, options);
});

Handlebars.registerHelper('with', function(context, options) {
  return options.fn(context);
});
;
// lib/handlebars/compiler/parser.js
/* Jison generated parser */
var handlebars = (function(){

var parser = {trace: function trace() { },
yy: {},
symbols_: {"error":2,"root":3,"program":4,"EOF":5,"statements":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"OPEN_PARTIAL":24,"params":25,"hash":26,"param":27,"STRING":28,"INTEGER":29,"BOOLEAN":30,"hashSegments":31,"hashSegment":32,"ID":33,"EQUALS":34,"pathSegments":35,"SEP":36,"$accept":0,"$end":1},
terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"OPEN_PARTIAL",28:"STRING",29:"INTEGER",30:"BOOLEAN",33:"ID",34:"EQUALS",36:"SEP"},
productions_: [0,[3,2],[4,3],[4,1],[4,0],[6,1],[6,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,3],[13,4],[7,2],[17,3],[17,2],[17,2],[17,1],[25,2],[25,1],[27,1],[27,1],[27,1],[27,1],[26,1],[31,2],[31,1],[32,3],[32,3],[32,3],[32,3],[21,1],[35,3],[35,1]],
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {

var $0 = $$.length - 1;
switch (yystate) {
case 1: return $$[$0-1] 
break;
case 2: this.$ = new yy.ProgramNode($$[$0-2], $$[$0]) 
break;
case 3: this.$ = new yy.ProgramNode($$[$0]) 
break;
case 4: this.$ = new yy.ProgramNode([]) 
break;
case 5: this.$ = [$$[$0]] 
break;
case 6: $$[$0-1].push($$[$0]); this.$ = $$[$0-1] 
break;
case 7: this.$ = new yy.InverseNode($$[$0-2], $$[$0-1], $$[$0]) 
break;
case 8: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0]) 
break;
case 9: this.$ = $$[$0] 
break;
case 10: this.$ = $$[$0] 
break;
case 11: this.$ = new yy.ContentNode($$[$0]) 
break;
case 12: this.$ = new yy.CommentNode($$[$0]) 
break;
case 13: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]) 
break;
case 14: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]) 
break;
case 15: this.$ = $$[$0-1] 
break;
case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]) 
break;
case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true) 
break;
case 18: this.$ = new yy.PartialNode($$[$0-1]) 
break;
case 19: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1]) 
break;
case 20: 
break;
case 21: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]] 
break;
case 22: this.$ = [[$$[$0-1]].concat($$[$0]), null] 
break;
case 23: this.$ = [[$$[$0-1]], $$[$0]] 
break;
case 24: this.$ = [[$$[$0]], null] 
break;
case 25: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]; 
break;
case 26: this.$ = [$$[$0]] 
break;
case 27: this.$ = $$[$0] 
break;
case 28: this.$ = new yy.StringNode($$[$0]) 
break;
case 29: this.$ = new yy.IntegerNode($$[$0]) 
break;
case 30: this.$ = new yy.BooleanNode($$[$0]) 
break;
case 31: this.$ = new yy.HashNode($$[$0]) 
break;
case 32: $$[$0-1].push($$[$0]); this.$ = $$[$0-1] 
break;
case 33: this.$ = [$$[$0]] 
break;
case 34: this.$ = [$$[$0-2], $$[$0]] 
break;
case 35: this.$ = [$$[$0-2], new yy.StringNode($$[$0])] 
break;
case 36: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])] 
break;
case 37: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])] 
break;
case 38: this.$ = new yy.IdNode($$[$0]) 
break;
case 39: $$[$0-2].push($$[$0]); this.$ = $$[$0-2]; 
break;
case 40: this.$ = [$$[$0]] 
break;
}
},
table: [{3:1,4:2,5:[2,4],6:3,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],24:[1,15]},{1:[3]},{5:[1,16]},{5:[2,3],7:17,8:18,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,19],20:[2,3],22:[1,13],23:[1,14],24:[1,15]},{5:[2,5],14:[2,5],15:[2,5],16:[2,5],19:[2,5],20:[2,5],22:[2,5],23:[2,5],24:[2,5]},{4:20,6:3,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],24:[1,15]},{4:21,6:3,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],24:[1,15]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],24:[2,9]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],24:[2,10]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],24:[2,11]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],24:[2,12]},{17:22,21:23,33:[1,25],35:24},{17:26,21:23,33:[1,25],35:24},{17:27,21:23,33:[1,25],35:24},{17:28,21:23,33:[1,25],35:24},{21:29,33:[1,25],35:24},{1:[2,1]},{6:30,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],24:[1,15]},{5:[2,6],14:[2,6],15:[2,6],16:[2,6],19:[2,6],20:[2,6],22:[2,6],23:[2,6],24:[2,6]},{17:22,18:[1,31],21:23,33:[1,25],35:24},{10:32,20:[1,33]},{10:34,20:[1,33]},{18:[1,35]},{18:[2,24],21:40,25:36,26:37,27:38,28:[1,41],29:[1,42],30:[1,43],31:39,32:44,33:[1,45],35:24},{18:[2,38],28:[2,38],29:[2,38],30:[2,38],33:[2,38],36:[1,46]},{18:[2,40],28:[2,40],29:[2,40],30:[2,40],33:[2,40],36:[2,40]},{18:[1,47]},{18:[1,48]},{18:[1,49]},{18:[1,50],21:51,33:[1,25],35:24},{5:[2,2],8:18,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,2],22:[1,13],23:[1,14],24:[1,15]},{14:[2,20],15:[2,20],16:[2,20],19:[2,20],22:[2,20],23:[2,20],24:[2,20]},{5:[2,7],14:[2,7],15:[2,7],16:[2,7],19:[2,7],20:[2,7],22:[2,7],23:[2,7],24:[2,7]},{21:52,33:[1,25],35:24},{5:[2,8],14:[2,8],15:[2,8],16:[2,8],19:[2,8],20:[2,8],22:[2,8],23:[2,8],24:[2,8]},{14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],24:[2,14]},{18:[2,22],21:40,26:53,27:54,28:[1,41],29:[1,42],30:[1,43],31:39,32:44,33:[1,45],35:24},{18:[2,23]},{18:[2,26],28:[2,26],29:[2,26],30:[2,26],33:[2,26]},{18:[2,31],32:55,33:[1,56]},{18:[2,27],28:[2,27],29:[2,27],30:[2,27],33:[2,27]},{18:[2,28],28:[2,28],29:[2,28],30:[2,28],33:[2,28]},{18:[2,29],28:[2,29],29:[2,29],30:[2,29],33:[2,29]},{18:[2,30],28:[2,30],29:[2,30],30:[2,30],33:[2,30]},{18:[2,33],33:[2,33]},{18:[2,40],28:[2,40],29:[2,40],30:[2,40],33:[2,40],34:[1,57],36:[2,40]},{33:[1,58]},{14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],24:[2,13]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],24:[2,16]},{5:[2,17],14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],24:[2,17]},{5:[2,18],14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],24:[2,18]},{18:[1,59]},{18:[1,60]},{18:[2,21]},{18:[2,25],28:[2,25],29:[2,25],30:[2,25],33:[2,25]},{18:[2,32],33:[2,32]},{34:[1,57]},{21:61,28:[1,62],29:[1,63],30:[1,64],33:[1,25],35:24},{18:[2,39],28:[2,39],29:[2,39],30:[2,39],33:[2,39],36:[2,39]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],24:[2,19]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],24:[2,15]},{18:[2,34],33:[2,34]},{18:[2,35],33:[2,35]},{18:[2,36],33:[2,36]},{18:[2,37],33:[2,37]}],
defaultActions: {16:[2,1],37:[2,23],53:[2,21]},
parseError: function parseError(str, hash) {
    throw new Error(str);
},
parse: function parse(input) {
    var self = this,
        stack = [0],
        vstack = [null], // semantic value stack
        lstack = [], // location stack
        table = this.table,
        yytext = '',
        yylineno = 0,
        yyleng = 0,
        recovering = 0,
        TERROR = 2,
        EOF = 1;

    //this.reductionCount = this.shiftCount = 0;

    this.lexer.setInput(input);
    this.lexer.yy = this.yy;
    this.yy.lexer = this.lexer;
    if (typeof this.lexer.yylloc == 'undefined')
        this.lexer.yylloc = {};
    var yyloc = this.lexer.yylloc;
    lstack.push(yyloc);

    if (typeof this.yy.parseError === 'function')
        this.parseError = this.yy.parseError;

    function popStack (n) {
        stack.length = stack.length - 2*n;
        vstack.length = vstack.length - n;
        lstack.length = lstack.length - n;
    }

    function lex() {
        var token;
        token = self.lexer.lex() || 1; // $end = 1
        // if token isn't its numeric value, convert
        if (typeof token !== 'number') {
            token = self.symbols_[token] || token;
        }
        return token;
    };

    var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected;
    while (true) {
        // retreive state number from top of stack
        state = stack[stack.length-1];

        // use default actions if available
        if (this.defaultActions[state]) {
            action = this.defaultActions[state];
        } else {
            if (symbol == null)
                symbol = lex();
            // read action for current state and first input
            action = table[state] && table[state][symbol];
        }

        // handle parse error
        if (typeof action === 'undefined' || !action.length || !action[0]) {

            if (!recovering) {
                // Report error
                expected = [];
                for (p in table[state]) if (this.terminals_[p] && p > 2) {
                    expected.push("'"+this.terminals_[p]+"'");
                }
                var errStr = '';
                if (this.lexer.showPosition) {
                    errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+'\nExpecting '+expected.join(', ');
                } else {
                    errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
                                  (symbol == 1 /*EOF*/ ? "end of input" :
                                              ("'"+(this.terminals_[symbol] || symbol)+"'"));
                }
                this.parseError(errStr,
                    {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
            }

            // just recovered from another error
            if (recovering == 3) {
                if (symbol == EOF) {
                    throw new Error(errStr || 'Parsing halted.');
                }

                // discard current lookahead and grab another
                yyleng = this.lexer.yyleng;
                yytext = this.lexer.yytext;
                yylineno = this.lexer.yylineno;
                yyloc = this.lexer.yylloc;
                symbol = lex();
            }

            // try to recover from error
            while (1) {
                // check for error recovery rule in this state
                if ((TERROR.toString()) in table[state]) {
                    break;
                }
                if (state == 0) {
                    throw new Error(errStr || 'Parsing halted.');
                }
                popStack(1);
                state = stack[stack.length-1];
            }

            preErrorSymbol = symbol; // save the lookahead token
            symbol = TERROR;         // insert generic error symbol as new lookahead
            state = stack[stack.length-1];
            action = table[state] && table[state][TERROR];
            recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
        }

        // this shouldn't happen, unless resolve defaults are off
        if (action[0] instanceof Array && action.length > 1) {
            throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
        }

        switch (action[0]) {

            case 1: // shift
                //this.shiftCount++;

                stack.push(symbol);
                vstack.push(this.lexer.yytext);
                lstack.push(this.lexer.yylloc);
                stack.push(action[1]); // push state
                symbol = null;
                if (!preErrorSymbol) { // normal execution/no error
                    yyleng = this.lexer.yyleng;
                    yytext = this.lexer.yytext;
                    yylineno = this.lexer.yylineno;
                    yyloc = this.lexer.yylloc;
                    if (recovering > 0)
                        recovering--;
                } else { // error just occurred, resume old lookahead f/ before error
                    symbol = preErrorSymbol;
                    preErrorSymbol = null;
                }
                break;

            case 2: // reduce
                //this.reductionCount++;

                len = this.productions_[action[1]][1];

                // perform semantic action
                yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
                // default location, uses first token for firsts, last for lasts
                yyval._$ = {
                    first_line: lstack[lstack.length-(len||1)].first_line,
                    last_line: lstack[lstack.length-1].last_line,
                    first_column: lstack[lstack.length-(len||1)].first_column,
                    last_column: lstack[lstack.length-1].last_column
                };
                r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);

                if (typeof r !== 'undefined') {
                    return r;
                }

                // pop off stack
                if (len) {
                    stack = stack.slice(0,-1*len*2);
                    vstack = vstack.slice(0, -1*len);
                    lstack = lstack.slice(0, -1*len);
                }

                stack.push(this.productions_[action[1]][0]);    // push nonterminal (reduce)
                vstack.push(yyval.$);
                lstack.push(yyval._$);
                // goto new state = table[STATE][NONTERMINAL]
                newState = table[stack[stack.length-2]][stack[stack.length-1]];
                stack.push(newState);
                break;

            case 3: // accept
                return true;
        }

    }

    return true;
}};/* Jison generated lexer */
var lexer = (function(){

var lexer = ({EOF:1,
parseError:function parseError(str, hash) {
        if (this.yy.parseError) {
            this.yy.parseError(str, hash);
        } else {
            throw new Error(str);
        }
    },
setInput:function (input) {
        this._input = input;
        this._more = this._less = this.done = false;
        this.yylineno = this.yyleng = 0;
        this.yytext = this.matched = this.match = '';
        this.conditionStack = ['INITIAL'];
        this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
        return this;
    },
input:function () {
        var ch = this._input[0];
        this.yytext+=ch;
        this.yyleng++;
        this.match+=ch;
        this.matched+=ch;
        var lines = ch.match(/\n/);
        if (lines) this.yylineno++;
        this._input = this._input.slice(1);
        return ch;
    },
unput:function (ch) {
        this._input = ch + this._input;
        return this;
    },
more:function () {
        this._more = true;
        return this;
    },
pastInput:function () {
        var past = this.matched.substr(0, this.matched.length - this.match.length);
        return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
    },
upcomingInput:function () {
        var next = this.match;
        if (next.length < 20) {
            next += this._input.substr(0, 20-next.length);
        }
        return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
    },
showPosition:function () {
        var pre = this.pastInput();
        var c = new Array(pre.length + 1).join("-");
        return pre + this.upcomingInput() + "\n" + c+"^";
    },
next:function () {
        if (this.done) {
            return this.EOF;
        }
        if (!this._input) this.done = true;

        var token,
            match,
            col,
            lines;
        if (!this._more) {
            this.yytext = '';
            this.match = '';
        }
        var rules = this._currentRules();
        for (var i=0;i < rules.length; i++) {
            match = this._input.match(this.rules[rules[i]]);
            if (match) {
                lines = match[0].match(/\n.*/g);
                if (lines) this.yylineno += lines.length;
                this.yylloc = {first_line: this.yylloc.last_line,
                               last_line: this.yylineno+1,
                               first_column: this.yylloc.last_column,
                               last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
                this.yytext += match[0];
                this.match += match[0];
                this.matches = match;
                this.yyleng = this.yytext.length;
                this._more = false;
                this._input = this._input.slice(match[0].length);
                this.matched += match[0];
                token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]);
                if (token) return token;
                else return;
            }
        }
        if (this._input === "") {
            return this.EOF;
        } else {
            this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), 
                    {text: "", token: null, line: this.yylineno});
        }
    },
lex:function lex() {
        var r = this.next();
        if (typeof r !== 'undefined') {
            return r;
        } else {
            return this.lex();
        }
    },
begin:function begin(condition) {
        this.conditionStack.push(condition);
    },
popState:function popState() {
        return this.conditionStack.pop();
    },
_currentRules:function _currentRules() {
        return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
    }});
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {

var YYSTATE=YY_START
switch($avoiding_name_collisions) {
case 0: this.begin("mu"); if (yy_.yytext) return 14; 
break;
case 1: return 14; 
break;
case 2: return 24; 
break;
case 3: return 16; 
break;
case 4: return 20; 
break;
case 5: return 19; 
break;
case 6: return 19; 
break;
case 7: return 23; 
break;
case 8: return 23; 
break;
case 9: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.begin("INITIAL"); return 15; 
break;
case 10: return 22; 
break;
case 11: return 34; 
break;
case 12: return 33; 
break;
case 13: return 33; 
break;
case 14: return 36; 
break;
case 15: /*ignore whitespace*/ 
break;
case 16: this.begin("INITIAL"); return 18; 
break;
case 17: this.begin("INITIAL"); return 18; 
break;
case 18: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 28; 
break;
case 19: return 30; 
break;
case 20: return 30; 
break;
case 21: return 29; 
break;
case 22: return 33; 
break;
case 23: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 33; 
break;
case 24: return 'INVALID'; 
break;
case 25: return 5; 
break;
}
};
lexer.rules = [/^[^\x00]*?(?=(\{\{))/,/^[^\x00]+/,/^\{\{>/,/^\{\{#/,/^\{\{\//,/^\{\{\^/,/^\{\{\s*else\b/,/^\{\{\{/,/^\{\{&/,/^\{\{![\s\S]*?\}\}/,/^\{\{/,/^=/,/^\.(?=[} ])/,/^\.\./,/^[/.]/,/^\s+/,/^\}\}\}/,/^\}\}/,/^"(\\["]|[^"])*"/,/^true(?=[}\s])/,/^false(?=[}\s])/,/^[0-9]+(?=[}\s])/,/^[a-zA-Z0-9_$-]+(?=[=}\s/.])/,/^\[.*\]/,/^./,/^$/];
lexer.conditions = {"mu":{"rules":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],"inclusive":false},"INITIAL":{"rules":[0,1,25],"inclusive":true}};return lexer;})()
parser.lexer = lexer;
return parser;
})();
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
exports.parser = handlebars;
exports.parse = function () { return handlebars.parse.apply(handlebars, arguments); }
exports.main = function commonjsMain(args) {
    if (!args[1])
        throw new Error('Usage: '+args[0]+' FILE');
    if (typeof process !== 'undefined') {
        var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8");
    } else {
        var cwd = require("file").path(require("file").cwd());
        var source = cwd.join(args[1]).read({charset: "utf-8"});
    }
    return exports.parser.parse(source);
}
if (typeof module !== 'undefined' && require.main === module) {
  exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
}
};
;
// lib/handlebars/compiler/base.js
Handlebars.Parser = handlebars;

Handlebars.parse = function(string) {
  Handlebars.Parser.yy = Handlebars.AST;
  return Handlebars.Parser.parse(string);
};

Handlebars.print = function(ast) {
  return new Handlebars.PrintVisitor().accept(ast);
};

Handlebars.logger = {
  DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,

  // override in the host environment
  log: function(level, str) {}
};

Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); };
;
// lib/handlebars/compiler/ast.js
(function() {

  Handlebars.AST = {};

  Handlebars.AST.ProgramNode = function(statements, inverse) {
    this.type = "program";
    this.statements = statements;
    if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
  };

  Handlebars.AST.MustacheNode = function(params, hash, unescaped) {
    this.type = "mustache";
    this.id = params[0];
    this.params = params.slice(1);
    this.hash = hash;
    this.escaped = !unescaped;
  };

  Handlebars.AST.PartialNode = function(id, context) {
    this.type    = "partial";

    // TODO: disallow complex IDs

    this.id      = id;
    this.context = context;
  };

  var verifyMatch = function(open, close) {
    if(open.original !== close.original) {
      throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
    }
  };

  Handlebars.AST.BlockNode = function(mustache, program, close) {
    verifyMatch(mustache.id, close);
    this.type = "block";
    this.mustache = mustache;
    this.program  = program;
  };

  Handlebars.AST.InverseNode = function(mustache, program, close) {
    verifyMatch(mustache.id, close);
    this.type = "inverse";
    this.mustache = mustache;
    this.program  = program;
  };

  Handlebars.AST.ContentNode = function(string) {
    this.type = "content";
    this.string = string;
  };

  Handlebars.AST.HashNode = function(pairs) {
    this.type = "hash";
    this.pairs = pairs;
  };

  Handlebars.AST.IdNode = function(parts) {
    this.type = "ID";
    this.original = parts.join(".");

    var dig = [], depth = 0;

    for(var i=0,l=parts.length; i<l; i++) {
      var part = parts[i];

      if(part === "..") { depth++; }
      else if(part === "." || part === "this") { this.isScoped = true; }
      else { dig.push(part); }
    }

    this.parts    = dig;
    this.string   = dig.join('.');
    this.depth    = depth;
    this.isSimple = (dig.length === 1) && (depth === 0);
  };

  Handlebars.AST.StringNode = function(string) {
    this.type = "STRING";
    this.string = string;
  };

  Handlebars.AST.IntegerNode = function(integer) {
    this.type = "INTEGER";
    this.integer = integer;
  };

  Handlebars.AST.BooleanNode = function(bool) {
    this.type = "BOOLEAN";
    this.bool = bool;
  };

  Handlebars.AST.CommentNode = function(comment) {
    this.type = "comment";
    this.comment = comment;
  };

})();;
// lib/handlebars/utils.js
Handlebars.Exception = function(message) {
  var tmp = Error.prototype.constructor.apply(this, arguments);

  for (var p in tmp) {
    if (tmp.hasOwnProperty(p)) { this[p] = tmp[p]; }
  }
};
Handlebars.Exception.prototype = new Error;

// Build out our basic SafeString type
Handlebars.SafeString = function(string) {
  this.string = string;
};
Handlebars.SafeString.prototype.toString = function() {
  return this.string.toString();
};

(function() {
  var escape = {
    "<": "&lt;",
    ">": "&gt;",
    '"': "&quot;",
    "'": "&#x27;",
    "`": "&#x60;"
  };

  var badChars = /&(?!\w+;)|[<>"'`]/g;
  var possible = /[&<>"'`]/;

  var escapeChar = function(chr) {
    return escape[chr] || "&amp;";
  };

  Handlebars.Utils = {
    escapeExpression: function(string) {
      // don't escape SafeStrings, since they're already safe
      if (string instanceof Handlebars.SafeString) {
        return string.toString();
      } else if (string == null || string === false) {
        return "";
      }

      if(!possible.test(string)) { return string; }
      return string.replace(badChars, escapeChar);
    },

    isEmpty: function(value) {
      if (typeof value === "undefined") {
        return true;
      } else if (value === null) {
        return true;
      } else if (value === false) {
        return true;
      } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
        return true;
      } else {
        return false;
      }
    }
  };
})();;
// lib/handlebars/compiler/compiler.js
Handlebars.Compiler = function() {};
Handlebars.JavaScriptCompiler = function() {};

(function(Compiler, JavaScriptCompiler) {
  Compiler.OPCODE_MAP = {
    appendContent: 1,
    getContext: 2,
    lookupWithHelpers: 3,
    lookup: 4,
    append: 5,
    invokeMustache: 6,
    appendEscaped: 7,
    pushString: 8,
    truthyOrFallback: 9,
    functionOrFallback: 10,
    invokeProgram: 11,
    invokePartial: 12,
    push: 13,
    assignToHash: 15,
    pushStringParam: 16
  };

  Compiler.MULTI_PARAM_OPCODES = {
    appendContent: 1,
    getContext: 1,
    lookupWithHelpers: 2,
    lookup: 1,
    invokeMustache: 3,
    pushString: 1,
    truthyOrFallback: 1,
    functionOrFallback: 1,
    invokeProgram: 3,
    invokePartial: 1,
    push: 1,
    assignToHash: 1,
    pushStringParam: 1
  };

  Compiler.DISASSEMBLE_MAP = {};

  for(var prop in Compiler.OPCODE_MAP) {
    var value = Compiler.OPCODE_MAP[prop];
    Compiler.DISASSEMBLE_MAP[value] = prop;
  }

  Compiler.multiParamSize = function(code) {
    return Compiler.MULTI_PARAM_OPCODES[Compiler.DISASSEMBLE_MAP[code]];
  };

  Compiler.prototype = {
    compiler: Compiler,

    disassemble: function() {
      var opcodes = this.opcodes, opcode, nextCode;
      var out = [], str, name, value;

      for(var i=0, l=opcodes.length; i<l; i++) {
        opcode = opcodes[i];

        if(opcode === 'DECLARE') {
          name = opcodes[++i];
          value = opcodes[++i];
          out.push("DECLARE " + name + " = " + value);
        } else {
          str = Compiler.DISASSEMBLE_MAP[opcode];

          var extraParams = Compiler.multiParamSize(opcode);
          var codes = [];

          for(var j=0; j<extraParams; j++) {
            nextCode = opcodes[++i];

            if(typeof nextCode === "string") {
              nextCode = "\"" + nextCode.replace("\n", "\\n") + "\"";
            }

            codes.push(nextCode);
          }

          str = str + " " + codes.join(" ");

          out.push(str);
        }
      }

      return out.join("\n");
    },

    guid: 0,

    compile: function(program, options) {
      this.children = [];
      this.depths = {list: []};
      this.options = options;

      // These changes will propagate to the other compiler components
      var knownHelpers = this.options.knownHelpers;
      this.options.knownHelpers = {
        'helperMissing': true,
        'blockHelperMissing': true,
        'each': true,
        'if': true,
        'unless': true,
        'with': true
      };
      if (knownHelpers) {
        for (var name in knownHelpers) {
          this.options.knownHelpers[name] = knownHelpers[name];
        }
      }

      return this.program(program);
    },

    accept: function(node) {
      return this[node.type](node);
    },

    program: function(program) {
      var statements = program.statements, statement;
      this.opcodes = [];

      for(var i=0, l=statements.length; i<l; i++) {
        statement = statements[i];
        this[statement.type](statement);
      }
      this.isSimple = l === 1;

      this.depths.list = this.depths.list.sort(function(a, b) {
        return a - b;
      });

      return this;
    },

    compileProgram: function(program) {
      var result = new this.compiler().compile(program, this.options);
      var guid = this.guid++;

      this.usePartial = this.usePartial || result.usePartial;

      this.children[guid] = result;

      for(var i=0, l=result.depths.list.length; i<l; i++) {
        depth = result.depths.list[i];

        if(depth < 2) { continue; }
        else { this.addDepth(depth - 1); }
      }

      return guid;
    },

    block: function(block) {
      var mustache = block.mustache;
      var depth, child, inverse, inverseGuid;

      var params = this.setupStackForMustache(mustache);

      var programGuid = this.compileProgram(block.program);

      if(block.program.inverse) {
        inverseGuid = this.compileProgram(block.program.inverse);
        this.declare('inverse', inverseGuid);
      }

      this.opcode('invokeProgram', programGuid, params.length, !!mustache.hash);
      this.declare('inverse', null);
      this.opcode('append');
    },

    inverse: function(block) {
      var params = this.setupStackForMustache(block.mustache);

      var programGuid = this.compileProgram(block.program);

      this.declare('inverse', programGuid);

      this.opcode('invokeProgram', null, params.length, !!block.mustache.hash);
      this.opcode('append');
    },

    hash: function(hash) {
      var pairs = hash.pairs, pair, val;

      this.opcode('push', '{}');

      for(var i=0, l=pairs.length; i<l; i++) {
        pair = pairs[i];
        val  = pair[1];

        this.accept(val);
        this.opcode('assignToHash', pair[0]);
      }
    },

    partial: function(partial) {
      var id = partial.id;
      this.usePartial = true;

      if(partial.context) {
        this.ID(partial.context);
      } else {
        this.opcode('push', 'depth0');
      }

      this.opcode('invokePartial', id.original);
      this.opcode('append');
    },

    content: function(content) {
      this.opcode('appendContent', content.string);
    },

    mustache: function(mustache) {
      var params = this.setupStackForMustache(mustache);

      this.opcode('invokeMustache', params.length, mustache.id.original, !!mustache.hash);

      if(mustache.escaped) {
        this.opcode('appendEscaped');
      } else {
        this.opcode('append');
      }
    },

    ID: function(id) {
      this.addDepth(id.depth);

      this.opcode('getContext', id.depth);

      this.opcode('lookupWithHelpers', id.parts[0] || null, id.isScoped || false);

      for(var i=1, l=id.parts.length; i<l; i++) {
        this.opcode('lookup', id.parts[i]);
      }
    },

    STRING: function(string) {
      this.opcode('pushString', string.string);
    },

    INTEGER: function(integer) {
      this.opcode('push', integer.integer);
    },

    BOOLEAN: function(bool) {
      this.opcode('push', bool.bool);
    },

    comment: function() {},

    // HELPERS
    pushParams: function(params) {
      var i = params.length, param;

      while(i--) {
        param = params[i];

        if(this.options.stringParams) {
          if(param.depth) {
            this.addDepth(param.depth);
          }

          this.opcode('getContext', param.depth || 0);
          this.opcode('pushStringParam', param.string);
        } else {
          this[param.type](param);
        }
      }
    },

    opcode: function(name, val1, val2, val3) {
      this.opcodes.push(Compiler.OPCODE_MAP[name]);
      if(val1 !== undefined) { this.opcodes.push(val1); }
      if(val2 !== undefined) { this.opcodes.push(val2); }
      if(val3 !== undefined) { this.opcodes.push(val3); }
    },

    declare: function(name, value) {
      this.opcodes.push('DECLARE');
      this.opcodes.push(name);
      this.opcodes.push(value);
    },

    addDepth: function(depth) {
      if(depth === 0) { return; }

      if(!this.depths[depth]) {
        this.depths[depth] = true;
        this.depths.list.push(depth);
      }
    },

    setupStackForMustache: function(mustache) {
      var params = mustache.params;

      this.pushParams(params);

      if(mustache.hash) {
        this.hash(mustache.hash);
      }

      this.ID(mustache.id);

      return params;
    }
  };

  JavaScriptCompiler.prototype = {
    // PUBLIC API: You can override these methods in a subclass to provide
    // alternative compiled forms for name lookup and buffering semantics
    nameLookup: function(parent, name, type) {
			if (/^[0-9]+$/.test(name)) {
        return parent + "[" + name + "]";
      } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
	    	return parent + "." + name;
			}
			else {
				return parent + "['" + name + "']";
      }
    },

    appendToBuffer: function(string) {
      if (this.environment.isSimple) {
        return "return " + string + ";";
      } else {
        return "buffer += " + string + ";";
      }
    },

    initializeBuffer: function() {
      return this.quotedString("");
    },
    // END PUBLIC API

    compile: function(environment, options, context, asObject) {
      this.environment = environment;
      this.options = options || {};

      this.name = this.environment.name;
      this.isChild = !!context;
      this.context = context || {
        programs: [],
        aliases: { self: 'this' },
        registers: {list: []}
      };

      this.preamble();

      this.stackSlot = 0;
      this.stackVars = [];

      this.compileChildren(environment, options);

      var opcodes = environment.opcodes, opcode;

      this.i = 0;

      for(l=opcodes.length; this.i<l; this.i++) {
        opcode = this.nextOpcode(0);

        if(opcode[0] === 'DECLARE') {
          this.i = this.i + 2;
          this[opcode[1]] = opcode[2];
        } else {
          this.i = this.i + opcode[1].length;
          this[opcode[0]].apply(this, opcode[1]);
        }
      }

      return this.createFunctionContext(asObject);
    },

    nextOpcode: function(n) {
      var opcodes = this.environment.opcodes, opcode = opcodes[this.i + n], name, val;
      var extraParams, codes;

      if(opcode === 'DECLARE') {
        name = opcodes[this.i + 1];
        val  = opcodes[this.i + 2];
        return ['DECLARE', name, val];
      } else {
        name = Compiler.DISASSEMBLE_MAP[opcode];

        extraParams = Compiler.multiParamSize(opcode);
        codes = [];

        for(var j=0; j<extraParams; j++) {
          codes.push(opcodes[this.i + j + 1 + n]);
        }

        return [name, codes];
      }
    },

    eat: function(opcode) {
      this.i = this.i + opcode.length;
    },

    preamble: function() {
      var out = [];

      if (!this.isChild) {
        var copies = "helpers = helpers || Handlebars.helpers;";
        if(this.environment.usePartial) { copies = copies + " partials = partials || Handlebars.partials;"; }
        out.push(copies);
      } else {
        out.push('');
      }

      if (!this.environment.isSimple) {
        out.push(", buffer = " + this.initializeBuffer());
      } else {
        out.push("");
      }

      // track the last context pushed into place to allow skipping the
      // getContext opcode when it would be a noop
      this.lastContext = 0;
      this.source = out;
    },

    createFunctionContext: function(asObject) {
      var locals = this.stackVars;
      if (!this.isChild) {
        locals = locals.concat(this.context.registers.list);
      }

      if(locals.length > 0) {
        this.source[1] = this.source[1] + ", " + locals.join(", ");
      }

      // Generate minimizer alias mappings
      if (!this.isChild) {
        var aliases = []
        for (var alias in this.context.aliases) {
          this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
        }
      }

      if (this.source[1]) {
        this.source[1] = "var " + this.source[1].substring(2) + ";";
      }

      // Merge children
      if (!this.isChild) {
        this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
      }

      if (!this.environment.isSimple) {
        this.source.push("return buffer;");
      }

      var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];

      for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
        params.push("depth" + this.environment.depths.list[i]);
      }

      if(params.length === 4 && !this.environment.usePartial) { params.pop(); }

      if (asObject) {
        params.push(this.source.join("\n  "));

        return Function.apply(this, params);
      } else {
        var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n  ' + this.source.join("\n  ") + '}';
        Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
        return functionSource;
      }
    },

    appendContent: function(content) {
      this.source.push(this.appendToBuffer(this.quotedString(content)));
    },

    append: function() {
      var local = this.popStack();
      this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
      if (this.environment.isSimple) {
        this.source.push("else { " + this.appendToBuffer("''") + " }");
      }
    },

    appendEscaped: function() {
      var opcode = this.nextOpcode(1), extra = "";
      this.context.aliases.escapeExpression = 'this.escapeExpression';

      if(opcode[0] === 'appendContent') {
        extra = " + " + this.quotedString(opcode[1][0]);
        this.eat(opcode);
      }

      this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")" + extra));
    },

    getContext: function(depth) {
      if(this.lastContext !== depth) {
        this.lastContext = depth;
      }
    },

    lookupWithHelpers: function(name, isScoped) {
      if(name) {
        var topStack = this.nextStack();

        this.usingKnownHelper = false;

        var toPush;
        if (!isScoped && this.options.knownHelpers[name]) {
          toPush = topStack + " = " + this.nameLookup('helpers', name, 'helper');
          this.usingKnownHelper = true;
        } else if (isScoped || this.options.knownHelpersOnly) {
          toPush = topStack + " = " + this.nameLookup('depth' + this.lastContext, name, 'context');
        } else {
          toPush =  topStack + " = "
              + this.nameLookup('helpers', name, 'helper')
              + " || "
              + this.nameLookup('depth' + this.lastContext, name, 'context');
        }
        
        toPush += ';';
        this.source.push(toPush);
      } else {
        this.pushStack('depth' + this.lastContext);
      }
    },

    lookup: function(name) {
      var topStack = this.topStack();
      this.source.push(topStack + " = (" + topStack + " === null || " + topStack + " === undefined || " + topStack + " === false ? " +
 				topStack + " : " + this.nameLookup(topStack, name, 'context') + ");");
    },

    pushStringParam: function(string) {
      this.pushStack('depth' + this.lastContext);
      this.pushString(string);
    },

    pushString: function(string) {
      this.pushStack(this.quotedString(string));
    },

    push: function(name) {
      this.pushStack(name);
    },

    invokeMustache: function(paramSize, original, hasHash) {
      this.populateParams(paramSize, this.quotedString(original), "{}", null, hasHash, function(nextStack, helperMissingString, id) {
        if (!this.usingKnownHelper) {
          this.context.aliases.helperMissing = 'helpers.helperMissing';
          this.context.aliases.undef = 'void 0';
          this.source.push("else if(" + id + "=== undef) { " + nextStack + " = helperMissing.call(" + helperMissingString + "); }");
          if (nextStack !== id) {
            this.source.push("else { " + nextStack + " = " + id + "; }");
          }
        }
      });
    },

    invokeProgram: function(guid, paramSize, hasHash) {
      var inverse = this.programExpression(this.inverse);
      var mainProgram = this.programExpression(guid);

      this.populateParams(paramSize, null, mainProgram, inverse, hasHash, function(nextStack, helperMissingString, id) {
        if (!this.usingKnownHelper) {
          this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
          this.source.push("else { " + nextStack + " = blockHelperMissing.call(" + helperMissingString + "); }");
        }
      });
    },

    populateParams: function(paramSize, helperId, program, inverse, hasHash, fn) {
      var needsRegister = hasHash || this.options.stringParams || inverse || this.options.data;
      var id = this.popStack(), nextStack;
      var params = [], param, stringParam, stringOptions;

      if (needsRegister) {
        this.register('tmp1', program);
        stringOptions = 'tmp1';
      } else {
        stringOptions = '{ hash: {} }';
      }

      if (needsRegister) {
        var hash = (hasHash ? this.popStack() : '{}');
        this.source.push('tmp1.hash = ' + hash + ';');
      }

      if(this.options.stringParams) {
        this.source.push('tmp1.contexts = [];');
      }

      for(var i=0; i<paramSize; i++) {
        param = this.popStack();
        params.push(param);

        if(this.options.stringParams) {
          this.source.push('tmp1.contexts.push(' + this.popStack() + ');');
        }
      }

      if(inverse) {
        this.source.push('tmp1.fn = tmp1;');
        this.source.push('tmp1.inverse = ' + inverse + ';');
      }

      if(this.options.data) {
        this.source.push('tmp1.data = data;');
      }

      params.push(stringOptions);

      this.populateCall(params, id, helperId || id, fn);
    },

    populateCall: function(params, id, helperId, fn) {
      var paramString = ["depth0"].concat(params).join(", ");
      var helperMissingString = ["depth0"].concat(helperId).concat(params).join(", ");

      var nextStack = this.nextStack();

      if (this.usingKnownHelper) {
        this.source.push(nextStack + " = " + id + ".call(" + paramString + ");");
      } else {
        this.context.aliases.functionType = '"function"';
        this.source.push("if(typeof " + id + " === functionType) { " + nextStack + " = " + id + ".call(" + paramString + "); }");
      }
      fn.call(this, nextStack, helperMissingString, id);
      this.usingKnownHelper = false;
    },

    invokePartial: function(context) {
      this.pushStack("self.invokePartial(" + this.nameLookup('partials', context, 'partial') + ", '" + context + "', " + this.popStack() + ", helpers, partials);");
    },

    assignToHash: function(key) {
      var value = this.popStack();
      var hash = this.topStack();

      this.source.push(hash + "['" + key + "'] = " + value + ";");
    },

    // HELPERS

    compiler: JavaScriptCompiler,

    compileChildren: function(environment, options) {
      var children = environment.children, child, compiler;

      for(var i=0, l=children.length; i<l; i++) {
        child = children[i];
        compiler = new this.compiler();

        this.context.programs.push('');     // Placeholder to prevent name conflicts for nested children
        var index = this.context.programs.length;
        child.index = index;
        child.name = 'program' + index;
        this.context.programs[index] = compiler.compile(child, options, this.context);
      }
    },

    programExpression: function(guid) {
      if(guid == null) { return "self.noop"; }

      var child = this.environment.children[guid],
          depths = child.depths.list;
      var programParams = [child.index, child.name, "data"];

      for(var i=0, l = depths.length; i<l; i++) {
        depth = depths[i];

        if(depth === 1) { programParams.push("depth0"); }
        else { programParams.push("depth" + (depth - 1)); }
      }

      if(depths.length === 0) {
        return "self.program(" + programParams.join(", ") + ")";
      } else {
        programParams.shift();
        return "self.programWithDepth(" + programParams.join(", ") + ")";
      }
    },

    register: function(name, val) {
      this.useRegister(name);
      this.source.push(name + " = " + val + ";");
    },

    useRegister: function(name) {
      if(!this.context.registers[name]) {
        this.context.registers[name] = true;
        this.context.registers.list.push(name);
      }
    },

    pushStack: function(item) {
      this.source.push(this.nextStack() + " = " + item + ";");
      return "stack" + this.stackSlot;
    },

    nextStack: function() {
      this.stackSlot++;
      if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
      return "stack" + this.stackSlot;
    },

    popStack: function() {
      return "stack" + this.stackSlot--;
    },

    topStack: function() {
      return "stack" + this.stackSlot;
    },

    quotedString: function(str) {
      return '"' + str
        .replace(/\\/g, '\\\\')
        .replace(/"/g, '\\"')
        .replace(/\n/g, '\\n')
        .replace(/\r/g, '\\r') + '"';
    }
  };

  var reservedWords = ("break case catch continue default delete do else finally " +
                       "for function if in instanceof new return switch this throw " + 
                       "try typeof var void while with null true false").split(" ");

  var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};

  for(var i=0, l=reservedWords.length; i<l; i++) {
    compilerWords[reservedWords[i]] = true;
  }

	JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
		if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
			return true;
		}
		return false;
	}

})(Handlebars.Compiler, Handlebars.JavaScriptCompiler);

Handlebars.precompile = function(string, options) {
  options = options || {};

  var ast = Handlebars.parse(string);
  var environment = new Handlebars.Compiler().compile(ast, options);
  return new Handlebars.JavaScriptCompiler().compile(environment, options);
};

Handlebars.compile = function(string, options) {
  options = options || {};

  var ast = Handlebars.parse(string);
  var environment = new Handlebars.Compiler().compile(ast, options);
  var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
  return Handlebars.template(templateSpec);
};
;
// lib/handlebars/vm.js
Handlebars.VM = {
  template: function(templateSpec) {
    // Just add water
    var container = {
      escapeExpression: Handlebars.Utils.escapeExpression,
      invokePartial: Handlebars.VM.invokePartial,
      programs: [],
      program: function(i, fn, data) {
        var programWrapper = this.programs[i];
        if(data) {
          return Handlebars.VM.program(fn, data);
        } else if(programWrapper) {
          return programWrapper;
        } else {
          programWrapper = this.programs[i] = Handlebars.VM.program(fn);
          return programWrapper;
        }
      },
      programWithDepth: Handlebars.VM.programWithDepth,
      noop: Handlebars.VM.noop
    };

    return function(context, options) {
      options = options || {};
      return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
    };
  },

  programWithDepth: function(fn, data, $depth) {
    var args = Array.prototype.slice.call(arguments, 2);

    return function(context, options) {
      options = options || {};

      return fn.apply(this, [context, options.data || data].concat(args));
    };
  },
  program: function(fn, data) {
    return function(context, options) {
      options = options || {};

      return fn(context, options.data || data);
    };
  },
  noop: function() { return ""; },
  invokePartial: function(partial, name, context, helpers, partials) {
    if(partial === undefined) {
      throw new Handlebars.Exception("The partial " + name + " could not be found");
    } else if(partial instanceof Function) {
      return partial(context, {helpers: helpers, partials: partials});
    } else if (!Handlebars.compile) {
      throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in vm mode");
    } else {
      partials[name] = Handlebars.compile(partial);
      return partials[name](context, {helpers: helpers, partials: partials});
    }
  }
};

Handlebars.template = Handlebars.VM.template;
;


/**
 * Custom addition for the Kanso package. Export the same browser interface
 * when used as a CommonJS module in CouchDB.
 */

if (typeof module !== 'undefined' && typeof exports !== 'undefined') {
    var exports = module.exports = Handlebars;
}

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["404.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  
  return "\n            <img src=\"static/img/logo.png\"/>\n            ";}

function program3(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n                <h2 class=\"name_logo\">";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.market_name);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app_settings.market_name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h2>\n            ";
  return buffer;}

  buffer += "<div id=\"secondbar\">\n  <div class=\"container\">\n    <div class=\"row\">\n      <div class=\"span5\">\n        <div class=\"icon\">\n            <a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">\n            ";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.use_garden_market_logo);
  stack2 = helpers['if'];
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.program(3, program3, data);
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n            </a>\n        </div>\n      </div>\n      <div class=\"span10\">\n          <form class=\"search\" action=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "search\">\n              <input type=\"text\" name=\"q\" placeholder=\"Search\" />\n              <button class=\"btn\">&rang;</button>\n          </form>\n\n      </div>\n    </div>\n  </div>\n</div>";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["app_details.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  
  return "\n            <img src=\"../static/img/logo.png\"/>\n            ";}

function program3(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n                <h2 class=\"name_logo\">";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.market_name);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app_settings.market_name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h2>\n            ";
  return buffer;}

function program5(depth0,data) {
  
  var buffer = "", stack1, stack2;
  buffer += "\n                                <div class=\"item ";
  stack1 = helpers.active || depth0.active;
  stack2 = helpers['if'];
  tmp1 = self.program(6, program6, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\">\n                                    <img src=\"";
  stack1 = helpers.src || depth0.src;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "src", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\" />\n                                </div>\n                            ";
  return buffer;}
function program6(depth0,data) {
  
  
  return "active";}

function program8(depth0,data) {
  
  
  return "\n                          <!-- Carousel nav -->\n                          <a class=\"carousel-control left\" href=\"#myCarousel\" data-slide=\"prev\">&lsaquo;</a>\n                          <a class=\"carousel-control right\" href=\"#myCarousel\" data-slide=\"next\">&rsaquo;</a>\n                          ";}

function program10(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n              <p>";
  stack1 = depth0;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "this", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</p>\n            ";
  return buffer;}

function program12(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n                      <tr>\n                          <th>Donate</th>\n                          <td>";
  stack1 = helpers.flattr_link || depth0.flattr_link;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "flattr_link", { hash: {} }); }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "</td>\n                      </tr>\n                    ";
  return buffer;}

function program14(depth0,data,depth1) {
  
  var buffer = "", stack1;
  buffer += "\n                            <a class=\"tag\" href=\"";
  stack1 = helpers.baseURL || depth1.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "...baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "category/";
  stack1 = depth0;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "this", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">";
  stack1 = depth0;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "this", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</a>\n                          ";
  return buffer;}

function program16(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n                      <tr>\n                          <th>Website</th>\n                          <td><a href=\"";
  stack1 = helpers.meta || depth0.meta;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.config);
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.url);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "meta.config.url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">";
  stack1 = helpers.meta || depth0.meta;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.config);
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.url);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "meta.config.url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</a></td>\n                      </tr>\n                    ";
  return buffer;}

  buffer += "<div id=\"secondbar\">\n  <div class=\"container\">\n    <div class=\"row\">\n      <div class=\"span5\">\n        <div class=\"icon\">\n            <a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">\n            ";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.use_garden_market_logo);
  stack2 = helpers['if'];
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.program(3, program3, data);
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n            </a>\n        </div>\n      </div>\n      <div class=\"span10\">\n          <form class=\"search\" action=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "search\">\n              <input type=\"text\" name=\"q\" placeholder=\"Search\" />\n              <button class=\"btn\">&rang;</button>\n          </form>\n\n      </div>\n    </div>\n  </div>\n</div>\n\n<div class=\"container\">\n  <div id=\"content\">\n    <div class=\"row app_top\">\n\n        <div id=\"details_sidebar\" class=\"span5\">\n            <img src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "_db/";
  stack1 = helpers.id || depth0.id;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "id", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/";
  stack1 = helpers.icon_96 || depth0.icon_96;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "icon_96", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\" width=\"96\" style=\"float: left\"/>\n            <h2>";
  stack1 = helpers.title || depth0.title;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "title", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h2>\n            <div class=\"author\">\n              By <a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "user/";
  stack1 = helpers.meta || depth0.meta;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.pushed_by);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "meta.pushed_by", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">";
  stack1 = helpers.meta || depth0.meta;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.pushed_by);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "meta.pushed_by", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</a>\n            </div>\n            <div style=\"clear: both\"></div>\n\n          <h4>";
  stack1 = helpers.meta || depth0.meta;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.config);
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.description);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "meta.config.description", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h4>\n          <a class=\"btn large primary\" href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "details/";
  stack1 = helpers.id || depth0.id;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "id", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/install\">Install</a>\n\n        </div>\n        <div class=\"span11\">\n            <div id=\"screenshot_container\">\n                <div class=\"paper\">\n                    <div class=\"paper-inner\">\n                        <div id=\"myCarousel\" class=\"carousel slide\" data-interval=\"\">\n                          <!-- Carousel items -->\n                          <div class=\"carousel-inner\">\n\n                            ";
  stack1 = helpers.screenshots || depth0.screenshots;
  stack2 = helpers.each;
  tmp1 = self.program(5, program5, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n\n                          </div>\n                          ";
  stack1 = helpers.screenshots_many || depth0.screenshots_many;
  stack2 = helpers['if'];
  tmp1 = self.program(8, program8, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n                        </div>\n\n                        <div class=\"loading\">Loading&hellip;</div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n    <div class=\"row extra-details\" >\n        <div class=\"span5 description-span\">\n\n            &nbsp;\n        </div>\n        <div class=\"span11 about-span\">\n            <h3>Description</h3>\n            ";
  stack1 = helpers.long_description_paragraphs || depth0.long_description_paragraphs;
  stack2 = helpers.each;
  tmp1 = self.program(10, program10, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n            <table class=\"\">\n                <tbody class=\"info_table\">\n                    ";
  stack1 = helpers.flattr_link || depth0.flattr_link;
  stack2 = helpers['if'];
  tmp1 = self.program(12, program12, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n                    <tr>\n                        <th>Version</th>\n                        <td><b>";
  stack1 = helpers.meta || depth0.meta;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.config);
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.version);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "meta.config.version", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</b> last updated ";
  stack1 = helpers.updated || depth0.updated;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "updated", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</td>\n                    </tr>\n                    <tr>\n                        <th>Categories</th>\n                        <td class=\"categories\">\n                          ";
  stack1 = helpers.meta || depth0.meta;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.config);
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.categories);
  stack2 = helpers.each;
  tmp1 = self.programWithDepth(program14, data, depth0);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n                        </td>\n                    </tr>\n                    ";
  stack1 = helpers.meta || depth0.meta;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.config);
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.url);
  stack2 = helpers['if'];
  tmp1 = self.program(16, program16, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n                </tbody>\n            </table>\n        </div>\n    </div>\n  </div>\n</div>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["app_details_install.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  
  return "\n            <img src=\"../../static/img/logo.png\"/>\n            ";}

function program3(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n                <h2 class=\"name_logo\">";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.market_name);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app_settings.market_name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h2>\n            ";
  return buffer;}

  buffer += "<div id=\"secondbar\">\n  <div class=\"container\">\n    <div class=\"row\">\n      <div class=\"span5\">\n        <div class=\"icon\">\n            <a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">\n            ";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.use_garden_market_logo);
  stack2 = helpers['if'];
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.program(3, program3, data);
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n            </a>\n        </div>\n      </div>\n      <div class=\"span10\">\n\n\n      </div>\n    </div>\n  </div>\n</div>\n\n<div class=\"container\">\n  <div id=\"content\">\n    <div class=\"row app_top\">\n      <div id=\"details_sidebar\" class=\"span5\">\n          <img src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "_db/";
  stack1 = helpers.id || depth0.id;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "id", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/";
  stack1 = helpers.icon_96 || depth0.icon_96;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "icon_96", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\" width=\"96\" style=\"float: left\"/>\n          <h2>";
  stack1 = helpers.title || depth0.title;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "title", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h2>\n          <div class=\"author\">\n              By <a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "user/";
  stack1 = helpers.meta || depth0.meta;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.pushed_by);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "meta.pushed_by", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">";
  stack1 = helpers.meta || depth0.meta;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.pushed_by);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "meta.pushed_by", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</a>\n          </div>\n          <div style=\"clear: both\"></div>\n\n          <h4>";
  stack1 = helpers.meta || depth0.meta;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.config);
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.description);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "meta.config.description", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h4>\n          <div class=\"install_msg\"></div>\n          <ol>\n              <li><span class=\"current\">Select Installation Location</span>\n                    <ol class=\"sub\">\n                        <li>Signup/Login</li>\n                    </ol>\n              </li>\n              <li>Confirm And Complete</li>\n          </ol>\n      </div>\n\n      <div class=\"span10 install_where\">\n\n          <h1>1. Select Installation Location</h1>\n          <table class=\"table zebra-striped\">\n\n              <tbody>\n\n\n              </tbody>\n          </table>\n\n          <div class=\"advanced\">\n              <a href=\"http://garden20.com/get\" target=\"_blank\">Get your own Garden</a><br/>\n              [<a href=\"#\" class=\"advanced-toggle\">Show Advanced Locations</a>]\n          </div>\n          <a href=\"../";
  stack1 = helpers.id || depth0.id;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "id", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">Prev</a>\n          <a href=\"#\" class=\"btn large primary next\">Next</a>\n          <h3 class=\"please-wait\" style=\"display: none;\">Please wait...</h3>\n\n      </div>\n    </div>\n  </div>\n</div>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["app_details_manual_install.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  
  return "\n            <img src=\"static/img/logo.png\"/>\n            ";}

function program3(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n                <h2 class=\"name_logo\">";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.market_name);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app_settings.market_name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h2>\n            ";
  return buffer;}

  buffer += "<div id=\"secondbar\">\n  <div class=\"container\">\n    <div class=\"row\">\n      <div class=\"span5\">\n        <div class=\"icon\">\n            <a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">\n            ";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.use_garden_market_logo);
  stack2 = helpers['if'];
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.program(3, program3, data);
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n            </a>\n        </div>\n      </div>\n      <div class=\"span10\">\n          <form class=\"search\" action=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "search\">\n              <input type=\"text\" name=\"q\" placeholder=\"Search\" />\n              <button class=\"btn\">&rang;</button>\n          </form>\n\n      </div>\n    </div>\n  </div>\n</div>\n\n<div class=\"container\">\n  <div id=\"content\">\n    <div class=\"row\">\n      <div class=\"span11  main-section\">\n          <h2>Installation Location</h2>\n          <table class=\"table\" style=\"border: 0\">\n            <tbody>\n            <tr>\n                <td class=\"type_td\">\n                    <img src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/static/img/couch.png\"  alt=\"Couch\"/>\n                    <div class=\"type\">Couch</div>\n                </td>\n                <td>\n                    <div class=\"name\">\n                        CouchDB manual install\n                    </div>\n                </td>\n            </tr>\n\n            </tbody>\n          </table>\n\n      </div>\n    </div>\n    <div class=\"row\">\n        <div class=\"manual_install_instructions\">\n          <h2>Manual install (bash script)</h2>\n          <pre class=\"upload_url\"></pre>\n          <p>Copy and paste the above command into your bash console, you will be prompted for the URL to install the app to. You may want to <a rel=\"external\" class=\"read\" href=\"#\">read the script</a> first.</p>\n        </div>\n    </div>\n  </div>\n</div>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["app_list.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression, blockHelperMissing=helpers.blockHelperMissing;

function program1(depth0,data,depth1) {
  
  var buffer = "", stack1;
  buffer += "\n  <li>\n    <a href=\"";
  stack1 = helpers.baseURL || depth1.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "...baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "details/";
  stack1 = helpers.id || depth0.id;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "id", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">\n      <div class=\"thumbnail\">\n        <img src=\"";
  stack1 = helpers.baseURL || depth1.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "...baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "_db/";
  stack1 = helpers.id || depth0.id;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "id", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/";
  stack1 = helpers.promo_image || depth0.promo_image;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "promo_image", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\" />\n        <div class=\"description\">\n          <div class=\"description-inner\">";
  stack1 = helpers.short_description || depth0.short_description;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "short_description", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</div>\n        </div>\n        <div class=\"name\">";
  stack1 = helpers.title || depth0.title;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "title", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</div>\n      </div>\n    </a>\n  </li>\n";
  return buffer;}

  buffer += "&nbsp;\n<ul class=\"apps\">\n";
  stack1 = helpers.rows || depth0.rows;
  tmp1 = self.programWithDepth(program1, data, depth0);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, tmp1); }
  else { stack1 = blockHelperMissing.call(depth0, stack1, tmp1); }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n</ul>\n<div class=\"clearapps\"></div>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["base.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n\n      var _gaq = _gaq || [];\n      _gaq.push(['_setAccount', '";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.google_analytics_code);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app_settings.google_analytics_code", { hash: {} }); }
  buffer += escapeExpression(stack1) + "']);\n      _gaq.push(['_trackPageview']);\n\n      (function() {\n        var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;\n        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';\n        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);\n      })();\n      ";
  return buffer;}

  buffer += "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"> \n  <head> \n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /> \n    <link rel=\"stylesheet\" type=\"text/css\" href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "static/css/hljs_javascript.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "static/css/garden.css\" />\n    <title>";
  stack1 = helpers.title || depth0.title;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "title", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</title>\n    <script type=\"text/javascript\">\n        /** hack. since we cant do this server side correctly! **/\n        /* This just makes sure we have a trailing slash on our url */\n        var db_name = '";
  stack1 = helpers.db_name || depth0.db_name;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "db_name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "';\n        if (window.location.pathname === '/' + db_name) {\n            var ensure_have_slash =  '/' + db_name + '/';\n            if (window.location.search) {\n                ensure_have_slash + window.location.search;\n            }\n            window.location = ensure_have_slash;\n        }\n    </script>\n  </head> \n  <body>\n\n    <div id=\"content\">\n      ";
  stack1 = helpers.content || depth0.content;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "content", { hash: {} }); }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n    </div>\n\n    <script type=\"text/javascript\" src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "static/js/jquery-1.7.1.min.js\"></script>\n    <script type=\"text/javascript\" src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "static/js/json2.js\"></script>\n    <script type=\"text/javascript\" src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "modules.js\"></script>\n    <script type=\"text/javascript\" src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "static/js/bootstrap-carousel.js\"></script>\n    <script type=\"text/javascript\" src=\"/dashboard/_design/dashboard/_rewrite/static/js/topbar.js\"></script>\n    <script type=\"text/javascript\">\n        var ui = require('lib/ui');\n        ui.init();\n        ";
  stack1 = helpers.onload || depth0.onload;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "onload", { hash: {} }); }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n      ";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.use_google_analytics);
  stack2 = helpers['if'];
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n    </script>\n  </body>\n</html>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["category_list.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, stack3, tmp1, self=this, functionType="function", blockHelperMissing=helpers.blockHelperMissing, helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  
  return "class=\"active\"";}

function program3(depth0,data,depth1) {
  
  var buffer = "", stack1, stack2, stack3;
  buffer += "\n  <li ";
  stack1 = helpers.key || depth0.key;
  stack2 = helpers.current_category || depth1.current_category;
  stack3 = helpers.ifequal || depth0.ifequal;
  tmp1 = self.program(4, program4, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  if(typeof stack3 === functionType) { stack1 = stack3.call(depth0, stack2, stack1, tmp1); }
  else { stack1 = blockHelperMissing.call(depth0, stack3, stack2, stack1, tmp1); }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += ">\n      <a href=\"";
  stack1 = helpers.baseURL || depth1.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "...baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "category/";
  stack1 = helpers.key || depth0.key;
  stack2 = helpers.uc || depth0.uc;
  if(typeof stack2 === functionType) { stack1 = stack2.call(depth0, stack1, { hash: {} }); }
  else if(stack2=== undef) { stack1 = helperMissing.call(depth0, "uc", stack1, { hash: {} }); }
  else { stack1 = stack2; }
  buffer += escapeExpression(stack1) + "\">";
  stack1 = helpers.name || depth0.name;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "name", { hash: {} }); }
  buffer += escapeExpression(stack1) + " (";
  stack1 = helpers.count || depth0.count;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "count", { hash: {} }); }
  buffer += escapeExpression(stack1) + ")</a>\n    </li>\n  ";
  return buffer;}
function program4(depth0,data) {
  
  
  return "class=\"active\"";}

  buffer += "<ul id=\"categories\" class=\"menu\">\n  <li ";
  stack1 = "all";
  stack2 = helpers.current_category || depth0.current_category;
  stack3 = helpers.ifequal || depth0.ifequal;
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  if(typeof stack3 === functionType) { stack1 = stack3.call(depth0, stack2, stack1, tmp1); }
  else { stack1 = blockHelperMissing.call(depth0, stack3, stack2, stack1, tmp1); }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += ">\n    <a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">All (";
  stack1 = helpers.total_apps || depth0.total_apps;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "total_apps", { hash: {} }); }
  buffer += escapeExpression(stack1) + ")</a>\n  </li>\n  ";
  stack1 = helpers.categories || depth0.categories;
  stack2 = helpers.each;
  tmp1 = self.programWithDepth(program3, data, depth0);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n</ul>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["category_page.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  
  return "\n            <img src=\"static/img/logo.png\"/>\n            ";}

function program3(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n                <h2 class=\"name_logo\">";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.market_name);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app_settings.market_name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h2>\n            ";
  return buffer;}

  buffer += "<div id=\"secondbar\">\n  <div class=\"container\">\n    <div class=\"row\">\n      <div class=\"span5\">\n        <div class=\"icon\">\n            <a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">\n            ";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.use_garden_market_logo);
  stack2 = helpers['if'];
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.program(3, program3, data);
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n            </a>\n        </div>\n      </div>\n      <div class=\"span10\">\n          <form class=\"search\" action=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "search\">\n              <input type=\"text\" name=\"q\" placeholder=\"Search\" />\n              <button class=\"btn\">&rang;</button>\n          </form>\n\n      </div>\n    </div>\n  </div>\n</div>\n\n<div class=\"container\">\n  <div id=\"content\">\n    <div class=\"row\">\n\n      <div class=\"span12 main-section\" id=\"category-page\" data-category=\"";
  stack1 = helpers.category || depth0.category;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "category", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">\n        <h3 >Category: ";
  stack1 = helpers.category || depth0.category;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "category", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h3>\n        ";
  stack1 = "app_list.html";
  stack2 = helpers.include || depth0.include;
  if(typeof stack2 === functionType) { stack1 = stack2.call(depth0, stack1, { hash: {} }); }
  else if(stack2=== undef) { stack1 = helperMissing.call(depth0, "include", stack1, { hash: {} }); }
  else { stack1 = stack2; }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n      </div>\n\n      <div id=\"sidebar\" class=\"span4\">\n        <ul class=\"menu\">\n          <li><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">Home</a></li>\n          <li><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/upload_app\">Upload app</a></li>\n        </ul>\n        <ul id=\"categories\" class=\"menu\">\n          <li><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/all\">All</a></li>\n        </ul>\n      </div>\n\n    </div>\n  </div>\n</div>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["github_info.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n<tr>\n        <th>Issues</th>\n        <td><a href=\"";
  stack1 = helpers.html_url || depth0.html_url;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "html_url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/issues\">Tracker</a> - ";
  stack1 = helpers.open_issues_count || depth0.open_issues_count;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "open_issues_count", { hash: {} }); }
  buffer += escapeExpression(stack1) + " issues</td>\n</tr>\n";
  return buffer;}

function program3(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n<tr>\n    <th>Tag</th>\n    <td><a href=\"";
  stack1 = helpers.html_url || depth0.html_url;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "html_url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/tree/";
  stack1 = helpers.git_commit || depth0.git_commit;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "git_commit", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">";
  stack1 = helpers.git_commit_small || depth0.git_commit_small;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "git_commit_small", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</a></td>\n</tr>\n";
  return buffer;}

  buffer += "<tr>\n    <th>Github</th>\n    <td><a href=\"";
  stack1 = helpers.html_url || depth0.html_url;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "html_url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/stargazers\"> ";
  stack1 = helpers.watchers || depth0.watchers;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "watchers", { hash: {} }); }
  buffer += escapeExpression(stack1) + " watchers</a></td>\n</tr>\n<tr>\n    <th>Owner</th>\n    <td>\n        <img src=\"";
  stack1 = helpers.owner || depth0.owner;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.avatar_url);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "owner.avatar_url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\" />\n        <br/>\n        ";
  stack1 = helpers.owner || depth0.owner;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.login);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "owner.login", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\n    </td>\n</tr>\n";
  stack1 = helpers.has_issues || depth0.has_issues;
  stack2 = helpers['if'];
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n";
  stack1 = helpers.git_commit || depth0.git_commit;
  stack2 = helpers['if'];
  tmp1 = self.program(3, program3, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["home.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  
  return "\n            <img src=\"static/img/logo.png\"/>\n            ";}

function program3(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n                <h2 class=\"name_logo\">";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.market_name);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app_settings.market_name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h2>\n            ";
  return buffer;}

  buffer += "<div id=\"secondbar\">\n  <div class=\"container\">\n    <div class=\"row\">\n      <div class=\"span5\">\n        <div class=\"icon\">\n            <a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">\n            ";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.use_garden_market_logo);
  stack2 = helpers['if'];
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.program(3, program3, data);
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n            </a>\n        </div>\n      </div>\n      <div class=\"span10\">\n          <form class=\"search\" action=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "search\">\n              <input type=\"text\" name=\"q\" placeholder=\"Search\" />\n              <button class=\"btn\">&rang;</button>\n          </form>\n\n      </div>\n    </div>\n  </div>\n</div>\n\n<div class=\"container\">\n  <div id=\"content\">\n    <div class=\"row\">\n\n      <div class=\"span12 main-section\">\n        ";
  stack1 = "app_list.html";
  stack2 = helpers.include || depth0.include;
  if(typeof stack2 === functionType) { stack1 = stack2.call(depth0, stack1, { hash: {} }); }
  else if(stack2=== undef) { stack1 = helperMissing.call(depth0, "include", stack1, { hash: {} }); }
  else { stack1 = stack2; }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n      </div>\n\n      <div id=\"sidebar\" class=\"span4\">\n        <ul class=\"menu\">\n          <li class=\"active\"><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">Home</a></li>\n          <li><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "upload_app\">Upload app</a></li>\n        </ul>\n        <ul id=\"categories\" class=\"menu\">\n          <li><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "all\">All</a></li>\n        </ul>\n      </div>\n\n    </div>\n  </div>\n</div>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["install.sh"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;


  buffer += "#!/bin/bash\n\necho -e \"\\nEnter the URL of the database you'd like to install to, including username and password.\\n(eg, http://admin:password@localhost:5984/";
  stack1 = helpers.app || depth0.app;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app", { hash: {} }); }
  buffer += escapeExpression(stack1) + ")\\n\";\n\necho -n \"Target: \";\nwhile read -u 2 inputline \ndo\ntarget=\"$inputline\"\n\nif [ -z \"${target}\" ];\nthen\n    exit\nfi\n\necho -e \"\\nCreating the database\";\ncurl -X PUT ${target}\n\necho -e \"\\nInstalling the app\";\ncurl -X GET ";
  stack1 = helpers.app_url || depth0.app_url;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app_url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "?attachments=true -H \"Accept: application/json\" | \\\n    sed s/\\\"_rev\\\"[^,]*,// | \\\n    curl -X PUT ${target}/_design/";
  stack1 = helpers.app || depth0.app;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app", { hash: {} }); }
  buffer += escapeExpression(stack1) + " -H \"Content-Type: application/json\" -d @- \\\n    && echo -e \"\\n${target}/_design/";
  stack1 = helpers.app || depth0.app;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app", { hash: {} }); }
  buffer += escapeExpression(stack1);
  stack1 = helpers.open_path || depth0.open_path;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "open_path", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\" && exit\n\ndone\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["install_popup_content.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;


  buffer += "<div id=\"install_options\">\n  <div id=\"create_dashboard\">\n      <h2>Use Garden20</h2>\n      <a href=\"";
  stack1 = helpers.create_dashboard_url || depth0.create_dashboard_url;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "create_dashboard_url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">\n        <img src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/static/img/garden20.png\" alt=\"Create dashboard\" />\n      </a>\n      <p>A free, online platform for hosting your apps.</p>\n  </div>\n  <div id=\"install_to_dashboard\">\n      <h2>Install to Dashboard</h2>\n      <img src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/static/img/install_to_dashboard.png\" alt=\"Install to dashboard\" />\n      <p>Install to an existing Dashboard<br/><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/help/dashboard\">Learn more</a></p>\n  </div>\n  <div id=\"manual_install\">\n    <h2>Manual Install</h2>\n    <img src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/static/img/manual_install.png\" alt=\"Manual install\" />\n    <p>Manually install to a CouchDB without a Dashboard</p>\n  </div>\n\n\n</div>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["install_popup_dashboard.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression, blockHelperMissing=helpers.blockHelperMissing;

function program1(depth0,data) {
  
  var buffer = "", stack1, stack2;
  buffer += "\n    <ul class=\"dashboard_urls\">\n      ";
  stack1 = helpers.dashboard_urls || depth0.dashboard_urls;
  stack2 = helpers.head || depth0.head;
  tmp1 = self.program(2, program2, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  if(typeof stack2 === functionType) { stack1 = stack2.call(depth0, stack1, tmp1); }
  else { stack1 = blockHelperMissing.call(depth0, stack2, stack1, tmp1); }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n      ";
  stack1 = helpers.dashboard_urls || depth0.dashboard_urls;
  stack2 = helpers.tail || depth0.tail;
  tmp1 = self.program(4, program4, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  if(typeof stack2 === functionType) { stack1 = stack2.call(depth0, stack1, tmp1); }
  else { stack1 = blockHelperMissing.call(depth0, stack2, stack1, tmp1); }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n      <li><input type=\"radio\" name=\"dashboard_url\" id=\"dashboard_custom\" value=\"\"><label for=\"dashboard_custom\"><input type=\"text\" id=\"dashboard_custom_text\" name=\"custom_dashboard_url\" placeholder=\"Enter URL\" /></label></li>\n    </ul>\n  ";
  return buffer;}
function program2(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n        <li><input checked=\"checked\" type=\"radio\" name=\"dashboard_url\" id=\"dashboard_url_";
  stack1 = helpers.id || depth0.id;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "id", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\" value=\"";
  stack1 = helpers.url || depth0.url;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\"><label for=\"dashboard_url_";
  stack1 = helpers.id || depth0.id;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "id", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">";
  stack1 = helpers.url || depth0.url;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</label></li>\n      ";
  return buffer;}

function program4(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n        <li><input type=\"radio\" name=\"dashboard_url\" id=\"dashboard_url_";
  stack1 = helpers.id || depth0.id;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "id", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\" value=\"";
  stack1 = helpers.url || depth0.url;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\"><label for=\"dashboard_url_";
  stack1 = helpers.id || depth0.id;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "id", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">";
  stack1 = helpers.url || depth0.url;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</label></li>\n      ";
  return buffer;}

function program6(depth0,data) {
  
  
  return "\n    <ul class=\"dashboard_urls\">\n      <li><input checked=\"checked\" type=\"radio\" name=\"dashboard_url\" id=\"dashboard_custom\" value=\"\"><label for=\"dashboard_custom\"><input type=\"text\" id=\"dashboard_custom_text\" name=\"custom_dashboard_url\" placeholder=\"Enter URL\" /></label></li>\n    </ul>\n  ";}

  buffer += "<div class=\"dashboard_install_instructions\">\n  <img src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/static/img/download.png\" />\n  <h2>Install to:</h2>\n  ";
  stack1 = helpers.dashboard_urls || depth0.dashboard_urls;
  stack2 = helpers['if'];
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.program(6, program6, data);
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n</div>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["install_popup_default.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression, blockHelperMissing=helpers.blockHelperMissing;

function program1(depth0,data) {
  
  var buffer = "", stack1, stack2;
  buffer += "\n    <ul class=\"dashboard_urls\">\n      ";
  stack1 = helpers.dashboard_urls || depth0.dashboard_urls;
  stack2 = helpers.head || depth0.head;
  tmp1 = self.program(2, program2, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  if(typeof stack2 === functionType) { stack1 = stack2.call(depth0, stack1, tmp1); }
  else { stack1 = blockHelperMissing.call(depth0, stack2, stack1, tmp1); }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n    </ul>\n  ";
  return buffer;}
function program2(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n        <li><label for=\"dashboard_url_";
  stack1 = helpers.id || depth0.id;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "id", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">";
  stack1 = helpers.url || depth0.url;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</label></li>\n      ";
  return buffer;}

  buffer += "<div class=\"dashboard_install_instructions\">\n  <img src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/static/img/download.png\" />\n  <h2>Install to:</h2>\n  ";
  stack1 = helpers.dashboard_urls || depth0.dashboard_urls;
  stack2 = helpers['if'];
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n</div>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["install_popup_manual.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;


  buffer += "<div class=\"manual_install_instructions\">\n  <h2>Manual install (bash script)</h2>\n  <pre>curl ";
  stack1 = helpers.install_script_url || depth0.install_script_url;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "install_script_url", { hash: {} }); }
  buffer += escapeExpression(stack1) + " | bash</pre>\n  <p>Copy and paste the above command into your bash console, you will be prompted for the URL to install the app to. You may want to <a rel=\"external\" href=\"";
  stack1 = helpers.install_script_url || depth0.install_script_url;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "install_script_url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">read the script</a> first.</p>\n</div>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["own_garden_option.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  var buffer = "", stack1, stack2;
  buffer += "\n<tr ";
  stack1 = helpers.advanced || depth0.advanced;
  stack2 = helpers['if'];
  tmp1 = self.program(2, program2, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += ">\n    <td class=\"radio\">\n        <input type=\"radio\" name=\"install\" value=\"";
  stack1 = helpers.url || depth0.url;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "url", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\" ";
  stack1 = helpers.primary || depth0.primary;
  stack2 = helpers['if'];
  tmp1 = self.program(4, program4, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "/>\n    </td>\n    <td class=\"type_td\">\n        ";
  stack1 = helpers.img || depth0.img;
  stack2 = helpers['if'];
  tmp1 = self.program(6, program6, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.program(8, program8, data);
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n        <div class=\"type\">";
  stack1 = helpers.type || depth0.type;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "type", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</div>\n    </td>\n    <td>\n        <div class=\"name\">\n        ";
  stack1 = helpers.other || depth0.other;
  stack2 = helpers['if'];
  tmp1 = self.program(10, program10, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.program(12, program12, data);
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n        </div>\n    </td>\n</tr>\n";
  return buffer;}
function program2(depth0,data) {
  
  
  return " class=\"advanced-row\"";}

function program4(depth0,data) {
  
  
  return "checked=\"checked\"";}

function program6(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n          <img src=\"";
  stack1 = helpers.img || depth0.img;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "img", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\"/>\n        ";
  return buffer;}

function program8(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n          <img src=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/static/img/install_to_dashboard.png\"  alt=\"";
  stack1 = helpers.type || depth0.type;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "type", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\"/>\n        ";
  return buffer;}

function program10(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n            <input type=\"text\" name=\"other-url\" id=\"other-url\" placeholder=\"";
  stack1 = helpers.name || depth0.name;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\" class=\"span7\"/>\n        ";
  return buffer;}

function program12(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n                ";
  stack1 = helpers.name || depth0.name;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\n        ";
  return buffer;}

  stack1 = helpers.dashboard_urls || depth0.dashboard_urls;
  stack2 = helpers.each;
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["popup.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0;


  buffer += "<div class=\"note-container\">\n  <div class=\"note-top\"></div>\n  <div class=\"note\">\n    <div class=\"note-inner\">\n      <div id=\"install_options\">\n        ";
  stack1 = helpers.content || depth0.content;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "content", { hash: {} }); }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n      </div>\n    </div>\n    <div class=\"note-actions\">\n      <a href=\"#\" class=\"closebtn btn\">Close</a>\n    </div>\n  </div>\n  <div class=\"note-corner-border\"></div>\n  <div class=\"note-corner\"></div>\n</div>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["screenshots.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression, blockHelperMissing=helpers.blockHelperMissing;

function program1(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n    <li>\n      <div class=\"paper\">\n        <div class=\"paper-inner\">\n          <img src=\"";
  stack1 = helpers.src || depth0.src;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "src", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\" />\n        </div>\n      </div>\n    </li>\n    ";
  return buffer;}

function program3(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n  <li class=\"active\" data-screenshot=\"";
  stack1 = helpers.src || depth0.src;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "src", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\"></li>\n";
  return buffer;}

function program5(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n  <li data-screenshot=\"";
  stack1 = helpers.src || depth0.src;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "src", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\"></li>\n";
  return buffer;}

  buffer += "<div id=\"screenshots\">\n  <ul>\n    ";
  stack1 = helpers.screenshots || depth0.screenshots;
  stack2 = helpers.each;
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n  </ul>\n  <a class=\"prev\">&lt;</a>\n  <a class=\"next\">&gt;</a>\n</div>\n<ul id=\"screenshot_nav\">\n";
  stack1 = helpers.screenshots || depth0.screenshots;
  stack2 = helpers.head || depth0.head;
  tmp1 = self.program(3, program3, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  if(typeof stack2 === functionType) { stack1 = stack2.call(depth0, stack1, tmp1); }
  else { stack1 = blockHelperMissing.call(depth0, stack2, stack1, tmp1); }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n";
  stack1 = helpers.screenshots || depth0.screenshots;
  stack2 = helpers.tail || depth0.tail;
  tmp1 = self.program(5, program5, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  if(typeof stack2 === functionType) { stack1 = stack2.call(depth0, stack1, tmp1); }
  else { stack1 = blockHelperMissing.call(depth0, stack2, stack1, tmp1); }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n</ul>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["search.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  
  return "\n            <img src=\"static/img/logo.png\"/>\n            ";}

function program3(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n                <h2 class=\"name_logo\">";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.market_name);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app_settings.market_name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h2>\n            ";
  return buffer;}

  buffer += "<div id=\"secondbar\">\n  <div class=\"container\">\n    <div class=\"row\">\n      <div class=\"span5\">\n        <div class=\"icon\">\n            <a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">\n            ";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.use_garden_market_logo);
  stack2 = helpers['if'];
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.program(3, program3, data);
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n            </a>\n        </div>\n      </div>\n      <div class=\"span10\">\n          <form class=\"search\">\n              <input type=\"text\" name=\"q\" placeholder=\"Search\" value=\"";
  stack1 = helpers.terms || depth0.terms;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "terms", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\"/>\n              <button class=\"btn\">&rang;</button>\n          </form>\n\n      </div>\n    </div>\n  </div>\n</div>\n<div class=\"container\">\n  <div id=\"content\">\n    <div class=\"row\">\n        <div class=\"span12 main-section search\">\n          <p class=\"count\"></p>\n          <table>\n              <tbody class=\"results\">\n\n              </tbody>\n          </table>\n        </div>\n        <div id=\"sidebar\" class=\"span4\">\n\n            <ul class=\"menu\">\n\n              <li><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">Home</a></li>\n              <li><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "upload_app\">Upload app</a></li>\n              <li class=\"active\"><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "search\">Search Results</a></li>\n            </ul>\n            <ul id=\"categories\" class=\"menu\">\n              <li><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "all\">All</a></li>\n            </ul>\n        </div>\n    </div>\n  </div>\n</div>";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["search_results.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n<tr>\n    <td>\n        <a href=\"details/";
  stack1 = helpers.name || depth0.name;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\"><img src=\"_db/";
  stack1 = helpers._id || depth0._id;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "_id", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/";
  stack1 = helpers.icon_96 || depth0.icon_96;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "icon_96", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\" width=\"96\" style=\"float: left\"/></a>\n        <a href=\"details/";
  stack1 = helpers.name || depth0.name;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\" class=\"title\">";
  stack1 = helpers.display_name || depth0.display_name;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "display_name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</a>\n        <div class=\"by\">\n            by <a href=\"user/";
  stack1 = helpers.by || depth0.by;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "by", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">";
  stack1 = helpers.by || depth0.by;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "by", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</a>\n        </div>\n        <div>\n            <a href=\"details/";
  stack1 = helpers.name || depth0.name;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/install\" class=\"btn primary\">INSTALL</a>\n        </div>\n    </td>\n    <td>";
  stack1 = helpers.description || depth0.description;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "description", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</td>\n</tr>\n\n";
  return buffer;}

  stack1 = helpers.results || depth0.results;
  stack2 = helpers.each;
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.noop;
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { return stack1; }
  else { return ''; }});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["upload_app.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  
  return "\n            <img src=\"static/img/logo.png\"/>\n            ";}

function program3(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n                <h2 class=\"name_logo\">";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.market_name);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app_settings.market_name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h2>\n            ";
  return buffer;}

  buffer += "<div id=\"secondbar\">\n  <div class=\"container\">\n    <div class=\"row\">\n      <div class=\"span5\">\n        <div class=\"icon\">\n            <a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">\n            ";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.use_garden_market_logo);
  stack2 = helpers['if'];
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.program(3, program3, data);
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n            </a>\n        </div>\n      </div>\n      <div class=\"span10\">\n          <form class=\"search\" action=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "search\">\n              <input type=\"text\" name=\"q\" placeholder=\"Search\" />\n              <button class=\"btn\">&rang;</button>\n          </form>\n\n      </div>\n    </div>\n  </div>\n</div>\n\n<div class=\"container\">\n  <div id=\"content\">\n    <div class=\"row\">\n\n      <div class=\"upload_instructions span11 main-section\">\n        <h2>Uploading an app</h2>\n        <p>\n          If you don't already have an account on this site, you'll need to create\n          one before uploading an app to the Garden. Make sure you have <a href=\"http://kan.so\">Kanso</a>\n          0.1.4 or higher, then use the following command to upload your app:\n        </p>\n        <pre class=\"upload_url\"></pre>\n\n        <hr/>\n\n        <h2>Required properties</h2>\n        <p>\n          Before uploading an app to the Garden, you need to ensure you provide\n          the following properties in your <code>kanso.json</code> file.\n        </p>\n\n        <h4>Icons</h4>\n        <p>\n          You must supply at least four icon sizes: 16x16, 48x48, 96x96, 128x128.\n          No image processing happens after you upload the app to the garden,\n          you are expected to properly format and scale your icons beforehand.\n        </p>\n        <p>\n          Icon sizes should be normalized, so they have the same visual weight.\n          For information on what makes a good icon, see the guidance on \n          <a href=\"http://code.google.com/chrome/webstore/docs/images.html\">supplying images</a>\n          from the Chrome Web Store.\n        </p>\n<pre><code class=\"javascript\">{\n    \"attachments\": [\"icons\", ...],\n    \"icons\": {\n        \"16\": \"icons/app_icon_16.png\",\n        \"48\": \"icons/app_icon_48.png\",\n        \"96\": \"icons/app_icon_96.png\",\n        \"128\": \"icons/app_icon_128.png\"\n    },\n    \"dependencies\": {\n        \"attachments\": null,\n        ...\n    },\n    ...\n}</code></pre>\n        <p>\n          <strong>Note:</strong> In the above example the icons are added as\n          attachments before referencing the attachment paths for each icon size.\n        </p>\n\n        <h4>Categories</h4>\n        <p>\n          Each app must belong to at least one category (though\n          preferably only one). If you're able to fit your app under an existing\n          category that is much preferred to adding a new one.\n        </p>\n<pre><code class=\"javascript\">{\n    \"categories\": [\"productivity\"],\n    ...\n}</code></pre>\n\n        <h4>Descriptions</h4>\n        <p>\n          Apps require two descriptions: a short one used on the listing page and\n          a long one used only on the details page. Note that the short description\n          appears above the long one on the details page, so don't start the long\n          description by repeating the short one.\n        </p>\n<pre><code class=\"javascript\">{\n    \"description\": \"This is a short description\",\n    \"long_description\": \"This is a much longer description, possibly with multiple paragraphs.\\n\\nSeparate paragraphs with two newline characters.\",\n    ...\n}</code></pre>\n\n        <h4>Promotional images</h4>\n        <p>\n          All apps should have at least a small promotional image 210x150 pixels.\n          This will be used to display your app on listings pages.\n          More sizes may be added in the future.\n        </p>\n<pre><code class=\"javascript\">{\n    \"attachments\": [\"images\", ...],\n    \"promo_images\": {\n        \"small\": \"images/promo_small.png\"\n    },\n    \"dependencies\": {\n        \"attachments\": null,\n        ...\n    }\n    ...\n}</code></pre>\n        <strong>Note:</strong> In the above example the promo images are added as\n        attachments before referencing the attachment paths for each size.\n\n        <h4>Screenshots</h4>\n        <p>\n          You must provide at least one screenshot with the app, though two or\n          three would be better. These images will be displayed on the app details\n          page and will be constrained to a 640x400 space. These images are not\n          processed before displaying, so consider resizing them appropriately\n          before uploading.\n        </p>\n<pre><code class=\"javascript\">{\n    \"attachments\": [\"images\", ...],\n    \"screenshots\": [\n        \"images/screenshot1.png\",\n        \"images/screenshot2.png\",\n        \"images/screenshot3.png\"\n    ],\n    \"dependencies\": {\n        \"attachments\": null,\n        ...\n    }\n    ...\n}</code></pre>\n        <strong>Note:</strong> In the above example the screenshots are added as\n        attachments before referencing the attachment paths for each screenshot.\n\n        <h4>Website</h4>\n        <p>\n          Please provide your GitHub repository as your website. This provides the best experience\n          for the market, end users, and other developers.\n          Just add the <code>url</code> property in your <code>kanso.json</code> file.\n          It will be displayed on the app detail page.\n        </p>\n<pre><code class=\"javascript\">{\n    \"url\": \"https://github.com/kanso/garden\"\n    ...\n}</code></pre>\n\n          <h4>Payment</h4>\n          <p>\n              App developers should be rewarded for their hard work.\n              We have provided integration with <a href=\"http://flattr.com/\">flatter</a>\n              so you can receive payments for your app. No payment is forced on a user in a market, but flattr\n              provides the best open model for payments currently. Another advantage is that international developers\n              can partake of the benefits.\n              Just add the <code>flattr_user_id</code> in your <code>kanso.json</code> file.\n          </p>\n<pre><code class=\"javascript\">{\n    \"flattr_user_id\": \"myname\"\n    ...\n}</code></pre>\n\n      </div>\n\n      <div id=\"sidebar\" class=\"span4\">\n        <ul class=\"menu\">\n          <li><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">Home</a></li>\n          <li class=\"active\"><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "upload_app\">Upload app</a></li>\n        </ul>\n        <ul id=\"categories\" class=\"menu\">\n          <li><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "all\">All</a></li>\n        </ul>\n      </div>\n\n    </div>\n  </div>\n</div>\n";
  return buffer;});
})();

(function() {
  var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates["user_page.html"] = template(function (Handlebars,depth0,helpers,partials,data) {
  helpers = helpers || Handlebars.helpers;
  var buffer = "", stack1, stack2, tmp1, self=this, functionType="function", helperMissing=helpers.helperMissing, undef=void 0, escapeExpression=this.escapeExpression;

function program1(depth0,data) {
  
  
  return "\n            <img src=\"static/img/logo.png\"/>\n            ";}

function program3(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n                <h2 class=\"name_logo\">";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.market_name);
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "app_settings.market_name", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h2>\n            ";
  return buffer;}

  buffer += "<div id=\"secondbar\">\n  <div class=\"container\">\n    <div class=\"row\">\n      <div class=\"span5\">\n        <div class=\"icon\">\n            <a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">\n            ";
  stack1 = helpers.app_settings || depth0.app_settings;
  stack1 = (stack1 === null || stack1 === undefined || stack1 === false ? stack1 : stack1.use_garden_market_logo);
  stack2 = helpers['if'];
  tmp1 = self.program(1, program1, data);
  tmp1.hash = {};
  tmp1.fn = tmp1;
  tmp1.inverse = self.program(3, program3, data);
  stack1 = stack2.call(depth0, stack1, tmp1);
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n            </a>\n        </div>\n      </div>\n      <div class=\"span10\">\n          <form class=\"search\" action=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "search\">\n              <input type=\"text\" name=\"q\" placeholder=\"Search\" />\n              <button class=\"btn\">&rang;</button>\n          </form>\n\n      </div>\n    </div>\n  </div>\n</div>\n\n<div class=\"container\">\n  <div id=\"content\">\n    <div class=\"row\">\n\n      <div class=\"span12 main-section\">\n         <h3>Apps by: ";
  stack1 = helpers.user || depth0.user;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "user", { hash: {} }); }
  buffer += escapeExpression(stack1) + "</h3>\n        ";
  stack1 = "app_list.html";
  stack2 = helpers.include || depth0.include;
  if(typeof stack2 === functionType) { stack1 = stack2.call(depth0, stack1, { hash: {} }); }
  else if(stack2=== undef) { stack1 = helperMissing.call(depth0, "include", stack1, { hash: {} }); }
  else { stack1 = stack2; }
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n      </div>\n\n      <div id=\"sidebar\" class=\"span4\">\n        <ul class=\"menu\">\n          <li><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "\">Home</a></li>\n          <li><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/upload_app\">Upload app</a></li>\n        </ul>\n        <ul id=\"categories\" class=\"menu\">\n          <li><a href=\"";
  stack1 = helpers.baseURL || depth0.baseURL;
  if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); }
  else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "baseURL", { hash: {} }); }
  buffer += escapeExpression(stack1) + "/all\">All</a></li>\n        </ul>\n      </div>\n\n    </div>\n  </div>\n</div>\n";
  return buffer;});
})();
_isFunction = function(obj) {
    return !!(obj && obj.constructor && obj.call && obj.apply);
};

Handlebars.registerHelper('uc', function (str) {
    return encodeURIComponent(str);
});

var head = function (arr, fn) {
    if (_isFunction(fn)) {
        // block helper
        return fn(arr[0]);
    }
    else {
        return arr[0];
    }
};

Handlebars.registerHelper('first', head);
Handlebars.registerHelper('head', head);

Handlebars.registerHelper('tail', function (arr, fn) {
    if (_isFunction(fn)) {
        // block helper
        var out = '';
        for (var i = 1, len = arr.length; i < len; i++) {
            out += fn(arr[i]);
        }
        return out;
    }
    else {
        return arr.slice(1);
    }
});

// TODO: add optional context argument?
Handlebars.registerHelper('include', function (name) {
    if (!exports.templates[name]) {
        throw new Error('Template Not Found: ' + name);
    }
    return exports.templates[name](this, {});
});

Handlebars.registerHelper('ifequal', function (val1, val2, fn, elseFn) {
    if (val1 === val2) {
        return fn();
    }
    else if (elseFn) {
        return elseFn();
    }
});

/*
    Handlebars "join" block helper that supports arrays of objects or strings.
    (implementation found here: https://github.com/wycats/handlebars.js/issues/133)
    
    If "delimiter" is not speficified, then it defaults to ",".
    You can use "start", and "end" to do a "slice" of the array.

    Use with objects:
    {{#join people delimiter=" and "}}{{name}}, {{age}}{{/join}}
    
    Use with arrays:
    {{join jobs delimiter=", " start="1" end="2"}}
*/

Handlebars.registerHelper('join', function(items, block) {
    var delimiter = block.hash.delimiter || ",", 
        start = start = block.hash.start || 0, 
        len = items ? items.length : 0,
        end = block.hash.end || len,
        out = "";
    
        if(end > len) end = len;
    
    if ('function' === typeof block) {
        for (i = start; i < end; i++) {
            if (i > start) out += delimiter;
            if('string' === typeof items[i])
                out += items[i];
            else
                out += block(items[i]);
        }
        return out;
    } else { 
        return [].concat(items).slice(start, end).join(delimiter);
    }
});


})};

/********** sanitize **********/

kanso.moduleCache["sanitize"] = {load: (function (module, exports, require) {

/**
 * Input sanitization, escaping, and construction functions
 * covering security-sensitive areas.
 *
 * @module
 */

/**
 * Module dependencies
 */

var _ = require('underscore')._;

/**
 *
 * @name escapeUrlParams(s)
 * @param {Object} obj An object containing url parameters, with
 *          parameter names stored as property names (or keys).
 * @returns {String}
 * @api public
 */

exports.escapeUrlParams = exports.url = function (obj)
{
    var rv = [ ];

    for (var key in obj) {
        rv.push(
            encodeURIComponent(key) +
                '=' + encodeURIComponent(obj[key])
        );
    }

    return (rv.length > 0 ? ('?' + rv.join('&')) : '');
};


/**
 * Encodes required characters as HTML entities so a string
 * can be included in a page. This function must be used to
 * avoid Cross-site Scripting attacks.
 *
 * @name escapeHtml(s)
 * @param {String} s
 * @returns {String}
 * @api public
 */

exports.escapeHtml = exports.h = function (s)
{
    s = ('' + s); /* Coerce to string */
    s = s.replace(/&/g, '&amp;');
    s = s.replace(/</g, '&lt;');
    s = s.replace(/>/g, '&gt;');
    s = s.replace(/"/g, '&quot;');
    s = s.replace(/'/g, '&#39;');
    return s;
};


/**
 * Encodes selected characters in a string, so that the string
 * can be safely used within a Javascript string constant. This
 * function must be used to avoid cross-site scripting attacks
 * (or, in some cases, arbitrary server-side code execution).
 *
 * @name escapeJavascriptString(s)
 * @param {String} s
 * @returns {String}
 * @api public
 */

exports.escapeJavascriptString = exports.js = function (s)
{
    s = ('' + s); /* Coerce to string */
    s = s.replace(/'/g, "\\'");
    s = s.replace(/"/g, '\\"');
    return s;
};


/**
 * Encodes selected characters in a string, so that the string
 * can be safely used within an XML character data section.
 * This function must be used to avoid cross-site scripting attacks.
 *
 * @name escapeXmlCharacterData(s)
 * @param {String} s
 * @returns {String}
 * @api public
 */

exports.escapeXmlCharacterData = exports.cdata = function (s)
{
    s = ('' + s); /* Coerce to string */
    s = s.replace(/\]\]>/g, '');
    return s;
};


/**
 * Encodes selected characters in a string, so that the string
 * can be safely used on the right side of a CSS attribute selector's
 * equal sign. This function should be used when using a user-modifiable
 * string to match an element by attribute -- either in jQuery, or as part
 * of a dynamically-generated CSS selector.
 *
 * @name escapeAttributeSelectorValue(s)
 * @param {String} s
 * @returns {String}
 * @api public
 */

exports.escapeAttributeSelectorValue = exports.css = function (s)
{
    s = ('' + s); /* Coerce to string */
    s = s.replace(/['"\\=\[\]]/, '\\$1');
    return s;
};


/**
 * Takes any number of arguments, and combines them together
 * to safely form a string that's suitable for use as an
 * identifier or name.
 *
 * @name generateAbstractIdentifier(rv, args, esc_fn, final_fn)
 * @param {Array} rv An accumulator array, used to store results.
 * @param {Array} args An array of arguments to the id-generating function.
 * @param {Array} esc_fn An input-sanitizing function, to be applied to
 *          each component of the identifier before it's emitted.
 * @param {Array} join_fn A function that accepts an array of sanitized
 *          identifier components, and produces a string.
 * @returns {String}
 */

exports.generateAbstractIdentifier = function (rv, args, esc_fn, join_fn) {
    if (args.length <= 0) {
        return null;
    }
    for (var i = 0, len = args.length; i < len; ++i) {
        var arg = args[i];
        if (arg !== undefined && arg !== null) {
            if (_.isArray(arg)) {
                /* Avoid recursion; limit to one level deep */
                for (var j = 0, lenj = arg.length; j < lenj; ++j) {
                    if (arg[j] !== undefined && arg[j] !== null) {
                        rv.push(esc_fn(arg[j]));
                    }
                }
            } else {
                rv.push(esc_fn(arg));
            }
        }
    }
    return join_fn(rv);
};


/**
 * Takes any number of arguments, and combines them together
 * to safely form a string that's suitable for use as a DOM
 * element identifier.
 *
 * @name generateDomIdentifier()
 * @returns {String}
 * @api public
 */

exports.generateDomIdentifier = exports.id = function (/* ... */) {
    return exports.generateAbstractIdentifier(
        [ 'id' ], arguments, function (x) {
            return ('' + x).replace(/[^A-Za-z0-9_]+/, '_');
        }, function (x) {
            return x.join('_');
        }
    );
};


/**
 * Takes any number of arguments, and combines them together
 * to safely form a string that's suitable for use in a DOM
 * element's name attribute.
 *
 * @name generateDomName()
 * @returns {String}
 * @api public
 */

exports.generateDomName = exports.name = function (/* ... */) {
    return exports.generateAbstractIdentifier(
        [ ], arguments, function (x) {
            return ('' + x).replace(/[\'\"]+/, '_');
        }, function (x) {
            return x.join('.');
        }
    );
};


})};

/********** datelib **********/

kanso.moduleCache["datelib"] = {load: (function (module, exports, require) {

// Thanks to underscore.js for this method

exports.isDate = function(obj) {
    return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
};


/*
 * Takes an ISO time or Date object and returns a string representing how
 * long ago the date represents in human-friendly terms. If the date is over one
 * month ago, returns the date in the form YYYY-MM-DD
 *
 * Adapted from John Resig's prettyDate function:
 * http://ejohn.org/blog/javascript-pretty-date/
 */

exports.prettify = function (d) {
    if (typeof d === 'string') {
        d = new Date(d);
    }
    var now = new Date(),
        diff = ((now.getTime() - d.getTime()) / 1000),
        day_diff = Math.floor(diff / 86400);

    if (isNaN(day_diff) || day_diff < 0) {
        return;
    }

    if (day_diff === 0) {
        if (diff < 60)    return "just now";
        if (diff < 120)   return "1 minute ago";
        if (diff < 3600)  return Math.floor( diff / 60 ) + " minutes ago";
        if (diff < 7200)  return "1 hour ago";
        if (diff < 86400) return Math.floor( diff / 3600 ) + " hours ago";
    }
    if (day_diff === 1) return "yesterday";
    if (day_diff < 7)   return day_diff + " days ago";
    if (day_diff < 31)  return Math.ceil( day_diff / 7 ) + " weeks ago";

    var yyyy = d.getFullYear();
    var mm = d.getMonth();
    var dd = d.getDate();

    return yyyy + '-' + mm + '-' + dd;
};

/**
 * Returns an ISO date string from a Date object
 */

exports.ISODateString = function (d) {
    if (!d) {
        // default to current time
        var d = new Date();
    }
    function pad(n){
        return n < 10 ? '0' + n : n;
    }
    return d.getUTCFullYear() + '-' +
        pad(d.getUTCMonth() + 1) + '-' +
        pad(d.getUTCDate()) + 'T' +
        pad(d.getUTCHours()) + ':' +
        pad(d.getUTCMinutes()) + ':' +
        pad(d.getUTCSeconds()) + 'Z';
};


})};

/********** path **********/

kanso.moduleCache["path"] = {load: (function (module, exports, require) {

/**
 * Path functions ported from node.js to work in CouchDB and the browser.
 * This module is used internally by Kanso, although you can use it in your
 * apps too if you find the functions useful.
 *
 * @module
 */

// Copyright Joyent, Inc. and other Node contributors.
//
// 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.

/**
 * From node.js v0.2.6
 */

/**
 * Joins multiple paths together using '/'. Accepts a arbitrary number of
 * strings as arguments, returning the joined result as a single string.
 *
 * @name join(path, [...])
 * @returns {String}
 * @api public
 */

exports.join = function () {
    return exports.normalize(Array.prototype.join.call(arguments, "/"));
};

/**
 * Normalizes a path split into an array, taking care of '..' and '.' parts.
 *
 * @name normalizeArray(parts, [keepBlanks])
 * @param {Array} parts
 * @param {Boolean} keepBlanks
 * @returns {Array}
 * @api public
 */

exports.normalizeArray = function (parts, keepBlanks) {
    var directories = [], prev;
    for (var i = 0, l = parts.length - 1; i <= l; i++) {
        var directory = parts[i];

        // if it's blank, but it's not the first thing, and not the last
        // thing, skip it.
        if (directory === "" && i !== 0 && i !== l && !keepBlanks) {
            continue;
        }

        // if it's a dot, and there was some previous dir already, then
        // skip it.
        if (directory === "." && prev !== undefined) {
            continue;
        }

        // if it starts with "", and is a . or .., then skip it.
        if (directories.length === 1 && directories[0] === "" && (directory === "." || directory === "..")) {
            continue;
        }

        if (directory === ".." && directories.length && prev !== ".." && prev !== "." && prev !== undefined && (prev !== "" || keepBlanks)) {
            directories.pop();
            prev = directories.slice(-1)[0];
        }
        else {
            if (prev === ".") {
                directories.pop();
            }
            directories.push(directory);
            prev = directory;
        }
    }
    return directories;
};

/**
 * Normalize a string path, taking care of '..' and '.' parts.
 *
 * @name normalize(path, [keepBlanks])
 * @param {String} path
 * @param {Boolean} keepBlanks
 * @returns string
 * @api public
 */

exports.normalize = function (path, keepBlanks) {
    return exports.normalizeArray(path.split("/"), keepBlanks).join("/");
};

/**
 * Return the directory name of a path. Similar to the Unix dirname command.
 *
 * @name dirname(path)
 * @param {String} path
 * @returns {String}
 * @api public
 */

exports.dirname = function (path) {
    if (path.length > 1 && '/' === path[path.length - 1]) {
        path = path.replace(/\/+$/, '');
    }
    var lastSlash = path.lastIndexOf('/');
    switch (lastSlash) {
    case -1:
        return '.';
    case 0:
        return '/';
    default:
        return path.substring(0, lastSlash);
    }
};

/**
 * Return the last portion of a path. Similar to the Unix basename command.
 *
 * **Example**
 * <pre><code class="javascript">path.basename('/foo/bar/baz/asdf/quux.html')
 * // returns
 * 'quux.html'
 *
 * path.basename('/foo/bar/baz/asdf/quux.html', '.html')
 * // returns
 * 'quux'
 * </code></pre>
 *
 * @name basename(path, ext)
 * @param {String} path
 * @param {String} ext
 * @returns {String}
 * @api public
 */

exports.basename = function (path, ext) {
    var f = path.substr(path.lastIndexOf("/") + 1);
    if (ext && f.substr(-1 * ext.length) === ext) {
        f = f.substr(0, f.length - ext.length);
    }
    return f;
};

/**
 * Return the extension of the path. Everything after the last '.' in the last
 * portion of the path. If there is no '.' in the last portion of the path or
 * the only '.' is the first character, then it returns an empty string.
 *
 * <pre><code class="javascript">path.extname('index.html')
 * // returns
 * '.html'
 *
 * path.extname('index')
 * // returns
 * ''
 * </code></pre>
 *
 * @name extname(path)
 * @param {String} path
 * @returns {String}
 * @api public
 */

exports.extname = function (path) {
    var dot = path.lastIndexOf('.'),
        slash = path.lastIndexOf('/');
    // The last dot must be in the last path component, and it (the last dot)
    // must not start the last path component (i.e. be a dot that signifies a
    // hidden file in UNIX).
    return dot <= slash + 1 ? '' : path.substring(dot);
};



})};

/********** url **********/

kanso.moduleCache["url"] = {load: (function (module, exports, require) {

/**
 * Adapted for use in the browser and packaged for Kanso by Caolan McMahon.
 * This build does not include IDNA Support in order to avoid the punycode
 * dependency.
 *
 * @module
 */


// Copyright Joyent, Inc. and other Node contributors.
//
// 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.


// IDNA SUPPORT REMOVED FOR KANSO BUILD
//var punycode = require('punycode');


// ADDED FOR BROWSER SUPPORT - functions borrowed from underscore.js
var _keys = Object.keys || function(obj) {
  if (obj !== Object(obj)) throw new TypeError('Invalid object');
  var keys = [];
  for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key;
  return keys;
};
var nativeIndexOf = Array.prototype.indexOf;
var _indexOf = function(array, item) {
  if (array == null) return -1;
  var i, l;
  if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
  for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
  return -1;
};


exports.parse = urlParse;
exports.resolve = urlResolve;
exports.resolveObject = urlResolveObject;
exports.format = urlFormat;

// Reference: RFC 3986, RFC 1808, RFC 2396

// define these here so at least they only have to be
// compiled once on the first module load.
var protocolPattern = /^([a-z0-9.+-]+:)/i,
    portPattern = /:[0-9]+$/,
    // RFC 2396: characters reserved for delimiting URLs.
    delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'],
    // RFC 2396: characters not allowed for various reasons.
    unwise = ['{', '}', '|', '\\', '^', '~', '[', ']', '`'].concat(delims),
    // Allowed by RFCs, but cause of XSS attacks.  Always escape these.
    autoEscape = ['\''],
    // Characters that are never ever allowed in a hostname.
    // Note that any invalid chars are also handled, but these
    // are the ones that are *expected* to be seen, so we fast-path
    // them.
    nonHostChars = ['%', '/', '?', ';', '#']
      .concat(unwise).concat(autoEscape),
    nonAuthChars = ['/', '@', '?', '#'].concat(delims),
    hostnameMaxLen = 255,
    hostnamePartPattern = /^[a-zA-Z0-9][a-z0-9A-Z_-]{0,62}$/,
    hostnamePartStart = /^([a-zA-Z0-9][a-z0-9A-Z_-]{0,62})(.*)$/,
    // protocols that can allow "unsafe" and "unwise" chars.
    unsafeProtocol = {
      'javascript': true,
      'javascript:': true
    },
    // protocols that never have a hostname.
    hostlessProtocol = {
      'javascript': true,
      'javascript:': true
    },
    // protocols that always have a path component.
    pathedProtocol = {
      'http': true,
      'https': true,
      'ftp': true,
      'gopher': true,
      'file': true,
      'http:': true,
      'ftp:': true,
      'gopher:': true,
      'file:': true
    },
    // protocols that always contain a // bit.
    slashedProtocol = {
      'http': true,
      'https': true,
      'ftp': true,
      'gopher': true,
      'file': true,
      'http:': true,
      'https:': true,
      'ftp:': true,
      'gopher:': true,
      'file:': true
    },
    querystring = require('querystring');

function urlParse(url, parseQueryString, slashesDenoteHost) {
  if (url && typeof(url) === 'object' && url.href) return url;

  if (typeof url !== 'string') {
    throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
  }

  var out = {},
      rest = url;

  // cut off any delimiters.
  // This is to support parse stuff like "<http://foo.com>"
  for (var i = 0, l = rest.length; i < l; i++) {
    if (_indexOf(delims, rest.charAt(i)) === -1) break;
  }
  if (i !== 0) rest = rest.substr(i);


  var proto = protocolPattern.exec(rest);
  if (proto) {
    proto = proto[0];
    var lowerProto = proto.toLowerCase();
    out.protocol = lowerProto;
    rest = rest.substr(proto.length);
  }

  // figure out if it's got a host
  // user@server is *always* interpreted as a hostname, and url
  // resolution will treat //foo/bar as host=foo,path=bar because that's
  // how the browser resolves relative URLs.
  if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
    var slashes = rest.substr(0, 2) === '//';
    if (slashes && !(proto && hostlessProtocol[proto])) {
      rest = rest.substr(2);
      out.slashes = true;
    }
  }

  if (!hostlessProtocol[proto] &&
      (slashes || (proto && !slashedProtocol[proto]))) {
    // there's a hostname.
    // the first instance of /, ?, ;, or # ends the host.
    // don't enforce full RFC correctness, just be unstupid about it.

    // If there is an @ in the hostname, then non-host chars *are* allowed
    // to the left of the first @ sign, unless some non-auth character
    // comes *before* the @-sign.
    // URLs are obnoxious.
    var atSign = rest.indexOf('@');
    if (atSign !== -1) {
      // there *may be* an auth
      var hasAuth = true;
      for (var i = 0, l = nonAuthChars.length; i < l; i++) {
        var index = rest.indexOf(nonAuthChars[i]);
        if (index !== -1 && index < atSign) {
          // not a valid auth.  Something like http://foo.com/bar@baz/
          hasAuth = false;
          break;
        }
      }
      if (hasAuth) {
        // pluck off the auth portion.
        out.auth = rest.substr(0, atSign);
        rest = rest.substr(atSign + 1);
      }
    }

    var firstNonHost = -1;
    for (var i = 0, l = nonHostChars.length; i < l; i++) {
      var index = rest.indexOf(nonHostChars[i]);
      if (index !== -1 &&
          (firstNonHost < 0 || index < firstNonHost)) firstNonHost = index;
    }

    if (firstNonHost !== -1) {
      out.host = rest.substr(0, firstNonHost);
      rest = rest.substr(firstNonHost);
    } else {
      out.host = rest;
      rest = '';
    }

    // pull out port.
    var p = parseHost(out.host);
    var keys = _keys(p);
    for (var i = 0, l = keys.length; i < l; i++) {
      var key = keys[i];
      out[key] = p[key];
    }

    // we've indicated that there is a hostname,
    // so even if it's empty, it has to be present.
    out.hostname = out.hostname || '';

    // validate a little.
    if (out.hostname.length > hostnameMaxLen) {
      out.hostname = '';
    } else {
      var hostparts = out.hostname.split(/\./);
      for (var i = 0, l = hostparts.length; i < l; i++) {
        var part = hostparts[i];
        if (!part) continue;
        if (!part.match(hostnamePartPattern)) {
          var newpart = '';
          for (var j = 0, k = part.length; j < k; j++) {
            if (part.charCodeAt(j) > 127) {
              // we replace non-ASCII char with a temporary placeholder
              // we need this to make sure size of hostname is not
              // broken by replacing non-ASCII by nothing
              newpart += 'x';
            } else {
              newpart += part[j];
            }
          }
          // we test again with ASCII char only
          if (!newpart.match(hostnamePartPattern)) {
            var validParts = hostparts.slice(0, i);
            var notHost = hostparts.slice(i + 1);
            var bit = part.match(hostnamePartStart);
            if (bit) {
              validParts.push(bit[1]);
              notHost.unshift(bit[2]);
            }
            if (notHost.length) {
              rest = '/' + notHost.join('.') + rest;
            }
            out.hostname = validParts.join('.');
            break;
          }
        }
      }
    }

    // hostnames are always lower case.
    out.hostname = out.hostname.toLowerCase();


    // IDNA SUPPORT REMOVED FOR KANSO BUILD

    // IDNA Support: Returns a puny coded representation of "domain".
    // It only converts the part of the domain name that
    // has non ASCII characters. I.e. it dosent matter if
    // you call it with a domain that already is in ASCII.
    /*
    var domainArray = out.hostname.split('.');
    var newOut = [];
    for (var i = 0; i < domainArray.length; ++i) {
      var s = domainArray[i];
      newOut.push(s.match(/[^A-Za-z0-9_-]/) ?
          'xn--' + punycode.encode(s) : s);
    }
    out.hostname = newOut.join('.');
    */


    out.host = (out.hostname || '') +
        ((out.port) ? ':' + out.port : '');
    out.href += out.host;
  }

  // now rest is set to the post-host stuff.
  // chop off any delim chars.
  if (!unsafeProtocol[lowerProto]) {

    // First, make 100% sure that any "autoEscape" chars get
    // escaped, even if encodeURIComponent doesn't think they
    // need to be.
    for (var i = 0, l = autoEscape.length; i < l; i++) {
      var ae = autoEscape[i];
      var esc = encodeURIComponent(ae);
      if (esc === ae) {
        esc = escape(ae);
      }
      rest = rest.split(ae).join(esc);
    }

    // Now make sure that delims never appear in a url.
    var chop = rest.length;
    for (var i = 0, l = delims.length; i < l; i++) {
      var c = rest.indexOf(delims[i]);
      if (c !== -1) {
        chop = Math.min(c, chop);
      }
    }
    rest = rest.substr(0, chop);
  }


  // chop off from the tail first.
  var hash = rest.indexOf('#');
  if (hash !== -1) {
    // got a fragment string.
    out.hash = rest.substr(hash);
    rest = rest.slice(0, hash);
  }
  var qm = rest.indexOf('?');
  if (qm !== -1) {
    out.search = rest.substr(qm);
    out.query = rest.substr(qm + 1);
    if (parseQueryString) {
      out.query = querystring.parse(out.query);
    }
    rest = rest.slice(0, qm);
  } else if (parseQueryString) {
    // no query string, but parseQueryString still requested
    out.search = '';
    out.query = {};
  }
  if (rest) out.pathname = rest;
  if (slashedProtocol[proto] &&
      out.hostname && !out.pathname) {
    out.pathname = '/';
  }

  //to support http.request
  if (out.pathname || out.search) {
    out.path = (out.pathname ? out.pathname : '') +
               (out.search ? out.search : '');
  }

  // finally, reconstruct the href based on what has been validated.
  out.href = urlFormat(out);
  return out;
}

// format a parsed object into a url string
function urlFormat(obj) {
  // ensure it's an object, and not a string url.
  // If it's an obj, this is a no-op.
  // this way, you can call url_format() on strings
  // to clean up potentially wonky urls.
  if (typeof(obj) === 'string') obj = urlParse(obj);

  var auth = obj.auth || '';
  if (auth) {
    auth = auth.split('@').join('%40');
    for (var i = 0, l = nonAuthChars.length; i < l; i++) {
      var nAC = nonAuthChars[i];
      auth = auth.split(nAC).join(encodeURIComponent(nAC));
    }
    auth += '@';
  }

  var protocol = obj.protocol || '',
      host = (obj.host !== undefined) ? auth + obj.host :
          obj.hostname !== undefined ? (
              auth + obj.hostname +
              (obj.port ? ':' + obj.port : '')
          ) :
          false,
      pathname = obj.pathname || '',
      query = obj.query &&
              ((typeof obj.query === 'object' &&
                _keys(obj.query).length) ?
                 querystring.stringify(obj.query) :
                 '') || '',
      search = obj.search || (query && ('?' + query)) || '',
      hash = obj.hash || '';

  if (protocol && protocol.substr(protocol.length - 1) !== ':') protocol += ':';

  // only the slashedProtocols get the //.  Not mailto:, xmpp:, etc.
  // unless they had them to begin with.
  if (obj.slashes ||
      (!protocol || slashedProtocol[protocol]) && host !== false) {
    host = '//' + (host || '');
    if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
  } else if (!host) {
    host = '';
  }

  if (hash && hash.charAt(0) !== '#') hash = '#' + hash;
  if (search && search.charAt(0) !== '?') search = '?' + search;

  return protocol + host + pathname + search + hash;
}

function urlResolve(source, relative) {
  return urlFormat(urlResolveObject(source, relative));
}

function urlResolveObject(source, relative) {
  if (!source) return relative;

  source = urlParse(urlFormat(source), false, true);
  relative = urlParse(urlFormat(relative), false, true);

  // hash is always overridden, no matter what.
  source.hash = relative.hash;

  if (relative.href === '') {
    source.href = urlFormat(source);
    return source;
  }

  // hrefs like //foo/bar always cut to the protocol.
  if (relative.slashes && !relative.protocol) {
    relative.protocol = source.protocol;
    //urlParse appends trailing / to urls like http://www.example.com
    if (slashedProtocol[relative.protocol] &&
        relative.hostname && !relative.pathname) {
      relative.path = relative.pathname = '/';
    }
    relative.href = urlFormat(relative);
    return relative;
  }

  if (relative.protocol && relative.protocol !== source.protocol) {
    // if it's a known url protocol, then changing
    // the protocol does weird things
    // first, if it's not file:, then we MUST have a host,
    // and if there was a path
    // to begin with, then we MUST have a path.
    // if it is file:, then the host is dropped,
    // because that's known to be hostless.
    // anything else is assumed to be absolute.
    if (!slashedProtocol[relative.protocol]) {
      relative.href = urlFormat(relative);
      return relative;
    }
    source.protocol = relative.protocol;
    if (!relative.host && !hostlessProtocol[relative.protocol]) {
      var relPath = (relative.pathname || '').split('/');
      while (relPath.length && !(relative.host = relPath.shift()));
      if (!relative.host) relative.host = '';
      if (!relative.hostname) relative.hostname = '';
      if (relPath[0] !== '') relPath.unshift('');
      if (relPath.length < 2) relPath.unshift('');
      relative.pathname = relPath.join('/');
    }
    source.pathname = relative.pathname;
    source.search = relative.search;
    source.query = relative.query;
    source.host = relative.host || '';
    source.auth = relative.auth;
    source.hostname = relative.hostname || relative.host;
    source.port = relative.port;
    //to support http.request
    if (source.pathname !== undefined || source.search !== undefined) {
      source.path = (source.pathname ? source.pathname : '') +
                    (source.search ? source.search : '');
    }
    source.slashes = source.slashes || relative.slashes;
    source.href = urlFormat(source);
    return source;
  }

  var isSourceAbs = (source.pathname && source.pathname.charAt(0) === '/'),
      isRelAbs = (
          relative.host !== undefined ||
          relative.pathname && relative.pathname.charAt(0) === '/'
      ),
      mustEndAbs = (isRelAbs || isSourceAbs ||
                    (source.host && relative.pathname)),
      removeAllDots = mustEndAbs,
      srcPath = source.pathname && source.pathname.split('/') || [],
      relPath = relative.pathname && relative.pathname.split('/') || [],
      psychotic = source.protocol &&
          !slashedProtocol[source.protocol];

  // if the url is a non-slashed url, then relative
  // links like ../.. should be able
  // to crawl up to the hostname, as well.  This is strange.
  // source.protocol has already been set by now.
  // Later on, put the first path part into the host field.
  if (psychotic) {

    delete source.hostname;
    delete source.port;
    if (source.host) {
      if (srcPath[0] === '') srcPath[0] = source.host;
      else srcPath.unshift(source.host);
    }
    delete source.host;
    if (relative.protocol) {
      delete relative.hostname;
      delete relative.port;
      if (relative.host) {
        if (relPath[0] === '') relPath[0] = relative.host;
        else relPath.unshift(relative.host);
      }
      delete relative.host;
    }
    mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
  }

  if (isRelAbs) {
    // it's absolute.
    source.host = (relative.host || relative.host === '') ?
                      relative.host : source.host;
    source.hostname = (relative.hostname || relative.hostname === '') ?
                      relative.hostname : source.hostname;
    source.search = relative.search;
    source.query = relative.query;
    srcPath = relPath;
    // fall through to the dot-handling below.
  } else if (relPath.length) {
    // it's relative
    // throw away the existing file, and take the new path instead.
    if (!srcPath) srcPath = [];
    srcPath.pop();
    srcPath = srcPath.concat(relPath);
    source.search = relative.search;
    source.query = relative.query;
  } else if ('search' in relative) {
    // just pull out the search.
    // like href='?foo'.
    // Put this after the other two cases because it simplifies the booleans
    if (psychotic) {
      source.hostname = source.host = srcPath.shift();
      //occationaly the auth can get stuck only in host
      //this especialy happens in cases like
      //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
      var authInHost = source.host && source.host.indexOf('@') > 0 ?
                       source.host.split('@') : false;
      if (authInHost) {
        source.auth = authInHost.shift();
        source.host = source.hostname = authInHost.shift();
      }
    }
    source.search = relative.search;
    source.query = relative.query;
    //to support http.request
    if (source.pathname !== undefined || source.search !== undefined) {
      source.path = (source.pathname ? source.pathname : '') +
                    (source.search ? source.search : '');
    }
    source.href = urlFormat(source);
    return source;
  }
  if (!srcPath.length) {
    // no path at all.  easy.
    // we've already handled the other stuff above.
    delete source.pathname;
    //to support http.request
    if (!source.search) {
      source.path = '/' + source.search;
    } else {
      delete source.path;
    }
    source.href = urlFormat(source);
    return source;
  }
  // if a url ENDs in . or .., then it must get a trailing slash.
  // however, if it ends in anything else non-slashy,
  // then it must NOT get a trailing slash.
  var last = srcPath.slice(srcPath.length - 1)[0];
  var hasTrailingSlash = (
      (source.host || relative.host) && (last === '.' || last === '..') ||
      last === '');

  // strip single dots, resolve double dots to parent dir
  // if the path tries to go above the root, `up` ends up > 0
  var up = 0;
  for (var i = srcPath.length; i >= 0; i--) {
    last = srcPath[i];
    if (last == '.') {
      srcPath.splice(i, 1);
    } else if (last === '..') {
      srcPath.splice(i, 1);
      up++;
    } else if (up) {
      srcPath.splice(i, 1);
      up--;
    }
  }

  // if the path is allowed to go above the root, restore leading ..s
  if (!mustEndAbs && !removeAllDots) {
    for (; up--; up) {
      srcPath.unshift('..');
    }
  }

  if (mustEndAbs && srcPath[0] !== '' &&
      (!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
    srcPath.unshift('');
  }

  if (hasTrailingSlash && (srcPath.join('/').substr(srcPath.join('/').length - 1) !== '/')) {
    srcPath.push('');
  }

  var isAbsolute = srcPath[0] === '' ||
      (srcPath[0] && srcPath[0].charAt(0) === '/');

  // put the host back
  if (psychotic) {
    source.hostname = source.host = isAbsolute ? '' :
                                    srcPath.length ? srcPath.shift() : '';
    //occationaly the auth can get stuck only in host
    //this especialy happens in cases like
    //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
    var authInHost = source.host && source.host.indexOf('@') > 0 ?
                     source.host.split('@') : false;
    if (authInHost) {
      source.auth = authInHost.shift();
      source.host = source.hostname = authInHost.shift();
    }
  }

  mustEndAbs = mustEndAbs || (source.host && srcPath.length);

  if (mustEndAbs && !isAbsolute) {
    srcPath.unshift('');
  }

  source.pathname = srcPath.join('/');
  //to support request.http
  if (source.pathname !== undefined || source.search !== undefined) {
    source.path = (source.pathname ? source.pathname : '') +
                  (source.search ? source.search : '');
  }
  source.auth = relative.auth || source.auth;
  source.slashes = source.slashes || relative.slashes;
  source.href = urlFormat(source);
  return source;
}

function parseHost(host) {
  var out = {};
  var port = portPattern.exec(host);
  if (port) {
    port = port[0];
    out.port = port.substr(1);
    host = host.substr(0, host.length - port.length);
  }
  if (host) out.hostname = host;
  return out;
}


})};

/********** jsonp **********/

kanso.moduleCache["jsonp"] = {load: (function (module, exports, require) {

var _ = require('underscore')._;


/**
 * Checks to see if a function name is valid and safe for JSONP
 */

exports.validFunctionName = function (name) {
    var re = /^[$A-Za-z_][0-9A-Za-z_]*$/;
    var reserved = [
        'instanceof',
        'typeof',
        'break',
        'do',
        'new',
        'var',
        'case',
        'else',
        'return',
        'void',
        'catch',
        'finally',
        'continue',
        'for',
        'switch',
        'while',
        'this',
        'with',
        'debugger',
        'function',
        'throw',
        'default',
        'if',
        'try',
        'delete',
        'in'
    ];
    return (re.test(name) && _.indexOf(reserved, name) === -1);
};


/**
 * Creates a CouchDB response object. Checks if the callback is valid and
 * returns a 400 (Bad request) if not, or the JSON encoded data wrapped in the
 * callback function if it is. If the callback is empty or undefined then a
 * plain JSON response is used. The extensions object will be used to extend
 * the response object before returning it.
 */

exports.response = function (callback, data, extensions) {
    var res;
    if (!callback) {
        // standard JSON request
        res = {
            code: 200,
            headers: {"Content-Type": "application/json"},
            body: JSON.stringify(data)
        };
    }
    else if (!exports.validFunctionName(callback)) {
        // invalid JSONP callback name
        res = {
            code: 400,
            headers: {"Content-Type": "application/json"},
            body: JSON.stringify({
                error:"bad_request",
                reason:"invalid_callback"
            })
        };
    }
    else {
        // JSONP request
        res = {
            code: 200,
            headers: {"Content-Type": "application/json"},
            body: callback + '(' + JSON.stringify(data) + ');'
        };
    }
    if (extensions) {
        return _.extend(res, extensions);
    }
    return res;
};


})};

/********** cookies **********/

kanso.moduleCache["cookies"] = {load: (function (module, exports, require) {

/*global escape: false */

/**
 * Functions related to the manipulation and reading of cookies.
 *
 * @module
 */

function isBrowser() {
    return (typeof(window) !== 'undefined');
}


/**
 * Read cookies currently stored in the browser, returning an object
 * keyed by cookie name.
 *
 * @name readBrowserCookies()
 * @returns Object
 * @api public
 */

exports.readBrowserCookies = function () {
    if (!isBrowser()) {
        throw new Error('readBrowserCookies cannot be called server-side');
    }
    var cookies = {};
    var parts = document.cookie.split(';');
    for (var i = 0, len = parts.length; i < len; i++) {
        var name = parts[i].split('=')[0];
        var value = parts[i].split('=').slice(1).join('=');
        cookies[name] = value;
    }
    return cookies;
};

exports.readCookie = function(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') c = c.substring(1, c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
    }
    return null;
}


/**
 * Reads browser cookies and returned the value of the named cookie.
 *
 * @name readBrowserCookie(name)
 * @returns {String}
 * @api public
 */

exports.readBrowserCookie = function (name) {
    return exports.readBrowserCookies()[name];
};

/**
 * Creates a string for storing a cookie on the browser.
 *
 * @name cookieString(req, opt)
 * @param {Request Object} req
 * @param {Object} opt
 * @returns {String}
 * @api public
 */

exports.cookieString = function (req, opt) {
    var str = escape(opt.name) + '=' + escape(opt.value) + '; path=' + opt.path;
    if (opt.days) {
        var expires = new Date().setTime(
            new Date().getTime() + 1000 * 60 * 60 * 24 * opt.days
        );
        str += '; expires=' + expires.toGMTString();
    }
    return str;
};

/**
 * Sets a cookie on the browser, for use client-side only.
 *
 * @name setBrowserCookie(req, opt)
 * @param {Request Object} req
 * @param {Object} opt
 * @api public
 */

exports.setBrowserCookie = function (req, opt) {
    if (!isBrowser()) {
        throw new Error('setBrowserCookie cannot be called server-side');
    }
    var str = (typeof opt === 'string') ? opt: exports.cookieString(req, opt);
    document.cookie = str;
};

/**
 * Creates a Set-Cookie header on a response object.
 *
 * @name setResponseCookie(req, res, opt)
 * @param {Request Object} req
 * @param {Response Object} res
 * @param {Object} opt
 * @api public
 */

exports.setResponseCookie = function (req, res, opt) {
    var str = (typeof opt === 'string') ? opt: exports.cookieString(req, opt);
    if (typeof res !== 'object') {
        res = {code: 200, body: res};
    }
    if (!res.headers) {
        res.headers = {};
    }
    // TODO: is it possible to send multiple set-cookie headers by turning
    // headers into an array like in node?
    // XXX: just replacing all cookies for now - not ideal!
    res.headers['Set-Cookie'] = str;
};


})};

/********** flattr **********/

kanso.moduleCache["flattr"] = {load: (function (module, exports, require) {



exports.getFlattrUserId = function(installDoc) {
    if (installDoc && installDoc.kanso && installDoc.kanso.config && installDoc.kanso.config.flattr_user_id) {
        return installDoc.kanso.config.flattr_user_id;
    }
    if (installDoc.config && installDoc.config.flattr_user_id) {
        return installDoc.config.flattr_user_id;
    }
    return null;
}


exports.hasFlattr = function(installDoc) {
    var user_id = exports.getFlattrUserId(installDoc);
    if (user_id) return true;
    return false;
}

exports.getFlattrDetailsFromInstallDoc = function(installDoc) {
    return {
        user_id : exports.getFlattrUserId(installDoc),
        url : installDoc.src,
        title : encodeURIComponent(installDoc.kanso.config.name),
        description : encodeURIComponent(installDoc.kanso.config.description),
        category : 'software'
    }
}

exports.createFlattrDetailsFromKanso = function(kanso, url) {
    return {
        user_id : exports.getFlattrUserId(kanso),
        url : url,
        title : encodeURIComponent(kanso.config.name),
        description : encodeURIComponent(kanso.config.description),
        category : 'software'
    }
}

exports.generateFlattrUrl = function(flattrDetails) {

    var flattrLink =    'https://flattr.com/submit/auto' +
                        '?user_id=' + flattrDetails.user_id +
                        '&url=' + flattrDetails.url +
                        '&title=' + flattrDetails.title +
                        '&description=' + flattrDetails.description +
                        '&category=' + flattrDetails.category;
     return flattrLink;
}

exports.generateFlatterLinkHtml = function(flattrDetails, /*optional*/ tooltip) {
    if (!tooltip) tooltip = 'Flattr this app!';
    var flattrLink = exports.generateFlattrUrl(flattrDetails);
    return '<a class="flattr_link" href="' + flattrLink + '"  title="'+tooltip+'"><img src="https://api.flattr.com/button/flattr-badge-large.png" alt="'+tooltip+'" /></a>';
}

})};

/********** underscore **********/

kanso.moduleCache["underscore"] = {load: (function (module, exports, require) {

//     Underscore.js 1.2.0
//     (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
//     Underscore is freely distributable under the MIT license.
//     Portions of Underscore are inspired or borrowed from Prototype,
//     Oliver Steele's Functional, and John Resig's Micro-Templating.
//     For all details and documentation:
//     http://documentcloud.github.com/underscore

(function() {

  // Baseline setup
  // --------------

  // Establish the root object, `window` in the browser, or `global` on the server.
  var root = this;

  // Save the previous value of the `_` variable.
  var previousUnderscore = root._;

  // Establish the object that gets returned to break out of a loop iteration.
  var breaker = {};

  // Save bytes in the minified (but not gzipped) version:
  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;

  // Create quick reference variables for speed access to core prototypes.
  var slice            = ArrayProto.slice,
      unshift          = ArrayProto.unshift,
      toString         = ObjProto.toString,
      hasOwnProperty   = ObjProto.hasOwnProperty;

  // All **ECMAScript 5** native function implementations that we hope to use
  // are declared here.
  var
    nativeForEach      = ArrayProto.forEach,
    nativeMap          = ArrayProto.map,
    nativeReduce       = ArrayProto.reduce,
    nativeReduceRight  = ArrayProto.reduceRight,
    nativeFilter       = ArrayProto.filter,
    nativeEvery        = ArrayProto.every,
    nativeSome         = ArrayProto.some,
    nativeIndexOf      = ArrayProto.indexOf,
    nativeLastIndexOf  = ArrayProto.lastIndexOf,
    nativeIsArray      = Array.isArray,
    nativeKeys         = Object.keys,
    nativeBind         = FuncProto.bind;

  // Create a safe reference to the Underscore object for use below.
  var _ = function(obj) { return new wrapper(obj); };

  // Export the Underscore object for **CommonJS**, with backwards-compatibility
  // for the old `require()` API. If we're not in CommonJS, add `_` to the
  // global object.
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = _;
    _._ = _;
  } else {
    // Exported as a string, for Closure Compiler "advanced" mode.
    root['_'] = _;
  }

  // Current version.
  _.VERSION = '1.2.0';

  // Collection Functions
  // --------------------

  // The cornerstone, an `each` implementation, aka `forEach`.
  // Handles objects with the built-in `forEach`, arrays, and raw objects.
  // Delegates to **ECMAScript 5**'s native `forEach` if available.
  var each = _.each = _.forEach = function(obj, iterator, context) {
    if (obj == null) return;
    if (nativeForEach && obj.forEach === nativeForEach) {
      obj.forEach(iterator, context);
    } else if (obj.length === +obj.length) {
      for (var i = 0, l = obj.length; i < l; i++) {
        if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
      }
    } else {
      for (var key in obj) {
        if (hasOwnProperty.call(obj, key)) {
          if (iterator.call(context, obj[key], key, obj) === breaker) return;
        }
      }
    }
  };

  // Return the results of applying the iterator to each element.
  // Delegates to **ECMAScript 5**'s native `map` if available.
  _.map = function(obj, iterator, context) {
    var results = [];
    if (obj == null) return results;
    if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
    each(obj, function(value, index, list) {
      results[results.length] = iterator.call(context, value, index, list);
    });
    return results;
  };

  // **Reduce** builds up a single result from a list of values, aka `inject`,
  // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
  _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
    var initial = memo !== void 0;
    if (obj == null) obj = [];
    if (nativeReduce && obj.reduce === nativeReduce) {
      if (context) iterator = _.bind(iterator, context);
      return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
    }
    each(obj, function(value, index, list) {
      if (!initial) {
        memo = value;
        initial = true;
      } else {
        memo = iterator.call(context, memo, value, index, list);
      }
    });
    if (!initial) throw new TypeError("Reduce of empty array with no initial value");
    return memo;
  };

  // The right-associative version of reduce, also known as `foldr`.
  // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
  _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
    if (obj == null) obj = [];
    if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
      if (context) iterator = _.bind(iterator, context);
      return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
    }
    var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse();
    return _.reduce(reversed, iterator, memo, context);
  };

  // Return the first value which passes a truth test. Aliased as `detect`.
  _.find = _.detect = function(obj, iterator, context) {
    var result;
    any(obj, function(value, index, list) {
      if (iterator.call(context, value, index, list)) {
        result = value;
        return true;
      }
    });
    return result;
  };

  // Return all the elements that pass a truth test.
  // Delegates to **ECMAScript 5**'s native `filter` if available.
  // Aliased as `select`.
  _.filter = _.select = function(obj, iterator, context) {
    var results = [];
    if (obj == null) return results;
    if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
    each(obj, function(value, index, list) {
      if (iterator.call(context, value, index, list)) results[results.length] = value;
    });
    return results;
  };

  // Return all the elements for which a truth test fails.
  _.reject = function(obj, iterator, context) {
    var results = [];
    if (obj == null) return results;
    each(obj, function(value, index, list) {
      if (!iterator.call(context, value, index, list)) results[results.length] = value;
    });
    return results;
  };

  // Determine whether all of the elements match a truth test.
  // Delegates to **ECMAScript 5**'s native `every` if available.
  // Aliased as `all`.
  _.every = _.all = function(obj, iterator, context) {
    var result = true;
    if (obj == null) return result;
    if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
    each(obj, function(value, index, list) {
      if (!(result = result && iterator.call(context, value, index, list))) return breaker;
    });
    return result;
  };

  // Determine if at least one element in the object matches a truth test.
  // Delegates to **ECMAScript 5**'s native `some` if available.
  // Aliased as `any`.
  var any = _.some = _.any = function(obj, iterator, context) {
    iterator = iterator || _.identity;
    var result = false;
    if (obj == null) return result;
    if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
    each(obj, function(value, index, list) {
      if (result |= iterator.call(context, value, index, list)) return breaker;
    });
    return !!result;
  };

  // Determine if a given value is included in the array or object using `===`.
  // Aliased as `contains`.
  _.include = _.contains = function(obj, target) {
    var found = false;
    if (obj == null) return found;
    if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
    any(obj, function(value) {
      if (found = value === target) return true;
    });
    return found;
  };

  // Invoke a method (with arguments) on every item in a collection.
  _.invoke = function(obj, method) {
    var args = slice.call(arguments, 2);
    return _.map(obj, function(value) {
      return (method.call ? method || value : value[method]).apply(value, args);
    });
  };

  // Convenience version of a common use case of `map`: fetching a property.
  _.pluck = function(obj, key) {
    return _.map(obj, function(value){ return value[key]; });
  };

  // Return the maximum element or (element-based computation).
  _.max = function(obj, iterator, context) {
    if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
    if (!iterator && _.isEmpty(obj)) return -Infinity;
    var result = {computed : -Infinity};
    each(obj, function(value, index, list) {
      var computed = iterator ? iterator.call(context, value, index, list) : value;
      computed >= result.computed && (result = {value : value, computed : computed});
    });
    return result.value;
  };

  // Return the minimum element (or element-based computation).
  _.min = function(obj, iterator, context) {
    if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
    if (!iterator && _.isEmpty(obj)) return Infinity;
    var result = {computed : Infinity};
    each(obj, function(value, index, list) {
      var computed = iterator ? iterator.call(context, value, index, list) : value;
      computed < result.computed && (result = {value : value, computed : computed});
    });
    return result.value;
  };

  // Shuffle an array.
  _.shuffle = function(obj) {
    var shuffled = [], rand;
    each(obj, function(value, index, list) {
      if (index == 0) {
        shuffled[0] = value;
      } else {
        rand = Math.floor(Math.random() * (index + 1));
        shuffled[index] = shuffled[rand];
        shuffled[rand] = value;
      }
    });
    return shuffled;
  };

  // Sort the object's values by a criterion produced by an iterator.
  _.sortBy = function(obj, iterator, context) {
    return _.pluck(_.map(obj, function(value, index, list) {
      return {
        value : value,
        criteria : iterator.call(context, value, index, list)
      };
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }), 'value');
  };

  // Groups the object's values by a criterion produced by an iterator
  _.groupBy = function(obj, iterator) {
    var result = {};
    each(obj, function(value, index) {
      var key = iterator(value, index);
      (result[key] || (result[key] = [])).push(value);
    });
    return result;
  };

  // Use a comparator function to figure out at what index an object should
  // be inserted so as to maintain order. Uses binary search.
  _.sortedIndex = function(array, obj, iterator) {
    iterator || (iterator = _.identity);
    var low = 0, high = array.length;
    while (low < high) {
      var mid = (low + high) >> 1;
      iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
    }
    return low;
  };

  // Safely convert anything iterable into a real, live array.
  _.toArray = function(iterable) {
    if (!iterable)                return [];
    if (iterable.toArray)         return iterable.toArray();
    if (_.isArray(iterable))      return slice.call(iterable);
    if (_.isArguments(iterable))  return slice.call(iterable);
    return _.values(iterable);
  };

  // Return the number of elements in an object.
  _.size = function(obj) {
    return _.toArray(obj).length;
  };

  // Array Functions
  // ---------------

  // Get the first element of an array. Passing **n** will return the first N
  // values in the array. Aliased as `head`. The **guard** check allows it to work
  // with `_.map`.
  _.first = _.head = function(array, n, guard) {
    return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
  };

  // Returns everything but the last entry of the array. Especcialy useful on
  // the arguments object. Passing **n** will return all the values in
  // the array, excluding the last N. The **guard** check allows it to work with
  // `_.map`.
  _.initial = function(array, n, guard) {
    return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
  };

  // Get the last element of an array. Passing **n** will return the last N
  // values in the array. The **guard** check allows it to work with `_.map`.
  _.last = function(array, n, guard) {
    return (n != null) && !guard ? slice.call(array, array.length - n) : array[array.length - 1];
  };

  // Returns everything but the first entry of the array. Aliased as `tail`.
  // Especially useful on the arguments object. Passing an **index** will return
  // the rest of the values in the array from that index onward. The **guard**
  // check allows it to work with `_.map`.
  _.rest = _.tail = function(array, index, guard) {
    return slice.call(array, (index == null) || guard ? 1 : index);
  };

  // Trim out all falsy values from an array.
  _.compact = function(array) {
    return _.filter(array, function(value){ return !!value; });
  };

  // Return a completely flattened version of an array.
  _.flatten = function(array) {
    return _.reduce(array, function(memo, value) {
      if (_.isArray(value)) return memo.concat(_.flatten(value));
      memo[memo.length] = value;
      return memo;
    }, []);
  };

  // Return a version of the array that does not contain the specified value(s).
  _.without = function(array) {
    return _.difference(array, slice.call(arguments, 1));
  };

  // Produce a duplicate-free version of the array. If the array has already
  // been sorted, you have the option of using a faster algorithm.
  // Aliased as `unique`.
  _.uniq = _.unique = function(array, isSorted, iterator) {
    var initial = iterator ? _.map(array, iterator) : array;
    var result = [];
    _.reduce(initial, function(memo, el, i) {
      if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
        memo[memo.length] = el;
        result[result.length] = array[i];
      }
      return memo;
    }, []);
    return result;
  };

  // Produce an array that contains the union: each distinct element from all of
  // the passed-in arrays.
  _.union = function() {
    return _.uniq(_.flatten(arguments));
  };

  // Produce an array that contains every item shared between all the
  // passed-in arrays. (Aliased as "intersect" for back-compat.)
  _.intersection = _.intersect = function(array) {
    var rest = slice.call(arguments, 1);
    return _.filter(_.uniq(array), function(item) {
      return _.every(rest, function(other) {
        return _.indexOf(other, item) >= 0;
      });
    });
  };

  // Take the difference between one array and another.
  // Only the elements present in just the first array will remain.
  _.difference = function(array, other) {
    return _.filter(array, function(value){ return !_.include(other, value); });
  };

  // Zip together multiple lists into a single array -- elements that share
  // an index go together.
  _.zip = function() {
    var args = slice.call(arguments);
    var length = _.max(_.pluck(args, 'length'));
    var results = new Array(length);
    for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
    return results;
  };

  // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
  // we need this function. Return the position of the first occurrence of an
  // item in an array, or -1 if the item is not included in the array.
  // Delegates to **ECMAScript 5**'s native `indexOf` if available.
  // If the array is large and already in sort order, pass `true`
  // for **isSorted** to use binary search.
  _.indexOf = function(array, item, isSorted) {
    if (array == null) return -1;
    var i, l;
    if (isSorted) {
      i = _.sortedIndex(array, item);
      return array[i] === item ? i : -1;
    }
    if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
    for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
    return -1;
  };

  // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
  _.lastIndexOf = function(array, item) {
    if (array == null) return -1;
    if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
    var i = array.length;
    while (i--) if (array[i] === item) return i;
    return -1;
  };

  // Generate an integer Array containing an arithmetic progression. A port of
  // the native Python `range()` function. See
  // [the Python documentation](http://docs.python.org/library/functions.html#range).
  _.range = function(start, stop, step) {
    if (arguments.length <= 1) {
      stop = start || 0;
      start = 0;
    }
    step = arguments[2] || 1;

    var len = Math.max(Math.ceil((stop - start) / step), 0);
    var idx = 0;
    var range = new Array(len);

    while(idx < len) {
      range[idx++] = start;
      start += step;
    }

    return range;
  };

  // Function (ahem) Functions
  // ------------------

  // Create a function bound to a given object (assigning `this`, and arguments,
  // optionally). Binding with arguments is also known as `curry`.
  // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
  // We check for `func.bind` first, to fail fast when `func` is undefined.
  _.bind = function(func, obj) {
    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
    var args = slice.call(arguments, 2);
    return function() {
      return func.apply(obj, args.concat(slice.call(arguments)));
    };
  };

  // Bind all of an object's methods to that object. Useful for ensuring that
  // all callbacks defined on an object belong to it.
  _.bindAll = function(obj) {
    var funcs = slice.call(arguments, 1);
    if (funcs.length == 0) funcs = _.functions(obj);
    each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
    return obj;
  };

  // Memoize an expensive function by storing its results.
  _.memoize = function(func, hasher) {
    var memo = {};
    hasher || (hasher = _.identity);
    return function() {
      var key = hasher.apply(this, arguments);
      return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
    };
  };

  // Delays a function for the given number of milliseconds, and then calls
  // it with the arguments supplied.
  _.delay = function(func, wait) {
    var args = slice.call(arguments, 2);
    return setTimeout(function(){ return func.apply(func, args); }, wait);
  };

  // Defers a function, scheduling it to run after the current call stack has
  // cleared.
  _.defer = function(func) {
    return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
  };

  // Internal function used to implement `_.throttle` and `_.debounce`.
  var limit = function(func, wait, debounce) {
    var timeout;
    return function() {
      var context = this, args = arguments;
      var throttler = function() {
        timeout = null;
        func.apply(context, args);
      };
      if (debounce) clearTimeout(timeout);
      if (debounce || !timeout) timeout = setTimeout(throttler, wait);
    };
  };

  // Returns a function, that, when invoked, will only be triggered at most once
  // during a given window of time.
  _.throttle = function(func, wait) {
    return limit(func, wait, false);
  };

  // Returns a function, that, as long as it continues to be invoked, will not
  // be triggered. The function will be called after it stops being called for
  // N milliseconds.
  _.debounce = function(func, wait) {
    return limit(func, wait, true);
  };

  // Returns a function that will be executed at most one time, no matter how
  // often you call it. Useful for lazy initialization.
  _.once = function(func) {
    var ran = false, memo;
    return function() {
      if (ran) return memo;
      ran = true;
      return memo = func.apply(this, arguments);
    };
  };

  // Returns the first function passed as an argument to the second,
  // allowing you to adjust arguments, run code before and after, and
  // conditionally execute the original function.
  _.wrap = function(func, wrapper) {
    return function() {
      var args = [func].concat(slice.call(arguments));
      return wrapper.apply(this, args);
    };
  };

  // Returns a function that is the composition of a list of functions, each
  // consuming the return value of the function that follows.
  _.compose = function() {
    var funcs = slice.call(arguments);
    return function() {
      var args = slice.call(arguments);
      for (var i = funcs.length - 1; i >= 0; i--) {
        args = [funcs[i].apply(this, args)];
      }
      return args[0];
    };
  };

  // Returns a function that will only be executed after being called N times.
  _.after = function(times, func) {
    return function() {
      if (--times < 1) { return func.apply(this, arguments); }
    };
  };

  // Object Functions
  // ----------------

  // Retrieve the names of an object's properties.
  // Delegates to **ECMAScript 5**'s native `Object.keys`
  _.keys = nativeKeys || function(obj) {
    if (obj !== Object(obj)) throw new TypeError('Invalid object');
    var keys = [];
    for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key;
    return keys;
  };

  // Retrieve the values of an object's properties.
  _.values = function(obj) {
    return _.map(obj, _.identity);
  };

  // Return a sorted list of the function names available on the object.
  // Aliased as `methods`
  _.functions = _.methods = function(obj) {
    var names = [];
    for (var key in obj) {
      if (_.isFunction(obj[key])) names.push(key);
    }
    return names.sort();
  };

  // Extend a given object with all the properties in passed-in object(s).
  _.extend = function(obj) {
    each(slice.call(arguments, 1), function(source) {
      for (var prop in source) {
        if (source[prop] !== void 0) obj[prop] = source[prop];
      }
    });
    return obj;
  };

  // Fill in a given object with default properties.
  _.defaults = function(obj) {
    each(slice.call(arguments, 1), function(source) {
      for (var prop in source) {
        if (obj[prop] == null) obj[prop] = source[prop];
      }
    });
    return obj;
  };

  // Create a (shallow-cloned) duplicate of an object.
  _.clone = function(obj) {
    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
  };

  // Invokes interceptor with the obj, and then returns obj.
  // The primary purpose of this method is to "tap into" a method chain, in
  // order to perform operations on intermediate results within the chain.
  _.tap = function(obj, interceptor) {
    interceptor(obj);
    return obj;
  };

  // Internal recursive comparison function.
  function eq(a, b, stack) {
    // Identical objects are equal. `0 === -0`, but they aren't identical.
    // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
    if (a === b) return a !== 0 || 1 / a == 1 / b;
    // A strict comparison is necessary because `null == undefined`.
    if (a == null) return a === b;
    // Compare object types.
    var typeA = typeof a;
    if (typeA != typeof b) return false;
    // Optimization; ensure that both values are truthy or falsy.
    if (!a != !b) return false;
    // `NaN` values are equal.
    if (_.isNaN(a)) return _.isNaN(b);
    // Compare string objects by value.
    var isStringA = _.isString(a), isStringB = _.isString(b);
    if (isStringA || isStringB) return isStringA && isStringB && String(a) == String(b);
    // Compare number objects by value.
    var isNumberA = _.isNumber(a), isNumberB = _.isNumber(b);
    if (isNumberA || isNumberB) return isNumberA && isNumberB && +a == +b;
    // Compare boolean objects by value. The value of `true` is 1; the value of `false` is 0.
    var isBooleanA = _.isBoolean(a), isBooleanB = _.isBoolean(b);
    if (isBooleanA || isBooleanB) return isBooleanA && isBooleanB && +a == +b;
    // Compare dates by their millisecond values.
    var isDateA = _.isDate(a), isDateB = _.isDate(b);
    if (isDateA || isDateB) return isDateA && isDateB && a.getTime() == b.getTime();
    // Compare RegExps by their source patterns and flags.
    var isRegExpA = _.isRegExp(a), isRegExpB = _.isRegExp(b);
    if (isRegExpA || isRegExpB) {
      // Ensure commutative equality for RegExps.
      return isRegExpA && isRegExpB &&
             a.source == b.source &&
             a.global == b.global &&
             a.multiline == b.multiline &&
             a.ignoreCase == b.ignoreCase;
    }
    // Ensure that both values are objects.
    if (typeA != 'object') return false;
    // Unwrap any wrapped objects.
    if (a._chain) a = a._wrapped;
    if (b._chain) b = b._wrapped;
    // Invoke a custom `isEqual` method if one is provided.
    if (_.isFunction(a.isEqual)) return a.isEqual(b);
    // Assume equality for cyclic structures. The algorithm for detecting cyclic structures is
    // adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
    var length = stack.length;
    while (length--) {
      // Linear search. Performance is inversely proportional to the number of unique nested
      // structures.
      if (stack[length] == a) return true;
    }
    // Add the first object to the stack of traversed objects.
    stack.push(a);
    var size = 0, result = true;
    if (a.length === +a.length || b.length === +b.length) {
      // Compare object lengths to determine if a deep comparison is necessary.
      size = a.length;
      result = size == b.length;
      if (result) {
        // Deep compare array-like object contents, ignoring non-numeric properties.
        while (size--) {
          // Ensure commutative equality for sparse arrays.
          if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
        }
      }
    } else {
      // Deep compare objects.
      for (var key in a) {
        if (hasOwnProperty.call(a, key)) {
          // Count the expected number of properties.
          size++;
          // Deep compare each member.
          if (!(result = hasOwnProperty.call(b, key) && eq(a[key], b[key], stack))) break;
        }
      }
      // Ensure that both objects contain the same number of properties.
      if (result) {
        for (key in b) {
          if (hasOwnProperty.call(b, key) && !size--) break;
        }
        result = !size;
      }
    }
    // Remove the first object from the stack of traversed objects.
    stack.pop();
    return result;
  }

  // Perform a deep comparison to check if two objects are equal.
  _.isEqual = function(a, b) {
    return eq(a, b, []);
  };

  // Is a given array or object empty?
  _.isEmpty = function(obj) {
    if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
    for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
    return true;
  };

  // Is a given value a DOM element?
  _.isElement = function(obj) {
    return !!(obj && obj.nodeType == 1);
  };

  // Is a given value an array?
  // Delegates to ECMA5's native Array.isArray
  _.isArray = nativeIsArray || function(obj) {
    return toString.call(obj) === '[object Array]';
  };

  // Is a given variable an object?
  _.isObject = function(obj) {
    return obj === Object(obj);
  };

  // Is a given variable an arguments object?
  _.isArguments = function(obj) {
    return !!(obj && hasOwnProperty.call(obj, 'callee'));
  };

  // Is a given value a function?
  _.isFunction = function(obj) {
    return !!(obj && obj.constructor && obj.call && obj.apply);
  };

  // Is a given value a string?
  _.isString = function(obj) {
    return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
  };

  // Is a given value a number?
  _.isNumber = function(obj) {
    return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));
  };

  // Is the given value `NaN`? `NaN` happens to be the only value in JavaScript
  // that does not equal itself.
  _.isNaN = function(obj) {
    return obj !== obj;
  };

  // Is a given value a boolean?
  _.isBoolean = function(obj) {
    return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
  };

  // Is a given value a date?
  _.isDate = function(obj) {
    return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
  };

  // Is the given value a regular expression?
  _.isRegExp = function(obj) {
    return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
  };

  // Is a given value equal to null?
  _.isNull = function(obj) {
    return obj === null;
  };

  // Is a given variable undefined?
  _.isUndefined = function(obj) {
    return obj === void 0;
  };

  // Utility Functions
  // -----------------

  // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
  // previous owner. Returns a reference to the Underscore object.
  _.noConflict = function() {
    root._ = previousUnderscore;
    return this;
  };

  // Keep the identity function around for default iterators.
  _.identity = function(value) {
    return value;
  };

  // Run a function **n** times.
  _.times = function (n, iterator, context) {
    for (var i = 0; i < n; i++) iterator.call(context, i);
  };

  // Escape a string for HTML interpolation.
  _.escape = function(string) {
    return (''+string).replace(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
  };

  // Add your own custom functions to the Underscore object, ensuring that
  // they're correctly added to the OOP wrapper as well.
  _.mixin = function(obj) {
    each(_.functions(obj), function(name){
      addToWrapper(name, _[name] = obj[name]);
    });
  };

  // Generate a unique integer id (unique within the entire client session).
  // Useful for temporary DOM ids.
  var idCounter = 0;
  _.uniqueId = function(prefix) {
    var id = idCounter++;
    return prefix ? prefix + id : id;
  };

  // By default, Underscore uses ERB-style template delimiters, change the
  // following template settings to use alternative delimiters.
  _.templateSettings = {
    evaluate    : /<%([\s\S]+?)%>/g,
    interpolate : /<%=([\s\S]+?)%>/g,
    escape      : /<%-([\s\S]+?)%>/g
  };

  // JavaScript micro-templating, similar to John Resig's implementation.
  // Underscore templating handles arbitrary delimiters, preserves whitespace,
  // and correctly escapes quotes within interpolated code.
  _.template = function(str, data) {
    var c  = _.templateSettings;
    var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
      'with(obj||{}){__p.push(\'' +
      str.replace(/\\/g, '\\\\')
         .replace(/'/g, "\\'")
         .replace(c.escape, function(match, code) {
           return "',_.escape(" + code.replace(/\\'/g, "'") + "),'";
         })
         .replace(c.interpolate, function(match, code) {
           return "'," + code.replace(/\\'/g, "'") + ",'";
         })
         .replace(c.evaluate || null, function(match, code) {
           return "');" + code.replace(/\\'/g, "'")
                              .replace(/[\r\n\t]/g, ' ') + "__p.push('";
         })
         .replace(/\r/g, '\\r')
         .replace(/\n/g, '\\n')
         .replace(/\t/g, '\\t')
         + "');}return __p.join('');";
    var func = new Function('obj', tmpl);
    return data ? func(data) : func;
  };

  // The OOP Wrapper
  // ---------------

  // If Underscore is called as a function, it returns a wrapped object that
  // can be used OO-style. This wrapper holds altered versions of all the
  // underscore functions. Wrapped objects may be chained.
  var wrapper = function(obj) { this._wrapped = obj; };

  // Expose `wrapper.prototype` as `_.prototype`
  _.prototype = wrapper.prototype;

  // Helper function to continue chaining intermediate results.
  var result = function(obj, chain) {
    return chain ? _(obj).chain() : obj;
  };

  // A method to easily add functions to the OOP wrapper.
  var addToWrapper = function(name, func) {
    wrapper.prototype[name] = function() {
      var args = slice.call(arguments);
      unshift.call(args, this._wrapped);
      return result(func.apply(_, args), this._chain);
    };
  };

  // Add all of the Underscore functions to the wrapper object.
  _.mixin(_);

  // Add all mutator Array functions to the wrapper.
  each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
    var method = ArrayProto[name];
    wrapper.prototype[name] = function() {
      method.apply(this._wrapped, arguments);
      return result(this._wrapped, this._chain);
    };
  });

  // Add all accessor Array functions to the wrapper.
  each(['concat', 'join', 'slice'], function(name) {
    var method = ArrayProto[name];
    wrapper.prototype[name] = function() {
      return result(method.apply(this._wrapped, arguments), this._chain);
    };
  });

  // Start chaining a wrapped Underscore object.
  wrapper.prototype.chain = function() {
    this._chain = true;
    return this;
  };

  // Extracts the result from a wrapped and chained object.
  wrapper.prototype.value = function() {
    return this._wrapped;
  };

})();

})};

/********** querystring **********/

kanso.moduleCache["querystring"] = {load: (function (module, exports, require) {

var _ = require('underscore')._;

/**
 * Querystring functions ported from node.js to work in CouchDB and the browser.
 * This module is used internally by Kanso, although you can use it in your
 * apps too if you find the functions useful.
 *
 * @module
 */


// Copyright Joyent, Inc. and other Node contributors.
//
// 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.

// Query String Utilities

var QueryString = exports;

/**
 * Decodes a URI Component, provided so that it could be overridden if
 * necessary.
 *
 * @name unescape(str)
 * @param {String} str
 * @returns {String}
 * @api public
 */

QueryString.unescape = function (str) {
    return decodeURIComponent(str);
};

/**
 * Encodes a URI Component, provided so that it could be overridden if
 * necessary.
 *
 * @name escape(str)
 * @param {String} str
 * @returns {String}
 * @api public
 */

QueryString.escape = function (str) {
    return encodeURIComponent(str);
};

var stringifyPrimitive = function (v) {
    switch (typeof v) {
    case 'string':
        return v;

    case 'boolean':
        return v ? 'true' : 'false';

    case 'number':
        return isFinite(v) ? v : '';

    default:
        return '';
    }
};

/**
 * Serialize an object to a query string. Optionally override the default
 * separator and assignment characters.
 *
 * **Example:**
 *
 * <pre><code class="javascript">
 * querystring.stringify({foo: 'bar'})
 * // returns
 * 'foo=bar'
 *
 * querystring.stringify({foo: 'bar', baz: 'bob'}, ';', ':')
 * // returns
 * 'foo:bar;baz:bob'
 * </code></pre>
 *
 * @name stringify(obj, [sep, eq, name])
 * @param {Object} obj
 * @param {String} sep
 * @param {String} eq
 * @param {String} name
 * @returns {String}
 * @api public
 */

QueryString.stringify = QueryString.encode = function (obj, sep, eq, name) {
    sep = sep || '&';
    eq = eq || '=';
    obj = (obj === null) ? undefined : obj;

    if (typeof obj === 'object') {
        return _.map(_.keys(obj), function (k) {
            if (_.isArray(obj[k])) {
                return _.map(obj[k], function (v) {
                    return QueryString.escape(stringifyPrimitive(k)) +
                           eq +
                           QueryString.escape(stringifyPrimitive(v));
                }).join(sep);
            }
            else {
                return QueryString.escape(stringifyPrimitive(k)) +
                       eq +
                       QueryString.escape(stringifyPrimitive(obj[k]));
            }
        }).join(sep);
    }
    if (!name) {
        return '';
    }
    return QueryString.escape(stringifyPrimitive(name)) + eq +
           QueryString.escape(stringifyPrimitive(obj));
};

/**
 * Deserialize a query string to an object. Optionally override the default
 * separator and assignment characters.
 *
 * **Example:**
 *
 * <pre><code class="javascript">
 * querystring.parse('a=b&b=c')
 * // returns
 * // { a: 'b', b: 'c' }
 * </code></pre>
 *
 * @name decode(qs, [sep, eq])
 * @param {String} qs
 * @param {String} sep
 * @param {String} eq
 * @returns {Object}
 * @api public
 */

QueryString.parse = QueryString.decode = function (qs, sep, eq) {
    sep = sep || '&';
    eq = eq || '=';
    var obj = {};

    if (typeof qs !== 'string' || qs.length === 0) {
        return obj;
    }

    qs.split(sep).forEach(function (kvp) {
        var x = kvp.split(eq);
        var k = QueryString.unescape(x[0]);
        var v = QueryString.unescape(x.slice(1).join(eq));

        if (!(k in obj)) {
            obj[k] = v;
        }
        else if (!_.isArray(obj[k])) {
            obj[k] = [obj[k], v];
        }
        else {
            obj[k].push(v);
        }
    });

    return obj;
};


})};

/********** settings/packages/market **********/

kanso.moduleCache["settings/packages/market"] = {load: (function (module, exports, require) {

module.exports = {"name":"market","version":"0.0.5","description":"Host your own app market.","long_description":"The Garden Market app lets you host your own app market. Users can upload apps, and you can manage them according to your own terms. \n\n You are also able to sync with other existing garden-markets.","url":"https://github.com/garden20/garden-market","categories":["market"],"flattr_user_id":"eckoit","icons":{"16":"static/img/promo/icon_16.png","48":"static/img/promo/icon_48.png","96":"static/img/promo/icon_96.png","128":"static/img/promo/icon_128.png"},"promo_images":{"small":"static/img/promo/promo_small.png"},"screenshots":["static/img/promo/screenshot1.png","static/img/promo/screenshot2.png"],"settings_schema":{"description":"Market Settings","type":"object","properties":{"use_garden_market_logo":{"type":"boolean"},"market_name":{"type":"string"},"use_google_analytics":{"type":"boolean"},"google_analytics_code":{"type":"string"},"admin_review_apps":{"type":"boolean"}}},"modules":["lib","views"],"load":"lib/app","less":{"compress":true,"compile":"static/css/garden.less","remove_from_attachments":true},"handlebars":{"templates":"templates"},"attachments":"static","dependencies":{"db":null,"events":null,"session":null,"settings":null,"modules":null,"properties":null,"attachments":null,"handlebars":null,"handlebars-helpers":">=0.0.5","less-precompiler":null,"sanitize":null,"datelib":null,"path":null,"url":null,"jsonp":null,"cookies":null,"flattr":null},"minify":false};

})};

/********** settings/root **********/

kanso.moduleCache["settings/root"] = {load: (function (module, exports, require) {

module.exports = require("settings/packages/market");

})};

