(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/**
 *
 *  Core functionality for Xil based functionality
 *
 **/
    // strict mode may cause exceptions in Safari 6.0
"use strict"; // jshint ignore:line

var constants = require('./config/Constants'),
    options = require('./config/Options'),
    jsonSerializer = require('./utils/JSONSerializer'),
    utils = require('./utils/Utils'),
    trace = require('./utils/trace'),
    registry = require('./registry/Registry'),
    pubsub = require('./events/EventPubSub'),
    Exception = require('./events/Exception'),
    CoreClass = require('./utils/Class');

// Import our libraries and make them part of our namespace
var _ = require('./libs/underscore-1.6.custom');
var $ = require('./libs/zepto-1.1.3.custom');


// Load up the Zepto/jQuery extensions
require('./utils/ObjectExtensions');


module.exports = {
    // ===============================================================
    // EXPOSE CORE FUNCTIONALITY
    // ===============================================================
    registry : registry,
    utils : utils,
    jsonSerializer : jsonSerializer,
    constants : constants,
    log : constants.log,
    options : options,
    Exception : Exception,
    Class : CoreClass,
    _ : _,
    $ : $,


    // ===============================================================
    // EXPOSE PubSub APIs FUNCTIONALITY
    // ===============================================================
    pubsub : pubsub,
    publishEvent : pubsub.publishEvent,
    subscribeForEvent : pubsub.subscribeForEvent,
    unSubscribe : pubsub.unSubscribe,
    unSubscribeForEvent : pubsub.unSubscribeForEvent,


    //----------------------------------------------
    // Dependency Injection Strategy - i.e. registering components
    // Register an named object/component that you want to get a handle to later. Can be used for dependency injection
    // Will be exported into your namespace under appName.components.name
    //----------------------------------------------
    registerComponent : registry.registerComponent,
    getComponent : registry.getComponent,


    //================================================
    // HELPER FUNCTIONS
    //================================================
    trace : trace,
    publishException : pubsub.publishException
};




},{"./config/Constants":2,"./config/Options":3,"./events/EventPubSub":6,"./events/Exception":8,"./libs/underscore-1.6.custom":9,"./libs/zepto-1.1.3.custom":10,"./registry/Registry":11,"./utils/Class":13,"./utils/JSONSerializer":14,"./utils/ObjectExtensions":15,"./utils/Utils":16,"./utils/trace":17}],2:[function(require,module,exports){
module.exports = {

    kWildCard : "*",
    kFunctionRegex : /^(.*?)(\((.*?)\))?$/,

    events : {
        kInitialize : "initialize",
        kException : "exception"
    },

    log : {
        INFO : "INFO",
        WARN : "WARN",
        ERROR : "ERROR",
        CRITICAL : "CRITICAL",
        DEPRECATED : "DEPRECATED"
    },

    interfaces : {
        kDataResolver : "dataResolver"
    },

    // events that are meant to be fired via Xil.$(el).trigger()
    jqEvents : {
        kXilNamespace : "xil" // namespace added to events triggered by Xil
    },

    registry : {
        kComponents : "components"
    }
};


},{}],3:[function(require,module,exports){
// class: Options

module.exports = {
    appId : null, // Id of the application (used for logging and exporting functionality back into YOUR namespace)
    logToConsole : true // Send any errors encountered to the console.
};

},{}],4:[function(require,module,exports){
arguments[4][2][0].apply(exports,arguments)
},{"dup":2}],5:[function(require,module,exports){
arguments[4][3][0].apply(exports,arguments)
},{"dup":3}],6:[function(require,module,exports){
/* class: EventPubSub
 *
 * About:
 * 	This is a singleton javascript class that
 *  provides functionality for managing the publication/subscription model for UI related events
 *
 *  Notes:
 *      Garbage collection in JS is reference counted so a JS object wont get collected if there is a reference to it here in the pub/sub
 *      We have to unsubscribe it so it removes the reference and it can be collected.

 *	Copyright <c> 2012-2013 Intuit, Inc. All rights reserved
 */

var Events = require('./Events');
var utils = require('../utils/Utils');
var _ = require('../libs/underscore-1.6.custom');
var log = require('../config/Constants').log;
var Exception = require('./Exception');
var constants = require('../config/constants');
var trace = require('../utils/trace');
var _dispatcher = _.clone(Events);

var pubsub = module.exports = {

    /***********************************************************************
     * subscribe to the event; sign up for notification that an event has occurred
     *
     * @param type - the event to subscribe to
     * @param callback - the callback function
     * @param context - the context (value of 'this' when callback is called)
     * @param param - optional parameter that gets passed as the last arg to the callback
     */
    subscribeForEvent : function (type, callback, context, param) {
        _dispatcher.off(type, callback, context);  // prevent duplicate subscriptions
        _dispatcher.on(type, callback, context, param);
    },

    /***********************************************************************
     * unsubscribe; remove all listeners that have the given context
     * @param context
     */
    unSubscribe : function (context) {
        if (context) {
            _dispatcher.off(null, null, context);
        }
    },

    /***********************************************************************
     * Used for unsubscribing listeners based whether their context meets certain criteria.
     * For example, a function could be passed in to test whether the context is an html element, and if so whether it's still attached to the DOM.
     *
     * @param fn - the condition to test the context for
     */
    unSubscribeIfContextCondition : function (fn) {
        if (_.isFunction(fn)) {
            _dispatcher.offIfContextCondition(fn);
        }
    },

    /***********************************************************************
     * unsubscribe from an Event; remove the object/callback from the callback list
     *
     * @param type - the event to unsubscribe from
     * @param callback - the callback, optional
     * @param context - the  context object, optional
     */
    unSubscribeForEvent : function (type, callback, context) {
        if (!_.isFunction(callback) && _.isObject(callback) && !context) {
            // Old API was unSubscribeForEvent(type, context)
            context = callback;
            callback = null;
            this.publishException("PubSub", "Xil.unSubscribeForEvent - pass (type, callback, context)", log.DEPRECATED);
        }
        if (type) {  // null type would unsubscribe everything
            _dispatcher.off(type, callback, context);
        }
    },

    /***********************************************************************
     * publish Event; notify all subscribers that an event has happened
     *
     * @param type - the event to publish
     * @param arg1, arg2, arg3,... - optional arguments
     */
    publishEvent : function (type) {
        var arr = [].slice.call(arguments); // arr includes type and optional args
        _dispatcher.trigger.apply(_dispatcher, arr);
        trace("Event published: " + arguments[0], "EVENT");
    },

    publishException : function (component, msg, logType, ex) {
        if (component instanceof Exception) {
            pubsub.publishEvent(constants.events.kException, component);
        }
        else {
            pubsub.publishEvent(constants.events.kException, new Exception(component, msg, logType || log.ERROR, ex));
        }

    },

    /***********************************************************************
     *   Debug
     */
    debug : function (fn) {
        return _dispatcher.debug(fn);
    }
};
	

},{"../config/Constants":2,"../config/constants":4,"../libs/underscore-1.6.custom":9,"../utils/Utils":16,"../utils/trace":17,"./Events":7,"./Exception":8}],7:[function(require,module,exports){
/*
 // Events module from Backbone.Events
 // Original author: Jeremy Ashkenas.
 // Modified at Intuit: added namespaced event support, removed space-separated multi-event and event-map stuff
 // ---------------
 //
 // A module that can be mixed in to *any object* in order to provide it with
 // custom events. You may bind with `on` or remove with `off` callback
 // functions to an event; `trigger`-ing an event fires all callbacks in
 // succession.
 //
 //     var object = {};
 //     _.extend(object, Xil.utils.events);
 //     object.on('expand', function(){ alert('expanded'); });
 //     object.trigger('expand');
 * ---------------
 *
 * namespaces are created by adding a period after the event type.
 *
 * for example, a listener that subscribes to "change.aaa.bbb" will be called when any of these are triggered:
 *      "change", "change.aaa", "change.aaa.bbb", "change.aaa.bbb.ccc", etc.
 * that same listener will NOT be called when these are triggered:
 *      "change.bbb", "change.bbb.aaa", "change.ccc"
 *
 * note the slight difference between Xil.$ event namespaces and the way they are treated here.
 * in X_core.$, "change.bbb.aaa" is considered the same as "change.aaa.bbb".  in this case they're considered different.
 *
 */

var _ = require('../libs/underscore-1.6.custom');
var constants = require('../config/Constants');
var log = require('../config/Constants').log;

var Events = {

    // Bind an event to a `callback` function. Passing `"all"` will bind
    // the callback to all events fired.
    on : function (name, callback, context, param) {
        var self = this;
        if (!name || !callback) {
            Xil.publishException("PubSub", "Invalid subscribe: missing event name or callback ", log.ERROR);

            return self;
        }
        var evts = name.match(constants.eventSeparatorMatch);

        _.each(evts, function (evt) {
            var onEvent = parseEventName(evt);

            self._events = self._events || {};
            if (!self._events[onEvent.type]) {
                self._events[onEvent.type] = [];
            }
            self._events[onEvent.type].push(
                {
                    type : onEvent.type,
                    namespace : onEvent.namespace,
                    callback : callback,
                    context : context,
                    ctx : context || self,
                    param : param
                });
        });
        return self;
    },

    // Remove one or many callbacks. If `context` is null, removes all
    // callbacks with that function. If `callback` is null, removes all
    // callbacks for the event. If `name` is null, removes all bound
    // callbacks for all events.
    off : function (name, callback, context) {
        var retain, ev, events, types, type, i, l, j, k;
        if (!this._events) {
            return this;
        }
        if (!name && !callback && !context) {
            this._events = {};
            return this;
        }

        var self = this;
        var eventList = name ? name.match(constants.eventSeparatorMatch) : _.keys(this._events);

        _.each(eventList, function (evt) {
            var offEvent = parseEventName(evt);

            type = [offEvent.type];
            events = self._events[type];
            if (events) {
                self._events[type] = retain = [];
                for (j = 0, k = events.length; j < k; j++) {
                    ev = events[j];
                    if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
                        (context && context !== ev.context)) {
                        retain.push(ev);
                    }
                    else if (offEvent.namespace &&
                             (!ev.namespace || (ev.namespace + '.').indexOf(offEvent.namespace + '.') < 0)) {
                        retain.push(ev);
                    }
                }
                if (!retain.length) {
                    delete self._events[type];
                }
            }

        });

        return this;
    },


    // passes each event object to the optional function.
    // returns the total event count.
    debug : function (fn) {
        var totalEvents = 0;
        _.each(this._events, function (eventsForType, type) {
            totalEvents += eventsForType.length;
            //console.log(type + ": ", eventsForType.length);
            if (_.isFunction(fn)) {
                _.each(eventsForType, function (ev) {
                    fn(ev);
                });
            }
        });

        return totalEvents;
    },

    // Bind an event to only be triggered a single time. After the first time
    // the callback is invoked, it will be removed.
    once : function (name, callback, context) {
        if (!callback) {
            return this;
        }
        var self = this;
        var once = _.once(function () {
            self.off(name, once);
            callback.apply(this, arguments);
        });
        once._callback = callback;
        return this.on(name, once, context);
    },


    offIfContextCondition : function (fn) {
        var retainForType, ev, events, types, eventType, typeIndex, numTypes, eventIndex, numEventsForType;
        var numRemoved = 0;
        if (!_.isFunction(fn) || !this._events) {
            return this;
        }

        types = _.keys(this._events);
        for (typeIndex = 0, numTypes = types.length; typeIndex < numTypes; typeIndex++) {
            eventType = types[typeIndex];

            events = this._events[eventType];
            if (events) {
                this._events[eventType] = retainForType = [];
                for (eventIndex = 0, numEventsForType = events.length; eventIndex <
                                                                       numEventsForType; eventIndex++) {
                    ev = events[eventIndex];
                    if (!fn(ev.context)) {
                        retainForType.push(ev);
                    }
                    else {
                        numRemoved += 1;  // debugging only
                    }
                }
                if (!retainForType.length) {
                    delete this._events[eventType];
                }
            }
        }

        //            console.log("offIfContextCondition: removed ", numRemoved);
        return this;
    },

    // Trigger one or many events, firing all bound callbacks. Callbacks are
    // passed the same arguments as `trigger` is, apart from the event name
    // (unless you're listening on `"all"`, which will cause your callback to
    // receive the true name of the event as the first argument).
    trigger : function (name) {
        //console.log("trigger:", name);
        var events;
        if (!this._events) {
            return this;
        }
        var payload_s = [].slice.call(arguments, 1);

        var trigEvent = parseEventName(name);
        if (trigEvent.namespace) {
            // if triggered event has a namespace, find all the events that match it (those that start with the same namespace)
            events = _.filter(this._events[trigEvent.type], function (e) {
                return !e.namespace || (trigEvent.namespace + '.').indexOf(e.namespace + '.') === 0;
            });
        }
        else {
            // no namespace in triggered event: only listeners that have no namespace will be triggered
            events = _.filter(this._events[trigEvent.type], function (e) {
                return !e.namespace;
            });
        }

        var allEvents = this._events.all;
        if (events) {
            triggerEvents(events, payload_s);
        }
        if (allEvents) {
            triggerEvents(allEvents, arguments);
        }
        return this;
    },

    // Tell this object to stop listening to either specific events ... or
    // to every object it's currently listening to.
    stopListening : function (obj, name, callback) {
        var listeners = this._listeners;
        if (!listeners) {
            return this;
        }
        var deleteListener = !name && !callback;
        if (_.isObject(name)) {
            callback = this;
        }
        if (obj) {
            (listeners = {})[obj._listenerId] = obj;
        }
        for (var id in listeners) {
            listeners[id].off(name, callback, this);
            if (deleteListener) {
                delete this._listeners[id];
            }
        }
        return this;
    }

};

module.exports = Events;


// A difficult-to-believe, but optimized internal dispatch function for
// triggering events. Tries to keep the usual cases speedy (most internal
// Xil events have 1 or 2 arguments).
var triggerEvents = function (events, customArgs) {
    var ev, eventIndex = -1, numEvents = events.length, a1 = customArgs[0], a2 = customArgs[1], a3 = customArgs[2];
    switch (customArgs.length) {
        case 0:
            while (++eventIndex < numEvents) {
                ev = events[eventIndex];
                if (ev.param) {
                    ev.callback.call(ev.ctx, ev.param);
                }
                else {
                    ev.callback.call(ev.ctx);
                }
            }
            return;
        case 1:
            while (++eventIndex < numEvents) {
                ev = events[eventIndex];
                if (ev.param) {
                    ev.callback.call(ev.ctx, a1, ev.param);
                }
                else {
                    ev.callback.call(ev.ctx, a1);
                }
            }
            return;
        case 2:
            while (++eventIndex < numEvents) {
                ev = events[eventIndex];
                if (ev.param) {
                    ev.callback.call(ev.ctx, a1, a2, ev.param);
                }
                else {
                    ev.callback.call(ev.ctx, a1, a2);
                }
            }
            return;
        default:
            while (++eventIndex < numEvents) {
                ev = events[eventIndex];
                if (ev.param) {
                    var evArgs = (customArgs.slice()).concat(ev.param);
                    ev.callback.apply(ev.ctx, evArgs);
                }
                else {
                    ev.callback.apply(ev.ctx, customArgs);
                }
            }
    }
};

// split an event name into a type and a namespace.
// the namespace is everything after the first '.' if it exists.
var parseEventName = function (name) {
    var dotIndex, type, namespace;
    if (name === undefined) {
        return {};
    }
    if (!_.isString(name)) {
        name = name.toString();
    }
    dotIndex = name.indexOf('.');
    if (dotIndex >= 0) {
        namespace = name.substr(dotIndex);
        type = name.substr(0, dotIndex);
    }
    else {
        namespace = undefined;
        type = name;
    }
    return {type : type, namespace : namespace};
};

var listenMethods = {listenTo : 'on', listenToOnce : 'once'};

// Inversion-of-control versions of `on` and `once`. Tell *this* object to
// listen to an event in another object ... keeping track of what it's
// listening to.
_.each(listenMethods, function (implementation, method) {
    Events[method] = function (obj, name, callback) {
        var listeners = this._listeners || (this._listeners = {});
        var id = obj._listenerId || (obj._listenerId = _.uniqueId('l'));
        listeners[id] = obj;
        if (_.isObject(name)) {
            callback = this;
        }
        obj[implementation](name, callback, this);
        return this;
    };
});

},{"../config/Constants":2,"../libs/underscore-1.6.custom":9}],8:[function(require,module,exports){
//============================================================================
// class: Exception
// about:
//  Object to encapsulate Errors generated in the Xil Framework
//
//============================================================================
var Class = require('../utils/Class');

var Exception = Class.extend({
    // -------------------------------
    // Function: construct
    // construct the exception
    //
    // Parameters:
    //   component - a string to identify the component
    //   msg - the string error message
    //   type - the string error type
    //   ex - the optional exception to be thrown
    // -------------------------------
    construct: function (component/*string*/, msg /*string*/, type/*string*/, ex /*optional - exception thrown*/) {
        this.component = component;
        this.msg = msg;
        this.logType = type ? type : Xil.log.ERROR;
        this.exeptionObj = ex;
        this.context = [];
    },

    // -------------------------------
    // Function: addContext
    // set the context
    //
    // Parameters:
    //   ctx - the context to set
    // -------------------------------
    addContext: function (ctx) {
        this.context.push(ctx);
    },

    // output a string representation of the exception.
    // this function is needed in order to see a meaningful error in the browser's console
    toString: function() {
        return "XilException: " + this.msg;
    }
});

module.exports = Exception;

},{"../utils/Class":13}],9:[function(require,module,exports){
//     Underscore.js 1.6.0
//     http://underscorejs.org
//     (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
//     Underscore may be freely distributed under the MIT license.

(function() {

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

    // Establish the root object, `window` in the browser, or `exports` 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
        push             = ArrayProto.push,
        slice            = ArrayProto.slice,
        concat           = ArrayProto.concat,
        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) {
        if (obj instanceof _) return obj;
        if (!(this instanceof _)) return new _(obj);
        this._wrapped = obj;
    };

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

    // Current version.
    _.VERSION = '1.6.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 obj;
        if (nativeForEach && obj.forEach === nativeForEach) {
            obj.forEach(iterator, context);
        } else if (obj.length === +obj.length) {
            for (var i = 0, length = obj.length; i < length; i++) {
                if (iterator.call(context, obj[i], i, obj) === breaker) return;
            }
        } else {
            var keys = _.keys(obj);
            for (var i = 0, length = keys.length; i < length; i++) {
                if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
            }
        }
        return obj;
    };

    // Return the results of applying the iterator to each element.
    // Delegates to **ECMAScript 5**'s native `map` if available.
    _.map = _.collect = 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.push(iterator.call(context, value, index, list));
        });
        return results;
    };

    var reduceError = 'Reduce of empty array with no initial value';

    // **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 = arguments.length > 2;
        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(reduceError);
        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) {
//        var initial = arguments.length > 2;
//        if (obj == null) obj = [];
//        if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
//            if (context) iterator = _.bind(iterator, context);
//            return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
//        }
//        var length = obj.length;
//        if (length !== +length) {
//            var keys = _.keys(obj);
//            length = keys.length;
//        }
//        each(obj, function(value, index, list) {
//            index = keys ? keys[--length] : --length;
//            if (!initial) {
//                memo = obj[index];
//                initial = true;
//            } else {
//                memo = iterator.call(context, memo, obj[index], index, list);
//            }
//        });
//        if (!initial) throw new TypeError(reduceError);
//        return memo;
//    };

    // Return the first value which passes a truth test. Aliased as `detect`.
//    _.find = _.detect = function(obj, predicate, context) {
//        var result;
//        any(obj, function(value, index, list) {
//            if (predicate.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, predicate, context) {
        var results = [];
        if (obj == null) return results;
        if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context);
        each(obj, function(value, index, list) {
            if (predicate.call(context, value, index, list)) results.push(value);
        });
        return results;
    };

    // Return all the elements for which a truth test fails.
//    _.reject = function(obj, predicate, context) {
//        return _.filter(obj, function(value, index, list) {
//            return !predicate.call(context, value, index, list);
//        }, context);
//    };

    // 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, predicate, context) {
        predicate || (predicate = _.identity);
        var result = true;
        if (obj == null) return result;
        if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context);
        each(obj, function(value, index, list) {
            if (!(result = result && predicate.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, predicate, context) {
        predicate || (predicate = _.identity);
        var result = false;
        if (obj == null) return result;
        if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context);
        each(obj, function(value, index, list) {
            if (result || (result = predicate.call(context, value, index, list))) return breaker;
        });
        return !!result;
    };

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

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

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

    // Convenience version of a common use case of `filter`: selecting only objects
    // containing specific `key:value` pairs.
//    _.where = function(obj, attrs) {
//        return _.filter(obj, _.matches(attrs));
//    };

    // Convenience version of a common use case of `find`: getting the first object
    // containing specific `key:value` pairs.
//    _.findWhere = function(obj, attrs) {
//        return _.find(obj, _.matches(attrs));
//    };

    // Return the maximum element or (element-based computation).
    // Can't optimize arrays of integers longer than 65,535 elements.
    // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
//    _.max = function(obj, iterator, context) {
//        if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
//            return Math.max.apply(Math, obj);
//        }
//        var result = -Infinity, lastComputed = -Infinity;
//        each(obj, function(value, index, list) {
//            var computed = iterator ? iterator.call(context, value, index, list) : value;
//            if (computed > lastComputed) {
//                result = value;
//                lastComputed = computed;
//            }
//        });
//        return result;
//    };

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

    // Shuffle an array, using the modern version of the
    // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisherâ€“Yates_shuffle).
//    _.shuffle = function(obj) {
//        var rand;
//        var index = 0;
//        var shuffled = [];
//        each(obj, function(value) {
//            rand = _.random(index++);
//            shuffled[index - 1] = shuffled[rand];
//            shuffled[rand] = value;
//        });
//        return shuffled;
//    };

    // Sample **n** random values from a collection.
    // If **n** is not specified, returns a single random element.
    // The internal `guard` argument allows it to work with `map`.
//    _.sample = function(obj, n, guard) {
//        if (n == null || guard) {
//            if (obj.length !== +obj.length) obj = _.values(obj);
//            return obj[_.random(obj.length - 1)];
//        }
//        return _.shuffle(obj).slice(0, Math.max(0, n));
//    };

    // An internal function to generate lookup iterators.
//    var lookupIterator = function(value) {
//        if (value == null) return _.identity;
//        if (_.isFunction(value)) return value;
//        return _.property(value);
//    };

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

    // An internal function used for aggregate "group by" operations.
//    var group = function(behavior) {
//        return function(obj, iterator, context) {
//            var result = {};
//            iterator = lookupIterator(iterator);
//            each(obj, function(value, index) {
//                var key = iterator.call(context, value, index, obj);
//                behavior(result, key, value);
//            });
//            return result;
//        };
//    };

    // Groups the object's values by a criterion. Pass either a string attribute
    // to group by, or a function that returns the criterion.
//    _.groupBy = group(function(result, key, value) {
//        _.has(result, key) ? result[key].push(value) : result[key] = [value];
//    });

    // Indexes the object's values by a criterion, similar to `groupBy`, but for
    // when you know that your index values will be unique.
//    _.indexBy = group(function(result, key, value) {
//        result[key] = value;
//    });

    // Counts instances of an object that group by a certain criterion. Pass
    // either a string attribute to count by, or a function that returns the
    // criterion.
//    _.countBy = group(function(result, key) {
//        _.has(result, key) ? result[key]++ : result[key] = 1;
//    });

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

    // Safely create a real, live array from anything iterable.
//    _.toArray = function(obj) {
//        if (!obj) return [];
//        if (_.isArray(obj)) return slice.call(obj);
//        if (obj.length === +obj.length) return _.map(obj, _.identity);
//        return _.values(obj);
//    };

    // Return the number of elements in an object.
    _.size = function(obj) {
        if (obj == null) return 0;
        return (obj.length === +obj.length) ? obj.length : _.keys(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` and `take`. The **guard** check
    // allows it to work with `_.map`.
//    _.first = _.head = _.take = function(array, n, guard) {
//        if (array == null) return void 0;
//        if ((n == null) || guard) return array[0];
//        if (n < 0) return [];
//        return slice.call(array, 0, n);
//    };

    // Returns everything but the last entry of the array. Especially 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) {
//        if (array == null) return void 0;
//        if ((n == null) || guard) return array[array.length - 1];
//        return slice.call(array, Math.max(array.length - n, 0));
//    };

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

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

    // Internal implementation of a recursive `flatten` function.
    var flatten = function(input, shallow, output) {
        if (shallow && _.every(input, _.isArray)) {
            return concat.apply(output, input);
        }
        each(input, function(value) {
            if (_.isArray(value) || _.isArguments(value)) {
                shallow ? push.apply(output, value) : flatten(value, shallow, output);
            } else {
                output.push(value);
            }
        });
        return output;
    };

    // Flatten out an array, either recursively (by default), or just one level.
    _.flatten = function(array, shallow) {
        return flatten(array, shallow, []);
    };

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

    // Split an array into two arrays: one whose elements all satisfy the given
    // predicate, and one whose elements all do not satisfy the predicate.
//    _.partition = function(array, predicate) {
//        var pass = [], fail = [];
//        each(array, function(elem) {
//            (predicate(elem) ? pass : fail).push(elem);
//        });
//        return [pass, fail];
//    };

    // 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, context) {
        if (_.isFunction(isSorted)) {
            context = iterator;
            iterator = isSorted;
            isSorted = false;
        }
        var initial = iterator ? _.map(array, iterator, context) : array;
        var results = [];
        var seen = [];
        each(initial, function(value, index) {
            if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
                seen.push(value);
                results.push(array[index]);
            }
        });
        return results;
    };

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

    // Produce an array that contains every item shared between all the
    // passed-in arrays.
//    _.intersection = function(array) {
//        var rest = slice.call(arguments, 1);
//        return _.filter(_.uniq(array), function(item) {
//            return _.every(rest, function(other) {
//                return _.contains(other, item);
//            });
//        });
//    };

    // Take the difference between one array and a number of other arrays.
    // Only the elements present in just the first array will remain.
    _.difference = function(array) {
        var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
        return _.filter(array, function(value){ return !_.contains(rest, value); });
    };

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

    // Converts lists into objects. Pass either a single array of `[key, value]`
    // pairs, or two parallel arrays of the same length -- one of keys, and one of
    // the corresponding values.
//    _.object = function(list, values) {
//        if (list == null) return {};
//        var result = {};
//        for (var i = 0, length = list.length; i < length; i++) {
//            if (values) {
//                result[list[i]] = values[i];
//            } else {
//                result[list[i][0]] = list[i][1];
//            }
//        }
//        return result;
//    };

    // 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 = 0, length = array.length;
//        if (isSorted) {
//            if (typeof isSorted == 'number') {
//                i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
//            } else {
//                i = _.sortedIndex(array, item);
//                return array[i] === item ? i : -1;
//            }
//        }
//        if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
//        for (; i < length; i++) if (array[i] === item) return i;
//        return -1;
//    };

    // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
//    _.lastIndexOf = function(array, item, from) {
//        if (array == null) return -1;
//        var hasIndex = from != null;
//        if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
//            return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
//        }
//        var i = (hasIndex ? from : 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 length = Math.max(Math.ceil((stop - start) / step), 0);
//        var idx = 0;
//        var range = new Array(length);
//
//        while(idx < length) {
//            range[idx++] = start;
//            start += step;
//        }
//
//        return range;
//    };

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

    // Reusable constructor function for prototype setting.
    var ctor = function(){};

    // Create a function bound to a given object (assigning `this`, and arguments,
    // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
    // available.
    _.bind = function(func, context) {
        var args, bound;
        if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
        if (!_.isFunction(func)) throw new TypeError;
        args = slice.call(arguments, 2);
        return bound = function() {
            if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
            ctor.prototype = func.prototype;
            var self = new ctor;
            ctor.prototype = null;
            var result = func.apply(self, args.concat(slice.call(arguments)));
            if (Object(result) === result) return result;
            return self;
        };
    };

    // Partially apply a function by creating a version that has had some of its
    // arguments pre-filled, without changing its dynamic `this` context. _ acts
    // as a placeholder, allowing any combination of arguments to be pre-filled.
//    _.partial = function(func) {
//        var boundArgs = slice.call(arguments, 1);
//        return function() {
//            var position = 0;
//            var args = boundArgs.slice();
//            for (var i = 0, length = args.length; i < length; i++) {
//                if (args[i] === _) args[i] = arguments[position++];
//            }
//            while (position < arguments.length) args.push(arguments[position++]);
//            return func.apply(this, args);
//        };
//    };

    // Bind a number of an object's methods to that object. Remaining arguments
    // are the method names to be bound. 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) throw new Error('bindAll must be passed function names');
//        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 _.has(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(null, 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)));
//    };

    // Returns a function, that, when invoked, will only be triggered at most once
    // during a given window of time. Normally, the throttled function will run
    // as much as it can, without ever going more than once per `wait` duration;
    // but if you'd like to disable the execution on the leading edge, pass
    // `{leading: false}`. To disable execution on the trailing edge, ditto.
//    _.throttle = function(func, wait, options) {
//        var context, args, result;
//        var timeout = null;
//        var previous = 0;
//        options || (options = {});
//        var later = function() {
//            previous = options.leading === false ? 0 : _.now();
//            timeout = null;
//            result = func.apply(context, args);
//            context = args = null;
//        };
//        return function() {
//            var now = _.now();
//            if (!previous && options.leading === false) previous = now;
//            var remaining = wait - (now - previous);
//            context = this;
//            args = arguments;
//            if (remaining <= 0) {
//                clearTimeout(timeout);
//                timeout = null;
//                previous = now;
//                result = func.apply(context, args);
//                context = args = null;
//            } else if (!timeout && options.trailing !== false) {
//                timeout = setTimeout(later, remaining);
//            }
//            return result;
//        };
//    };

    // 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. If `immediate` is passed, trigger the function on the
    // leading edge, instead of the trailing.
//    _.debounce = function(func, wait, immediate) {
//        var timeout, args, context, timestamp, result;
//
//        var later = function() {
//            var last = _.now() - timestamp;
//            if (last < wait) {
//                timeout = setTimeout(later, wait - last);
//            } else {
//                timeout = null;
//                if (!immediate) {
//                    result = func.apply(context, args);
//                    context = args = null;
//                }
//            }
//        };
//
//        return function() {
//            context = this;
//            args = arguments;
//            timestamp = _.now();
//            var callNow = immediate && !timeout;
//            if (!timeout) {
//                timeout = setTimeout(later, wait);
//            }
//            if (callNow) {
//                result = func.apply(context, args);
//                context = args = null;
//            }
//
//            return result;
//        };
//    };

    // 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;
            memo = func.apply(this, arguments);
            func = null;
            return memo;
        };
    };

    // 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 _.partial(wrapper, func);
//    };

    // 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 = arguments;
//        return function() {
//            var args = 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 = function(obj) {
        if (!_.isObject(obj)) return [];
        if (nativeKeys) return nativeKeys(obj);
        var keys = [];
        for (var key in obj) if (_.has(obj, key)) keys.push(key);
        return keys;
    };

    // Retrieve the values of an object's properties.
    _.values = function(obj) {
        var keys = _.keys(obj);
        var length = keys.length;
        var values = new Array(length);
        for (var i = 0; i < length; i++) {
            values[i] = obj[keys[i]];
        }
        return values;
    };

    // Convert an object into a list of `[key, value]` pairs.
//    _.pairs = function(obj) {
//        var keys = _.keys(obj);
//        var length = keys.length;
//        var pairs = new Array(length);
//        for (var i = 0; i < length; i++) {
//            pairs[i] = [keys[i], obj[keys[i]]];
//        }
//        return pairs;
//    };

    // Invert the keys and values of an object. The values must be serializable.
//    _.invert = function(obj) {
//        var result = {};
//        var keys = _.keys(obj);
//        for (var i = 0, length = keys.length; i < length; i++) {
//            result[obj[keys[i]]] = keys[i];
//        }
//        return result;
//    };

    // 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) {
            if (source) {
                for (var prop in source) {
                    obj[prop] = source[prop];
                }
            }
        });
        return obj;
    };

    // Return a copy of the object only containing the whitelisted properties.
    _.pick = function(obj) {
        var copy = {};
        var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
        each(keys, function(key) {
            if (key in obj) copy[key] = obj[key];
        });
        return copy;
    };

    // Return a copy of the object without the blacklisted properties.
    _.omit = function(obj) {
        var copy = {};
        var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
        for (var key in obj) {
            if (!_.contains(keys, key)) copy[key] = obj[key];
        }
        return copy;
    };

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

    // Create a (shallow-cloned) duplicate of an object.
    _.clone = function(obj) {
        if (!_.isObject(obj)) return 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 for `isEqual`.
//    var eq = function(a, b, aStack, bStack) {
//        // 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 || b == null) return a === b;
//        // Unwrap any wrapped objects.
//        if (a instanceof _) a = a._wrapped;
//        if (b instanceof _) b = b._wrapped;
//        // Compare `[[Class]]` names.
//        var className = toString.call(a);
//        if (className != toString.call(b)) return false;
//        switch (className) {
//            // Strings, numbers, dates, and booleans are compared by value.
//            case '[object String]':
//                // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
//                // equivalent to `new String("5")`.
//                return a == String(b);
//            case '[object Number]':
//                // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
//                // other numeric values.
//                return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
//            case '[object Date]':
//            case '[object Boolean]':
//                // Coerce dates and booleans to numeric primitive values. Dates are compared by their
//                // millisecond representations. Note that invalid dates with millisecond representations
//                // of `NaN` are not equivalent.
//                return +a == +b;
//            // RegExps are compared by their source patterns and flags.
//            case '[object RegExp]':
//                return a.source == b.source &&
//                       a.global == b.global &&
//                       a.multiline == b.multiline &&
//                       a.ignoreCase == b.ignoreCase;
//        }
//        if (typeof a != 'object' || typeof b != 'object') return false;
//        // 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 = aStack.length;
//        while (length--) {
//            // Linear search. Performance is inversely proportional to the number of
//            // unique nested structures.
//            if (aStack[length] == a) return bStack[length] == b;
//        }
//        // Objects with different constructors are not equivalent, but `Object`s
//        // from different frames are.
//        var aCtor = a.constructor, bCtor = b.constructor;
//        if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
//                                 _.isFunction(bCtor) && (bCtor instanceof bCtor))
//            && ('constructor' in a && 'constructor' in b)) {
//            return false;
//        }
//        // Add the first object to the stack of traversed objects.
//        aStack.push(a);
//        bStack.push(b);
//        var size = 0, result = true;
//        // Recursively compare objects and arrays.
//        if (className == '[object Array]') {
//            // Compare array lengths to determine if a deep comparison is necessary.
//            size = a.length;
//            result = size == b.length;
//            if (result) {
//                // Deep compare the contents, ignoring non-numeric properties.
//                while (size--) {
//                    if (!(result = eq(a[size], b[size], aStack, bStack))) break;
//                }
//            }
//        } else {
//            // Deep compare objects.
//            for (var key in a) {
//                if (_.has(a, key)) {
//                    // Count the expected number of properties.
//                    size++;
//                    // Deep compare each member.
//                    if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
//                }
//            }
//            // Ensure that both objects contain the same number of properties.
//            if (result) {
//                for (key in b) {
//                    if (_.has(b, key) && !(size--)) break;
//                }
//                result = !size;
//            }
//        }
//        // Remove the first object from the stack of traversed objects.
//        aStack.pop();
//        bStack.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, string, or object empty?
    // An "empty" object has no enumerable own-properties.
//    _.isEmpty = function(obj) {
//        if (obj == null) return true;
//        if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
//        for (var key in obj) if (_.has(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);
    };

    // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
    each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
        _['is' + name] = function(obj) {
            return toString.call(obj) == '[object ' + name + ']';
        };
    });

    // Define a fallback version of the method in browsers (ahem, IE), where
    // there isn't any inspectable "Arguments" type.
    if (!_.isArguments(arguments)) {
        _.isArguments = function(obj) {
            return !!(obj && _.has(obj, 'callee'));
        };
    }

    // Optimize `isFunction` if appropriate.
    if (typeof (/./) !== 'function') {
        _.isFunction = function(obj) {
            return typeof obj === 'function';
        };
    }

    // Is a given object a finite number?
//    _.isFinite = function(obj) {
//        return isFinite(obj) && !isNaN(parseFloat(obj));
//    };

    // Is the given value `NaN`? (NaN is the only number which does not equal itself).
//    _.isNaN = function(obj) {
//        return _.isNumber(obj) && 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 equal to null?
    _.isNull = function(obj) {
        return obj === null;
    };

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

    // Shortcut function for checking if an object has a given property directly
    // on itself (in other words, not on a prototype).
    _.has = function(obj, key) {
        return hasOwnProperty.call(obj, key);
    };

    // 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;
//    };

//    _.constant = function(value) {
//        return function () {
//            return value;
//        };
//    };

//    _.property = function(key) {
//        return function(obj) {
//            return obj[key];
//        };
//    };

    // Returns a predicate for checking whether an object has a given set of `key:value` pairs.
//    _.matches = function(attrs) {
//        return function(obj) {
//            if (obj === attrs) return true; //avoid comparing an object to itself.
//            for (var key in attrs) {
//                if (attrs[key] !== obj[key])
//                    return false;
//            }
//            return true;
//        }
//    };

    // Run a function **n** times.
//    _.times = function(n, iterator, context) {
//        var accum = Array(Math.max(0, n));
//        for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
//        return accum;
//    };

    // Return a random integer between min and max (inclusive).
//    _.random = function(min, max) {
//        if (max == null) {
//            max = min;
//            min = 0;
//        }
//        return min + Math.floor(Math.random() * (max - min + 1));
//    };

    // A (possibly faster) way to get the current timestamp as an integer.
//    _.now = Date.now || function() { return new Date().getTime(); };

    // List of HTML entities for escaping.
//    var entityMap = {
//        escape: {
//            '&': '&amp;',
//            '<': '&lt;',
//            '>': '&gt;',
//            '"': '&quot;',
//            "'": '&#x27;'
//        }
//    };
//    entityMap.unescape = _.invert(entityMap.escape);
//
//    // Regexes containing the keys and values listed immediately above.
//    var entityRegexes = {
//        escape:   new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
//        unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
//    };
//
//    // Functions for escaping and unescaping strings to/from HTML interpolation.
//    _.each(['escape', 'unescape'], function(method) {
//        _[method] = function(string) {
//            if (string == null) return '';
//            return ('' + string).replace(entityRegexes[method], function(match) {
//                return entityMap[method][match];
//            });
//        };
//    });

    // If the value of the named `property` is a function then invoke it with the
    // `object` as context; otherwise, return it.
//    _.result = function(object, property) {
//        if (object == null) return void 0;
//        var value = object[property];
//        return _.isFunction(value) ? value.call(object) : value;
//    };

    // Add your own custom functions to the Underscore object.
    _.mixin = function(obj) {
        each(_.functions(obj), function(name) {
            var func = _[name] = obj[name];
            _.prototype[name] = function() {
                var args = [this._wrapped];
                push.apply(args, arguments);
                return result.call(this, func.apply(_, args));
            };
        });
    };

    // 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
    };

    // When customizing `templateSettings`, if you don't want to define an
    // interpolation, evaluation or escaping regex, we need one that is
    // guaranteed not to match.
    var noMatch = /(.)^/;

    // Certain characters need to be escaped so that they can be put into a
    // string literal.
    var escapes = {
        "'":      "'",
        '\\':     '\\',
        '\r':     'r',
        '\n':     'n',
        '\t':     't',
        '\u2028': 'u2028',
        '\u2029': 'u2029'
    };

    var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/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(text, data, settings) {
        var render;
        settings = _.defaults({}, settings, _.templateSettings);

        // Combine delimiters into one regular expression via alternation.
        var matcher = new RegExp([
                                     (settings.escape || noMatch).source,
                                     (settings.interpolate || noMatch).source,
                                     (settings.evaluate || noMatch).source
                                 ].join('|') + '|$', 'g');

        // Compile the template source, escaping string literals appropriately.
        var index = 0;
        var source = "__p+='";
        text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
            source += text.slice(index, offset)
                .replace(escaper, function(match) { return '\\' + escapes[match]; });

            if (escape) {
                source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
            }
            if (interpolate) {
                source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
            }
            if (evaluate) {
                source += "';\n" + evaluate + "\n__p+='";
            }
            index = offset + match.length;
            return match;
        });
        source += "';\n";

        // If a variable is not specified, place data values in local scope.
        if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';

        source = "var __t,__p='',__j=Array.prototype.join," +
                 "print=function(){__p+=__j.call(arguments,'');};\n" +
                 source + "return __p;\n";

        try {
            render = new Function(settings.variable || 'obj', '_', source);
        } catch (e) {
            e.source = source;
            throw e;
        }

        if (data) return render(data, _);
        var template = function(data) {
            return render.call(this, data, _);
        };

        // Provide the compiled function source as a convenience for precompilation.
        template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';

        return template;
    };

    // Add a "chain" function, which will delegate to the wrapper.
//    _/ = function(obj) {
//        return _(obj).chain();
//    };

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

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

    // 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];
        _.prototype[name] = function() {
            var obj = this._wrapped;
            method.apply(obj, arguments);
            if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
            return result.call(this, obj);
        };
    });

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

    _.extend(_.prototype, {

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

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

    });

    // AMD registration happens at the end for compatibility with AMD loaders
    // that may not enforce next-turn semantics on modules. Even though general
    // practice for AMD registration is to be anonymous, underscore registers
    // as a named module because, like jQuery, it is a base library that is
    // popular enough to be bundled in a third party lib, but not be part of
    // an AMD load request. Those cases could generate an error when an
    // anonymous define() is called outside of a loader request.
//    if (typeof define === 'function' && define.amd) {
//        define('underscore', [], function() {
//            return _;
//        });
//    }
}).call(this);

},{}],10:[function(require,module,exports){
/* Zepto v1.1.3-10-gf4129d5 - zepto data ajax deferred callbacks selector event fx fx_methods - zeptojs.com/license */

var jsonSerializer = require('../utils/JSONSerializer');

var Zepto = (function () {
    var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter,
        document = window.document,
        elementDisplay = {}, classCache = {},
        cssNumber = {'column-count' : 1, 'columns' : 1, 'font-weight' : 1, 'line-height' : 1, 'opacity' : 1, 'z-index' : 1, 'zoom' : 1},
        fragmentRE = /^\s*<(\w+|!)[^>]*>/,
        singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
        tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
        rootNodeRE = /^(?:body|html)$/i,
        capitalRE = /([A-Z])/g,

    // special attributes that should be get/set via method calls
        methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'],

        adjacencyOperators = ['after', 'prepend', 'before', 'append'],
        table = document.createElement('table'),
        tableRow = document.createElement('tr'),
        containers = {
            'tr' : document.createElement('tbody'),
            'tbody' : table, 'thead' : table, 'tfoot' : table,
            'td' : tableRow, 'th' : tableRow,
            '*' : document.createElement('div')
        },
        readyRE = /complete|loaded|interactive/,
        simpleSelectorRE = /^[\w-]*$/,
        class2type = {},
        toString = class2type.toString,
        zepto = {},
        camelize, uniq,
        tempParent = document.createElement('div'),
        propMap = {
            'tabindex' : 'tabIndex',
            'readonly' : 'readOnly',
            'for' : 'htmlFor',
            'class' : 'className',
            'maxlength' : 'maxLength',
            'cellspacing' : 'cellSpacing',
            'cellpadding' : 'cellPadding',
            'rowspan' : 'rowSpan',
            'colspan' : 'colSpan',
            'usemap' : 'useMap',
            'frameborder' : 'frameBorder',
            'contenteditable' : 'contentEditable'
        },
        isArray = Array.isArray ||
                  function (object) {
                      return object instanceof Array
                  }

    zepto.matches = function (element, selector) {
        if (!selector || !element || element.nodeType !== 1) {
            return false
        }
        var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector ||
                              element.oMatchesSelector || element.matchesSelector
        if (matchesSelector) {
            return matchesSelector.call(element, selector)
        }
        // fall back to performing a selector:
        var match, parent = element.parentNode, temp = !parent
        if (temp) {
            (parent = tempParent).appendChild(element)
        }
        match = ~zepto.qsa(parent, selector).indexOf(element)
        temp && tempParent.removeChild(element)
        return match
    }

    function type (obj) {
        return obj == null ? String(obj) :
        class2type[toString.call(obj)] || "object"
    }

    function isFunction (value) {
        return type(value) == "function"
    }

    function isWindow (obj) {
        return obj != null && obj == obj.window
    }

    function isDocument (obj) {
        return obj != null && obj.nodeType == obj.DOCUMENT_NODE
    }

    function isObject (obj) {
        return type(obj) == "object"
    }

    function isPlainObject (obj) {
        return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype
    }

    function likeArray (obj) {
        return typeof obj.length == 'number'
    }

    function compact (array) {
        return filter.call(array, function (item) {
            return item != null
        })
    }

    function flatten (array) {
        return array.length > 0 ? $.fn.concat.apply([], array) : array
    }

    camelize = function (str) {
        return str.replace(/-+(.)?/g, function (match, chr) {
            return chr ? chr.toUpperCase() : ''
        })
    }
    function dasherize (str) {
        return str.replace(/::/g, '/')
            .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
            .replace(/([a-z\d])([A-Z])/g, '$1_$2')
            .replace(/_/g, '-')
            .toLowerCase()
    }

    uniq = function (array) {
        return filter.call(array, function (item, idx) {
            return array.indexOf(item) == idx
        })
    }

    function classRE (name) {
        return name in classCache ?
            classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'))
    }

    function maybeAddPx (name, value) {
        return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value
    }

    function defaultDisplay (nodeName) {
        var element, display
        if (!elementDisplay[nodeName]) {
            element = document.createElement(nodeName)
            document.body.appendChild(element)
            display = getComputedStyle(element, '').getPropertyValue("display")
            element.parentNode.removeChild(element)
            display == "none" && (display = "block")
            elementDisplay[nodeName] = display
        }
        return elementDisplay[nodeName]
    }

    function children (element) {
        return 'children' in element ?
            slice.call(element.children) :
            $.map(element.childNodes, function (node) {
                if (node.nodeType == 1) {
                    return node
                }
            })
    }

    // `$.zepto.fragment` takes a html string and an optional tag name
    // to generate DOM nodes nodes from the given html string.
    // The generated DOM nodes are returned as an array.
    // This function can be overriden in plugins for example to make
    // it compatible with browsers that don't support the DOM fully.
    zepto.fragment = function (html, name, properties) {
        var dom, nodes, container

        // A special case optimization for a single tag
        if (singleTagRE.test(html)) {
            dom = $(document.createElement(RegExp.$1))
        }

        if (!dom) {
            if (html.replace) {
                html = html.replace(tagExpanderRE, "<$1></$2>")
            }
            if (name === undefined) {
                name = fragmentRE.test(html) && RegExp.$1
            }
            if (!(name in containers)) {
                name = '*'
            }

            container = containers[name]
            container.innerHTML = '' + html
            dom = $.each(slice.call(container.childNodes), function () {
                container.removeChild(this)
            })
        }

        if (isPlainObject(properties)) {
            nodes = $(dom)
            $.each(properties, function (key, value) {
                if (methodAttributes.indexOf(key) > -1) {
                    nodes[key](value)
                }
                else {
                    nodes.attr(key, value)
                }
            })
        }

        return dom
    }

    // `$.zepto.Z` swaps out the prototype of the given `dom` array
    // of nodes with `$.fn` and thus supplying all the Zepto functions
    // to the array. Note that `__proto__` is not supported on Internet
    // Explorer. This method can be overriden in plugins.
    zepto.Z = function (dom, selector) {
        dom = dom || []
        dom.__proto__ = $.fn
        dom.selector = selector || ''
        return dom
    }

    // `$.zepto.isZ` should return `true` if the given object is a Zepto
    // collection. This method can be overriden in plugins.
    zepto.isZ = function (object) {
        return object instanceof zepto.Z
    }

    // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and
    // takes a CSS selector and an optional context (and handles various
    // special cases).
    // This method can be overriden in plugins.
    zepto.init = function (selector, context) {
        var dom
        // If nothing given, return an empty Zepto collection
        if (!selector) {
            return zepto.Z()
        }
        // Optimize for string selectors
        else if (typeof selector == 'string') {
            selector = selector.trim()
            // If it's a html fragment, create nodes from it
            // Note: In both Chrome 21 and Firefox 15, DOM error 12
            // is thrown if the fragment doesn't begin with <
            if (selector[0] == '<' && fragmentRE.test(selector)) {
                dom = zepto.fragment(selector, RegExp.$1, context), selector = null
            }
            // If there's a context, create a collection on that context first, and select
            // nodes from there
            else if (context !== undefined) {
                return $(context).find(selector)
            }
            // If it's a CSS selector, use it to select nodes.
            else {
                dom = zepto.qsa(document, selector)
            }
        }
        // If a function is given, call it when the DOM is ready
        else if (isFunction(selector)) {
            return $(document).ready(selector)
        }
        // If a Zepto collection is given, just return it
        else if (zepto.isZ(selector)) {
            return selector
        }
        else {
            // normalize array if an array of nodes is given
            if (isArray(selector)) {
                dom = compact(selector)
            }
            // Wrap DOM nodes.
            else if (isObject(selector)) {
                dom = [selector], selector = null
            }
            // If it's a html fragment, create nodes from it
            else if (fragmentRE.test(selector)) {
                dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
            }
            // If there's a context, create a collection on that context first, and select
            // nodes from there
            else if (context !== undefined) {
                return $(context).find(selector)
            }
            // And last but no least, if it's a CSS selector, use it to select nodes.
            else {
                dom = zepto.qsa(document, selector)
            }
        }
        // create a new Zepto collection from the nodes found
        return zepto.Z(dom, selector)
    }

    // `$` will be the base `Zepto` object. When calling this
    // function just call `$.zepto.init, which makes the implementation
    // details of selecting nodes and creating Zepto collections
    // patchable in plugins.
    $ = function (selector, context) {
        return zepto.init(selector, context)
    }

    function extend (target, source, deep) {
        for (key in source)
            if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
                if (isPlainObject(source[key]) && !isPlainObject(target[key])) {
                    target[key] = {}
                }
                if (isArray(source[key]) && !isArray(target[key])) {
                    target[key] = []
                }
                extend(target[key], source[key], deep)
            }
            else if (source[key] !== undefined) {
                target[key] = source[key]
            }
    }

    // Copy all but undefined properties from one or more
    // objects to the `target` object.
    $.extend = function (target) {
        var deep, args = slice.call(arguments, 1)
        if (typeof target == 'boolean') {
            deep = target
            target = args.shift()
        }
        args.forEach(function (arg) {
            extend(target, arg, deep)
        })
        return target
    }

    // `$.zepto.qsa` is Zepto's CSS selector implementation which
    // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`.
    // This method can be overriden in plugins.
    zepto.qsa = function (element, selector) {
        var found,
            maybeID = selector[0] == '#',
            maybeClass = !maybeID && selector[0] == '.',
            nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, // Ensure that a 1 char tag name still gets checked
            isSimple = simpleSelectorRE.test(nameOnly)
        return (isDocument(element) && isSimple && maybeID) ?
            ( (found = element.getElementById(nameOnly)) ? [found] : [] ) :
            (element.nodeType !== 1 && element.nodeType !== 9) ? [] :
                slice.call(
                    isSimple && !maybeID ?
                        maybeClass ? element.getElementsByClassName(nameOnly) : // If it's simple, it could be a class
                            element.getElementsByTagName(selector) : // Or a tag
                        element.querySelectorAll(selector) // Or it's not simple, and we need to query all
                )
    }

    function filtered (nodes, selector) {
        return selector == null ? $(nodes) : $(nodes).filter(selector)
    }

    $.contains = function (parent, node) {
        return parent !== node && parent.contains(node)
    }

    function funcArg (context, arg, idx, payload) {
        return isFunction(arg) ? arg.call(context, idx, payload) : arg
    }

    function setAttribute (node, name, value) {
        value == null ? node.removeAttribute(name) : node.setAttribute(name, value)
    }

    // access className property while respecting SVGAnimatedString
    function className (node, value) {
        var klass = node.className,
            svg = klass && klass.baseVal !== undefined

        if (value === undefined) {
            return svg ? klass.baseVal : klass
        }
        svg ? (klass.baseVal = value) : (node.className = value)
    }

    // "true"  => true
    // "false" => false
    // "null"  => null
    // "42"    => 42
    // "42.5"  => 42.5
    // "08"    => "08"
    // JSON    => parse if valid
    // String  => self
    function deserializeValue (value) {
        var num
        try {
            return value ?
            value == "true" ||
            ( value == "false" ? false :
                value == "null" ? null :
                    !/^0/.test(value) && !isNaN(num = Number(value)) ? num :
                        /^[\[\{]/.test(value) ? $.parseJSON(value, true) :
                            value )
                : value
        }
        catch (e) {
            return value
        }
    }

    $.type = type
    $.isFunction = isFunction
    $.isWindow = isWindow
    $.isArray = isArray
    $.isPlainObject = isPlainObject

    $.isEmptyObject = function (obj) {
        var name
        for (name in obj) return false
        return true
    }

    $.inArray = function (elem, array, i) {
        return emptyArray.indexOf.call(array, elem, i)
    }

    $.camelCase = camelize
    $.trim = function (str) {
        return str == null ? "" : String.prototype.trim.call(str)
    }

    // plugin compatibility
    $.uuid = 0
    $.support = {}
    $.expr = {}

    $.map = function (elements, callback) {
        var value, values = [], i, key
        if (likeArray(elements)) {
            for (i = 0; i < elements.length; i++) {
                value = callback(elements[i], i)
                if (value != null) {
                    values.push(value)
                }
            }
        }
        else {
            for (key in elements) {
                value = callback(elements[key], key)
                if (value != null) {
                    values.push(value)
                }
            }
        }
        return flatten(values)
    }

    $.each = function (elements, callback) {
        var i, key
        if (likeArray(elements)) {
            for (i = 0; i < elements.length; i++)
                if (callback.call(elements[i], i, elements[i]) === false) {
                    return elements
                }
        }
        else {
            for (key in elements)
                if (callback.call(elements[key], key, elements[key]) === false) {
                    return elements
                }
        }

        return elements
    }

    $.grep = function (elements, callback) {
        return filter.call(elements, callback)
    }


    $.parseJSON = function (json, nonStrict) {
        return jsonSerializer.toJSON(json, nonStrict);
    };


    // Populate the class2type map
    $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function (i, name) {
        class2type["[object " + name + "]"] = name.toLowerCase()
    });

    // Define methods that will be available on all
    // Zepto collections
    $.fn = {
        // Because a collection acts like an array
        // copy over these useful array functions.
        forEach : emptyArray.forEach,
        reduce : emptyArray.reduce,
        push : emptyArray.push,
        sort : emptyArray.sort,
        indexOf : emptyArray.indexOf,
        concat : emptyArray.concat,

        // `map` and `slice` in the jQuery API work differently
        // from their array counterparts
        map : function (fn) {
            return $($.map(this, function (el, i) {
                return fn.call(el, i, el)
            }))
        },
        slice : function () {
            return $(slice.apply(this, arguments))
        },

        ready : function (callback) {
            // need to check if document.body exists for IE as that browser reports
            // document ready when it hasn't yet created the body element
            if (readyRE.test(document.readyState) && document.body) {
                callback($)
            }
            else {
                document.addEventListener('DOMContentLoaded', function () {
                    callback($)
                }, false)
            }
            return this
        },
        get : function (idx) {
            return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length]
        },
        toArray : function () {
            return this.get()
        },
        size : function () {
            return this.length
        },
        remove : function () {
            return this.each(function () {
                if (this.parentNode != null) {
                    this.parentNode.removeChild(this)
                }
            })
        },
        each : function (callback) {
            emptyArray.every.call(this, function (el, idx) {
                return callback.call(el, idx, el) !== false
            })
            return this
        },
        filter : function (selector) {
            if (isFunction(selector)) {
                return this.not(this.not(selector))
            }
            return $(filter.call(this, function (element) {
                return zepto.matches(element, selector)
            }))
        },
        add : function (selector, context) {
            return $(uniq(this.concat($(selector, context))))
        },
        is : function (selector) {
            return this.length > 0 && zepto.matches(this[0], selector)
        },
        not : function (selector) {
            var nodes = []
            if (isFunction(selector) && selector.call !== undefined) {
                this.each(function (idx) {
                    if (!selector.call(this, idx)) {
                        nodes.push(this)
                    }
                })
            }
            else {
                var excludes = typeof selector == 'string' ? this.filter(selector) :
                    (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector)
                this.forEach(function (el) {
                    if (excludes.indexOf(el) < 0) {
                        nodes.push(el)
                    }
                })
            }
            return $(nodes)
        },
        has : function (selector) {
            return this.filter(function () {
                return isObject(selector) ?
                    $.contains(this, selector) :
                    $(this).find(selector).size()
            })
        },
        eq : function (idx) {
            return idx === -1 ? this.slice(idx) : this.slice(idx, +idx + 1)
        },
        first : function () {
            var el = this[0]
            return el && !isObject(el) ? el : $(el)
        },
        last : function () {
            var el = this[this.length - 1]
            return el && !isObject(el) ? el : $(el)
        },
        find : function (selector) {
            var result, $this = this
            if (!selector) {
                result = []
            }
            else if (typeof selector == 'object') {
                result = $(selector).filter(function () {
                    var node = this
                    return emptyArray.some.call($this, function (parent) {
                        return $.contains(parent, node)
                    })
                })
            }
            else if (this.length == 1) {
                result = $(zepto.qsa(this[0], selector))
            }
            else {
                result = this.map(function () {
                    return zepto.qsa(this, selector)
                })
            }
            return result
        },
        closest : function (selector, context) {
            var node = this[0], collection = false
            if (typeof selector == 'object') {
                collection = $(selector)
            }
            while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector))) {
                node = node !== context && !isDocument(node) && node.parentNode
            }
            return $(node)
        },
        parents : function (selector) {
            var ancestors = [], nodes = this
            while (nodes.length > 0) {
                nodes = $.map(nodes, function (node) {
                    if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) {
                        ancestors.push(node)
                        return node
                    }
                })
            }
            return filtered(ancestors, selector)
        },
        parent : function (selector) {
            return filtered(uniq(this.pluck('parentNode')), selector)
        },
        children : function (selector) {
            return filtered(this.map(function () {
                return children(this)
            }), selector)
        },
        contents : function () {
            return this.map(function () {
                return slice.call(this.childNodes)
            })
        },
        siblings : function (selector) {
            return filtered(this.map(function (i, el) {
                return filter.call(children(el.parentNode), function (child) {
                    return child !== el
                })
            }), selector)
        },
        empty : function () {
            return this.each(function () {
                this.innerHTML = ''
            })
        },
        // `pluck` is borrowed from Prototype.js
        pluck : function (property) {
            return $.map(this, function (el) {
                return el[property]
            })
        },
        show : function () {
            return this.each(function () {
                this.style.display == "none" && (this.style.display = '')
                if (getComputedStyle(this, '').getPropertyValue("display") == "none") {
                    this.style.display = defaultDisplay(this.nodeName)
                }
            })
        },
        replaceWith : function (newContent) {
            return this.before(newContent).remove()
        },
        wrap : function (structure) {
            var func = isFunction(structure)
            if (this[0] && !func) {
                var dom = $(structure).get(0),
                    clone = dom.parentNode || this.length > 1
            }

            return this.each(function (index) {
                $(this).wrapAll(
                    func ? structure.call(this, index) :
                        clone ? dom.cloneNode(true) : dom
                )
            })
        },
        wrapAll : function (structure) {
            if (this[0]) {
                $(this[0]).before(structure = $(structure))
                var children
                // drill down to the inmost element
                while ((children = structure.children()).length) {
                    structure = children.first()
                }
                $(structure).append(this)
            }
            return this
        },
        wrapInner : function (structure) {
            var func = isFunction(structure)
            return this.each(function (index) {
                var self = $(this), contents = self.contents(),
                    dom = func ? structure.call(this, index) : structure
                contents.length ? contents.wrapAll(dom) : self.append(dom)
            })
        },
        unwrap : function () {
            this.parent().each(function () {
                $(this).replaceWith($(this).children())
            })
            return this
        },
        clone : function () {
            return this.map(function () {
                return this.cloneNode(true)
            })
        },
        hide : function () {
            return this.css("display", "none")
        },
        toggle : function (setting) {
            return this.each(function () {
                var el = $(this)
                    ;
                (setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide()
            })
        },
        prev : function (selector) {
            return $(this.pluck('previousElementSibling')).filter(selector || '*')
        },
        next : function (selector) {
            return $(this.pluck('nextElementSibling')).filter(selector || '*')
        },
        html : function (html) {
            return arguments.length === 0 ?
                (this.length > 0 ? this[0].innerHTML : null) :
                this.each(function (idx) {
                    var originHtml = this.innerHTML
                    $(this).empty().append(funcArg(this, html, idx, originHtml))
                })
        },
        text : function (text) {
            return arguments.length === 0 ?
                (this.length > 0 ? this[0].textContent : null) :
                this.each(function () {
                    this.textContent = (text === undefined) ? '' : '' + text
                })
        },
        attr : function (name, value) {
            var result
            return (typeof name == 'string' && value === undefined) ?
                (this.length == 0 || this[0].nodeType !== 1 ? undefined :
                    (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() :
                        (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result
                ) :
                this.each(function (idx) {
                    if (this.nodeType !== 1) {
                        return
                    }
                    if (isObject(name)) {
                        for (key in name) setAttribute(this, key, name[key])
                    }
                    else {
                        setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))
                    }
                })
        },
        removeAttr : function (name) {
            return this.each(function () {
                this.nodeType === 1 && setAttribute(this, name)
            })
        },
        prop : function (name, value) {
            name = propMap[name] || name
            return (value === undefined) ?
                (this[0] && this[0][name]) :
                this.each(function (idx) {
                    this[name] = funcArg(this, value, idx, this[name])
                })
        },
        data : function (name, value) {
            var data = this.attr('data-' + name.replace(capitalRE, '-$1').toLowerCase(), value)
            return data !== null ? deserializeValue(data) : undefined
        },
        val : function (value) {
            return arguments.length === 0 ?
                (this[0] && (this[0].multiple ?
                    $(this[0]).find('option').filter(function () {
                        return this.selected
                    }).pluck('value') :
                    this[0].value)
                ) :
                this.each(function (idx) {
                    this.value = funcArg(this, value, idx, this.value)
                })
        },
        offset : function (coordinates) {
            if (coordinates) {
                return this.each(function (index) {
                    var $this = $(this),
                        coords = funcArg(this, coordinates, index, $this.offset()),
                        parentOffset = $this.offsetParent().offset(),
                        props = {
                            top :  coords.top - parentOffset.top,
                            left : coords.left - parentOffset.left
                        }

                    if ($this.css('position') == 'static') {
                        props['position'] = 'relative'
                    }
                    $this.css(props)
                })
            }
            if (this.length == 0) {
                return null
            }
            var obj = this[0].getBoundingClientRect()
            return {
                left : obj.left + window.pageXOffset,
                top :  obj.top + window.pageYOffset,
                width : Math.round(obj.width),
                height : Math.round(obj.height)
            }
        },
        css : function (property, value) {
            if (arguments.length < 2) {
                var element = this[0], computedStyle = getComputedStyle(element, '')
                if (!element) {
                    return
                }
                if (typeof property == 'string') {
                    return element.style[camelize(property)] || computedStyle.getPropertyValue(property)
                }
                else if (isArray(property)) {
                    var props = {}
                    $.each(isArray(property) ? property : [property], function (_, prop) {
                        props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop))
                    })
                    return props
                }
            }

            var css = ''
            if (type(property) == 'string') {
                if (!value && value !== 0) {
                    this.each(function () {
                        this.style.removeProperty(dasherize(property))
                    })
                }
                else {
                    css = dasherize(property) + ":" + maybeAddPx(property, value)
                }
            }
            else {
                for (key in property)
                    if (!property[key] && property[key] !== 0) {
                        this.each(function () {
                            this.style.removeProperty(dasherize(key))
                        })
                    }
                    else {
                        css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'
                    }
            }

            return this.each(function () {
                this.style.cssText += ';' + css
            })
        },
        index : function (element) {
            return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0])
        },
        hasClass : function (name) {
            if (!name) {
                return false
            }
            return emptyArray.some.call(this, function (el) {
                return this.test(className(el))
            }, classRE(name))
        },
        addClass : function (name) {
            if (!name) {
                return this
            }
            return this.each(function (idx) {
                classList = []
                var cls = className(this), newName = funcArg(this, name, idx, cls)
                newName.split(/\s+/g).forEach(function (klass) {
                    if (!$(this).hasClass(klass)) {
                        classList.push(klass)
                    }
                }, this)
                classList.length && className(this, cls + (cls ? " " : "") + classList.join(" "))
            })
        },
        removeClass : function (name) {
            return this.each(function (idx) {
                if (name === undefined) {
                    return className(this, '')
                }
                classList = className(this)
                funcArg(this, name, idx, classList).split(/\s+/g).forEach(function (klass) {
                    classList = classList.replace(classRE(klass), " ")
                })
                className(this, classList.trim())
            })
        },
        toggleClass : function (name, when) {
            if (!name) {
                return this
            }
            return this.each(function (idx) {
                var $this = $(this), names = funcArg(this, name, idx, className(this))
                names.split(/\s+/g).forEach(function (klass) {
                    (when === undefined ? !$this.hasClass(klass) : when) ?
                        $this.addClass(klass) : $this.removeClass(klass)
                })
            })
        },
        scrollTop : function (value) {
            if (!this.length) {
                return
            }
            var hasScrollTop = 'scrollTop' in this[0]
            if (value === undefined) {
                return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset
            }
            return this.each(hasScrollTop ?
                function () {
                    this.scrollTop = value
                } :
                function () {
                    this.scrollTo(this.scrollX, value)
                })
        },
        scrollLeft : function (value) {
            if (!this.length) {
                return
            }
            var hasScrollLeft = 'scrollLeft' in this[0]
            if (value === undefined) {
                return hasScrollLeft ? this[0].scrollLeft : this[0].pageXOffset
            }
            return this.each(hasScrollLeft ?
                function () {
                    this.scrollLeft = value
                } :
                function () {
                    this.scrollTo(value, this.scrollY)
                })
        },
        position : function () {
            if (!this.length) {
                return
            }

            var elem = this[0],
            // Get *real* offsetParent
                offsetParent = this.offsetParent(),
            // Get correct offsets
                offset = this.offset(),
                parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? {top : 0, left : 0} : offsetParent.offset()

            // Subtract element margins
            // note: when an element has margin: auto the offsetLeft and marginLeft
            // are the same in Safari causing offset.left to incorrectly be 0
            offset.top -= parseFloat($(elem).css('margin-top')) || 0
            offset.left -= parseFloat($(elem).css('margin-left')) || 0

            // Add offsetParent borders
            parentOffset.top += parseFloat($(offsetParent[0]).css('border-top-width')) || 0
            parentOffset.left += parseFloat($(offsetParent[0]).css('border-left-width')) || 0

            // Subtract the two offsets
            return {
                top :  offset.top - parentOffset.top,
                left : offset.left - parentOffset.left
            }
        },
        offsetParent : function () {
            return this.map(function () {
                var parent = this.offsetParent || document.body
                while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static") {
                    parent = parent.offsetParent
                }
                return parent
            })
        }
    }

    // for now
    $.fn.detach = $.fn.remove

        // Generate the `width` and `height` functions
    ;
    ['width', 'height'].forEach(function (dimension) {
        var dimensionProperty =
            dimension.replace(/./, function (m) {
                return m[0].toUpperCase()
            })

        $.fn[dimension] = function (value) {
            var offset, el = this[0]
            if (value === undefined) {
                return isWindow(el) ? el['inner' + dimensionProperty] :
                    isDocument(el) ? el.documentElement['scroll' + dimensionProperty] :
                    (offset = this.offset()) && offset[dimension]
            }
            else {
                return this.each(function (idx) {
                    el = $(this)
                    el.css(dimension, funcArg(this, value, idx, el[dimension]()))
                })
            }
        }
    })

    function traverseNode (node, fun) {
        fun(node)
        for (var key in node.childNodes) traverseNode(node.childNodes[key], fun)
    }

    // Generate the `after`, `prepend`, `before`, `append`,
    // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods.
    adjacencyOperators.forEach(function (operator, operatorIndex) {
        var inside = operatorIndex % 2 //=> prepend, append

        $.fn[operator] = function () {
            // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings
            var argType, nodes = $.map(arguments, function (arg) {
                    argType = type(arg)
                    return argType == "object" || argType == "array" || arg == null ?
                        arg : zepto.fragment(arg)
                }),
                parent, copyByClone = this.length > 1
            if (nodes.length < 1) {
                return this
            }

            return this.each(function (_, target) {
                parent = inside ? target : target.parentNode

                // convert all methods to a "before" operation
                target = operatorIndex == 0 ? target.nextSibling :
                    operatorIndex == 1 ? target.firstChild :
                        operatorIndex == 2 ? target :
                            null

                nodes.forEach(function (node) {
                    if (copyByClone) {
                        node = node.cloneNode(true)
                    }
                    else if (!parent) {
                        return $(node).remove()
                    }

                    traverseNode(parent.insertBefore(node, target), function (el) {
                        if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
                            (!el.type || el.type === 'text/javascript') && !el.src) {
                            window['eval'].call(window, el.innerHTML)
                        }
                    })
                })
            })
        }

        // after    => insertAfter
        // prepend  => prependTo
        // before   => insertBefore
        // append   => appendTo
        $.fn[inside ? operator + 'To' : 'insert' + (operatorIndex ? 'Before' : 'After')] = function (html) {
            $(html)[operator](this)
            return this
        }
    })

    zepto.Z.prototype = $.fn

    // Export internal API functions in the `$.zepto` namespace
    zepto.uniq = uniq
    zepto.deserializeValue = deserializeValue
    $.zepto = zepto

    return $
})();


(function ($) {
    var data = {}, dataAttr = $.fn.data, camelize = $.camelCase,
        exp = $.expando = 'Zepto' + (+new Date()), emptyArray = []

    // Get value from node:
    // 1. first try key as given,
    // 2. then try camelized key,
    // 3. fall back to reading "data-*" attribute.
    function getData (node, name) {
        var id = node[exp], store = id && data[id]
        if (name === undefined) {
            return store || setData(node)
        }
        else {
            if (store) {
                if (name in store) {
                    return store[name]
                }
                var camelName = camelize(name)
                if (camelName in store) {
                    return store[camelName]
                }
            }
            return dataAttr.call($(node), name)
        }
    }

    // Store value under camelized key on node
    function setData (node, name, value) {
        var id = node[exp] || (node[exp] = ++$.uuid),
            store = data[id] || (data[id] = attributeData(node))
        if (name !== undefined) {
            store[camelize(name)] = value
        }
        return store
    }

    // Read all "data-*" attributes from a node
    function attributeData (node) {
        var store = {}
        $.each(node.attributes || emptyArray, function (i, attr) {
            if (attr.name.indexOf('data-') == 0) {
                store[camelize(attr.name.replace('data-', ''))] =
                $.zepto.deserializeValue(attr.value)
            }
        })
        return store
    }

    $.fn.data = function (name, value) {
        return value === undefined ?
            // set multiple values via object
            $.isPlainObject(name) ?
                this.each(function (i, node) {
                    $.each(name, function (key, value) {
                        setData(node, key, value)
                    })
                }) :
                // get value from first element
                this.length == 0 ? undefined : getData(this[0], name) :
            // set value on all elements
            this.each(function () {
                setData(this, name, value)
            })
    }

    $.fn.removeData = function (names) {
        if (typeof names == 'string') {
            names = names.split(/\s+/)
        }
        return this.each(function () {
            var id = this[exp], store = id && data[id]
            if (store) {
                $.each(names || store, function (key) {
                    delete store[names ? camelize(this) : key]
                })
            }
        })
    }

        // Generate extended `remove` and `empty` functions
    ;
    ['remove', 'empty'].forEach(function (methodName) {
        var origFn = $.fn[methodName]
        $.fn[methodName] = function () {
            var elements = this.find('*')
            if (methodName === 'remove') {
                elements = elements.add(this)
            }
            elements.removeData()
            return origFn.call(this)
        }
    })
})(Zepto);



(function ($) {
    var jsonpID = 0,
        document = window.document,
        key,
        name,
        rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
        scriptTypeRE = /^(?:text|application)\/javascript/i,
        xmlTypeRE = /^(?:text|application)\/xml/i,
        jsonType = 'application/json',
        htmlType = 'text/html',
        blankRE = /^\s*$/

    // trigger a custom event and return false if it was cancelled
    function triggerAndReturn (context, eventName, data) {
        var event = $.Event(eventName)
        $(context).trigger(event, data)
        return !event.isDefaultPrevented()
    }

    // trigger an Ajax "global" event
    function triggerGlobal (settings, context, eventName, data) {
        if (settings.global) {
            return triggerAndReturn(context || document, eventName, data)
        }
    }

    // Number of active Ajax requests
    $.active = 0

    function ajaxStart (settings) {
        if (settings.global && $.active++ === 0) {
            triggerGlobal(settings, null, 'ajaxStart')
        }
    }

    function ajaxStop (settings) {
        if (settings.global && !(--$.active)) {
            triggerGlobal(settings, null, 'ajaxStop')
        }
    }

    // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable
    function ajaxBeforeSend (xhr, settings) {
        var context = settings.context
        if (settings.beforeSend.call(context, xhr, settings) === false ||
            triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false) {
            return false
        }

        triggerGlobal(settings, context, 'ajaxSend', [xhr, settings])
    }

    function ajaxSuccess (data, xhr, settings, deferred) {
        var context = settings.context, status = 'success'
        settings.success.call(context, data, status, xhr)
        if (deferred) {
            deferred.resolveWith(context, [data, status, xhr])
        }
        triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data])
        ajaxComplete(status, xhr, settings)
    }

    // type: "timeout", "error", "abort", "parsererror"
    function ajaxError (error, type, xhr, settings, deferred) {
        var context = settings.context
        settings.error.call(context, xhr, type, error)
        if (deferred) {
            deferred.rejectWith(context, [xhr, type, error])
        }
        triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type])
        ajaxComplete(type, xhr, settings)
    }

    // status: "success", "notmodified", "error", "timeout", "abort", "parsererror"
    function ajaxComplete (status, xhr, settings) {
        var context = settings.context
        settings.complete.call(context, xhr, status)
        triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings])
        ajaxStop(settings)
    }

    // Empty function, used as default callback
    function empty () {
    }

    $.ajaxJSONP = function (options, deferred) {
        if (!('type' in options)) {
            return $.ajax(options)
        }

        var _callbackName = options.jsonpCallback,
            callbackName = ($.isFunction(_callbackName) ?
                    _callbackName() : _callbackName) || ('jsonp' + (++jsonpID)),
            script = document.createElement('script'),
            originalCallback = window[callbackName],
            responseData,
            abort = function (errorType) {
                $(script).triggerHandler('error', errorType || 'abort')
            },
            xhr = {abort : abort}, abortTimeout

        if (deferred) {
            deferred.promise(xhr)
        }

        $(script).on('load error', function (e, errorType) {
            clearTimeout(abortTimeout)
            $(script).off().remove()

            if (e.type == 'error' || !responseData) {
                ajaxError(null, errorType || 'error', xhr, options, deferred)
            }
            else {
                ajaxSuccess(responseData[0], xhr, options, deferred)
            }

            window[callbackName] = originalCallback
            if (responseData && $.isFunction(originalCallback)) {
                originalCallback(responseData[0])
            }

            originalCallback = responseData = undefined
        })

        if (ajaxBeforeSend(xhr, options) === false) {
            abort('abort')
            return xhr
        }

        window[callbackName] = function () {
            responseData = arguments
        }

        script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName)
        document.head.appendChild(script)

        if (options.timeout > 0) {
            abortTimeout = setTimeout(function () {
                abort('timeout')
            }, options.timeout)
        }

        return xhr
    }

    $.ajaxSettings = {
        // Default type of request
        type : 'GET',
        // Callback that is executed before request
        beforeSend : empty,
        // Callback that is executed if the request succeeds
        success : empty,
        // Callback that is executed the the server drops error
        error : empty,
        // Callback that is executed on request complete (both: error and success)
        complete : empty,
        // The context for the callbacks
        context : null,
        // Whether to trigger "global" Ajax events
        global : true,
        // Transport
        xhr : function () {
            return new window.XMLHttpRequest()
        },
        // MIME types mapping
        // IIS returns Javascript as "application/x-javascript"
        accepts : {
            script : 'text/javascript, application/javascript, application/x-javascript',
            json : jsonType,
            xml : 'application/xml, text/xml',
            html : htmlType,
            text : 'text/plain'
        },
        // Whether the request is to another domain
        crossDomain : false,
        // Default timeout
        timeout : 0,
        // Whether data should be serialized to string
        processData : true,
        // Whether the browser should be allowed to cache GET responses
        cache : true
    }

    function mimeToDataType (mime) {
        if (mime) {
            mime = mime.split(';', 2)[0]
        }
        return mime && ( mime == htmlType ? 'html' :
                mime == jsonType ? 'json' :
                    scriptTypeRE.test(mime) ? 'script' :
                    xmlTypeRE.test(mime) && 'xml' ) || 'text'
    }

    function appendQuery (url, query) {
        if (query == '') {
            return url
        }
        return (url + '&' + query).replace(/[&?]{1,2}/, '?')
    }

    // serialize payload and append it to the URL for GET requests
    function serializeData (options) {
        if (options.processData && options.data && $.type(options.data) != "string") {
            options.data = $.param(options.data, options.traditional)
        }
        if (options.data && (!options.type || options.type.toUpperCase() == 'GET')) {
            options.url = appendQuery(options.url, options.data), options.data = undefined
        }
    }

    $.ajax = function (options) {
        var settings = $.extend({}, options || {}),
            deferred = $.Deferred && $.Deferred()
        for (key in $.ajaxSettings) if (settings[key] === undefined) {
            settings[key] = $.ajaxSettings[key]
        }

        ajaxStart(settings)

        if (!settings.crossDomain) {
            settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) &&
                                   RegExp.$2 != window.location.host
        }

        if (!settings.url) {
            settings.url = window.location.toString()
        }
        serializeData(settings)
        if (settings.cache === false) {
            settings.url = appendQuery(settings.url, '_=' + Date.now())
        }

        var dataType = settings.dataType, hasPlaceholder = /\?.+=\?/.test(settings.url)
        if (dataType == 'jsonp' || hasPlaceholder) {
            if (!hasPlaceholder) {
                settings.url = appendQuery(settings.url,
                    settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?')
            }
            return $.ajaxJSONP(settings, deferred)
        }

        var mime = settings.accepts[dataType],
            headers = {},
            setHeader = function (name, value) {
                headers[name.toLowerCase()] = [name, value]
            },
            protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol,
            xhr = settings.xhr(),
            nativeSetHeader = xhr.setRequestHeader,
            abortTimeout

        if (deferred) {
            deferred.promise(xhr)
        }

        if (!settings.crossDomain) {
            setHeader('X-Requested-With', 'XMLHttpRequest')
        }
        setHeader('Accept', mime || '*/*')
        if (mime = settings.mimeType || mime) {
            if (mime.indexOf(',') > -1) {
                mime = mime.split(',', 2)[0]
            }
            xhr.overrideMimeType && xhr.overrideMimeType(mime)
        }
        if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET')) {
            setHeader('Content-Type', settings.contentType || 'application/x-www-form-urlencoded')
        }

        if (settings.headers) {
            for (name in settings.headers) setHeader(name, settings.headers[name])
        }
        xhr.setRequestHeader = setHeader

        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                xhr.onreadystatechange = empty
                clearTimeout(abortTimeout)
                var result, error = false
                if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) {
                    dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type'))
                    result = xhr.responseText

                    try {
                        // http://perfectionkills.com/global-eval-what-are-the-options/
                        if (dataType == 'script') {
                            (1, eval)(result)
                        }
                        else if (dataType == 'xml') {
                            result = xhr.responseXML
                        }
                        else if (dataType == 'json') {
                            result = blankRE.test(result) ? null : $.parseJSON(result)
                        }
                    }
                    catch (e) {
                        error = e
                    }

                    if (error) {
                        ajaxError(error, 'parsererror', xhr, settings, deferred)
                    }
                    else {
                        ajaxSuccess(result, xhr, settings, deferred)
                    }
                }
                else {
                    ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred)
                }
            }
        }

        if (ajaxBeforeSend(xhr, settings) === false) {
            xhr.abort()
            ajaxError(null, 'abort', xhr, settings, deferred)
            return xhr
        }

        if (settings.xhrFields) {
            for (name in settings.xhrFields) xhr[name] = settings.xhrFields[name]
        }

        var async = 'async' in settings ? settings.async : true
        xhr.open(settings.type, settings.url, async, settings.username, settings.password)

        for (name in headers) nativeSetHeader.apply(xhr, headers[name])

        if (settings.timeout > 0) {
            abortTimeout = setTimeout(function () {
                xhr.onreadystatechange = empty
                xhr.abort()
                ajaxError(null, 'timeout', xhr, settings, deferred)
            }, settings.timeout)
        }

        // avoid sending empty string (#319)
        xhr.send(settings.data ? settings.data : null)
        return xhr
    }

    // handle optional data/success arguments
    function parseArguments (url, data, success, dataType) {
        if ($.isFunction(data)) {
            dataType = success, success = data, data = undefined
        }
        if (!$.isFunction(success)) {
            dataType = success, success = undefined
        }
        return {
            url : url, data : data, success : success, dataType : dataType
        }
    }

    $.get = function (/* url, data, success, dataType */) {
        return $.ajax(parseArguments.apply(null, arguments))
    }

    $.post = function (/* url, data, success, dataType */) {
        var options = parseArguments.apply(null, arguments)
        options.type = 'POST'
        return $.ajax(options)
    }

    $.getJSON = function (/* url, data, success */) {
        var options = parseArguments.apply(null, arguments)
        options.dataType = 'json'
        return $.ajax(options)
    }

    $.fn.load = function (url, data, success) {
        if (!this.length) {
            return this
        }
        var self = this, parts = url.split(/\s/), selector,
            options = parseArguments(url, data, success),
            callback = options.success
        if (parts.length > 1) {
            options.url = parts[0], selector = parts[1]
        }
        options.success = function (response) {
            self.html(selector ?
                $('<div>').html(response.replace(rscript, "")).find(selector)
                : response)
            callback && callback.apply(self, arguments)
        }
        $.ajax(options)
        return this
    }

    var escape = encodeURIComponent

    function serialize (params, obj, traditional, scope) {
        var type, array = $.isArray(obj), hash = $.isPlainObject(obj)
        $.each(obj, function (key, value) {
            type = $.type(value)
            if (scope) {
                key = traditional ? scope :
                scope + '[' + (hash || type == 'object' || type == 'array' ? key : '') + ']'
            }
            // handle data in serializeArray() format
            if (!scope && array) {
                params.add(value.name, value.value)
            }
            // recurse into nested objects
            else if (type == "array" || (!traditional && type == "object")) {
                serialize(params, value, traditional, key)
            }
            else {
                params.add(key, value)
            }
        })
    }

    $.param = function (obj, traditional) {
        var params = []
        params.add = function (k, v) {
            this.push(escape(k) + '=' + escape(v))
        }
        serialize(params, obj, traditional)
        return params.join('&').replace(/%20/g, '+')
    }
})(Zepto);




(function ($) {
    var slice = Array.prototype.slice

    function Deferred (func) {
        var tuples = [
                // action, add listener, listener list, final state
                ["resolve", "done", $.Callbacks({once : 1, memory : 1}), "resolved"],
                ["reject", "fail", $.Callbacks({once : 1, memory : 1}), "rejected"],
                ["notify", "progress", $.Callbacks({memory : 1})]
            ],
            state = "pending",
            promise = {
                state : function () {
                    return state
                },
                always : function () {
                    deferred.done(arguments).fail(arguments)
                    return this
                },
                then : function (/* fnDone [, fnFailed [, fnProgress]] */) {
                    var fns = arguments
                    return Deferred(function (defer) {
                        $.each(tuples, function (i, tuple) {
                            var fn = $.isFunction(fns[i]) && fns[i]
                            deferred[tuple[1]](function () {
                                var returned = fn && fn.apply(this, arguments)
                                if (returned && $.isFunction(returned.promise)) {
                                    returned.promise()
                                        .done(defer.resolve)
                                        .fail(defer.reject)
                                        .progress(defer.notify)
                                }
                                else {
                                    var context = this === promise ? defer.promise() : this,
                                        values = fn ? [returned] : arguments
                                    defer[tuple[0] + "With"](context, values)
                                }
                            })
                        })
                        fns = null
                    }).promise()
                },

                promise : function (obj) {
                    return obj != null ? $.extend(obj, promise) : promise
                }
            },
            deferred = {}

        $.each(tuples, function (i, tuple) {
            var list = tuple[2],
                stateString = tuple[3]

            promise[tuple[1]] = list.add

            if (stateString) {
                list.add(function () {
                    state = stateString
                }, tuples[i ^ 1][2].disable, tuples[2][2].lock)
            }

            deferred[tuple[0]] = function () {
                deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments)
                return this
            }
            deferred[tuple[0] + "With"] = list.fireWith
        })

        promise.promise(deferred)
        if (func) {
            func.call(deferred, deferred)
        }
        return deferred
    }

    $.when = function (sub) {
        var resolveValues = slice.call(arguments),
            len = resolveValues.length,
            i = 0,
            remain = len !== 1 || (sub && $.isFunction(sub.promise)) ? len : 0,
            deferred = remain === 1 ? sub : Deferred(),
            progressValues, progressContexts, resolveContexts,
            updateFn = function (i, ctx, val) {
                return function (value) {
                    ctx[i] = this
                    val[i] = arguments.length > 1 ? slice.call(arguments) : value
                    if (val === progressValues) {
                        deferred.notifyWith(ctx, val)
                    }
                    else if (!(--remain)) {
                        deferred.resolveWith(ctx, val)
                    }
                }
            }

        if (len > 1) {
            progressValues = new Array(len)
            progressContexts = new Array(len)
            resolveContexts = new Array(len)
            for (; i < len; ++i) {
                if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) {
                    resolveValues[i].promise()
                        .done(updateFn(i, resolveContexts, resolveValues))
                        .fail(deferred.reject)
                        .progress(updateFn(i, progressContexts, progressValues))
                }
                else {
                    --remain
                }
            }
        }
        if (!remain) {
            deferred.resolveWith(resolveContexts, resolveValues)
        }
        return deferred.promise()
    }

    $.Deferred = Deferred
})(Zepto);




(function ($) {
    // Create a collection of callbacks to be fired in a sequence, with configurable behaviour
    // Option flags:
    //   - once: Callbacks fired at most one time.
    //   - memory: Remember the most recent context and arguments
    //   - stopOnFalse: Cease iterating over callback list
    //   - unique: Permit adding at most one instance of the same callback
    $.Callbacks = function (options) {
        options = $.extend({}, options)

        var memory, // Last fire value (for non-forgettable lists)
            fired,  // Flag to know if list was already fired
            firing, // Flag to know if list is currently firing
            firingStart, // First callback to fire (used internally by add and fireWith)
            firingLength, // End of the loop when firing
            firingIndex, // Index of currently firing callback (modified by remove if needed)
            list = [], // Actual callback list
            stack = !options.once && [], // Stack of fire calls for repeatable lists
            fire = function (data) {
                memory = options.memory && data
                fired = true
                firingIndex = firingStart || 0
                firingStart = 0
                firingLength = list.length
                firing = true
                for (; list && firingIndex < firingLength; ++firingIndex) {
                    if (list[firingIndex].apply(data[0], data[1]) === false && options.stopOnFalse) {
                        memory = false
                        break
                    }
                }
                firing = false
                if (list) {
                    if (stack) {
                        stack.length && fire(stack.shift())
                    }
                    else if (memory) {
                        list.length = 0
                    }
                    else {
                        Callbacks.disable()
                    }
                }
            },

            Callbacks = {
                add : function () {
                    if (list) {
                        var start = list.length,
                            add = function (args) {
                                $.each(args, function (_, arg) {
                                    if (typeof arg === "function") {
                                        if (!options.unique || !Callbacks.has(arg)) {
                                            list.push(arg)
                                        }
                                    }
                                    else if (arg && arg.length && typeof arg !== 'string') {
                                        add(arg)
                                    }
                                })
                            }
                        add(arguments)
                        if (firing) {
                            firingLength = list.length
                        }
                        else if (memory) {
                            firingStart = start
                            fire(memory)
                        }
                    }
                    return this
                },
                remove : function () {
                    if (list) {
                        $.each(arguments, function (_, arg) {
                            var index
                            while ((index = $.inArray(arg, list, index)) > -1) {
                                list.splice(index, 1)
                                // Handle firing indexes
                                if (firing) {
                                    if (index <= firingLength) {
                                        --firingLength
                                    }
                                    if (index <= firingIndex) {
                                        --firingIndex
                                    }
                                }
                            }
                        })
                    }
                    return this
                },
                has : function (fn) {
                    return !!(list && (fn ? $.inArray(fn, list) > -1 : list.length))
                },
                empty : function () {
                    firingLength = list.length = 0
                    return this
                },
                disable : function () {
                    list = stack = memory = undefined
                    return this
                },
                disabled : function () {
                    return !list
                },
                lock : function () {
                    stack = undefined;
                    if (!memory) {
                        Callbacks.disable()
                    }
                    return this
                },
                locked : function () {
                    return !stack
                },
                fireWith : function (context, args) {
                    if (list && (!fired || stack)) {
                        args = args || []
                        args = [context, args.slice ? args.slice() : args]
                        if (firing) {
                            stack.push(args)
                        }
                        else {
                            fire(args)
                        }
                    }
                    return this
                },
                fire : function () {
                    return Callbacks.fireWith(this, arguments)
                },
                fired : function () {
                    return !!fired
                }
            }

        return Callbacks
    }
})(Zepto);




(function ($) {
    var zepto = $.zepto, oldQsa = zepto.qsa, oldMatches = zepto.matches

    function visible (elem) {
        elem = $(elem)
        return !!(elem.width() || elem.height()) && elem.css("display") !== "none"
    }

    // Implements a subset from:
    // http://api.jquery.com/category/selectors/jquery-selector-extensions/
    //
    // Each filter function receives the current index, all nodes in the
    // considered set, and a value if there were parentheses. The value
    // of `this` is the node currently being considered. The function returns the
    // resulting node(s), null, or undefined.
    //
    // Complex selectors are not supported:
    //   li:has(label:contains("foo")) + li:has(label:contains("bar"))
    //   ul.inner:first > li
    var filters = $.expr[':'] = {
        visible : function () {
            if (visible(this)) {
                return this
            }
        },
        hidden : function () {
            if (!visible(this)) {
                return this
            }
        },
        enabled : function () {
            if (!this.disabled) {
                return this
            }
        },
        disabled : function () {
            if (this.disabled) {
                return this
            }
        },
        selected : function () {
            if (this.selected) {
                return this
            }
        },
        checked : function () {
            if (this.checked) {
                return this
            }
        },
        parent : function () {
            return this.parentNode
        },
        first : function (idx) {
            if (idx === 0) {
                return this
            }
        },
        last : function (idx, nodes) {
            if (idx === nodes.length - 1) {
                return this
            }
        },
        eq : function (idx, _, value) {
            if (idx === value) {
                return this
            }
        },
        contains : function (idx, _, text) {
            if ($(this).text().indexOf(text) > -1) {
                return this
            }
        },
        has : function (idx, _, sel) {
            if (zepto.qsa(this, sel).length) {
                return this
            }
        }
    }

    var filterRe = new RegExp('(.*):(\\w+)(?:\\(([^)]+)\\))?$\\s*'),
        childRe = /^\s*>/,
        classTag = 'Zepto' + (+new Date())

    function process (sel, fn) {
        // quote the hash in `a[href^=#]` expression
        sel = sel.replace(/=#\]/g, '="#"]')
        var filter, arg, match = filterRe.exec(sel)
        if (match && match[2] in filters) {
            filter = filters[match[2]], arg = match[3]
            sel = match[1]
            if (arg) {
                var num = Number(arg)
                if (isNaN(num)) {
                    arg = arg.replace(/^["']|["']$/g, '')
                }
                else {
                    arg = num
                }
            }
        }
        return fn(sel, filter, arg)
    }

    zepto.qsa = function (node, selector) {
        return process(selector, function (sel, filter, arg) {
            try {
                var taggedParent
                if (!sel && filter) {
                    sel = '*'
                }
                else if (childRe.test(sel))
                // support "> *" child queries by tagging the parent node with a
                // unique class and prepending that classname onto the selector
                {
                    taggedParent = $(node).addClass(classTag), sel = '.' + classTag + ' ' + sel
                }

                var nodes = oldQsa(node, sel)
            }
            catch (e) {
                console.error('error performing selector: %o', selector)
                throw e
            }
            finally {
                if (taggedParent) {
                    taggedParent.removeClass(classTag)
                }
            }
            return !filter ? nodes :
                zepto.uniq($.map(nodes, function (n, i) {
                    return filter.call(n, i, nodes, arg)
                }))
        })
    }

    zepto.matches = function (node, selector) {
        return process(selector, function (sel, filter, arg) {
            return (!sel || oldMatches(node, sel)) &&
                   (!filter || filter.call(node, null, arg) === node)
        })
    }
})(Zepto) ;




(function ($) {
    var _zid = 1, undefined,
        slice = Array.prototype.slice,
        isFunction = $.isFunction,
        isString = function (obj) {
            return typeof obj == 'string'
        },
        handlers = {},
        specialEvents = {},
        focusinSupported = 'onfocusin' in window,
        focus = {focus : 'focusin', blur : 'focusout'},
        hover = {mouseenter : 'mouseover', mouseleave : 'mouseout'}

    specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'

    function zid (element) {
        return element._zid || (element._zid = _zid++)
    }

    function findHandlers (element, event, fn, selector) {
        event = parse(event)
        if (event.ns) {
            var matcher = matcherFor(event.ns)
        }
        return (handlers[zid(element)] || []).filter(function (handler) {
            return handler
                   && (!event.e || handler.e == event.e)
                   && (!event.ns || matcher.test(handler.ns))
                   && (!fn || zid(handler.fn) === zid(fn))
                   && (!selector || handler.sel == selector)
        })
    }

    function parse (event) {
        var parts = ('' + event).split('.')
        return {e : parts[0], ns : parts.slice(1).sort().join(' ')}
    }

    function matcherFor (ns) {
        return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
    }

    function eventCapture (handler, captureSetting) {
        return handler.del &&
               (!focusinSupported && (handler.e in focus)) || !!captureSetting
    }

    function realEvent (type) {
        return hover[type] || (focusinSupported && focus[type]) || type
    }

    function add (element, events, fn, data, selector, delegator, capture) {
        var id = zid(element), set = (handlers[id] || (handlers[id] = []))
        events.split(/\s/).forEach(function (event) {
            if (event == 'ready') {
                return $(document).ready(fn)
            }
            var handler = parse(event)
            handler.fn = fn
            handler.sel = selector
            // emulate mouseenter, mouseleave
            if (handler.e in hover) {
                fn = function (e) {
                    var related = e.relatedTarget
                    if (!related || (related !== this && !$.contains(this, related))) {
                        return handler.fn.apply(this, arguments)
                    }
                }
            }
            handler.del = delegator
            var callback = delegator || fn
            handler.proxy = function (e) {
                e = compatible(e)
                if (e.isImmediatePropagationStopped()) {
                    return
                }
                e.data = data
                var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))
                if (result === false) {
                    e.preventDefault(), e.stopPropagation()
                }
                return result
            }
            handler.i = set.length
            set.push(handler)
            if ('addEventListener' in element) {
                element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
            }
        })
    }

    function remove (element, events, fn, selector, capture) {
        var id = zid(element)
            ;
        (events || '').split(/\s/).forEach(function (event) {
            findHandlers(element, event, fn, selector).forEach(function (handler) {
                delete handlers[id][handler.i]
                if ('removeEventListener' in element) {
                    element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
                }
            })
        })
    }

    $.event = {add : add, remove : remove}

    $.proxy = function (fn, context) {
        if (isFunction(fn)) {
            var proxyFn = function () {
                return fn.apply(context, arguments)
            }
            proxyFn._zid = zid(fn)
            return proxyFn
        }
        else if (isString(context)) {
            return $.proxy(fn[context], fn)
        }
        else {
            throw new TypeError("expected function")
        }
    }

    $.fn.bind = function (event, data, callback) {
        return this.on(event, data, callback)
    }
    $.fn.unbind = function (event, callback) {
        return this.off(event, callback)
    }
    $.fn.one = function (event, selector, data, callback) {
        return this.on(event, selector, data, callback, 1)
    }

    var returnTrue = function () {
            return true
        },
        returnFalse = function () {
            return false
        },
        ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$)/,
        eventMethods = {
            preventDefault : 'isDefaultPrevented',
            stopImmediatePropagation : 'isImmediatePropagationStopped',
            stopPropagation : 'isPropagationStopped'
        }

    function compatible (event, source) {
        if (source || !event.isDefaultPrevented) {
            source || (source = event)

            $.each(eventMethods, function (name, predicate) {
                var sourceMethod = source[name]
                event[name] = function () {
                    this[predicate] = returnTrue
                    return sourceMethod && sourceMethod.apply(source, arguments)
                }
                event[predicate] = returnFalse
            });

            if (source.defaultPrevented !== undefined ? source.defaultPrevented :
                    'returnValue' in source ? source.returnValue === false :
                    source.getPreventDefault && source.getPreventDefault()) {
                event.isDefaultPrevented = returnTrue
            }
        }
        return event
    }

    function createProxy (event) {
        var key, proxy = {originalEvent : event}
        for (key in event)
            if (!ignoreProperties.test(key) && event[key] !== undefined) {
                proxy[key] = event[key]
            }

        return compatible(proxy, event)
    }

    $.fn.delegate = function (selector, event, callback) {
        return this.on(event, selector, callback)
    }
    $.fn.undelegate = function (selector, event, callback) {
        return this.off(event, selector, callback)
    }

    $.fn.live = function (event, callback) {
        $(document.body).delegate(this.selector, event, callback)
        return this
    }
    $.fn.die = function (event, callback) {
        $(document.body).undelegate(this.selector, event, callback)
        return this
    }

    $.fn.on = function (event, selector, data, callback, one) {
        var autoRemove, delegator, $this = this
        if (event && !isString(event)) {
            $.each(event, function (type, fn) {
                $this.on(type, selector, data, fn, one)
            })
            return $this
        }

        if (!isString(selector) && !isFunction(callback) && callback !== false) {
            callback = data, data = selector, selector = undefined
        }
        if (isFunction(data) || data === false) {
            callback = data, data = undefined
        }

        if (callback === false) {
            callback = returnFalse
        }

        return $this.each(function (_, element) {
            if (one) {
                autoRemove = function (e) {
                    remove(element, e.type, callback)
                    return callback.apply(this, arguments)
                }
            }

            if (selector) {
                delegator = function (e) {
                    var evt, match = $(e.target).closest(selector, element).get(0)
                    if (match && match !== element) {
                        evt = $.extend(createProxy(e), {currentTarget : match, liveFired : element})
                        return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))
                    }
                }
            }

            add(element, event, callback, data, selector, delegator || autoRemove)
        })
    }
    $.fn.off = function (event, selector, callback) {
        var $this = this
        if (event && !isString(event)) {
            $.each(event, function (type, fn) {
                $this.off(type, selector, fn)
            })
            return $this
        }

        if (!isString(selector) && !isFunction(callback) && callback !== false) {
            callback = selector, selector = undefined
        }

        if (callback === false) {
            callback = returnFalse
        }

        return $this.each(function () {
            remove(this, event, callback, selector)
        })
    }

    $.fn.trigger = function (event, args) {
        event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event)
        event._args = args
        return this.each(function () {
            // items in the collection might not be DOM elements
            if ('dispatchEvent' in this) {
                this.dispatchEvent(event)
            }
            else {
                $(this).triggerHandler(event, args)
            }
        })
    }

    // triggers event handlers on current element just as if an event occurred,
    // doesn't trigger an actual event, doesn't bubble
    $.fn.triggerHandler = function (event, args) {
        var e, result
        this.each(function (i, element) {
            e = createProxy(isString(event) ? $.Event(event) : event)
            e._args = args
            e.target = element
            $.each(findHandlers(element, event.type || event), function (i, handler) {
                result = handler.proxy(e)
                if (e.isImmediatePropagationStopped()) {
                    return false
                }
            })
        })
        return result
    }

        // shortcut methods for `.bind(event, fn)` for each event type
    ;
    ('focusin focusout load resize scroll unload click dblclick ' +
     'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave ' +
     'change select keydown keypress keyup error').split(' ').forEach(function (event) {
            $.fn[event] = function (callback) {
                return callback ?
                    this.bind(event, callback) :
                    this.trigger(event)
            }
        })

    ;
    ['focus', 'blur'].forEach(function (name) {
        $.fn[name] = function (callback) {
            if (callback) {
                this.bind(name, callback)
            }
            else {
                this.each(function () {
                    try {
                        this[name]()
                    }
                    catch (e) {
                    }
                })
            }
            return this
        }
    })

    $.Event = function (type, props) {
        if (!isString(type)) {
            props = type, type = props.type
        }
        var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
        if (props) {
            for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
        }
        event.initEvent(type, bubbles, true)
        return compatible(event)
    }

})(Zepto);


(function ($, undefined) {
    var prefix = '', eventPrefix, endEventName, endAnimationName,
        vendors = {Webkit : 'webkit', Moz : '', O : 'o'},
        document = window.document, testEl = document.createElement('div'),
        supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i,
        transform,
        transitionProperty, transitionDuration, transitionTiming, transitionDelay,
        animationName, animationDuration, animationTiming, animationDelay,
        cssReset = {}

    function dasherize (str) {
        return str.replace(/([a-z])([A-Z])/, '$1-$2').toLowerCase()
    }

    function normalizeEvent (name) {
        return eventPrefix ? eventPrefix + name : name.toLowerCase()
    }

    $.each(vendors, function (vendor, event) {
        if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
            prefix = '-' + vendor.toLowerCase() + '-'
            eventPrefix = event
            return false
        }
    })

    transform = prefix + 'transform'
    cssReset[transitionProperty = prefix + 'transition-property'] =
    cssReset[transitionDuration = prefix + 'transition-duration'] =
    cssReset[transitionDelay = prefix + 'transition-delay'] =
    cssReset[transitionTiming = prefix + 'transition-timing-function'] =
    cssReset[animationName = prefix + 'animation-name'] =
    cssReset[animationDuration = prefix + 'animation-duration'] =
    cssReset[animationDelay = prefix + 'animation-delay'] =
    cssReset[animationTiming = prefix + 'animation-timing-function'] = ''

    $.fx = {
        off : (eventPrefix === undefined && testEl.style.transitionProperty === undefined),
        speeds : {_default : 400, fast : 200, slow : 600},
        cssPrefix : prefix,
        transitionEnd : normalizeEvent('TransitionEnd'),
        animationEnd : normalizeEvent('AnimationEnd')
    }

    $.fn.animate = function (properties, duration, ease, callback, delay) {
        if ($.isFunction(duration)) {
            callback = duration, ease = undefined, duration = undefined
        }
        if ($.isFunction(ease)) {
            callback = ease, ease = undefined
        }
        if ($.isPlainObject(duration)) {
            ease = duration.easing, callback = duration.complete, delay = duration.delay, duration = duration.duration
        }
        if (duration) {
            duration = (typeof duration == 'number' ? duration :
                ($.fx.speeds[duration] || $.fx.speeds._default)) / 1000
        }
        if (delay) {
            delay = parseFloat(delay) / 1000
        }
        return this.anim(properties, duration, ease, callback, delay)
    }

    $.fn.anim = function (properties, duration, ease, callback, delay) {
        var key, cssValues = {}, cssProperties, transforms = '',
            that = this, wrappedCallback, endEvent = $.fx.transitionEnd,
            fired = false

        if (duration === undefined) {
            duration = $.fx.speeds._default / 1000
        }
        if (delay === undefined) {
            delay = 0
        }
        if ($.fx.off) {
            duration = 0
        }

        if (typeof properties == 'string') {
            // keyframe animation
            cssValues[animationName] = properties
            cssValues[animationDuration] = duration + 's'
            cssValues[animationDelay] = delay + 's'
            cssValues[animationTiming] = (ease || 'linear')
            endEvent = $.fx.animationEnd
        }
        else {
            cssProperties = []
            // CSS transitions
            for (key in properties)
                if (supportedTransforms.test(key)) {
                    transforms += key + '(' + properties[key] + ') '
                }
                else {
                    cssValues[key] = properties[key], cssProperties.push(dasherize(key))
                }

            if (transforms) {
                cssValues[transform] = transforms, cssProperties.push(transform)
            }
            if (duration > 0 && typeof properties === 'object') {
                cssValues[transitionProperty] = cssProperties.join(', ')
                cssValues[transitionDuration] = duration + 's'
                cssValues[transitionDelay] = delay + 's'
                cssValues[transitionTiming] = (ease || 'linear')
            }
        }

        wrappedCallback = function (event) {
            if (typeof event !== 'undefined') {
                if (event.target !== event.currentTarget) {
                    return
                } // makes sure the event didn't bubble from "below"
                $(event.target).unbind(endEvent, wrappedCallback)
            }
            else {
                $(this).unbind(endEvent, wrappedCallback)
            } // triggered by setTimeout

            fired = true
            $(this).css(cssReset)
            callback && callback.call(this)
        }
        if (duration > 0) {
            this.bind(endEvent, wrappedCallback)
            // transitionEnd is not always firing on older Android phones
            // so make sure it gets fired
            setTimeout(function () {
                if (fired) {
                    return
                }
                wrappedCallback.call(that)
            }, (duration * 1000) + 25)
        }

        // trigger page reflow so new elements can animate
        this.size() && this.get(0).clientLeft

        this.css(cssValues)

        if (duration <= 0) {
            setTimeout(function () {
                that.each(function () {
                    wrappedCallback.call(this)
                })
            }, 0)
        }

        return this
    }

    testEl = null
})(Zepto);




(function ($, undefined) {
    var document = window.document, docElem = document.documentElement,
        origShow = $.fn.show, origHide = $.fn.hide, origToggle = $.fn.toggle

    function anim (el, speed, opacity, scale, callback) {
        if (typeof speed == 'function' && !callback) {
            callback = speed, speed = undefined
        }
        var props = {opacity : opacity}
        if (scale) {
            props.scale = scale
            el.css($.fx.cssPrefix + 'transform-origin', '0 0')
        }
        return el.animate(props, speed, null, callback)
    }

    function hide (el, speed, scale, callback) {
        return anim(el, speed, 0, scale, function () {
            origHide.call($(this))
            callback && callback.call(this)
        })
    }

    $.fn.show = function (speed, callback) {
        origShow.call(this)
        if (speed === undefined) {
            speed = 0
        }
        else {
            this.css('opacity', 0)
        }
        return anim(this, speed, 1, '1,1', callback)
    }

    $.fn.hide = function (speed, callback) {
        if (speed === undefined) {
            return origHide.call(this)
        }
        else {
            return hide(this, speed, '0,0', callback)
        }
    }

    $.fn.toggle = function (speed, callback) {
        if (speed === undefined || typeof speed == 'boolean') {
            return origToggle.call(this, speed)
        }
        else {
            return this.each(function () {
                var el = $(this)
                el[el.css('display') == 'none' ? 'show' : 'hide'](speed, callback)
            })
        }
    }

    $.fn.fadeTo = function (speed, opacity, callback) {
        return anim(this, speed, opacity, null, callback)
    }

    $.fn.fadeIn = function (speed, callback) {
        var target = this.css('opacity')
        if (target > 0) {
            this.css('opacity', 0)
        }
        else {
            target = 1
        }
        return origShow.call(this).fadeTo(speed, target, callback)
    }

    $.fn.fadeOut = function (speed, callback) {
        return hide(this, speed, null, callback)
    }

    $.fn.fadeToggle = function (speed, callback) {
        return this.each(function () {
            var el = $(this)
            el[
                (el.css('opacity') == 0 || el.css('display') == 'none') ? 'fadeIn' : 'fadeOut'
                ](speed, callback)
        })
    }

    /* SlideDown */
    $.fn.slideDown = function (duration) {

        // get the element position to restore it then
        var position = this.css('position');

        // show element if it is hidden
        this.show();

        // place it so it displays as usually but hidden
        this.css({
            position : 'absolute',
            visibility : 'hidden'
        });

        // get naturally height, margin, padding
        var marginTop = this.css('margin-top');
        var marginBottom = this.css('margin-bottom');
        var paddingTop = this.css('padding-top');
        var paddingBottom = this.css('padding-bottom');
        var height = this.css('height');

        // set initial css for animation
        this.css({
            position : position,
            visibility : 'visible',
            overflow : 'hidden',
            height : 0,
            marginTop : 0,
            marginBottom : 0,
            paddingTop : 0,
            paddingBottom : 0
        });

        // animate to gotten height, margin and padding
        this.animate({
            height : height,
            marginTop : marginTop,
            marginBottom : marginBottom,
            paddingTop : paddingTop,
            paddingBottom : paddingBottom
        }, duration);

    };

    /* SlideUp */
    $.fn.slideUp = function (duration) {

        // active the function only if the element is visible
        if (this.height() > 0) {

            var target = this;

            // get the element position to restore it then
            var position = target.css('position');

            // get the element height, margin and padding to restore them then
            var height = target.css('height');
            var marginTop = target.css('margin-top');
            var marginBottom = target.css('margin-bottom');
            var paddingTop = target.css('padding-top');
            var paddingBottom = target.css('padding-bottom');

            // set initial css for animation
            this.css({
                visibility : 'visible',
                overflow : 'hidden',
                height : height,
                marginTop : marginTop,
                marginBottom : marginBottom,
                paddingTop : paddingTop,
                paddingBottom : paddingBottom
            });

            // animate element height, margin and padding to zero
            target.animate({
                    height : 0,
                    marginTop : 0,
                    marginBottom : 0,
                    paddingTop : 0,
                    paddingBottom : 0
                },
                {
                    // callback : restore the element position, height, margin and padding to original values
                    duration : duration,
                    queue : false,
                    complete : function () {
                        target.hide();
                        target.css({
                            visibility : 'visible',
                            overflow : 'hidden',
                            height : height,
                            marginTop : marginTop,
                            marginBottom : marginBottom,
                            paddingTop : paddingTop,
                            paddingBottom : paddingBottom
                        });
                    }
                });
        }
    };

    /* SlideToggle */
    $.fn.slideToggle = function (duration) {

        // if the element is hidden, slideDown !
        if (this.height() == 0) {
            this.slideDown(duration);
        }
        // if the element is visible, slideUp !
        else {
            this.slideUp(duration);
        }
    };


})(Zepto);


// Extensions to Zepto that it doesn't
(function ($) {

    $.fn.extend = function (obj) {
        $.extend($.fn, obj);
    };

    //    $.getScript = function(/* url, data, success, dataType */){
    //        return $.ajax(parseArguments.apply(null, arguments))
    //    }
    //
    //    $.getScript = function (src, func) {
    //        var script = document.createElement('script');
    //        script.async = "async";
    //        script.src = src;
    //        if (func) {
    //            script.onload = func;
    //        }
    //        document.getElementsByTagName("head")[0].appendChild(script);
    //    };

    // Evaluates a script in a global context
    // Workarounds based on findings by Jim Driscoll
    // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
    $.globalEval = function (code) {
        var script,
            indirect = eval;
        code = $.trim(code);

        if (code) {
            //// Solve for IE < 11
            //if (window.execScript) {
            //    return window["eval"].call(window, code);
            //}

            // If the code includes a valid, prologue position
            // strict mode pragma, execute code by injecting a
            // script tag into the document.
            if (code.indexOf("use strict") === 1) {
                script = document.createElement("script");
                script.text = code;
                document.head.appendChild(script).parentNode.removeChild(script);
            }
            else {
                // Otherwise, avoid the DOM node creation, insertion
                // and removal by using an indirect global eval
                indirect(code);
            }
        }
    };


    $.ready = function (callback) {
        if (document.addEventListener) {
            document.addEventListener("DOMContentLoaded", function (event) {
                callback(event);
            }, false);
        }
        else if (document.attachEvent) {
            document.attachEvent("onreadystatechange", function () {
                if (document.readyState === "complete") {
                    document.detachEvent("onreadystatechange", arguments.callee);
                    callback(event);
                }
            });
        }
    }

})(Zepto);

module.exports = Zepto;

},{"../utils/JSONSerializer":14}],11:[function(require,module,exports){
var BaseRegistry = require('./RegistryBase');
var constants = require('../config/Constants');
var _ = require('../libs/underscore-1.6.custom');

module.exports = _.extend(BaseRegistry, {
    registerComponent : function (name, componentImpl, dontReplace) {
        return this._register(constants.registry.kComponents, name, componentImpl, dontReplace);
    },

    getComponent : function (name) {
        return this._get(constants.registry.kComponents, name);
    }

});
module.exports = BaseRegistry;

},{"../config/Constants":2,"../libs/underscore-1.6.custom":9,"./RegistryBase":12}],12:[function(require,module,exports){
var Class = require('../utils/Class'),
    _ = require('../libs/underscore-1.6.custom'),
    utils = require('../utils/Utils'),
    trace = require('../utils/trace'),
    log = require('../config/Constants').log,
    pubsub = require('../events/EventPubSub');

module.exports = {
    _register : function (nameSpace, name, componentImpl, dontReplace) {
        var ns = this._createOrFindNameSpace(nameSpace);
        if (_.has(ns, name)) {
            if (dontReplace) {
                trace("registerComponent: Component '" + name + "' already exists. Not replacing", "Registry", log.INFO);
                return ns[name];
            }
            pubsub.publishException("Xil.registry", "Replacing component named: " + name, log.INFO);

            delete ns[name];
        }

        ns[name] = componentImpl;

        return componentImpl;
    },

    _get : function (nameSpace, name) {
        if (!this._registry[nameSpace]) {
            trace("Request for " + nameSpace + " : " + name + " does not exist", "Registry", log.INFO);
            return null;
        }
        else {
            if (!_.isUndefined(name)) {
                return this._registry[nameSpace][name] || null;
            }
            else {
                return null;
            }
        }
    },

    _getAll : function (nameSpace) {
        if (!this._registry[nameSpace]) {
            return null;
        }
        else {
            return this._registry[nameSpace];
        }
    },

    _getNames : function (nameSpace) {
        if (!this._registry[nameSpace]) {
            return [];
        }
        else {
            return _.keys(this._registry[nameSpace]);
        }
    },


    _remove : function (nameSpace, name) {
        if (this._registry[nameSpace]) {
            delete this._registry[nameSpace][name];
        }
    },

    _has : function (nameSpace, name) {
        if (this._registry[nameSpace]) {
            return this._registry[nameSpace][name] ? true : false;
        }
        else {
            return false;
        }
    },

    _createOrFindNameSpace : function (nameSpace) {
        if (!this._registry[nameSpace]) {
            this._registry[nameSpace] = {};
        }

        return this._registry[nameSpace];

    },

    _publishException : function (msg) {
        pubsub.publishException("Registry", msg, log.WARN);
    },

    _registry : {}
};
},{"../config/Constants":2,"../events/EventPubSub":6,"../libs/underscore-1.6.custom":9,"../utils/Class":13,"../utils/Utils":16,"../utils/trace":17}],13:[function(require,module,exports){
/* Simple JavaScript Inheritance
 * By John Resig http://ejohn.org/
 * Inspired by base2 and Prototype
 * MIT Licensed.
 *
 */

/* This is just a quick & dirty way to check if "function decompilation" works.
 * The RegExp.prototype.test method will take the argument and it will convert it to String, the 'Xil' reference inside the function is never evaluated.
 *
 * Why would you have to check this?
 *
 * Because the Function.prototype.toString method returns an implementation-dependent representation of a function,
 * and in some implementations, such older Safari versions, Mobile Opera, and some Blackberry browsers, they don't actually return anything useful.
 *
 */
var fnTest = /xil/.test(function () {
    return "xil";
}) ? /\b_super\b/ : /.*/;
var initializing = false;

// The base Class implementation (does nothing)
var Class = function () {};

// Create a new Class that inherits from this class
Class.extend = function extend (prop) {
    var _super = this.prototype;

    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    var prototype = new this();
    initializing = false;

    // Copy the properties over onto the new prototype
    for (var name in prop) {
        // Check if we're overwriting an existing function
        prototype[name] = typeof prop[name] == "function" &&
                          typeof _super[name] == "function" && fnTest.test(prop[name]) ?
            (function (name, fn) {
                return function () {
                    var tmp = this._super;

                    // Add a new ._super() method that is the same method
                    // but on the super-class
                    this._super = _super[name];

                    // The method only need to be bound temporarily, so we
                    // remove it when we're done executing
                    var ret = fn.apply(this, arguments);
                    this._super = tmp;

                    return ret;
                };
            })(name, prop[name]) : prop[name];  // jshint ignore:line
    }

    // The dummy class constructor
    function Class () {
        if (!initializing) {
            // All construction is actually done in the 'construct' method
            if (this.construct) {
                this.construct.apply(this, arguments);
            }
        }
    }

    // Populate our constructed prototype object
    Class.prototype = prototype;

    // Enforce the constructor to be what we expect
    Class.prototype.constructor = Class;

    // And make this class extend-able
    Class.extend = extend;

    return Class;
};

module.exports = Class;

},{}],14:[function(require,module,exports){
// class: JSONSerializer
// about:
// Convert a javascript object to a string for sending over the wire as a message
var _ = require('../libs/underscore-1.6.custom'),
    pubsub = require('../events/EventPubSub'),
    constants = require('../config/Constants');

module.exports = {

    // -------------------------------
    // Function: toString
    // Convert Object to String
    //
    // Parameters:
    //    obj - the object
    // -------------------------------
    toString : function (obj) {

        if (JSON) {
            return JSON.stringify(obj);
        }

        var t = typeof (obj);
        if (t != "object" || obj === null) {
            // simple data type
            if (t == "string") {
                obj = '"' + obj + '"';
            }
            return String(obj);
        }
        else {
            // recurse array or object
            var n, v, json = [], arr = (obj && obj.constructor == Array);

            for (n in obj) {
                v = obj[n];
                t = typeof(v);
                if (t == "string") {
                    v = '"' + v + '"';
                }
                else if (t == "object" && v !== null) {
                    v = this.toString(v);
                }
                else if (t == "function") {
                    continue;
                }
                json.push((arr ? "" : '"' + n + '":') + String(v));
            }

            return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
        }
    },

    // -------------------------------
    // Function: toJSON
    // Convert String to Object
    //  - nonstrict allows for input that does not conform strictly to the JSON standard
    //    i.e. single quotes or no quotes around names
    //
    // Parameters:
    //    jsonString - the JSON string
    //    nonStrict - boolean value to allow for non-strict parsing
    //    decodeStrings - boolean value to indicate to recursively iterate throught the object decoding strings that may have been encoded
    //
    // Returns
    //  JSON object if passed in string is valid
    //  empty object if passed in string is invalid
    //  the passed in string if it is not an string or empty
    // -------------------------------
    toJSON : function (jsonString, nonStrict, decodeStrings) {

        if (!_.isString(jsonString)) {
            return jsonString;
        }

        var json = {};

        // Make sure leading/trailing whitespace is removed (IE can't handle it)
        // For if there is  a null character at the end of a request, Mozilla will barf on it.
        // We'll just strip it.
        jsonString.trim();
        jsonString = jsonString.replace(/\0$/, "");

        try {
            // Attempt to parse using the native JSON parser first
            if (JSON && !nonStrict) {
                json = JSON.parse(jsonString);
            }
            else {
                json = ( new Function("return " + jsonString) )(); // jshint ignore:line
            }
        }
        catch (ex) {
            pubsub.publishException("Xil.utils.jsonSerializer", "Invalid JSON\n" + jsonString, constants.log.ERROR, ex);
        }

        if (decodeStrings) {
            return this.decodeJSONObj(json);
        }
        else {
            return json;
        }

    }

}
	
   

},{"../config/Constants":2,"../events/EventPubSub":6,"../libs/underscore-1.6.custom":9}],15:[function(require,module,exports){
// NB some code from diveintohtml5.org <http://diveintohtml5.org/>
// extend Xil.$.support functionality for input types and attributes

var $ = require('../libs/zepto-1.1.3.custom');
var _ = require('../libs/underscore-1.6.custom');
var utils = require('./utils');

$.extend($.support, (function () {
    var i, j;
    var input = document.createElement('input'),
        inputtypes = 'search number range color tel url email date month week time datetime datetime-local',
        inputattrs = 'autocomplete autofocus list placeholder max min multiple pattern required step';

    return {
        inputtypes : (function () {
            var types = inputtypes.split(' '),
                ret = {};
            for (i = 0, j = types.length; i < j; i++) {
                input.setAttribute('type', types[i]);
                ret[types[i]] = (input.type !== 'text');
            }
            return ret;
        })(),

        input : (function () {
            var attrs = inputattrs.split(' '),
                ret = {};

            for (i = 0, j = attrs.length; i < j; i++) {
                ret[attrs[i]] = !!(attrs[i] in input);
            }
            return ret;
        })()
    };
})());

utils.extend_$({
    isTextInput : function () {
        var texttypes = 'text password hidden search number color tel url email date month week time datetime datetime-local'.split(" "),
            $el = Xil.$(this);
        if ($el.is("textarea")) {
            return true;
        }
        else if ($el.is("input") && _.contains(texttypes, $el.attr("type"))) {
            return true;
        }
        return false;
    },
    isUserInputElement : function () {
        var $el = Xil.$(this);
        return ($el.is("input") || $el.is("select") || $el.is("textarea") );
    },

    //Helper Function for Caret positioning
    caret : function (begin, end) {
        var range;

        if (this.length === 0 || this.is(":hidden")) {
            return;
        }

        if (_.isNumber(begin)) {
            var _end = (_.isNumber(end)) ? end : begin;
            return this.each(function () {
                if (this.setSelectionRange) {
                    this.setSelectionRange(begin, _end);
                }
                else if (this.createTextRange) {
                    range = this.createTextRange();
                    range.collapse(true);
                    range.moveEnd('character', _end);
                    range.moveStart('character', begin);
                    range.select();
                }
            });
        }
        else {
            if (this[0].setSelectionRange) {
                begin = this[0].selectionStart;
                end = this[0].selectionEnd;
            }
            else if (document.selection && document.selection.createRange) {
                range = document.selection.createRange();
                begin = 0 - range.duplicate().moveStart('character', -100000);
                end = begin + range.text.length;
            }
            return {begin : begin, end : end};
        }
    }
});

//---------------------------------------
// class: ObjectExtensions
// about:
// Extend existing javascript primitives
//
//  !!NOTE : Do me a favor and dont extend the Array object
//           It adds enumerable properties to the array that get processed in 'for in' loops
//           Using Object.defineProperty wont work in all IE versions, so lets just stay away from extending the Array class.
//---------------------------------------

//------------------------------------------------
// Function:  Date.prototype.yyyymmdd
// add additional attributes to the Date
//------------------------------------------------
Date.prototype.yyyymmdd = function () {
    var yyyy = this.getFullYear().toString();
    var mm = (this.getMonth() + 1).toString(); // getMonth() is zero-based
    var dd = this.getDate().toString();
    return yyyy + (mm[1] ? mm : "0" + mm[0]) + (dd[1] ? dd : "0" + dd[0]); // padding
};

//------------------------------------------------
// Function: String.prototype.toNum
// String: converts a string to num
//
// Parameters:
//    bEmptyAsValid - boolean to treat null and empty as 0
//------------------------------------------------
String.prototype.toNum = function (/*treat null+empty as 0*/bEmptyAsValid) {
    if (this && this.length > 0) {
        // remove all crap
        var isNeg = this.charAt(0) === "-";
        var nVal = this.replace(/[^0-9.]/gi, "");
        nVal = parseFloat(nVal);
        if (isNaN(nVal)) {
            return null;
        }
        else {
            return (isNeg) ? 0 - nVal : nVal;
        }
    }

    return bEmptyAsValid ? 0 : null;
};


//------------------------------------------------
// Function: String.prototype.toDate
// String: try and convert a formatted string to a Date object
// Use the mask to figure out how the value is formatted
// Can return an invalid date if not formatted properly
//------------------------------------------------
String.prototype.toDate = function () {
    // MM/DD/YYYY or MM-DD-YYYY
    var parts = this.match(/(\d{2})[\/|\-](\d{2})[\/|\-](\d{4})/);
    if (parts) {
        return new Date(parts[3], parts[1] - 1, parts[2]);
    }
    // YYYY-MM-DD or YYYY/MM/DD
    parts = this.match(/(\d{4})[\/|\-](\d{2})[\/|\-](\d{4})/);
    if (parts) {
        return new Date(parts[1], parts[2] - 1, parts[3]);
    }
    // MM/YYYY or MM-YYYY
    parts = this.match(/(\d{2})[\/|\-](\d{4})/);
    if (parts) {
        return new Date(parts[2], parts[1] - 1, 1);
    }

    // MM/YY or MM-YY
    parts = this.match(/(\d{2})[\/|\-](\d{2})/);
    if (parts) {
        return new Date("20" + parts[2], parts[1] - 1, 1);
    }

    // YYYY
    if (this.match(/\d{4}/)) {
        return new Date(this, 0, 1);
    }

    return new Date(this); // Try and convert, may be invalid
};

//------------------------------------------------
// If trim is not supported
if (!String.prototype.trim) {
    // Function: String.prototype.trim
    // String: gets rid of leading and trailing spaces
    String.prototype.trim = function () {

        if (this && this.length > 0) {
            return this.replace(/^[\s]+|[\s]+$/g, "");
        }

        return "";
    };
}

//------------------------------------------------
// Function: String.prototype.removeQuotes
// String: remove surrounding quote marks
//------------------------------------------------
String.prototype.removeQuotes = function () {
    if (this.length === 0) {
        return;
    }
    return this.replace(/^["|'](.*?)["|']$/, "$1");
};




},{"../libs/underscore-1.6.custom":9,"../libs/zepto-1.1.3.custom":10,"./utils":18}],16:[function(require,module,exports){
/**
 * Core utility library
 */
var constants = require('../config/Constants'),
    _ = require('../libs/underscore-1.6.custom'),
    $ = require('../libs/zepto-1.1.3.custom'),
    options = require('../config/Options'),
    registry = require('../registry/Registry'),
    jsonSerializer = require('./JSONSerializer');


module.exports = {

    //------------------------------------------------------
    // Utility function to covert a string into an
    // existing class function
    // May return null if no function exists in the namespace
    //------------------------------------------------------
    stringToFunction : function (str, context) {
        var arr = str.split(".");

        var fn = (context || window);
        for (var i = 0, len = arr.length; i < len; i++) {
            fn = fn[arr[i]];
            if (typeof fn === 'undefined') {
                return null;
            }
        }
        return fn;
    },

    //-------------
    // Utility function to deep merge into target obj where source takes precedence
    // returns a new merged object
    // Dont merge Arrays or functions
    mergeObjects : function (target, source, dontAllowNewKeysInTarget) {
        var newObj = _.extend({}, target);
        var self = this;

        _.each(source, function (p, idx) {

            try {
                // Property in destination object set; update its value.
                if (_.isObject(p) && !_.isArray(p) && !_.isFunction(p)) {
                    newObj[idx] = self.mergeObjects(newObj[idx], p, dontAllowNewKeysInTarget);
                }
                else {
                    newObj[idx] = p;
                }
            }
            catch (e) { // Property in target object not set; create it and set its value.

                if (!dontAllowNewKeysInTarget) {
                    newObj[idx] = p;
                }
            }
        });


        return newObj;

    },

    /**
     * Manipulate the strings in a json object - using a deep nested decoding methodology
     *
     * Takes an object that could have string properties that have strings that need to be normalized in some manner, nested anywhere within the object.
     * Replaces those original strings with the value returned from your supplied manipulation function.
     *
     *
     * @param json the input object, which could get modified
     * @param manipulationFunc - function to perform against string values in the object takes
     * @return json - the input object, which might have been modified
     *
     */
    manipulateStringsInObj : function (json, manipulationFunc) {
        _.each(json, function (value, key, obj) {
            obj[key] = _decode(obj[key]);
        });

        function _decode (value) {
            if (_.isObject(value)) {
                _.each(value, function (_value, _key, _obj) {
                    _obj[_key] = _decode(_obj[_key]);
                });

                return value;

            }
            else if (_.isString(value)) {
                return manipulationFunc(value);
            }
            else {
                return value;
            }
        }

        return json;
    },


    /**
     * Decode the strings in a json object - using a deep nested decoding methodology
     *
     * Takes an object that could have string properties that have strings that may be encoded, nested anywhere within the object.
     * Replaces those encoded strings with the decoded ones.
     *
     * For example, you could pass in an object such as:
     * obj = {"x":5, "y":{"a":3, "b":"foo%22%22bar"}}
     * After the function returns, obj.y.b will contain the decoded value of foo""bar
     *
     * @param json the input object, which could get modified
     * @return json - the input object, which might have been modified
     *
     */
    decodeJSONObj : function (json) {
        this.manipulateStringsInObj(json, function (value) {
            return decodeURIComponent(value);
        });
    },

    // Check to see whether a DOM element is completely in view in the browser
    isElementCompletelyInView : function ($el) {
        var el = this.get$($el)[0];

        var rect = el.getBoundingClientRect();

        return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
        );
    },


    // Check to see whether a DOM element is at least partly visible in the browser
    isElementInView : function ($el) {
        var el = this.get$($el)[0];
        var rect = el.getBoundingClientRect();

        return (
        rect.top >= 0 &&
        rect.left >= 0
        );
    },

    // Check to see whether a DOM element is at least partly visible in the browser
    isElementTopView : function ($el) {
        var el = this.get$($el)[0];
        var rect = el.getBoundingClientRect();

        return rect.top >= 0;
    },

    htmlEscape : function (str) {
        return String(str)
            .replace(/&/g, '&amp;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;');
    },

    htmlUnescape : function (value) {
        return String(value)
            .replace(/&quot;/g, '"')
            .replace(/&#39;/g, "'")
            .replace(/&lt;/g, '<')
            .replace(/&gt;/g, '>')
            .replace(/&amp;/g, '&');
    },

    /**
     * get the element as a baseclass.$
     */
    get$ : function (container) {
        if ($.zepto.isZ(container)) {
            return container;
        }
        else if (typeof jQuery != 'undefined' && container instanceof jQuery) {
            return $(container[0])
        }
        else if (_.isString(container)) {
            return $("#" + container);
        }
        else {
            return $(container);
        }
    },

    extend_$ : function (extObj) {
        $.fn.extend(extObj);
        if (typeof jQuery != 'undefined') {
            jQuery.fn.extend(extObj);
        }
    },

    // -------------------------------
    // Function: evaluateSimpleOperand
    // Figure out what type of operands we're dealing with
    // This version does NOT support operands that are themselves expressions using action expression syntax
    // If it is a reference to a model value, get it
    // If it is an array, turn it into one,
    // Otherwise
    // See if we can convert it to a number, if so do it
    // otherwise treat is as a string
    //
    // Parameters:
    // operand - the argument to be evaluated to its primitive type
    // -------------------------------
    evaluateSimpleOperand : function (operand) {

        // Does the string represent an object
        var _isObject = function (inString) {
            return inString.match(/^\[(.*?)\]$/) || inString.match(/^\{(.*?)\}$/);
        };

        if (!operand) {
            return operand;
        }

        var v = operand; // default for object, boolean, string

        // Resolve any dynamic text/model refs and turn into an object if need be
        var dataResolver = registry.getComponent(constants.interfaces.kDataResolver);
        if (dataResolver) {
            v = dataResolver.resolveDynamicData(v);
        }
        if (_.isString(v) && _isObject(v)) {
            v = jsonSerializer.toJSON(v, true);
        }

        // turn true | false strings into booleans
        if (v == "true") {
            return true;
        }
        if (v == "false") {
            return false;
        }

        if (v && _.isString(v)) {
            try {
                v = decodeURIComponent(v); // unescape anything that may have been put in there
            }
            catch (ex) {
                // will fail if v has '%' but not a valid encoded string - leave it as is
            }
            // Turn the value into a number if applicable
            if (!isNaN(v.replace(/\,/g, ""))) {
                var num = parseFloat(v.replace(/\,/g, ""));
                return num;
            }
            else {
                // strip thr surrounding quotes, if found
                var inQuotes = v.match(/^'(.*)'$/) || v.match(/^"(.*)"$/);
                var str = inQuotes ? inQuotes[1] : v;
                return str;
            }
        }

        // if we got this far, it could be a number or an empty string
        return v;
    },

    /**
     * Replace a character that appears within parenthesis in a given string
     * @param str - the string to search and replace within
     * @param charToReplace - the character to replace
     * @param replacement - the replacement for the character
     * @return - the new string
     */
    replaceCharWithinParenthesis : function (str, charToReplace, replacement) {
        var newStr = "";
        var parenCount = 0;
        for (var i = 0; i < str.length; i++) {
            var currChar = str.charAt(i);
            if (currChar === "(") {
                parenCount += 1;
            }
            else if (currChar === ")") {
                parenCount = Math.max(parenCount - 1, 0);  // decrement, but not less than zero
            }
            if (currChar === charToReplace && parenCount > 0) {
                newStr += replacement;
            }
            else {
                newStr += currChar;
            }
        }
        return newStr;
    },

    // extract an array of arguments form comma separated values within parenthesis
    // arguments will be resolved to their native form and model references will be resolved
    //-----------------------------------------------------------------------------
    getArrayOfArgs : function (argsStr) {
        "use strict";
        if (!argsStr || argsStr.length === 0) {
            return [];
        }
        // ensure commas within quotes are treated as part of the string, not as arg separators
        argsStr = _replaceCommasWithinQuotes(argsStr);

        // remove spaces around commas, and leading & trailing spaces
        argsStr = argsStr.replace(/\s*,\s*/g, ",").replace(/^\s*/, "").replace(/\s*$/, "");
        if (argsStr.length === 0) {
            return null;
        }
        var args = argsStr.split(",");

        // evaluate each argument to get into its native form (array, string, number, or another expression)
        for (var i = 0; i < args.length; i++) {
            args[i] = args[i].replace(/_comma_/g, ",");  // restore commas that might have been replaced above
            args[i] = this.evaluateSimpleOperand(args[i]);
        }

        // replace commas within single or double quotes, so we can split arguments on remaining commas
        //---------------------------------------------------------------------------------------------
        function _replaceCommasWithinQuotes (str) {
            var newStr = "";
            var quoteCount = 0;
            var dblQuoteCount = 0;
            for (var i = 0; i < str.length; i++) {
                var currChar = str.charAt(i);
                if (currChar === "'" && dblQuoteCount === 0) {
                    quoteCount = 1 - quoteCount;
                }
                else if (currChar === '"' && quoteCount === 0) {
                    dblQuoteCount = 1 - dblQuoteCount;
                }
                if (currChar === "," && (quoteCount > 0 || dblQuoteCount > 0)) {
                    newStr += "_comma_";
                }
                else {
                    newStr += currChar;
                }
            }
            return newStr;
        }

        return args;
    }

};


},{"../config/Constants":2,"../config/Options":3,"../libs/underscore-1.6.custom":9,"../libs/zepto-1.1.3.custom":10,"../registry/Registry":11,"./JSONSerializer":14}],17:[function(require,module,exports){
var _ = require('../libs/underscore-1.6.custom'),
    options = require('../config/options');


module.exports = function () {
    /**
     * Write exceptions and information out to the console if specified
     */
    if (_.isObject(console) && options.logToConsole) {
        console.log.apply(console, arguments);
    }
};

},{"../config/options":5,"../libs/underscore-1.6.custom":9}],18:[function(require,module,exports){
arguments[4][16][0].apply(exports,arguments)
},{"../config/Constants":2,"../config/Options":3,"../libs/underscore-1.6.custom":9,"../libs/zepto-1.1.3.custom":10,"../registry/Registry":11,"./JSONSerializer":14,"dup":16}],19:[function(require,module,exports){
/** ===============================================================
 // API - The main interface to the Xinch validation module
 // ===============================================================
 **/
var Xil = require('../core/Xil'),
    binder = require('./bindings/binder'),
    options = require('./config/options'),
    registry = require('./registry/ValidationRegistry'),
    engine = require('./engine/ValidationEngine'),
    defaultStrategies = require('./strategies/DefaultStrategies')

// Load up our Zepto/jQuery extensions
require('./jqExtensions/jqFormatFn');
require('./jqExtensions/jqValidationFn');
require('./utils/date');

window.Xinch = Xil.utils.mergeObjects(Xil, {

    version : "1.0.1",
    options : options,

    /**
     * Get current version of functionality
     * @returns version number
     */
    getVersion : function () {
        return Xinch.version;
    },

    /**
     * Attach input formatters to the page (and validators if 'validateOnBlur' is set in the options)
     *
     * @param container - element id or Xinch.$ or jQuery object
     */
    attachFormatters : function (container) {
        var $container = Xil.utils.get$(container);
        if (!$container) {
            $container = Xil.$('body');
        }
        binder.attachFormatters($container, options.formatOptions);
        binder.attachValidators($container, options.validationOptions);
    },

    /**
     * Set/Override Xinch default options
     *
     *  validationOptions : {
     *      useValidator : true, // enable/disable the use the validation functionality
     *      tooltipPosition : 'top', // position of tooltip relative to input.  supported values: 'top', 'bottom', 'right'
     *      hideOnFocus : false, // hide the tooltip when input field gets focus
     *      showOnlyOne : false, // show only one error tooltip
     *      showMultipleErrorsPerInput : false, // if there is more than one error, show them all
     *      validateOnBlur : false, // Perform validation on blur of an element
     *      suppressErrors : false // Just validate and return the result, but don't show any errors messaging
     *   },
     *
     *   formatOptions : {
     *      useFormatter : true, // enable/disable autoformatting of input elements
     *      formatEvent : ["keyup", "blur", "paste"], // Android 2.x bug results in erroneous input when formatting after each key, we'll fix in the formatter code
     *      ignoreKeyCodes : [9, 16], // tab, shift
     *      formatOnLoad : false  // Future use to autoformat a the value if it is changed via data-binding
     *   }
     *
     * @param validationOpts -  object representing validation options
     * @param formatOpts     -  object representing formatting options
     */
    setOptions : function (validationOpts, formatOpts) {
        Xinch.setValidationOptions(validationOpts);
        Xinch.setFormatOptions(formatOpts);
    },
    setValidationOptions : function (opts) {
        opts = opts || {};
        var clientOpts = {
            validationOptions : opts
        };
        options = Xil.utils.mergeObjects(options, clientOpts);
    },
    setFormatOptions : function (opts) {
        opts = opts || {};
        var clientOpts = {
            formatOptions : opts
        };
        options = Xil.utils.mergeObjects(options, clientOpts);
    },

    /**
     * Override an existing or default validation/formatting strategy
     *
     * @param strategyName - strategy name
     * @param strategyObj - instance of Xinch.baseStrategy
     */
    registerInputStrategy : function (strategyName, strategyObj) {
        registry.registerStrategy(strategyName, strategyObj)
    },
    /**
     * Override an existing or default validation/formatting strategy
     *
     * @param strategyName - name of an existing strategy
     * @param overrideObj - contains the key(s) and the value(s) of the properties to override
     *  *  I.e.:
     *  { defaultMessage : "Hello there" }
     *  { validate : function () { .... } }
     *
     *  or a combo: {
         *        defaultMessage : "Foo Bar",
         *        format : function ($el, evt ) { .... },
         *        validate : function ($el) { .... },
         *  }
     */
    overrideInputStrategy : function (strategyName, overrideObj) {
        registry.overrideStrategy(strategyName, overrideObj)
    },


    /**
     * Validate a container and its descendant elements
     *
     * @param container - element id or Xinch.$ or jQuery object
     * @param options - OPTIONAL validation options {showOnlyOne , suppressErrors, showMultipleErrorsPerInput}
     * @returns {boolean} - true iff validation passes
     */
    validateSection : function (container, options) {
        return engine.validateAll(container, options)
    },


    /**
     * Validate an element
     *
     * @param element - element id or Xinch.$ or jQuery object
     * @param options - OPTIONAL, validation options {showOnlyOne , suppressErrors, showMultipleErrorsPerInput}
     * @returns {boolean} - true iff validation passes
     */
    validateElement : function (element, options) {
        return engine.validateField(element, options)
    },


    /**
     * Remove the error tooltips from a container and its descendant elements
     *
     * @param container - element id or Xinch.$ or jQuery object
     */
    removeErrorTips : function (container) {
        return engine.hideErrorTooltips(container)
    },


    /**
     * Does the container have any error tips showing
     * @param container - element id or Xinch.$ or jQuery object
     * @returns boolean
     */
    hasErrorToolTips : function (container) {
        return engine.hasErrorToolTips(container)
    },

    /**
     * Force show an error message on an element
     *
     * @param el - element id or Xinch.$ or jQuery object
     * @param msg - error message
     * @param options - validation options
     * @returns {*}
     */
    showErrorMsg : function (el, msg, options) {
        return engine.showErrorMsg(el, msg, options)
    },

    /**
     * Format the element based on the data-format attribute
     *
     * @param element - element id or Xinch.$ or jQuery object
     */
    formatElement : function (element) {
        if (!options.formatOptions.useFormatter) {
            return;
        }
        // check to see if we've already bound the formatter,
        // if not, do so.
        var $el = (Xil.utils.get$(element));
        var formatter = $el.data("formatter");
        if (!formatter) {
            formatter = $el.attr("data-format");
            $el.data("formatter", formatter);
        }

        //since the formatter expects an event, send a fake keydown. code 38 is 'up arrow' key
        $el.format(Xil.$.Event("keydown", {keyCode : 38}));
    }
});

module.exports = Xinch;

Xil.$.ready(function (evt) {
    Xinch.attachFormatters();
});

// Load up all the default strategies
Xil._.each(defaultStrategies, function (obj, name) {
    Xinch.registerInputStrategy(name, obj);
});









},{"../core/Xil":1,"./bindings/binder":20,"./config/options":23,"./engine/ValidationEngine":25,"./jqExtensions/jqFormatFn":26,"./jqExtensions/jqValidationFn":27,"./registry/ValidationRegistry":28,"./strategies/DefaultStrategies":30,"./utils/date":31}],20:[function(require,module,exports){
var globalOptions = require('../config/options');
var constants = require('../config/constants');
var _ = require('../../core/libs/underscore-1.6.custom');
var $ = require('../../core/Xil').$;
var utils = require('../../core/utils/Utils');
var jsonSerializer = require('../../core/utils/jsonSerializer');
var engine = require('../engine/ValidationEngine');

module.exports = {
    attachFormatters : function (container, options) {
        options = options || globalOptions.formatOptions || {};
        if (_.isBoolean(options.useFormatter) && !options.useFormatter) {
            return;
        }

        var $container = utils.get$(container);

        // Find all of the input fields that have a "data-format" attribute
        var fields = $($container).find("[data-format]");

        // Iterate through all of the fields, and setup listeners and strategies
        $.each(fields, function () {
            var $control = $(this),
                formatter = $control.attr("data-format"),
                opts = jsonSerializer.toJSON($control.attr("data-format-options"), true) || {},
                fme = opts.formatEvent ? opts.formatEvent : options.formatEvent,
                ignoreKeyCodes = opts.ignoreKeyCodes ? opts.ignoreKeyCodes : options.ignoreKeyCodes,
                formatEvts = _.isArray(fme) ? fme.join(" ") : fme;


            //attach the formatter object to the control, so on keyup we can just pull it from the control and use it
            $control.data("formatter", formatter);

            if ($control.isTextInput() && formatEvts) {
                $control.on(formatEvts, function (event) {
                    if (_.contains(ignoreKeyCodes, event.keyCode)) {
                        return;
                    }
                    var valBefore = $control.val();
                    $control.format(event);
                    if ($control.val() != valBefore) {
                        $control.trigger(constants.jqEvents.kAutoformatApplied);  // add event namespace corresponding to the event that triggered the formatOnLoad
                    }

                });
            }

            // Look for format on load options and attach it as a property for the data-binder to look at
            // if the control is not an input control and there is a data-format attached to it, set it up for formatOnLoad.
            var af = (_.isBoolean(opts.formatOnLoad)) ? opts.formatOnLoad : options.formatOnLoad;
            if (af || !$control.isUserInputElement()) {
                $control.prop("_Xinch.autoFormat", true);
                $control.format($.Event("keydown", {keyCode : 38}));
                $control.trigger(constants.jqEvents.kAutoformatApplied);
            }
        });
    },

    /**
     * Bind Input elements to validate on blur if specified
     * @param container
     * @param options - viewport options
     */
    attachValidators : function (container, options) {
        options = options || globalOptions.validationOptions || {};
        if (!options.useValidator) {
            return;
        }
        var $container = utils.get$(container);

        // iterate through all of the elements that have a "data-validate" attribute
        $($container).find("[data-validate]").each(function () {
            var $el = $(this);
            var opts = jsonSerializer.toJSON($el.attr("data-validate-options"), true) || {};
            opts = utils.mergeObjects(options, opts);
            if (opts.validateOnBlur === true) {
                engine.setValidateOnBlur($el, opts, true);
            }
        });
    }
};



},{"../../core/Xil":1,"../config/constants":22,"../config/options":23,"../engine/ValidationEngine":25}],21:[function(require,module,exports){
var BaseConstants = require('../../core/Xil').constants;
var utils = require('../../core/utils/Utils');

var constants =  utils.mergeObjects(BaseConstants, {
    events : {
        kValidationFailed : "validationFailed"
    },
    jqEvents : {
        kAutoformatApplied : "autoformat.Xinch"
    },
    registry : {
        kInputStrategies : "inputStrategies"
    }
});

module.exports = constants;
},{"../../core/Xil":1}],22:[function(require,module,exports){
arguments[4][21][0].apply(exports,arguments)
},{"../../core/Xil":1,"dup":21}],23:[function(require,module,exports){
var BaseOptions = require('../../core/Xil').options;
var utils = require('../../core/utils/Utils');

var options =  utils.mergeObjects(BaseOptions, {

    // validation module options --------------------------
    validationOptions : {
        useValidator : true, // enable/disable the use the validation functionality
        tooltipPosition : 'top', // position of tooltip relative to input.  supported values: 'top', 'bottom', 'right'
        hideOnFocus : false, // hide the tooltip when input field gets focus
        showOnlyOne : false, // show only one error tooltip
        showMultipleErrorsPerInput : false, // if there is more than one error, show them all
        validateOnBlur : false, // Perform validation on blur of an element
        suppressErrors : false // Just validate and return the result, but don't show any errors messaging
    },

    // input formatting module options --------------------------
    formatOptions : {
        useFormatter : true, // enable/disable autoformatting of input elements
        formatEvent : ["keyup", "blur", "paste"], // Android 2.x bug results in erroneous input when formatting after each key, we'll fix in the formatter code
        ignoreKeyCodes : [9, 16], // tab, shift
        formatOnLoad : false  // Future use to autoformat a the value if it is changed via data-binding
    }
});

module.exports = options;
},{"../../core/Xil":1}],24:[function(require,module,exports){
/*
 * Common interface for executing validation or formatting requests from the HTML elements
 */
var registry = require('../registry/ValidationRegistry');
var Xil = require('../../core/Xil');
var utils = require('../../core/utils/Utils');

module.exports = {

    validate : function ($el, name, params) {
        return _execute(_VALIDATE, $el, null, name, params);
    },

    format : function ($el, event, name, params) {
        return _execute(_FORMAT, $el, event, name, params);
    }


};

var _VALIDATE = "validate",
    _FORMAT = "format";

// -------------------------------
// Function: executeStrategy
// evaluate the validation or formatting strategy
//
// Parameters:
//   action - the validation or format action to execute
//   $el - the input element
//   name - the strategy name
//   params - the parameters
// -------------------------------
function _execute (/* either validate or format */action, $el, event, /* strategy name */name, params) {
    var value = $el.val(),
        result = null,
        strategy = registry.getStrategy(name);

    if (!strategy) {
        // Check to see if the specified strategy exists
        Xil.publishException("Xinch.strategies", "Missing strategy: '" + name + "'", Xil.log.WARN);
        return;
    }

    if (!strategy[action]) {
        // Check to see if the specified action (either validate or format) exists for this strategy
        Xil.publishException("Xinch.strategies", "Missing action:" + action + " for strategy:" + name, Xil.log.WARN);
        return;
    }

    // Now turn the parameters into an array
    // and resolve any model references
    var args = utils.getArrayOfArgs(params);

    try {
        // if there is no value and the name of the strategy is not
        // in the array of validator name that that should validate
        // when empty (i.e. required is one of them) then just return and don't validate. Otherwise
        // if the strategy is in the list, validate as if it were not empty
        if (action === _VALIDATE && !$el.val() && !strategy.validateIfEmpty) {
            return "";
        }

        // add the $element as the first parameter
        args.splice(0, 0, $el);
        //add the event if it's a formatter
        if (action === _FORMAT) {
            args.splice(1, 0, event);
        }
        result = strategy[action].apply(strategy, args);
    }
    catch (ex) {
        Xil.publishException("Xinch.strategies", "Invalid " + action + " definition " + name + ": " + params, Xil.log.WARN, ex);
    }

    return result;
}



},{"../../core/Xil":1,"../registry/ValidationRegistry":28}],25:[function(require,module,exports){
/**
 * Main business logic for executing validation rules,
 * managing error indicators and error styles
 */

var _ = require('../../core/libs/underscore-1.6.custom');
var $ = require('../../core/Xil').$;
var utils = require('../../core/utils/Utils');
var jsonSerializer = require('../../core/utils/jsonSerializer');
var options = require('../config/options');

module.exports = {

    /****************************************************************************
     * Function: validateAll
     * Validate every field(that has a validate attribute in the parent container)
     * Parameters:
     *   container - the input element (jquery element OR element Id)
     *   options - validation options
     *        tooltipPosition : 'top', // position of tooltip relative to input.  supported values: 'top', 'bottom', 'right'
     *        hideOnFocus : false, // hide the tooltip when input field gets focus
     *        showOnlyOne : false, // show only one error tooltip
     *        showMultipleErrorsPerInput : false, // if there is more than one error, show them all
     *        validateOnBlur : false, // Perform validation on blur of an element
     *        suppressErrors : false, // Just validate and return the result, but don't show any errors messaging
     *        useValidator : true // use the validation functionality of Xinch
     *
     * ****************************************************************************/
    validateAll : function (container, opts) {
        var $container = utils.get$(container);
        opts = utils.mergeObjects(options.validationOptions, opts || {});
        var self = this;

        if (!opts.useValidator) {
            return true;
        }

        var isValid = true;

        var showOnlyOne = opts.showOnlyOne;

        this.hideErrorTooltips($container);
        $($container).find("[data-validate]").not(":hidden").not(":disabled").each(function (idx, $el) {
            $el = $($el);
            // Set the focus if we still are good on validation
            var setFocus = isValid;
            // only update set isValid flag to false if validateField returns false, otherwise
            // don't update isValid's value
            isValid = self.validateField($el, opts, setFocus) ? isValid : false;

            if (showOnlyOne && !isValid) {
                return false;
            }
        });

        return isValid;
    },

    /****************************************************************************
     * Function: validateField
     * Validate an individual input field
     *
     * Parameters:
     *   el - the input element (jquery element OR element Id
     *   options - validation options
     *        tooltipPosition : 'top', // position of tooltip relative to input.  supported values: 'top', 'bottom', 'right'
     *        hideOnFocus : false, // hide the tooltip when input field gets focus
     *        showOnlyOne : false, // show only one error tooltip
     *        showMultipleErrorsPerInput : false, // if there is more than one error, show them all
     *        validateOnBlur : false, // Perform validation on blur of an element
     *        suppressErrors : false, // Just validate and return the result, but don't show any errors messaging
     *        useValidator : true // use the validation functionality of Xinch
     *
     ****************************************************************************/
    validateField : function (el, opts, setFocus) {
        var $el = utils.get$(el);
        opts = utils.mergeObjects(options.validationOptions, opts || {});

        var elOpts = $el.attr('data-validate-options');
        elOpts = jsonSerializer.toJSON(elOpts, true) || {};
        opts = utils.mergeObjects(opts, elOpts);

        if (!opts.useValidator) {
            return true;
        }

        this.setValidateOnBlur($el, opts, true);

        var suppressErrors = (opts.suppressErrors === true);

        // We added the 'validate' method to Xinch.$ elements in the validationBinder
        // will return an array of error messages
        var errors = $el.validate();

        if (errors.length > 0) {
            // hack for radio buttons, only show the error on the first one....
            if ($el.prop("type") == "radio") {
                var name = $el.prop("name");
                var first = $("body").find("input[name='" + name + "'][type=radio]:first");
                if ($el[0] != first[0]) {
                    return false;
                }
            }
            // there's an error, see if we need to display it
            if (!suppressErrors) {
                _showErrorTooltip($el, errors, opts);
                if (setFocus) {
                    _setFocus($el);
                }
            }

            return false;
        }
        else {
            // if there's an error tooltip, hide it
            _removeErrorTooltip($el);

            return true;
        }
    },

    hasErrorToolTips : function (container) {
        var $container;
        if (container) {
            $container = utils.get$(container);
        }
        else {
            $container = $("body");
        }

        return $container.find("[hasErrorTip]").length > 0;
    },

    /****************************************************************************
     * Function: hideErrorTooltips
     * hide all error tooltips
     * Parameters:
     *     container - the input element (jquery element OR element Id
     *                 If not container Id is supplied we'll remove all
     ****************************************************************************/
    hideErrorTooltips : function (container) {
        var $container;
        if (container) {
            $container = utils.get$(container);
        }
        else {
            $container = $("body");
        }


        $container.find("[hasErrorTip]").each(function (idx, $el) {
            _removeErrorTooltip($($el));
        });
        if ($container.attr("hasErrorTip")) {
            _removeErrorTooltip($container);
        }

    },

    showErrorMsg : function (el, msg, opts) {
        var $el = utils.get$(el);
        opts = utils.mergeObjects(options, opts || {});


        _showErrorTooltip($el, [msg], opts);
    },

    setValidateOnBlur : function ($el, opts) {
        // TODO - if bValidateOnBlur is false, remove the event handler
        var self = this;
        var handlerSet = $el.prop("validationBlurSet") === true;
        opts = utils.mergeObjects(options, opts || {});

        // Attach blur event so we'll re-validate when the user tabs off
        // but only do it once!!
        if (!handlerSet) {
            $el.prop("validationBlurSet", true);
            //if (bValidateOnBlur) {
            $el.on("blur", function () {
                //var revalidationOpts = options || {};
                //// Only show one popup at a time, but allow corrected fields to dismiss the validation bubble
                //if (revalidationOpts.showOnlyOne) {
                //    //                       revalidationOpts.suppressErrors = true;
                //}
                self.validateField(Mojo.$(this), opts);
            });
            //}
        }

    }
}

/****************************************************************************
 * Group: Private
 * Private APIs
 ****************************************************************************/

// -------------------------------
// Function: _removeErrorTooltip
// hide the error tooltip(s)
//
// Parameters:
//    $element - the input element
// -------------------------------
function _removeErrorTooltip ($element) {
    $element.removeClass('XinchValidationError');

    var tooltip = $element.data('errorTooltip');
    if (tooltip) {
        tooltip.fadeOut(100, function () {
            tooltip.remove();
        });
    }
    $element.removeData('errorTooltip');
    $element.removeAttr('hasErrorTip');
}


//-------------------------------
// set focus on an element, this will be a convenience to the user
// and scroll the input field into view if it is not already
//-------------------------------
function _setFocus ($el) {
    if (!utils.isElementInView($el)) {
        ////check if the offset - 70 is negative and use 0 if it is.
        //var elOffset = ($el.offset().top - 70 > 0) ? $el.offset().top - 70 : 0;
        ////scroll
        //Xinch.$('html,body').animate({scrollTop : elOffset}, 'fast');

        // Get the offset top of the element.
        var elOffset = $el.offset().top;

        // Scroll to the element.  Scroll location tries to be place element in middle of screen.
        $('html,body').animate({scrollTop : elOffset - Xinch.$(window).height() / 2}, 'fast');

    }

    // and focus it
    $el.focus().select();
}

// -------------------------------
// Function: _showErrorTooltip
// show the error tooltip(s)
//
// Parameters:
//    $element - the input element
//    error = the string error message
// -------------------------------
function _showErrorTooltip ($element, errors, options) {
    var errorMsg = "";

    if (options.showMultipleErrorsPerInput) {
        _.each(errors, function (error) {
            errorMsg += error + "<br/>";
        })
    }
    else {
        errorMsg = errors[0];
    }

    var targetLeft = $element.offset().left;
    var targetTop = $element.offset().top;
    var targetRight = targetLeft + $element.width();
    var tooltip;
    var tooltipArrowClass;

    var tooltipPosition = options.tooltipPosition || "top";
    var hideOnFocus = options.hideOnFocus;

    $element.addClass('XinchValidationError');

    switch (tooltipPosition) {
        case 'right':
            tooltipArrowClass = "errorTooltipArrowLeft";
            break;
        case 'bottom':
            tooltipArrowClass = "errorTooltipArrowUp";
            break;
        default:
            tooltipArrowClass = "errorTooltipArrowDown";
            break;
    }

    $element.attr("hasErrorTip", true);
    if ($element.data('errorTooltip') && $element.data('errorTooltip').is(':visible')) {
        tooltip = $element.data('errorTooltip');
        tooltip.html(errorMsg + "<div class='" + tooltipArrowClass + "' />");
    }
    else {
        tooltip = $("<div class='errorTooltip' style=''>" + errorMsg + "<div class='" + tooltipArrowClass +
                          "'></div></div>");
        tooltip.appendTo("body").hide().fadeIn(120);
        //            tooltip.appendTo($element.parent()).hide().fadeIn(120);
        $element.data('errorTooltip', tooltip);
    }

    // set the z-index
    //var z = $element.css("z-index");
    //tooltip.css('z-index', z);


    // always update the tooltip position, cuz sometimes the content of the tooltip gets bigger or smaller
    // so the tooltip container's size adjusts
    switch (tooltipPosition) {
        case 'right':
            tooltip.css("left", targetRight + 14);
            tooltip.css("top", targetTop + ($element.height() - tooltip.height()) / 2);
            break;
        case 'bottom':
            tooltip.css("left", targetLeft);
            tooltip.css("top", targetTop + $element.height() + 9);
            break;
        default:
            tooltip.css("left", targetLeft);
            tooltip.css("top", targetTop - tooltip.height() - 7);
            break;
    }


    // attach event listeners to the error tooltips, when the user clicks on them, the error tooltips should disappear
    tooltip.off("click").on("click", function () {
        _removeErrorTooltip($($element));
    });
    if (hideOnFocus && !$element.prop('clickHandlerSet')) {
        $element.prop('clickHandlerSet', true); // set so we don't double add the click handler
        $element.on("click", function () {
            _removeErrorTooltip($(this));
        });
    }
}







},{"../../core/Xil":1,"../config/options":23}],26:[function(require,module,exports){
/*
 * Formatter
 * Written by Greg Miller
 * Purpose: attach formatters to input fields so that data is formatted as the user types

 * This plugin is written for Xinch.$ 1.6+
 *
 * valid formatter values
 * 	mask : <masked char set using the '#' symbol for replaceable characters
 * 	date : <masked date value using Y=year, M=month, D=day,  ex. DD/MM/YYYY
 * 	number : <precision> or null if no formatting is requested
 */
var utils = require('../../core/utils/Utils');
var $ = require('../../core/Xil').$;
var executor = require('../engine/EventExecutor');

utils.extend_$({

    format : function (event) {
        var $control = $(this),
            formatter = $control.data("formatter"); // get the formatter object from the control

        if (!formatter) {
            return;
        }

        var vals = formatter.split("=");

        executor.format($control, event, vals[0], vals[1]);
    }
});


},{"../../core/Xil":1,"../engine/EventExecutor":24}],27:[function(require,module,exports){
var utils = require('../../core/utils/Utils');
var _ = require('../../core/libs/underscore-1.6.custom');
var Xil = require('../../core/Xil');
var constants = require('../config/constants');
var executor = require('../engine/EventExecutor');

utils.extend_$({

    validate : function () {
        var $el = this;

        // accepts a single validator string with commas already replaced with _comma_
        // returns an object with properties {type, args, message}
        function _parseSingleValidator(validatorStr) {
            var validator = {};
            validatorStr = utils.replaceCharWithinParenthesis(validatorStr, ":", "_colon_");
            var parts = validatorStr.split(':');

            // now put back the colons
            _.each(parts, function (itm, idx) {
                parts[idx] = itm.replace(/_colon_/g, ":");
            });

            // replace foo(something) with foo=something
            var expressionParts = constants.kFunctionRegex.exec(parts[0]);
            if (expressionParts) {
                validator.type = expressionParts[1];
                validator.args = expressionParts[3] ? expressionParts[3].replace(/_comma_/g, ",") : null;
            }
            validator.options = {};

            // get the options, which are after the colon (currently only :message(string) is used)
            for (var optionIndex = 1; optionIndex < parts.length; optionIndex += 1) {
                var optionParts = constants.kFunctionRegex.exec(parts[optionIndex]);
                var optionName = optionParts[1];
                validator.options[optionName] = optionParts[3] ? optionParts[3].replace(/_comma_/g, ",").replace(/_colon_/g, ":").removeQuotes() : null;
            }

            // Set the information up as a property
            var dv = $el.prop("validate") || {};
            dv[validator.type] = validator;
            $el.prop("validate", dv);

            return validator;
        }

        function _parseValidators(htmlAttr) {
            // we need to remove spaces around commas that are used to separate some parameters
            // but leave other spaces, which could occur within a custom message (as in regex validation)
            htmlAttr = htmlAttr.replace(/(^\s*)|(\s*$)/g, "");  // remove leading and trailing spaces
            htmlAttr = htmlAttr.replace(/(\s*,\s*)/g, ","); // remove spaces around commas
            // Encode stuff between parens (so we can split on commas)
            htmlAttr = utils.replaceCharWithinParenthesis(htmlAttr, ",", "_comma_");

            var validatorStrings = htmlAttr.split(',');
            var validators = [];
            _.each(validatorStrings, function (validatorStr, validatorIndex) {
                validators.push(_parseSingleValidator(validatorStr));
            });
            return validators;
        }

        // if this element doesn't have a validator attribute, or if the value of the validator attribute is empty, null or false,
        // or if the element is not an input, return right away
        if (!$el.attr("data-validate")) {
            return "";
        }

        var validatorStr = $el.attr("data-validate"),
            validationResult = null,
            errors = [],
            validators = _parseValidators(validatorStr);

        _.each(validators, function (validator) {
            validationResult = executor.validate($el, validator.type, validator.args);
            if (validationResult) {
                Xil.publishEvent(constants.events.kValidationFailed, $el.get(0));
                errors.push(validationResult);
            }
        });

        return errors;

    }
});

},{"../../core/Xil":1,"../config/constants":22,"../engine/EventExecutor":24}],28:[function(require,module,exports){

var BaseRegistry = require('../../core/Xil').registry;
var _ = require('../../core/libs/underscore-1.6.custom');
var $ = require('../../core/Xil').$;
var Xil = require('../../core/Xil');
var constants = require('../config/Constants');
var BaseStrategy = require('../strategies/BaseStrategy');

(function (base) {

    base.registerStrategy = function (name, strategyObj, dontReplace) {
        if (!_.isObject(strategyObj)) {
            throw new Xil.Exception("Xinch.strategies",
                    "Invalid strategy definition - must be an object or instance of Xinch.BaseStrategy : '" +
                    strategyObj + "'", Xil.log.ERROR);
        }
        if (!(strategyObj instanceof BaseStrategy)) {
            var s = BaseStrategy.extend(strategyObj);
            strategyObj = new s();
            s = null;
        }
        strategyObj._strategyName = name;
        return this._register(constants.registry.kInputStrategies, name, strategyObj, dontReplace);
    };

    base.overrideStrategy = function (name, overrideObj) {
        var s = this.getStrategy(name);
        if (s) {
            // if the strategy already exists, add/replace with the new properties
            $.extend(s, overrideObj);
            Xil.trace("Xinch.strategies: overriding " + name);

        }
        else {
            Xil.publishException("Xinch.strategies", "overrideStrategy: " + name + " does not exist - not overriding", Xil.log.WARN);
        }
    };

    base.getStrategy = function (name) {
        return this._get(constants.registry.kInputStrategies, name);
    };

    base.getStrategies = function () {
        return this._getAll(constants.registry.kInputStrategies);
    };

})(BaseRegistry);

module.exports = BaseRegistry;

},{"../../core/Xil":1,"../config/Constants":21,"../strategies/BaseStrategy":29}],29:[function(require,module,exports){
/*
 * class: BaseStrategy
 * Validation Strategy Interface
 */
var _ = require('../../core/libs/underscore-1.6.custom');
var $ = require('../../core/Xil').$;
var utils = require('../../core/utils/Utils');
var Class = require('../../core/Xil').Class;

module.exports = Class.extend({
    android : /Android|Kindle|Silk/.test(navigator.userAgent),
    ignoreKeyCodesOnMask : [8, 46, 127],
    exceptions : null, // (optional) - A list of valid exceptions for this strategy - if no exceptions are applicable, do not override.
    allowException : false,
    regex : null,
    defaultMessage : null, // defaultMessage needs to be null in the base class
    validateIfEmpty : false, // perform validation if the field is empty;
    getError : function ($el, otherMsg) {
        var dv = $el.prop("validate");
        // strategy name is injected during registration of the input strategy (validationRegistry)
        var msg = (dv && dv[this._strategyName] && dv[this._strategyName].options.message) ? dv[this._strategyName].options.message : this.defaultMessage || otherMsg;

        return utils.evaluateSimpleOperand(msg);
    },

    // -------------------------------
    // Function: construct
    // construct a new BaseStrategy
    //
    // variables:
    //   allowException - the boolean value to allow alternative formats
    //   maskChar - the default masking character
    // -------------------------------
    construct : function () {
        // Define our member variables;
        this.allowException = false;
        this.maskChar = "#";
        this._strategyName = "";
    },

    // -------------------------------
    // Function: validate
    // Base functionality tests against exceptions, then regex
    //
    // Parameters:
    //   value - the value to be validated
    // -------------------------------
    validate : function ($el) {
        var value = $el.val();

        // Test against exceptions
        // If exception passes, return no error
        if (this.allowException && this.exceptions && _.isObject(this.exceptions)) {
            if (this.containedInExceptions(value)) {
                return;
            }
        }

        // Test against regex
        if (this.regex) {
            var regexp = new RegExp(this.regex);
            if (!regexp.test(value)) {
                // doesn't match the regex
                return this.getError($el, "Invalid Entry");
            }
        }
    },

    // -------------------------------
    // Function: formatAgainstMask
    // format the element value using the elements mask
    //
    // Parameters:
    //   $el - the element's value will be formatted
    //   inVal - the mask format
    //   event - Xinch.$ event that triggered the formatting
    // -------------------------------
    formatAgainstMask : function ($el, inVal, event) {
        var startLength = inVal.length,
            endLength = 0,
            mask = this.mask,
            self = this,
            caretPos = $el.caret();

        if (_.contains(this.ignoreKeyCodesOnMask, event.keyCode)) {
            return;
        }

        // Test against exceptions
        // if the user input is partially contained in the exception array, exit right away
        if (this.allowException && this.exceptions && _.isObject(this.exceptions)) {
            if (this.containedInExceptions(inVal, true)) {
                return;
            }

        }

        //get the current position of the caret
        caretPos = caretPos ? caretPos.end : 0;

        inVal.replace(/\d/g, function () {
            var digit = arguments[0];
            mask = mask.replace(self.maskChar,digit);
            return arguments[0];
        });
        mask = mask.split(this.maskChar)[0];
        endLength = mask.length;

        this.setFormattedValue($el, mask, event, (startLength == endLength) ? caretPos : caretPos + (endLength - startLength));

    },

    setFormattedValue : function ($el, formattedVal, event, caretPos) {
        if ($el.isTextInput()) {
            $el.val(formattedVal);
        }
        else if (!$el.isUserInputElement()) {
            $el.text(formattedVal);
        }

        //Set the caret back to it's position after we lost it by using .val()
        //but only if caretPos has a value.
        // skip this for "blur" events, in order to leave the focus on the new field that received it.
        if (_.isNumber(caretPos) && event && event.type !== "blur") {
            // Android 2.x bug results in erroneous input when formatting after each key
            if (this.android) {
                setTimeout($.proxy($.fn.caret, $el, caretPos), 0);
            }
            else {
                $el.caret(caretPos);
            }
        }
    },

    // -------------------------------
    // Function: containedInExceptions
    // if the input string is partially contained in the exception array
    //
    // Parameters:
    //   s - the input string
    //   allowPartial - allow a substring of the exception
    // -------------------------------
    containedInExceptions : function (s, allowPartial) {
        var valid = false;
        if (this.exceptions) {
            if (s) {
                s = s.toLowerCase();
                s = s.replace(/ /g, "");
            }
            for (var i = 0; i < this.exceptions.length; i++) {
                var itm = this.exceptions[i].toLowerCase();
                itm = itm.replace(/ /g, "");

                if (s === itm) {
                    valid = true;
                    break;
                }
                else if (allowPartial && itm.indexOf(s) === 0) {
                    valid = true;
                    break;
                }
            }
        }
        return valid;

    }

});


},{"../../core/Xil":1}],30:[function(require,module,exports){
/*
 * class: DefaultStrategies
 * Default validation strategies provided by Xil
 * 
 * about:
 * Clients can add new strategies or override existing ones by 
 * creating an object that has a 'validate' method and adding it to the mix
 * via the Xinch.addValidationStrategy(<name>, <strategyObject>) call;
 * 
 */
var registry = require('../registry/ValidationRegistry');
var dateUtil = require('../utils/date');
var _ = require('../../core/libs/underscore-1.6.custom');
var $ = require('../../core/Xil').$;
var Xil = require('../../core/Xil');

module.exports = {
    // -------------------------------
    // Function: required
    // field is required
    //
    // Parameters:
    //   $el - the dom input element
    // -------------------------------
    required : {
        validateIfEmpty : true,
        validate : function ($el) {
            var err = this.validateAgainstType($el);
            if (err) {
                return this.getError($el, err);
            }
        },

        validateAgainstType : function ($el) {
            switch ($el.prop("type")) {
                case "text":
                case "password":
                case "textarea":
                case "file":
                /* falls through */
                default:
                    if (!$el.val()) {
                        return "This field is required";
                    }
                    break;
                case "checkbox" :
                    if (!$el.prop("checked")) {
                        return "This checkbox is required";
                    }
                    break;
                case "radio":
                    var container = $('body');
                    var name = $el.attr("name");
                    if (container.find("input[name='" + name + "'][type=radio]:checked").size() === 0) {
                        return "Please select an option";
                    }
                    break;
                // required for <select>
                case "select-one":
                    // added by paul@kinetek.net for select boxes, Thank you
                    if (!$el.val()) {
                        return "This field is required";
                    }
                    break;
                case "select-multiple":
                    // added by paul@kinetek.net for select boxes, Thank you
                    if (!$el.find("option:selected").val()) {
                        return "This field is required";
                    }
                    break;

            }
        }
    },

    // -------------------------------
    // Function: requiredIf
    // field is required if the model reference has a value
    //
    // Parameters:
    //   $el - the dom input element
    //
    // Note : use some of the functionality of 'required' validator to get type specific messaging
    // -------------------------------
    requiredIf : {
        validateIfEmpty : true,
        validate : function ($el, modelVal) {
            var rq = registry.getStrategy("required");
            var err = rq.validateAgainstType($el);

            if ((null !== modelVal && typeof modelVal !== "undefined" && modelVal !== "") && err) {
                return this.getError($el, err);
            }

        }

    },

    // -------------------------------
    // Function: requiredIf
    // field is required if the Xil expression evaluates to true
    //
    // Parameters:
    //   $el - the dom input element
    //
    // Note : use some of the functionality of 'required' validator to get type specific messaging
    // -------------------------------
    requiredIfExpression : {
        validateIfEmpty : true,
        validate : function ($el, exp) {
            var rq = registry.getStrategy("required");
            var err = rq.validateAgainstType($el);

            if (exp === true && err) {
                return this.getError($el, err);
            }

        }
    },

    // -------------------------------
    // Function: groupRequired
    // Validate input text fields are mutually exclusive
    // Display error if value exists in more than one of the grouped input fields
    // Display error if all grouped input fields are empty
    //
    // Parameters:
    //   value - the dom element value
    //   groupId - the id attribute of the grouped input elements
    // -------------------------------
    groupRequired : {
        validateIfEmpty : true,
        validate : function ($el, groupId) {
            var value = $el.val();
            // grab all the elements that have both grouprequired AND the group id in the data-validate tag
            // I do it this way instead of Xinch.$("input[data-validate*='groupRequired(" + groupId + ")']");
            // because if the user put quotes around the groupId in the HTML, then we done get a match
            var $groupElems = $("input[data-validate*='groupRequired'][data-validate*='" + groupId + "']").filter(":visible");

            // filter out groupRequiredMultiple
            $groupElems = $groupElems.filter(function () {
                return !(this.id.match(/groupRequiredMultiple/));
            });

            var entered = 0;
            $.each($groupElems, function () {
                if (this.value.length !== 0) {
                    entered++;
                }
            });
            // check if groupRequired input fields are not empty
            if (entered > 1) {
                return this.getError($el, "Only one field can be entered");
            }
            // check if groupRequired input fields are empty
            else if (entered === 0) {
                return this.getError($el, "One of these fields is required");
            }
        }
    },

    // -------------------------------
    // groupRequiredOneOrMore:
    // Validate input text fields are mutually exclusive
    // DON"T Display error if value exists in more than one of the grouped input fields (unlike the standard groupRequired)
    // Display error if all grouped input fields are empty
    // -------------------------------
    groupRequiredMultiple : {
        defaultMessage : "One or more of these fields is required",
        validateIfEmpty : true,
        validate : function ($el, groupId) {
            var value = $el.val();
            var $groupElems = $("input[data-validate*='groupRequiredMultiple'][data-validate*='" + groupId + "']").filter(":visible");

            var entered = 0;
            $.each($groupElems, function () {
                if (this.value.length !== 0) {
                    entered++;
                }
            });
            // check if number of elements is greater than number of empty elements
            if (entered === 0) {
                return this.getError($el);
            }
        }
    },

    // -------------------------------
    // Function: maxLength
    // Maximum characters allowed
    //
    // Parameters:
    //   value - the dom element value
    //   length - the Maximum characters allowed
    // -------------------------------
    maxLength : {
        validate : function ($el, length) {
            var value = $el.val();
            length = parseInt(length, 10);
            if (isNaN(length)) {
                Xil.publishException("Xinch.defaultStrategies.maxLength", "maxLength validation set with non numeric arguement of: " + length);
            }
            this._max = length;
            if (value.length > length) {
                return this.getError($el, ("Maximum " + length + " characters allowed"));
            }
        },
        format : function ($el, event, length) {
            length = parseInt(length, 10);
            if (isNaN(length)) {
                Xil.publishException("Xinch.defaultStrategies.maxLength", "maxLength validation set with non numeric arguement of: " + length);
            }
            if (!isNaN(length) && _.isNumber(length)) {
                if (($el.is("input") || $el.is("textarea")) && $el.val().length >= length) {
                    $el.val($el.val().slice(0, length));
                }
                else if ($el.text().length >= length) {
                    $el.text($el.text().slice(0, length));
                }
            }
        }
    },

    // -------------------------------
    // Function: minLength
    // Minimum characters required
    //
    // Parameters:
    //   value - the dom element value
    //   length - the Minimum characters allowed
    // -------------------------------
    minLength : {
        validate : function ($el, length) {
            var value = $el.val();
            length = parseInt(length, 10);
            if (isNaN(length)) {
                //invalid arguement. throw exception
                Xil.publishException("Xinch.defaultStrategies.minLength", "minLength validation set with non numeric arguement of: " + length);
            }
            if (_.isNumber(length) && !isNaN(length) && value.length < length) {
                return this.getError($el, ("Minimum " + length + " characters required"));
            }
        }
    },

    // -------------------------------
    // Function: exactlength
    // Value must be X number of characters
    //
    // Parameters:
    //   value - the dom element value
    //   length - the Exact number of characters allowed
    // -------------------------------
    exactLength : {
        validate : function ($el, length) {
            var value = $el.val();
            length = parseInt(length, 10);
            if (isNaN(length)) {
                Xil.publishException("Xinch.defaultStrategies.exactLength", "exactLength validation set with non numeric arguement of: " + length);
            }
            if (_.isNumber(length) && !isNaN(length) && value.length != length) {
                return this.getError($el, ("Value must be " + length + " characters"));
            }

        }
    },

    // -------------------------------
    // Function: max
    // Maximum value is
    //
    // Parameters:
    //   value - the dom element value
    //   max - the Maximum value allowed
    // -------------------------------
    max : {
        validate : function ($el, max) {
            var value = $el.val().replace(/\,/g, "");
            max = parseFloat(max);
            value = parseFloat(value);
            if (isNaN(max) || (!_.isNumber(max))) {
                Xil.publishException("Xinch.defaultStrategies.max", "max validation set with non numeric arguement of: " + max);
            }
            if (isNaN(value) || !_.isNumber(value) || (parseFloat(value) > parseFloat(max))) {
                return this.getError($el, ("Maximum value is " + max));
            }
        }
    },

    // -------------------------------
    // Function: min
    // Minimum value is
    //
    // Parameters:
    //   value - the dom element value
    //   min - the Minimum value allowed
    // -------------------------------
    min : {
        validate : function ($el, min) {
            var value = $el.val().replace(/\,/g, "");
            min = parseFloat(min);
            value = parseFloat(value);
            if (isNaN(min)) {
                Xil.publishException("Xinch.defaultStrategies.min", "min validation set with non numeric arguement of: " + min);
            }
            if (isNaN(value) || !_.isNumber(value) || (parseFloat(value) < parseFloat(min))) {
                return this.getError($el, ("Minimum value is " + min));
            }
        }
    },

    // -------------------------------
    // Function: multipleOf(x)
    // value is a multiple of x
    //
    // Parameters:
    //   value - the dom element value
    //   multiple - Integer which value must be a multiple of
    // -------------------------------
    multipleOf : {
        validate : function ($el, multiple) {
            var value = parseFloat($el.val().replace(/\,/g, ""));
            multiple = parseFloat(multiple);
            if (!_.isNumber(multiple) || isNaN(multiple)) {
                Xil.publishException("Xinch.defaultStrategies.min", "min validation set with non numeric arguement of: " + multiple);
            }
            if (isNaN(value) || !_.isNumber(value) || (parseFloat(value) % parseFloat(multiple) !== 0)) {
                return this.getError($el, ("Must be a multiple of " + multiple));
            }
        }
    },

    // -------------------------------
    // Function: same
    // Field must be same as fieldName
    //
    // Parameters:
    //   value - the dom element value
    //   fieldId - the id of the field to match the value to
    //   fieldName - the name of the field to match the value to
    //   caseInsensitive - bool field. true = not case sensitive match
    //           default is falsy
    // -------------------------------
    same : {
        validate : function ($el, fieldId, fieldName, caseInsensitive) {
            var value = $el.val();
            var otherEl = $("#" + fieldId);
            var equalVal = otherEl.val() ? otherEl.val() : otherEl.text();
            if (caseInsensitive && (value.toLowerCase() !== equalVal.toLowerCase())) {
                return this.getError($el, ("Field must be same as " + fieldName));
            }
            else if (!caseInsensitive && (value !== equalVal)) {
                return this.getError($el, ("Field must be same as " + fieldName + " (Case Sensitive)"));
            }
        }
    },
    // -------------------------------
    // Function: sameTrimmed
    // Field must be same as fieldName after leading
    // and trailing white space is removed.
    //
    // Parameters:
    //   value - the dom element value
    //   fieldId - the id of the field to match the value to
    //   fieldName - the name of the field to match the value to
    //   caseInsensitive - bool field. true = not case sensitive match
    //           default is falsy
    // -------------------------------
    sameTrimmed : {
        validate : function ($el, fieldId, fieldName, caseInsensitive) {
            var value = $.trim($el.val());
            var otherEl = $("#" + fieldId);
            var equalVal = otherEl.val() ? otherEl.val() : otherEl.text();

            //trim whitespace
            equalVal = $.trim(equalVal);
            if (caseInsensitive && (value.toLowerCase() !== equalVal.toLowerCase())) {
                return this.getError($el, ("Field must be same as " + fieldName));
            }
            else if (!caseInsensitive && (value !== equalVal)) {
                return this.getError($el, ("Field must be same as " + fieldName + " (Case Sensitive)"));
            }
        }
    },

    // -------------------------------
    // Function: notSame
    // Field cannot be same as fieldName
    //
    // Parameters:
    //   value - the dom element value
    //   fieldId - the id of the field to validate that the value does NOT match
    //   fieldName - the name of the field to validate that the value does NOT match
    //   caseInsensitive - bool field. true = not case sensitive match
    //           default is falsy
    // -------------------------------
    notSame : {
        validate : function ($el, fieldId, fieldName, caseInsensitive) {
            //trim whitespace
            var value = $el.val();
            var otherEl = $("#" + fieldId);
            var equalVal = otherEl.val() ? otherEl.val() : otherEl.text();
            //            var equalVal = $("#" + fieldId).val();
            if (caseInsensitive && (value.toLowerCase() === equalVal.toLowerCase())) {
                return this.getError($el, ("Field cannot be same as " + fieldName));
            }
            else if (!caseInsensitive && (value === equalVal)) {
                return this.getError($el, ("Field cannot be same as " + fieldName + " (Case Sensitive)"));
            }
        }
    },
    // -------------------------------
    // Function: notSame
    // Field cannot be same as fieldName after leading and trailing white splace
    // is removed.
    //
    // Parameters:
    //   value - the dom element value
    //   fieldId - the id of the field to validate that the value does NOT match
    //   fieldName - the name of the field to validate that the value does NOT match
    //   caseInsensitive - bool field. true = not case sensitive match
    //           default is falsy
    // -------------------------------
    notSameTrimmed : {
        validate : function ($el, fieldId, fieldName, caseInsensitive) {
            //trim whitespace
            var value = $.trim($el.val());
            var otherEl = $("#" + fieldId);
            var equalVal = otherEl.val() ? otherEl.val() : otherEl.text();
            //            var equalVal = $("#" + fieldId).val();
            //trim whitespace
            equalVal = $.trim(equalVal);
            if (caseInsensitive && (value.toLowerCase() === equalVal.toLowerCase())) {
                return this.getError($el, ("Field cannot be same as " + fieldName));
            }
            else if (!caseInsensitive && (value === equalVal)) {
                return this.getError($el, ("Field cannot be same as " + fieldName + " (Case Sensitive)"));
            }
        }
    },

    // -------------------------------
    // Function: equalsVal
    // Value must be equal/
    //
    // Parameters:
    //   value - the dom element value
    //   equalVal - the value to validate against
    // -------------------------------
    equalsVal : {
        validate : function ($el, equalVal) {
            var value = $el.val();
            if (value !== (equalVal + "")) {
                return this.getError($el, ("Value must be " + equalVal));
            }
        }
    },

    // -------------------------------
    // Function: notEqualsVal
    // Value cannot be equal
    //
    // Parameters:
    //   value - the dom element value
    //   notEqualVal - the value to validate against
    // -------------------------------
    notEqualsVal : {
        validate : function ($el, notEqualVal) {
            var value = $el.val();
            if (value === (notEqualVal + "")) {
                return this.getError($el, ("Value cannot be " + notEqualVal));
            }
        }
    },

    // -------------------------------
    // Function: email
    // valid email format is enforced
    // -------------------------------
    email : {
        defaultMessage :  "Invalid email address",
        // Shamelessly lifted from Scott Gonzalez via the Bassistance Validation plugin http://projects.scottsplayground.com/email_address_validation/
        regex : /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i
    },

    // -------------------------------
    // Function: ssn
    // valid ssn format is enforced
    //
    // Parameters:
    //   value - the dom element value
    //   allowException - the boolean value to allow alternative formats
    // -------------------------------
    ssn : {
        exceptions : ["applied for", "died", "tax exempt", "LAFCP", "unknown"],
        defaultRegex : /^\d\d\d\-\d\d\-\d\d\d\d$/,
        altRegex : /^\d\d\-\d\d\d\d\d\d\d$/,
        defaultMask : "###-##-####",
        altMask : "##-#######",
        getError : function ($el, altMsg) {
            return this._super($el, (this.allowException ? "Invalid SSN" : "Invalid SSN, must be " + this.mask));
        },
        // -------------------------------
        // Function: validate
        // validate a ssn
        //
        // Parameters:
        //   value - the string to validate
        //   p1 - allowException flag
        //   p2 - validate range
        // -------------------------------
        validate : function ($el, allowException, validateRange) {
            var value = $el.val();
            this.allowException = allowException;
            this.regex = this.defaultRegex;
            this.mask = this.defaultMask;

            // EINs is also a valid exception (eins start with nn-
            if (allowException && value.match(/^\d\d\-/)) {
                this.regex = this.altRegex;
                this.mask = this.altMask;
            }
            var msg = this._super($el);

            // Validate range if we passed the pattern check
            if (!msg && validateRange && !allowException) {
                //              Regular SSN range 001-01-0001 to 699-99-9999
                //                       700-01-0001 to 733-99-9999	 2003 increased 729 to 733 1/14/2004 jre
                //                       750-01-0001 to 765-99-9999	 2000
                //                       750-01-0001 to 763-99-9999  2001 change
                //                       764-01-0001 to 899-99-9999  2001 change
                //
                //              ATIN range        900-93-0000 to 999-93-9999
                //              ATIN range        is not included, given that this may not be entered in SSN field
                //              ITIN range        900-70-0000 to 999-80-9999

                var valid = false;
                var ssn = value.replace(/[^0-9]/g, "");
                var first = ssn.substr(0, 3);
                var second = ssn.substr(3, 2);
                var third = ssn.substr(5, 4);

                if ((first < 900) && (first !== 0) && (second !== 0) && (third !== 0)) {
                    valid = true;
                }
                // ITIN validation / ATIN
                else if ((first > 899) && (second > 69 && second < 81)) {
                    valid = true;
                }

                if (!valid) {
                    msg = "Invalid Social security number";
                }

            }

            return msg;
        },
        format : function ($el, event, allowException) {
            var inVal = $el.val() || $el.text();

            if (inVal === "") {
                return;
            }

            this.allowException = allowException;
            this.mask = this.defaultMask;

            // EINs is also a valid exception (eins start with nn-
            if (allowException && inVal.match(/^\d\d\-/)) {
                this.mask = this.altMask;
            }
            this.formatAgainstMask($el, inVal, event);
        }
    },
    // -------------------------------
    // Function: regex
    // validate based on a regular expression
    //
    // Parameters:
    //   value - the dom element value
    //   re - the regular expression to validate against
    //   message - the error message to display if validation doesn't pass
    regex : {
        validate : function ($el, expression, message) {
            this.defaultMessage = message;
            this.regex = new RegExp(expression);
            return this._super($el);
        }
    },
    // -------------------------------
    // Function: date
    // valid date format is enforced
    //
    // Parameters:
    //   value - the dom element value
    //   mask - the format to validate against
    //   allowException - the boolean value to allow alternative formats
    // -------------------------------
    date : {
        exceptions : ["various", "inherit", "inherited", "continue", "continues", "none"],
        defaultMask : "MM/DD/YYYY",
        getError : function ($el) {
            return this._super($el, "Invalid '" + this.mask + "' date");
        },
        // -------------------------------
        // Function: validate
        // validate a date
        //
        // Parameters:
        //   value - the string to validate
        //   p1 - date mask or allowException flag
        //   p2 - date mask or allowException flag
        // -------------------------------
        validate : function ($el, p1, p2) {  // parameters [mask,allowException] can be passed in either order
            var value = $el.val();
            // figure out which order the parameters were passed in
            if (_.isBoolean(p1)) {
                this.allowException = p1;
                this.mask = p2 || this.defaultMask;
            }
            else if (_.isBoolean(p2)) {
                this.allowException = p2;
                this.mask = p1 || this.defaultMask;
            }
            else {
                this.mask = p1 || this.defaultMask;
                this.allowException = false;
            }

            var m, d, y, parts;
            switch (this.mask) {
                case "MM/YYYY" :
                    this.regex = /^(?:0?[1-9]|1[0-2])\/(?:19\d\d|20\d\d)$/;
                    parts = value.split('/');
                    m = parts[0];
                    y = parts[1];
                    break;
                case "MM/YY" :
                    this.regex = /^(?:0?[1-9]|1[0-2])\/(\d\d)$/;
                    parts = value.split('/');
                    m = parts[0];
                    y = parts[1];
                    break;
                case "YYYY" :
                    this.regex = /^(?:19\d\d|20\d\d)$/;
                    y = value;
                    break;
                case "YYYY/MM/DD" :
                    this.regex = /^(?:19\d\d|20\d\d)\/(0?[1-9]|1[0-2])\/(0?[1-9]|[12][0-9]|3[01])$/;
                    parts = value.split('/');
                    m = parts[1];
                    d = parts[2];
                    y = parts[0];
                    break;
                case "MM/DD" :
                    this.regex = /^(?:(?:0?[1-9]|1[0-2])(\/|-)(?:0?[1-9]|[12][0-9]|3[01]))$/;
                    parts = value.split('/');
                    m = parts[0];
                    d = parts[1];
                    break;
                case "" :
                case "MM/DD/YYYY" :
                /* falls through */
                default :
                    this.regex = /^(?:(?:0?[1-9]|1[0-2])(\/|-)(?:0?[1-9]|[12][0-9]|3[01]))(\/|-)(?:19\d\d|20\d\d)$/;
                    parts = value.split('/');
                    m = parts[0];
                    d = parts[1];
                    y = parts[2];
                    break;  // use the default mask/regex
            }

            function _isValidDateRange (m, d, y) {
                m = parseInt(m, 10);
                d = parseInt(d, 10);
                y = parseInt(y, 10);

                // only year is in the range 1900 - 2099
                if (y && !m && !d) {
                    return (y >= 1900 && y < 2100);
                }
                if (m && (m < 1) || (m > 12)) {
                    return false;
                }
                if (d && (d < 1) || (d > 31)) {
                    return false;
                }
                if (((m == 4) || (m == 6) || (m == 9) || (m == 11)) && (d > 30)) {
                    return false;
                }
                if (m == 2 && d > 29) {
                    return false;
                }

                // figure out leap years
                if (d && m == 2) {
                    // is leap year
                    if ((y % 4 === 0) && (y % 100 !== 0) || (y % 400 === 0)) {
                        return d <= 29;
                    }
                    else {
                        return d < 29;
                    }
                }
                return true;
            }


            // See if we have date within ranges
            var err = _isValidDateRange(m, d, y) ? null : "Invalid date range";

            // if it passed the range check, see if it passes the valid date check
            if (!err) {
                err = this._super($el);
            }

            if (err) {
                return this.getError($el, err);
            }

        },
        format : function ($el, event, mask, allowException) {
            var inVal = $el.val() || $el.text(),
                outVal;

            if (inVal === "") {
                return;
            }

            // set up mask
            this.allowException = allowException;
            mask = mask || this.defaultMask;
            this.mask = mask;
            this.mask = this.mask.replace(/[A-Za-z]/gi, "#");

            outVal = inVal;

            var caretCnt = 0;
            var caret = $el.caret();
            if (mask.indexOf("YYYY") > -1) { // 4 digit year, try to upgrade  anything not 19 or 20
                if (outVal.charAt(6) && outVal.charAt(7) && outVal.charAt(5) === '/' && !outVal.charAt(8)) {
                    var leastSigYrDigits = outVal.substr(6, 2);
                    if (leastSigYrDigits !== "19" && leastSigYrDigits !== "20") {
                        // try to guess the full year, based on the two year digits entered
                        var fullYear = parseInt('20' + leastSigYrDigits, 10);  // this line becomes obsolete before the year 2100
                        var yearDifference = fullYear - dateUtil.getDate().getFullYear();
                        // assume dates are more likely to be from last century than more than two years in the future
                        if (yearDifference > 2) {
                            fullYear -= 100;
                        }
                        outVal = outVal.slice(0, 5) + fullYear.toString();

                        // unlike at other times, when we don't want to format on '0' key press,
                        // in this instance we do. We want to format on any keypress
                        // because it's part of a two digit year that should be expanded to four digits.
                        // so, after this expansion, call formatAgainstDateMask regardless of the users keypress.
                        this.formatAgainstMask($el, outVal, event);

                    }
                }
            }

            var monthIdx = mask.indexOf("MM");
            if (monthIdx > -1) { // 2 Digit month. Expand one-digit entries followed by a slash
                // Check whether the user has entered a one-digit month number followed by a slash
                if (outVal.charAt(monthIdx) && outVal.charAt(monthIdx + 1) === '/') {

                    // Expand month to two digits
                    var fullMonth = "0" + outVal.charAt(monthIdx);
                    if (!_.contains(this.ignoreKeyCodesOnMask, event.keyCode)) {
                        caretCnt++;
                    }

                    // Update return value
                    outVal = outVal.substring(0, monthIdx) + fullMonth + outVal.substring(monthIdx + 1);

                }
            }

            var dayIdx = mask.indexOf("DD");
            if (dayIdx > -1) { // 2 Digit day. Expand one-digit entries followed by a slash
                // Check whether the user has entered a one-digit day number followed by a slash
                if (outVal.charAt(dayIdx) && outVal.charAt(dayIdx + 1) === '/') {

                    // Expand day to two digits
                    var fullDay = "0" + outVal.charAt(dayIdx);
                    if (!_.contains(this.ignoreKeyCodesOnMask, event.keyCode)) {
                        caretCnt++;
                    }

                    // Update return value
                    outVal = outVal.substring(0, dayIdx) + fullDay + outVal.substring(dayIdx + 1);
                }
            }

            this.formatAgainstMask($el, outVal, event);
            if (caretCnt > 0) {
                $el.caret(caret.end + caretCnt);
            }
        }

    },

    // -------------------------------
    // Function: before
    // Date entered must be before date
    //
    // Parameters:
    //   value - the dom element value
    //   date - the date to validate against
    //   inclusive - the boolean value to include the date as a valid date
    // -------------------------------
    before : {
        validate : function ($el, date, inclusive) {
            var value = $el.val();
            try {
                var inputDate = value.toDate();
                var targetDate;
                switch (date) {
                    case "today" :
                    case "now" :
                        targetDate = dateUtil.getDate(true);
                        break;
                    default :
                        targetDate = date.toDate();
                        break;
                }
                var valid = true;
                if (inclusive) {
                    valid = (inputDate.yyyymmdd() <= targetDate.yyyymmdd());
                }
                else {
                    valid = (inputDate.yyyymmdd() < targetDate.yyyymmdd());
                }
                return valid ? null : this.getError($el, "Date entered must be" + ((inclusive) ? " on or" : "") + " before " + date);

            }
            catch (ex) {
                Xil.publishException("Validation", "before", Xil.log.ERROR, ex);
            }

        }
    },

    // -------------------------------
    // Function: after
    // Date entered must be after date
    //
    // Parameters:
    //   value - the dom element value
    //   date - the date to validate against
    //   inclusive - the boolean value to include the date as a valid date
    // -------------------------------
    after : {
        validate : function ($el, date, inclusive) {
            var value = $el.val();

            try {
                var inputDate = value.toDate();
                var targetDate;
                switch (date) {
                    case "today" :
                    case "now" :
                        targetDate = dateUtil.getDate(true);
                        break;
                    default :
                        targetDate = date.toDate();
                        break;
                }
                var valid = true;
                if (inclusive) {
                    valid = (inputDate.yyyymmdd() >= targetDate.yyyymmdd());
                }
                else {
                    valid = (inputDate.yyyymmdd() > targetDate.yyyymmdd());
                }
                return valid ? null : this.getError($el, "Date entered must be" + ((inclusive) ? " on or" : "") + " after " + date);

            }
            catch (ex) {
                Xil.publishException("Validation", "after", Xil.log.ERROR, ex);
            }

        }
    },

    // -------------------------------
    // Function: phone
    // valid phone format is enforced
    //
    // Parameters:
    //   value - the dom element value
    //   mask - the format to validate against
    // -------------------------------
    phone : {
        regex : /^\(\d\d\d\) \d\d\d[\-]\d\d\d\d$/,
        defaultMask : "(###) ###-####",
        getError : function ($el) {
            return this._super($el, "Invalid phone number, must be " + this.mask);
        },
        validate : function ($el, mask) {
            var value = $el.val();
            this.mask = mask || this.defaultMask;

            var pattern = this.mask.replace(/#/g, "\\d").replace(/\(/g, "\\(").replace(/\)/g, "\\)").replace(/\./g, "\\.");

            pattern = "^" + pattern + "$";
            this.regex = new RegExp("" + pattern);

            var msg = this._super($el);

            // apparently new phone numbers can have a 0 or 1 in any position
            //if (!msg) {
            //    var ph = value.replace(/[^0-9]/g, "");
            //    if (ph.charAt(0) == '0' || ph.charAt(0) == '1') msg = "First digit cannot be 0 or 1";
            //    if (ph.charAt(3) == '0' || ph.charAt(3) == '1') msg = "Fourth digit cannot be 0 or 1";
            //}

            //if msg is not undefined, that means error has been set, no need to validate for all zeros
            if (!msg && /[1-9]/.test(value) === false) {
                return "Phone number can't be all zeros.";
            }

            return msg;
        },

        format : function ($el, event, mask) {
            var inVal = $el.val() || $el.text();

            if (inVal === "") {
                return;
            }

            this.mask = mask || this.defaultMask;

            //get all the non-number chars in the mask, so they're not replaced (i.e. entered by the user)
            //            var reg = new RegExp(this.mask.replace(/^#/gi, ""), 'gi');

            this.formatAgainstMask($el, inVal, event);
        }
    },

    // -------------------------------
    // Function: number
    // Number, including positive, negative, and floating decimal.
    // see numberOnly validator for validating integers
    // -------------------------------
    number : {
        defaultMessage : "Invalid number",
        regex : /^[\-\+]?(\d+(\,\d{3})*\.?\d{0,9}|\.\d{1,9})$/,

        validate : function ($el, precision) {
            var value = $el.val();

            precision = precision || "100";

            var pattern = "[\\-\\+]?(\\d+(,\\d{3})*\\.?\\d{0,PRE}|\\.\\d{1,PRE})";
            pattern = pattern.replace(/PRE/g, precision);

            pattern = "^" + pattern + "$";
            this.regex = new RegExp("" + pattern);

            return this._super($el);
        },
        format : function ($el, event, precision, addPrecisionOnBlur) {

            var inVal = $el.val() || $el.text();
            var startLength = inVal.length;
            var endLength = 0;
            if (inVal === "") {
                return;
            }


            // save off the cursor position
            var caretPos = $el.caret();
            caretPos = caretPos ? caretPos.end : 0;

            // get rid of all the crap
            var isNeg = inVal.charAt(0) === "-";
            inVal = inVal.replace(/[^0-9.]/gi, "");

            if (isNeg) {
                inVal = "-" + inVal;
            }

            // Add the commas in the appropriate spots
            var parts = inVal.split(".");
            parts[0] = parts[0].replace(/0*(\d+)/, "$1"); // remove leading zero's except first one
            parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");

            // Get rid of anything after and including a second period
            // and truncate up to the precision
            if (!_.isUndefined(parts[1])) {
                parts[1] = parts[1].substr(0, precision);
                parts = [parts[0], parts[1]];
            }

            if (addPrecisionOnBlur && event.type === "blur") {
                if (!parts[1]) {
                    parts[1] = "";
                }
                for (var i = parts[1].length; i < precision; i++) {
                    parts[1] += "0";
                }

            }

            inVal = parts.join(".");
            endLength = inVal.length;

            // Set the value and the caret position
            this.setFormattedValue($el, inVal, event, (startLength == endLength) ? caretPos : caretPos + (endLength - startLength));
        }

    },

    // -------------------------------
    // Function: numberOnly
    // Numbers 0-9 only
    // -------------------------------
    numberOnly : {
        defaultMessage : "Value is not a number",
        regex : /^[0-9]+$/,

        format : function ($el, event) {
            var inVal = $el.val() || $el.text();

            if (inVal === "") {
                return;
            }
            if (inVal.replace(/[0-9]/gi, "").length === 0) {
                return inVal;
            }
            this.setFormattedValue($el, inVal.replace(/[^0-9]/g, ''));

        }
    },

    // -------------------------------
    // Function: alpha
    // Letters only
    // -------------------------------
    alpha : {
        defaultMessage : "Value must be all letters",
        regex : /^[a-zA-Z]*$/,
        validate : function ($el, allowSpace) {
            this.regex = allowSpace ? /^[a-zA-Z\s]*$/ : this.regex;
            return this._super($el);
        },
        format : function ($el, event, allowSpace) {
            var inVal = $el.val() || $el.text();

            var reg = allowSpace ? /[^a-zA-Z\s]/g : /[^a-zA-Z\s]/g;

            this.setFormattedValue($el, inVal.replace(reg, ""));
        }
    },

    alphaNumeric : {
        defaultMessage : "Should be alphanumeric",
        regex : /^[a-zA-Z0-9]*$/,

        validate : function ($el, allowSpace) {
            this.regex = allowSpace ? /^[a-zA-Z0-9\s]*$/ : this.regex;
            return this._super($el);
        },
        format : function ($el, event, allowSpace) {
            var inVal = $el.val() || $el.text();

            var reg = allowSpace ? /[^a-zA-Z0-9\s]/g : /[^a-zA-Z0-9]/g;

            this.setFormattedValue($el, inVal.replace(reg, ""));

        }
    },

    upperCase : {
        defaultMessage : "Must be all upper case",

        validate : function ($el) {
            var val = $el.val();
            var test = val === val.toUpperCase();
            if (!test) {
                return this.getError($el);
            }
            else {
                return null;
            }

        },
        format : function ($el, evt) {
            var val = $el.val() || $el.text();
            this.setFormattedValue($el, val.toUpperCase());
        }
    },

    lowerCase : {
        defaultMessage : "Must be all lower case",
        validate : function ($el) {
            var val = $el.val();
            var test = (val === val.toLowerCase());
            if (!test) {
                return this.getError($el);
            }
            else {
                return null;
            }

        },
        format : function ($el, evt) {
            var val = $el.val() || $el.text();
            this.setFormattedValue($el, val.toLowerCase());
        }
    },

    maskedNumber : {
        regex : /^$/,
        defaultMask : "",
        getError : function ($el) {
            return this._super($el, "Invalid input, format must be " + this.mask);
        },
        validate : function ($el, mask) {
            var value = $el.val();

            this.mask = mask || this.defaultMask;

            // escape all non alpha so we can turn it into a regex
            var pattern = this.mask.replace(/([^a-zA-Z0-9#])/g, "\\$1");
            // now replace the "#" as a digit
            pattern = pattern.replace(/#/g, "\\d");

            pattern = "^" + pattern + "$";
            this.regex = new RegExp("" + pattern);

            return this._super($el);
        },
        format : function ($el, event, mask) {
            var inVal = $el.val() || $el.text();

            if (inVal === "") {
                return;
            }

            // set up mask
            this.mask = mask ? mask : this.defaultMask;
            //this.mask = this.mask.replace(/[A-Za-z]/gi, "#").replace(/&#41;/g, ")");

            this.formatAgainstMask($el, inVal /*.replace(/[^0-9]/gi, ""*/, event);
        }
    },

    zip : {
        regex : /^\d{5}(\-\d{4})?$/,
        fivedigitMask : "#####",
        defaultMask : "#####-####",
        validate : function ($el, mustBePlusFour) {
            var value = $el.val();
            this.mask = this.defaultMask; //always use default mask

            if (mustBePlusFour && value.length != 10) {
                return this.getError($el, "Invalid zip code, must be #####-####");
            }
            if (!(value.length === 5 ||
                  value.length === 10)) //hard coded lengths for zip if mask arg is ommited and default mask is used( can be 5 or 10)
            {
                return this.getError($el, "Invalid zip code, must be #####  or  #####-####");
            }
        },

        format : function ($el, event) {
            var inVal = $el.val();

            if (inVal === "") {
                return;
            }


            // set up mask
            this.mask = (inVal.length > 5) ? this.defaultMask : this.fivedigitMask; //always use default mask
            //this.mask = this.mask.replace(/[A-Za-z]/gi, "#");

            this.formatAgainstMask($el, inVal /*.replace(/[^0-9]|-/gi, "")*/, event);

        }
    },

    creditCard : {
        defaultMask : "################",
        validate : function ($el) {
            var value = $el.val().replace("-", "").replace(/\s/g, "");
            var ccFirstTwo = value.slice(0, 2);
            if (ccFirstTwo === "34" || ccFirstTwo === "37") { //Amex Cards are only 15 digits
                if (value.length !== 15) {
                    //return error if if it's not 15 digits
                    return this.getError($el, "Invalid Credit Card Number");
                }
                else {
                    // if it is an amex of valid length
                    //need to prepend a "0" so that the luhn check will pass. Adding
                    //a zero to the beginning of a valid number wont affect the validation
                    value = "0" + value;
                }
            }
            else {
                if (value.length !== 16) {  //It's not an Amex, Should be 16 digits
                    return this.getError($el, "Invalid Credit Card Number");
                }
            }

            //passed the length requirement, now check the luhn validation.

            //###### Luhn Number validation ######
            var digit = 0, checksum = 0;
            for (var len = (value.length - 1); len >= 0; len--) {
                digit = parseInt(value.charAt(len), 10);
                if (len % 2) {
                    checksum += digit;
                }
                else {
                    digit = digit * 2;
                    if (digit >= 10) {
                        checksum += Math.floor(digit / 10);
                        checksum += digit % 10;
                    }
                    else {
                        checksum += digit;
                    }
                }
            }
            if (checksum % 10 === 0) {
                // return undefined (aka valid) is implied
            }
            else {
                return this.getError($el, "Invalid Credit Card Number");
            }

        },
        format : function ($el, event) {
            //todo: accept user specified mask?
            //in addition to the normal formatting options,
            var inVal = $el.val();

            var ccFirstTwo = inVal.slice(0, 2);
            //Check if it's Amex, if so, we need a 15 char mask, not 16
            if (ccFirstTwo === "34" || ccFirstTwo === "37") {
                this.mask = "###############";
            }
            else {
                this.mask = this.defaultMask;
            }

            if (inVal === "") {
                return;
            } //skip masking if empty.

            this.formatAgainstMask($el, inVal /*.replace(/[^0-9]/gi, "")*/ /* <-- replace non-numerics */, event);
        }
    },

    /*
     RTN - Bank Routing Number
     */
    rtn : {
        defaultMessage :"Invalid Routing Transit Number",
        validate : function ($el) {
            var passedRTN = $el.val();
            var valid = false;

            if (passedRTN.match("[-\\d]+")) {
                var strippedRTN = passedRTN.replace(/-/, "");
                if (strippedRTN.length == 9) {
                    var firstTwo = strippedRTN.substring(0, 2).toNum();
                    if (!((firstTwo >= 1 && firstTwo <= 12) || (firstTwo >= 21 && firstTwo <= 32))) {
                        valid = false;
                        return this.getError($el, "Invalid Routing Transit Number");
                    }
                    var sumOfMultipliers = 0;
                    var multipliers = [3, 7, 1, 3, 7, 1, 3, 7];
                    for (var i = 0; i < multipliers.length; i++) {
                        sumOfMultipliers += multipliers[i] * strippedRTN.charAt(i).toNum();
                    }
                    var checkDigit = strippedRTN.charAt(8).toNum();
                    var checkNumber = (10 - (sumOfMultipliers % 10)) % 10;
                    if (checkDigit == checkNumber) {
                        valid = true;
                    }
                }
            }

            if (!valid) {
                return this.getError($el);
            }
        }
    }

};

},{"../../core/Xil":1,"../registry/ValidationRegistry":28,"../utils/date":31}],31:[function(require,module,exports){

var $ = require('../../core/Xil').$;

module.exports = {
    /**
     * Dont like the date being set off the server, set it yourself
     */
    setDate : function (date) {
        _officialTime = date;
    },

    getDate : function () {
        if (!_officialTime) {
            return (new Date());
        }
        return _officialTime;
    }
}

var _officialTime;     // milliseconds since 1970

// Set the date off the server automatically
(function () {

    // Get the official date off your server
    // Make a head request and grab the date header
    $.ajax({
        type : "HEAD",
        url : "",
        success : function (data, status, xhr) {
            _officialTime = new Date(xhr.getResponseHeader("Date"));
        },
        error : function (xhr, type, error) {
            _officialTime = new Date(xhr.getResponseHeader("Date"));
        }
    });


})();
},{"../../core/Xil":1}]},{},[19]);
