(function() {

    // IE is awesome
    $.ajaxSetup({
        cache: false
    });

    function currencify(num) {
        var parts = String(num).split('.'),
            arr = [],
            n = parts[0];

        if ( ! parts[1] ) {
            parts[1] = '00';
        }
        while(n.length > 3) {
            arr.unshift(n.substr(n.length-3));
            n = n.substr(0,n.length-3);
        }
        if(n.length > 0) {
            arr.unshift(n);
        }
        n = arr.join(',');
        if (parts[1] && parts[1].length === 1) {
            parts[1] += '0';
        }
        return n+(parts[1] ? '.'+parts[1] : '');
    }

    // Fix for IE event triggering on file input change
    Backbone.View.prototype.ie_upload = function(e) {
        var self = this;
        if ($.browser.msie) {
            setTimeout(function() {
                if ($(e.target).val() !== '') {
                    $(e.target).blur();
                }
            }, 0);
        }
    };

    Backbone.Collection.prototype.save = function(collection, options) {
        var self = this;
        Backbone.sync('update', collection, {
            url:  options.url,
            success: function(coll, resp) {
                self.trigger('saved', coll);
            }
        });
    };

    // Add to modelbinding plugin to make hole par values save to the model
    // automatically
    var ParConvention = {
        selector: 'input.hole',
        handler: {
            bind: function(selector, view, model) {
                view.$(selector).each(function(index){
                    var element = view.$(this),
                        hole = element.closest('td.onehole').index(),
                        tee = element.closest('table').attr('data-label');

                    if (element.closest('tr').hasClass('backnine')) {
                        hole += 9;
                    }

                    element.bind("blur", function(ev){
                        var data = {},
                            par = model.get('pars').find(function(par) {
                                return par.get('label') === tee;
                            });
                        data['hole_'+hole] = element.val();
                        par.set(data);
                    });
                });
            }
        }
    };
    Backbone.ModelBinding.Conventions.hole_pars = ParConvention;
    // Add to modelbinding plugin to make prize amounts save to the model
    // automatically
    var PrizeConvention = {
        selector: 'input.prize_amount',
        handler: {
            bind: function(selector, view, model) {
                view.$(selector).each(function(index){
                    view.$(this).bind("change", function(ev){
                        model.set({amount: view.$(ev.target).val()});
                    });
                });
            }
        }
    };
    Backbone.ModelBinding.Conventions.prize_amounts = PrizeConvention;

    var ProScoreConvention = {
        selector: 'input.pro_score',
        handler: {
            bind: function(selector, view, model) {
                view.$(selector).each(function(index){
                    view.$(this).bind("change", function(ev){
                        model.set({total_score: view.$(ev.target).val()});
                    });
                });
            }
        }
    };
    Backbone.ModelBinding.Conventions.proscore = ProScoreConvention;

    var TagsConvention = {
        selector: 'input#tags',
        handler: {
            bind: function(selector, view, model) {
                view.$(selector).each(function(index){
                    view.$(this).bind("change", function(ev){
                        model.set({tags: view.$(ev.target).val()});
                    });
                });
            }
        }
    };
    Backbone.ModelBinding.Conventions.tags_values = TagsConvention;

    /* PaginatedCollection
    * Handles pagination. Surprise. Also sorting and searching, but one page at
    * a time. So when a search or sort runs, the search will be done server side
    * and the collection will be re-fetched.
    *
    * Use it like this:
    *
    *   MyCollection = PaginatedCollection.extend({
    *       model: MyModel,
    *       baseUrl: "/api/mymodels",
    *       mainUrl: "mymodels",
    *       perPage: 15,
    *       sortkey: 'name'
    *   });
    */
    var PaginatedCollection = Backbone.Collection.extend({
        initialize: function(options) {
            _.bindAll(this, 'parse', 'url', 'pageInfo', 'nextPage', 'previousPage');
            if (typeof options === 'undefined') {
                options = {};
            }
            this.page = options.page || 1;
            if (typeof this.perPage === 'undefined') {
                this.perPage = 10;
            }
            this.searchterm = '';
            this.sortdir = this.sortdir || 'asc';
        },
        navigate: true,
        fetch: function(options) {
            if (typeof options === 'undefined') {
                options = {};
            }
            this.trigger("fetching");
            var self = this;
            var success = options.success;
            options.success = function(resp) {
                self.trigger("fetched");
                if(success) { success(self, resp); }
            };
            if (! options.data) {
                options.data = {};
            }
            options.data.term = this.searchterm;
            options.data.sort = this.sortkey;
            options.data.dir = this.sortdir;
            options.data.perpage = this.perPage;
            return Backbone.Collection.prototype.fetch.call(this, options);
        },
        parse: function(resp) {
            this.page = resp.page;
            this.perPage = resp.perPage;
            this.total = resp.total;
            if (resp.slots) {
                this.slots = resp.slots;
            }
            return resp.models;
        },
        url: function() {
            return this.baseUrl + '/' + this.page;
        },
        pageInfo: function() {
            var info = {
                total: this.total,
                page: this.page,
                perPage: this.perPage,
                pages: Math.ceil(this.total / this.perPage),
                prev: false,
                next: false
            };

            var max = Math.min(this.total, this.page * this.perPage);

            if (this.total === this.pages * this.perPage) {
                max = this.total;
            }

            info.range = [(this.page - 1) * this.perPage + 1, max];

            if (this.page > 1) {
                info.prev = this.page - 1;
            }

            if (this.page < info.pages) {
                info.next = this.page + 1;
            }

            return info;
        },
        nextPage: function() {
            if (!this.pageInfo().next) {
                return false;
            }
            this.page = Number(this.page) + 1;
            if (this.navigate) {
                var mainUrl = typeof this.mainUrl === 'function' ? this.mainUrl() : this.mainUrl;
                window.router.navigate(mainUrl+'/'+this.page);
            }
            return this.fetch();
        },
        previousPage: function() {
            if (!this.pageInfo().prev) {
                return false;
            }
            this.page = Number(this.page) - 1;
            if (this.navigate) {
                var mainUrl = typeof this.mainUrl === 'function' ? this.mainUrl() : this.mainUrl;
                window.router.navigate(mainUrl+'/'+this.page);
            }
            return this.fetch();
        }
    });


    /* MGCollectionView
    * A basic collection view for other collection views to extend.
    * Handles rendering, and models being added to the collection.
    *
    * Use it like this:
    *
    *   MyCollectionView = MGCollectionView.extend({
    *       el: $('#somediv'),
    *       model: MyModel,
    *       model_view: ViewForThisModel
    *   });
    */
    var MGCollectionView = Backbone.View.extend({
        initialize: function(options) {
            if (options.mbus) {
                this.mbus = options.mbus;
            }
            _.bindAll(this, 'showme', 'hideme', 'additem', 'render');
            // find the wrapper for this list (it may be the same as el)
            this.wrap = this.el.closest('.listwrap');
            this.render_to = this.el;
            if (this.wrap.length > 0) {
                this.el = this.wrap;
            }
            this.view_cache = {};
            this.collection.bind('add destroy', this.render);
        },
        events: {
            'click .add': 'additem'
        },
        showme: function() {
            this.el.show();
            this.render_to.show();
        },
        hideme: function() {
            this.el.hide();
            this.render_to.hide();
        },
        additem: function(e) {
            e.preventDefault();
            this.collection.add({});
        },
        render: function() {
            var self = this;
            this.render_to.html('');
            if (this.collection.length === 0) {
                this.render_to.html('<tr><td colspan="5" class="no_results">There are no results to display</td></tr>');
            } else {
                this.collection.each(function(model) {
                    var id = model.isNew() ? model.cid : model.get('id');
                    if ( ! this.view_cache[id] ) {
                        this.view_cache[id] = new this.model_view({model: model, mbus: this.mbus});
                    }
                    this.render_to.append(this.view_cache[id].render().el);
                }, this);
                if (this.render_to.is('table')) {
                    this.render_to.find('tr:odd').removeClass('odd even').addClass('odd');
                    this.render_to.find('tr:even').removeClass('odd even').addClass('even');
                } else if (this.render_to.is('ul')) {
                    this.render_to.find('li:odd').removeClass('odd even').addClass('odd');
                    this.render_to.find('li:even').removeClass('odd even').addClass('even');
                }
            }
            this.delegateEvents();
            return this;
        }
    });

    /* PaginatedView
    * A basic collection view for other collection views to extend.
    * Handles pagination control and management.
    *
    * Use it like this:
    *
    *   MyPagedView = PaginatedView.extend({
    *       template: '#template-element-selector',
    *       collection: new MyPagedCollection,
    *       model_view: MyModelView // usually renders a tr element
    *   });
    */
    var PaginatedView = MGCollectionView.extend({
        initialize: function(options) {
            if ( ! options ) {
                options = {};
            }
            if (options.model_view) {
                this.model_view = options.model_view;
            }
            MGCollectionView.prototype.initialize.call(this, options);
            _.bindAll(this, 'previous', 'next', 'render', 'reload', 'setsearch', 'setsort');
            if (!this.controls_template) {
                this.controls_template = (options && options.controls_template) || '#pagination-template';
            }
            this.collection.page = options.page || 1;
            this.collection.bind('fetched', this.render);
        },
        events: _.extend(_.clone(MGCollectionView.prototype.events), {
            'click a.prevpage': 'previous',
            'click a.nextpage': 'next',
            'click .search': 'reload',
            'keyup #list-search': 'setsearch',
            'click .sortable': 'setsort'
        }),
        render: function() {
            if (this.always_show_controls || this.collection.pageInfo().pages > 1) {
                this.wrap.find('.pagination-controls')
                    .html($.mustache($(this.controls_template).html(), this.collection.pageInfo()));
            } else {
                this.wrap.find('.pagination-controls').html('');
            }

            if ( this.collection.searchterm ) {
                this.$('#list-search-label').attr( 'style', 'display:none;' );
            }

            this.$('#list-search').val(this.collection.searchterm);
            MGCollectionView.prototype.render.call(this);
            this.delegateEvents();
        },

        previous: function() {
            this.collection.previousPage();
            return false;
        },

        next: function() {
            this.collection.nextPage();
            return false;
        },
        reload: function() {
            this.collection.fetch();
        },
        setsort: function(e) {
            var key = $(e.target).attr('data-key');
            if (this.collection.sortkey === key) {
                this.collection.sortdir = this.collection.sortdir === 'asc' ? 'desc' : 'asc';
            } else {
                this.collection.sortdir = 'asc';
            }
            this.collection.sortkey = key;
            this.reload();
        },
        setsearch: function(e) {
            var term = this.$('#list-search').val(),
                key = e.keyCode ? e.keyCode : e.charCode;
            this.collection.searchterm = term;
            if (key === 13) {
                this.reload();
            }
        }
    });


    /* MGModel
    * A basic model for all relational models to extend.
    * Handles model saving/resetting via the backbone memento plugin.
    * Includes a url method that will handle plural/singular urls as needed.
    *
    * Use it like this:
    *
    *   Widget = MGModel.extend({
    *       relations: [...],
    *       label: 'widget' // can be singular or plural
    *   });
    */
    var MGModel = Backbone.RelationalModel.extend({
        initialize: function(options) {
            Backbone.RelationalModel.prototype.initialize.call(this, options);
            _.bindAll(this, 'memento_init', 'changed', 'save');
            this.bind('change', this.changed);
            this.memento_init();
        },
        fetch: function(options) {
            var self = this;
            if (typeof options.success === 'function') {
                var success = options.success;
            }
            if (typeof options.error === 'function') {
                var error = options.error;
            }
            Backbone.RelationalModel.prototype.fetch.call(this, _.extend(options, {
                success: function(model, resp) {
                    model.memento_init();
                    if (success) {
                        success(self, resp);
                    }
                },
                error: function(model, resp) {
                    if (error) {
                        error(self, resp);
                    }
                }
            }));
        },
        memento_init: function() {
            if (this.memento) {
                delete this.memento;
            }
            this.memento = new Backbone.Memento(this, {
                ignore: ['page', 'url']
            });
            this.memento.store();
        },
        url: function() {
            return this.isNew() ?
                '/api/' + this.label.pluralize() :
                '/api/' + this.label.singularize() + '/' + this.get('id');
        },
        changed: function(e) {
            this.memento.store();
        },
        save: function(attrs, options, callbacks) {
            // send the current pagesize and search/sort settings along with
            // the model so that the server can calculate pagination
            this.set({listparams: (this.collection.perPage || 15)+';'+(this.collection.searchterm || '')+';'+(this.collection.sortkey || '')+';'+(this.collection.sortdir || '')});
            Backbone.RelationalModel.prototype.save.call(this, attrs, {
                success: function(model, response) {
                    model.memento_init();
                    if (typeof callbacks.success === 'function') {
                        callbacks.success(model, response);
                    }
                    model.trigger('saved', model);
                },
                error: function(model, response) {
                    if (typeof callbacks.error === 'function') {
                        callbacks.error(model, response);
                    }
                    model.trigger('savefailed');
                }
            });
        }
    });


    /* MGModelRowView
    *  This is a base view to use for single models as part of a list
    *  (collection view). Usually a table row, but doesn't have to be.
    *
    *  Use it like this:
    *
    *   MySingleRowView = MGModelRowView.extend({
    *       tagName: 'tr',
    *       template: $('#single-row-template')
    *   });
    */
    var MGModelRowView = Backbone.View.extend({
        initialize: function(options) {
            if (options.mbus) {
                this.mbus = options.mbus;
            }
            _.bindAll(this, 'render', 'manage');
            this.model.bind('change', this.render);
        },
        events: {
            'click .manage': 'manage'
        },
        render: function() {
            $(this.el).html($.mustache(this.template.html(), this.model.toJSON()));
            this.delegateEvents();
            return this;
        },
        manage: function(e) {
            e.preventDefault();
            this.model.memento.store();
            this.mbus.trigger('editing', this.model);
        }
    });

    /* MGModelView
    *  This is a base view to use for single models. Handles events for saving,
    *  renders models (on change).
    *  Includes postal/zip code location magic. Just include something like
    *  this in your template:
    *
    *   <input type="text" name="postal_code" id="postal_code" value="{{#location}}{{postal_code}}{{/location}}" />
    *   <span id="location_pl" class="error">{{#location}}{{full_name}}{{/location}}</span>
    *   <input type="hidden" name="location_id" id="location_id" value="{{location_id}}" />
    *
    *  Use it like this:
    *
    *   MySingleModelView = MGModelView.extend({
    *       tagName: 'tr',
    *       template: $('#single-row-template')
    *   });
    */
    var MGModelView = Backbone.View.extend({
        initialize: function(options) {
            if (options.mbus) {
                this.mbus = options.mbus;
            }
            _.bindAll(this, 'showme', 'hideme', 'update_location', 'filechanged', 'render', 'manage', 'saveme', 'cancel');
            if (typeof options.render_on_change === 'undefined' || options.render_on_change) {
                this.model.bind('change', this.render);
            }

            if (this.el) {
                this.wrap = $(this.el).closest('.editwrap');
                this.render_to = $(this.el);
                if (this.wrap) {
                    this.el = this.wrap.length > 0 ? this.wrap : $(this.el);
                }
            } else {
                this.render_to = $(this.el);
            }
        },
        render: function() {
            this.render_to.html($.mustache(this.template.html(), this.model.toJSON()));
            this.delegateEvents();
            this.$('#postal_code').postalLocater();
            Backbone.ModelBinding.bind(this);
            return this;
        },
        showme: function() {
            this.el.show();
            this.render_to.show();
        },
        hideme: function() {
            this.el.hide();
            this.render_to.hide();
        },
        events: {
            'click .manage': 'manage',
            'click .save': 'saveme',
            'click .cancel': 'cancel',
            'change #location_id': 'update_location',
            'change :file': 'filechanged'
        },
        update_location: function(e, locdata) {
            this.model.set({location: new Location(locdata), location_id: locdata.id});
        },
        filechanged: function(e) {
            var $changed = $(e.target),
                elem = $changed.attr('name'),
                data = {};

            if ($changed.hasClass('noautoset')) {
                return;
            }

            data[elem] = $changed.val().replace(/^C:(?:\/|\\)fakepath(?:\/|\\)/, '').replace(/ /g, '_');
            this.model.set(data, {silent: true});
        },
        manage: function(e) {
            e.preventDefault();
            this.model.memento.store();
            this.mbus.trigger('edit', this.model);
        },
        saveme: function(e) {
            e.preventDefault();
            e.stopPropagation();
            this.mbus.trigger('save', this.model);
        },
        cancel: function(e) {
            e.preventDefault();
            e.stopPropagation();
            this.model.memento.restart();
            this.mbus.trigger('canceling', this.model);
        }
    });

    /* MGFormView
    *  This is a base view to use for forms that edit a single model. Handles
    *  events for saving and canceling the edit process.
    *  Includes postal/zip code location magic. Just include something like
    *  this in your template:
    *
    *   <input type="text" name="postal_code" id="postal_code" value="{{#location}}{{postal_code}}{{/location}}" />
    *   <span id="location_pl" class="error">{{#location}}{{full_name}}{{/location}}</span>
    *   <input type="hidden" name="location_id" id="location_id" value="{{location_id}}" />
    *
    *  Use it like this:
    *
    *   MyNiceForm = MGFormView.extend({
    *       tagName: 'tr',
    *       template: $('#form-template')
    *   });
    */
    var MGFormView = Backbone.View.extend({
        initialize: function(options) {
            //Backbone.View.prototype.initialize.call(this, options);
            if (options.mbus) {
                this.mbus = options.mbus;
            }
            _.bindAll(this, 'showme', 'hideme', 'update_location', 'filechanged', 'render', 'saveme', 'resetme', 'backtolist', 'cancel');
            //if (typeof options.render_on_change === 'undefined' || options.render_on_change)
                //this.model.bind('change', this.render);

            /*if (this.el) {
                this.wrap = $(this.el).closest('.editwrap');
                this.render_to = $(this.el);
                if (this.wrap)
                this.el = this.wrap.length > 0 ? this.wrap : $(this.el);
            } else {
                this.render_to = $(this.el);
            }*/
        },
        render: function() {
            //this.render_to.html($.mustache(this.template.html(), this.model.toJSON()));
            $(this.el).html($.mustache(this.template.html(), this.model.toJSON()));
            this.delegateEvents();
            this.$('#postal_code').postalLocater();
            Backbone.ModelBinding.bind(this);
            return this;
        },
        showme: function() {
            this.el.show();
            //this.render_to.show();
        },
        hideme: function() {
            this.el.hide();
            //this.render_to.hide();
        },
        events: {
            'click .save': 'saveme',
            'click .cancel': 'cancel',
            'click .reset': 'resetme',
            'click .backtolist': 'backtolist',
            'change #location_id': 'update_location',
            'change :file': 'filechanged',
            'click :file': 'ie_upload'
        },
        update_location: function(e, locdata) {
            this.model.set({location: new Location(locdata), location_id: locdata.id});
        },
        filechanged: function(e) {
            var $changed = $(e.target),
                elem = $changed.attr('name'),
                data = {};

            if ($changed.hasClass('noautoset')) {
                return;
            }

            data[elem] = $changed.val().replace(/^C:(?:\/|\\)fakepath(?:\/|\\)/, '').replace(/ /g, '_');
            this.model.set(data, {silent: true});
        },
        saveme: function(e) {
            e.preventDefault();
            e.stopPropagation();
            this.mbus.trigger('saving', this.model);
        },
        resetme: function(e) {
            e.preventDefault();
            e.stopPropagation();
            this.model.memento.restart();
            this.mbus.trigger('resetting', this.model);
        },
        backtolist: function(e) {
            e.preventDefault();
            e.stopPropagation();
            this.mbus.trigger('backtolist', this.model);
        },
        cancel: function(e) {
            e.preventDefault();
            e.stopPropagation();
            this.model.memento.restart();
            this.mbus.trigger('canceling', this.model);
        }
    });


    /* Models and their relations
    *  Define all models and how they relate to each other.
    */

    /* Location
    *  Simple model that represents a location. Not an MGModel because it
    *  doesn't get modified on the client.
    */
    Location = Backbone.RelationalModel.extend({
        url: function() {
            return '/api/location/' + this.get('id');
        }
    });
    Par = MGModel.extend({
        label: 'par',
        defaults: {
            out: [0,0,0,0,0,0,0,0,0],
            "in": [0,0,0,0,0,0,0,0,0],
            hole_1: 0,
            hole_2: 0,
            hole_3: 0,
            hole_4: 0,
            hole_5: 0,
            hole_6: 0,
            hole_7: 0,
            hole_8: 0,
            hole_9: 0,
            hole_10: 0,
            hole_11: 0,
            hole_12: 0,
            hole_13: 0,
            hole_14: 0,
            hole_15: 0,
            hole_16: 0,
            hole_17: 0,
            hole_18: 0
        }
    });
    Course = MGModel.extend({
        label: 'course',
        relations: [
            {
                type: Backbone.HasMany,
                key: 'pars',
                relatedModel: 'Par',
                collectionType: 'ParCollection',
                reverseRelation: {
                    key: 'course'
                }
            }
        ],
        url: function() {
            return this.isNew() ?
                '/api/facility/' + this.get('facility_id') + '/courses' :
                '/api/facility/' + this.get('facility_id') + '/course/' + this.get('id');
        },
        defaults: {
            name: '',
            facility_id: function() { return this.facility.get('id'); },
            pars: [new Par({label: 'Men'}), new Par({label: 'Ladies'})]
        }
    });
    Facility = MGModel.extend({
        label: 'facility',
        relations: [
            /*{
                type: Backbone.HasMany,
                key: 'courses',
                relatedModel: 'Course',
                collectionType: 'CourseCollection',
                reverseRelation: {
                    key: 'facility'
                }
            },*/
            {
                type: Backbone.HasMany,
                key: 'members',
                relatedModel: 'Player',
                collectionType: 'PlayerCollection',
                includeInJSON: 'id'
            },
            {
                type: Backbone.HasOne,
                key: 'location',
                relatedModel: 'Location',
                reverseRelation: {
                    type: Backbone.HasMany,
                    key: 'facilities',
                    relatedModel: 'Facility',
                    collectionType: 'FacilityCollection',
                    includeInJSON: 'id'
                }
            }
        ]
    });

    Pro = MGModel.extend({
        label: 'pro'
    });
    Player = MGModel.extend({
        label: 'player',
        relations: [
            {
                type: Backbone.HasOne,
                key: 'location',
                relatedModel: 'Location'
            },
            {
                type: Backbone.HasOne,
                key: 'facility',
                relatedModel: 'Facility'
            }
        ]
    });
    FacilityAdmin = MGModel.extend({
        label: 'facility_admin',
        relations: [
            {
                type: Backbone.HasOne,
                key: 'location',
                relatedModel: 'Location'
            },
            {
                type: Backbone.HasOne,
                key: 'facility',
                relatedModel: 'Facility'
            }
        ]
    });
    RealityTeam = MGModel.extend({
        label: 'team',
        relations: [
            {
                type: Backbone.HasMany,
                key: 'pros',
                relatedModel: 'Pro',
                collectionType: 'ProCollection'
            },
            {
                type: Backbone.HasOne,
                key: 'round',
                relatedModel: 'Round',
                reverseRelation: {
                    type: Backbone.HasOne,
                    key: 'team'
                }
            },
            {
                type: Backbone.HasOne,
                key: 'player',
                relatedModel: 'Player'
            }
        ]
    });
    FantasyTeam = MGModel.extend({
        label: 'team',
        relations: [
            {
                type: Backbone.HasMany,
                key: 'pros',
                relatedModel: 'Pro',
                collectionType: 'ProCollection'
            },
            {
                type: Backbone.HasOne,
                key: 'player',
                relatedModel: 'Player'
            }
        ]
    });
    Round = MGModel.extend({
        label: 'round',
        relations: [
            {
                type: Backbone.HasOne,
                key: 'player',
                relatedModel: 'Player',
                reverseRelation: {
                    key: 'rounds',
                    collectionType: 'RoundCollection'
                }
            },
            {
                type: Backbone.HasOne,
                key: 'pro',
                relatedModel: 'Pro',
                reverseRelation: {
                    key: 'rounds',
                    collectionType: 'RoundCollection'
                }
            },
            {
                type: Backbone.HasOne,
                key: 'course',
                relatedModel: 'Course',
                reverseRelation: {
                    key: 'rounds',
                    collectionType: 'RoundCollection'
                }
            }
        ]
    });

    Prize = MGModel.extend({
        label: 'prize'
    });
    Tournament = MGModel.extend({
        label: 'tournament',
        defaults: {
            pro_rounds: []
        },
        relations: [
            {
                type: Backbone.HasMany,
                key: 'pros',
                relatedModel: 'Pro',
                collectionType: 'ProCollection',
                includeInJSON: 'id'
            },
            {
                type: Backbone.HasMany,
                key: 'fantasyteams',
                relatedModel: 'FantasyTeam',
                collectionType: 'FantasyTeamCollection',
                includeInJSON: 'id',
                reverseRelation: {
                    key: 'tournament',
                    includeInJSON: 'id'
                }
            },
            {
                type: Backbone.HasMany,
                key: 'realityteams',
                relatedModel: 'RealityTeam',
                collectionType: 'RealityTeamCollection',
                includeInJSON: 'id',
                reverseRelation: {
                    key: 'tournament',
                    includeInJSON: 'id'
                }
            },
            {
                type: Backbone.HasMany,
                key: 'prizes',
                relatedModel: 'Prize',
                collectionType: 'PrizeCollection',
                reverseRelation: {
                    key: 'tournament',
                    includeInJSON: 'id'
                }
            }
        ]
    });
    Ad = MGModel.extend({
        label: 'ad'
    });

    /* Define all the collections used for the models above.
    */
    FacilityCollection = PaginatedCollection.extend({
        model: Facility,
        baseUrl: "/api/facilities",
        mainUrl: "facilities",
        perPage: 15,
        sortkey: 'name'
    });
    FacilityPickerCollection = PaginatedCollection.extend({
        model: Facility,
        baseUrl: "/api/facilities_near",
        mainUrl: "facilities",
        perPage: 5,
        sortkey: 'name',
        navigate: false
    });
    CourseCollection = Backbone.Collection.extend({
        model: Course,
        perPage: 15,
        url: function() {
            return '/api/facility/' + this.facility_id + '/courses';
        },
        comparator: function(course) {
            return course.isNew() ? -2 : course.get('name');
        }
    });
    ParCollection = Backbone.Collection.extend({
        model: Par
    });
    ProCollection = Backbone.Collection.extend({
        model: Pro,
        url: '/api/pros',
        sortkey: 'last_name'
    });
    PlayerCollection = PaginatedCollection.extend({
        model: Player,
        baseUrl: "/api/players",
        mainUrl: "users/players",
        perPage: 15,
        sortkey: 'last_name'
    });
    FacilityAdminCollection = PaginatedCollection.extend({
        model: FacilityAdmin,
        baseUrl: "/api/facility_admins",
        mainUrl: "users/facility_admins",
        perPage: 15,
        sortkey: 'last_name'
    });
    AdCollection = PaginatedCollection.extend({
        model: Ad,
        baseUrl: "/admin/ads",
        mainUrl: "ads",
        perPage: 15,
        sortkey: 'title'
    });
    FantasyTeamCollection = Backbone.Collection.extend({
        model: FantasyTeam
    });
    RealityTeamCollection = Backbone.Collection.extend({
        model: RealityTeam
    });
    RoundCollection = Backbone.Collection.extend({
        model: Round
    });
    ScoreCollection = PaginatedCollection.extend({
        model: Round,
        baseUrl: "/api/results",
        mainUrl: function() { return "leaderboard/"+this.tournament_id; },
        perPage: 20,
        comparator: function(round) {
            return round.get('team_rank');
        },
        fetch: function(options) {
            if (!options) {
                options = {};
            }
            if (!options.data) {
                options.data = {};
            }
            options.data.tournament_id = this.tournament_id;
            options.data.results_type = pmode;
            PaginatedCollection.prototype.fetch.call(this, options);
        }
    });
    PrizeCollection = Backbone.Collection.extend({
        model: Prize,
        comparator: function(prize) {
            return Number(prize.get('place'));
        }
    });
    TournamentCollection = PaginatedCollection.extend({
        model: Tournament,
        baseUrl: "/api/tournaments",
        mainUrl: "tournaments",
        perPage: 15,
        sortkey: 'start_date',
        sortdir: 'asc'
    });
    AdCollection = PaginatedCollection.extend({
        model: Ad,
        baseUrl: "/api/ads",
        mainUrl: "admin/ads",
        perPage: 15,
        sortkey: 'title',
        sortdir: 'asc'
    });


    /* Facility management
    */

    var FacilityAdminController = function(options) {
        var self = this;
        this.mymodel = Facility;
        this.tabs = ['Info', 'Scorecard'];
        this.mbus = _.extend({}, Backbone.Events);

        this.init = function() {
            self.editarea = $('#editform');
            self.editarea.html($.mustache($('#tab-structure-template').html(), {tabs: self.tabs}));
            self.editarea.find('ul.tabs li:first').addClass('current');
            self.editarea.find('.tabs').click(function(e){
                e.preventDefault();
                var $clicked = $(e.target).closest('li');
                $clicked.addClass('current').siblings().removeClass('current');
                $(self.editarea.find('.tabs-wrapper .tab-contents')[$clicked.index()]).siblings().hide().end().show();
            });

            self.facilityeditor = new FacilityEditor({
                mbus: this.mbus,
                el: self.editarea.find('.tab-contents:first')
            });
            self.courseeditor = new CourseEditor({
                mbus: this.mbus,
                el: self.editarea.find('.tab-contents:last'),
                collection: new CourseCollection()
            });
            self.facilitylist = new FacilityAdminList({
                collection: new FacilityCollection(),
                el: $('#facilitylist')
            });
            self.facilitylist.mbus = this.mbus;
        };
        this.init();

        this.mbus.bind('backtolist', function(model) {
            this.editarea.find('a:first').click();
            this._list_mode();
        }, this);

        this.facilitylist.collection.bind('add', function(model) {
            this.edit(model);
        }, this);
        this.courseeditor.collection.bind('add', function(model) {
            model.set({facility_id: self.facilityeditor.model.get('id')}, {silent: true});
            self.edit(model);
        });
        this.facilitylist.mbus.bind('editing', function(model) {
            this.edit(model);
        }, this);
        this.facilitylist.mbus.bind('canceling', function(model) {
            if (model.isNew()) {
                model.destroy();
            }
            this.facilityeditor.model = {};
            this._list_mode();
        }, this);
        this.facilitylist.mbus.bind('saving', function(model) {
            var self = this,
                isnew = model.isNew();

            model.save(model, {}, {
                success: function(model, resp) {
                    var p = model.get('page');
                    if (self.facilitylist.collection.page !== p || isnew) {
                        self.facilitylist.collection.page = p;
                        self.facilitylist.collection.fetch();
                    }
                    self.courseeditor.collection.facility_id = model.get('id');
                    humane.success('Saved');
                }
            });
        }, this);
    };
    _.extend(FacilityAdminController.prototype, {
        _list_mode: function() {
            var page = this.facilitylist.collection.page > 1 ? '/'+this.facilitylist.collection.page : '';
            window.router.navigate(this.facilitylist.collection.model.prototype.label.pluralize()+page);
            this.editarea.find('.tabs').hide();
            this.facilitylist.showme();
            this.editarea.hide();
        },
        _edit_mode: function() {
            var self = this;
            this.facilitylist.hideme();
            this.editarea.find('.tabs').show();
            window.router.navigate('facility/'+(this.facilityeditor.model.get('id') || 'new'));
            this.facilityeditor.render();
            this.courseeditor.render();
            this.editarea.show();
        },

        list: function(page) {
            var self = this;
            this.init();
            this.facilitylist.collection.page = (page ? page : 1);
            this.facilitylist.collection.fetch({success: function(collection, resp) {
                self._list_mode();
            }});
        },
        edit: function(id) {
            if (id instanceof this.mymodel) {
                this.facilityeditor.model = id;
                this.courseeditor.collection.facility_id = id.get('id');
                this.courseeditor.collection.reset(id.get('courses'));
                this._edit_mode();
            } else {
                var self = this;
                $.getJSON('/api/pageof/facility/'+id,
                    {perpage: this.facilitylist.collection.perPage},
                    function(data) {
                        self.facilitylist.collection.page = data.page;
                        self.facilitylist.collection.fetch({
                            success: function(coll, resp) {
                                if (id === 'new') {
                                    self.facilitylist.collection.add({});
                                } else {
                                    self.facilityeditor.model = coll.find(function(item) {
                                        return item.get('id') === id;
                                    });
                                }
                                self.courseeditor.collection.facility_id = id;
                                self.courseeditor.collection.reset(self.facilityeditor.model.get('courses'));
                                self._edit_mode();
                            }
                        });
                    }
                );
            }
        }
    });

    var SingleFacilityAdminController = function(options) {
        var self = this;
        this.mymodel = Facility;
        this.tabs = ['Info', 'Scorecard'];
        this.mbus = _.extend({}, Backbone.Events);

        this.init = function() {
            self.editarea = $('#editform');
            self.editarea.html($.mustache($('#tab-structure-template').html(), {tabs: self.tabs}));
            self.editarea.find('ul.tabs li:first').addClass('current');
            self.editarea.find('.tabs').click(function(e){
                e.preventDefault();
                var $clicked = $(e.target).closest('li');
                $clicked.addClass('current').siblings().removeClass('current');
                $(self.editarea.find('.tabs-wrapper .tab-contents')[$clicked.index()]).siblings().hide().end().show();
            });

            self.facilityeditor = new FacilityEditor({
                mbus: this.mbus,
                el: self.editarea.find('.tab-contents:first')
            });
            self.facilityeditor.template = $('#single-facility-edit-template');

            self.courseeditor = new CourseEditor({
                mbus: this.mbus,
                el: self.editarea.find('.tab-contents:last'),
                collection: new CourseCollection()
            });
        };
        this.init();

        this.courseeditor.collection.bind('add', function(model) {
            model.set({facility_id: self.facilityeditor.model.get('id')}, {silent: true});
        });
        this.facilityeditor.mbus.bind('canceling', function(model) {
            this._edit_mode();
        }, this);
        this.facilityeditor.mbus.bind('saving', function(model) {
            var self = this;

            model.save(model, {}, {
                success: function(model, resp) {
                    self.courseeditor.collection.facility_id = model.get('id');
                    humane.success('Saved');
                }
            });
        }, this);
    };
    _.extend(SingleFacilityAdminController.prototype, {
        _edit_mode: function() {
            var self = this;
            this.editarea.find('.tabs').show();
            this.facilityeditor.render();
            this.courseeditor.render();
            this.editarea.show();
        },

        edit: function() {
            var self = this;
            this.init();
            $.getJSON('/api/facility', function(data) {
                self.facilityeditor.model = new Facility(data);
                self.courseeditor.collection.reset(self.facilityeditor.model.get('courses'));
                self._edit_mode();
            });
        }
    });

    // A single row in the facility picker list
    var FacilityPickerRow = MGModelRowView.extend({
        tagName: 'tr',
        template: $('#facility-picker-row-template'),
        events: _.extend(_.clone(MGModelRowView.prototype.events), {
            'click a.selection': 'select_facility'
        }),
        initialize: function(options) {
            MGModelRowView.prototype.initialize.call(this, options);
            _.bindAll(this, 'render', 'select_facility');
        },
        select_facility: function(e) {
            e.preventDefault();
            e.stopPropagation();
            var self = this;

            this.model.set({selected: (this.model.get('selected') ? false : true)});
            this.model.collection.each(function(model) {
                if (model.id !== self.model.id) {
                    model.unset('selected');
                }
            });
            $.post('/api/player_facility',
                {facility_id: (this.model.get('selected') ? this.model.id : null)},
                function(data) {
                    self.$('td:last').find('a').show().attr('href', $('#fpicker_button').attr('href'));
                }
            );
        },
        render: function() {
            MGModelRowView.prototype.render.call(this);
            var $el = $(this.el);
            if (this.model.get('selected')) {
                $el.addClass('selected');
            } else {
                $el.removeClass('selected');
                $el.find('td:last').find('a').hide();
            }
            return this;
        }
    });
    var FacilityPickerList = PaginatedView.extend({
        el: $('#facilitylist'),
        collection: new FacilityPickerCollection(),
        model_view: FacilityPickerRow,
        controls_template: '#facility-picker-controls-template',
        always_show_controls: true
    });
    var FacilityPickerController = function(options) {
        var self = this;
        this.mbus = _.extend({}, Backbone.Events);
        this.mymodel = Facility;
        $('#facility_picker_wrap').html($.mustache($('#facility-picker-template').html()));
        this.facilitylist = new FacilityPickerList({el: $('#facilitylist'), mbus: this.mbus});

        this.facilitylist.$('.nofacility_send').unbind('click').bind('click', function() {
            self.facilitylist.$('#nofacility').ajaxSubmit({
                url: '/api/player_facility_waitlist',
                dataType: 'json',
                success: function(resp, status) {
                    if (resp.success) {
                        self.facilitylist.$('#nofacility').hide();
                        self.facilitylist.$('#thanks_waitlist').show();
                    }
                }
            });
        });
    };
    _.extend(FacilityPickerController.prototype, {
        list: function(page) {
            var self = this;
            this.facilitylist.collection.page = (page ? page : 1);
            this.facilitylist.collection.fetch({
                success: function(collection, resp) {
                    var page = self.facilitylist.collection.page > 1 ? '/'+self.facilitylist.collection.page : '';
                }
            });
        }
    });


    // A single row in the facility list
    var FacilityRow = MGModelRowView.extend({
        tagName: 'tr',
        template: $('#facility-row-template')
    });
    // A paged list of facilities
    var FacilityAdminList = PaginatedView.extend({
        el: $('#facilitylist'),
        collection: new FacilityCollection(),
        model_view: FacilityRow
    });
    // The facility editing interface
    var FacilityEditor = MGFormView.extend({
        template: $('#facility-edit-template'),
        initialize: function(options) {
            MGFormView.prototype.initialize.call(this, options);
            _.bindAll(this, 'render', 'upload_logo');
            this.el.show();
            this.mbus.bind('resetting', this.render);
        },
        events: _.extend(_.clone(MGFormView.prototype.events), {
            'change #logo_file': 'upload_logo'
        }),
        render: function(model) {
            if (model) {
                this.model = model;
            }
            MGFormView.prototype.render.call(this);
            var self = this;
            this.$('#facility_admin_name').autocomplete({
                source: '/api/facility_admin_search',
                select: function(e, ui) {
                    self.model.set({
                        facility_admin_id: ui.item.id,
                        facility_admin_name: ui.item.value
                    }, {silent: true});
                },
                change: function(e, ui) {
                    if ( ui.item === null ) {
                        self.model.set({
                            facility_admin_id: 0,
                            facility_admin_name: ''
                        }, {silent: true});
                    }
                }
            });
        },
        upload_logo: function() {
            var self = this;
            $('#logo_form').ajaxSubmit({
                url: '/api/facility_logo/'+(this.model.get('id') || ''),
                dataType: 'json',
                beforeSubmit: function(arr) {
                    $('#upload_status').show();
                },
                success: function(resp, status) {
                    if (resp.success) {
                        self.render();
                    } else {
                        alert(resp.message);
                        self.$('input#logo_file').val('');
                        $('#upload_status').hide();
                    }
                }
            });
        }
    });
    // A single row in the course list
    var CourseRow = MGFormView.extend({
        tagName: 'li',
        template: $('#course-edit-row-template'),
        events: _.extend(_.clone(MGFormView.prototype.events), {
            'click .resetme': 'resetme'
        }),
        initialize: function(options) {
            MGFormView.prototype.initialize.call(this, options);
            _.bindAll(this, 'render', 'resetme');
            this.model.bind('change', this.render);
        },
        render: function() {
            MGFormView.prototype.render.call(this);
            var holes = this.$('input.hole'),
                self = this;
            holes.unbind('focus').bind('focus', function(e) {
                this.select();
            });
            $(this.el).unbind('keydown').bind('keydown', function(e) {
                if ($(e.target).is('input.hole')) {
                    var key = e.which,
                        hole = $(e.target),
                        row = hole.closest('tr'),
                        subtotal = 0,
                        holeidx = holes.index(e.target);

                    if (key >= 50 && key <= 57) {
                        // 2-9
                        if (hole.val() === '1') {
                            hole.val('1'+(key-48));
                        } else {
                            hole.val(key-48);
                        }
                        if (holeidx+1 < holes.length && hole.val() > 1) {
                            holes[holeidx+1].focus();
                        }
                    } else if (key === 49) {
                        // 1
                        if (hole.val() === '1') {
                            hole.val(11);
                            if (holeidx+1 < holes.length && hole.val() > 1) {
                                holes[holeidx+1].focus();
                            }
                        } else {
                            hole.val(1);
                        }
                        //return false;
                    } else if (key === 48) {
                        // 0
                        if (hole.val() === '1') {
                            hole.val(10);
                            if (holeidx+1 < holes.length && hole.val() > 1) {
                                holes[holeidx+1].focus();
                            }
                        }
                        return false;
                    } else if (key === 8 || key === 9 || key > 90) {
                        return true;
                    } else {
                        return false;
                    }

                    // update totals
                    subtotal = _(row.find('input.hole')).reduce(function(m, n) { return m + Number($(n).val()); }, 0);
                    row.find('span.subtotal').text(subtotal);
                    row.closest('table').find('span.par_total')
                        .text(_(row.closest('table').find('span.subtotal'))
                              .reduce(function(m, n) { return m + Number($(n).text()); }, 0));

                    return false;
                }
            });
            // update totals
            $(this.el).find('table').each(function(k,table) {
                var $table = $(table);
                $table.find('tr.par').each(function(k,row) {
                    var $row = $(row);
                    $row.find('span.subtotal').text(_($row.find('input.hole')).reduce(function(m, n) { return m + Number($(n).val()); }, 0));
                    if ($row.is('.backnine')) {
                        $row.find('span.par_total').text(
                            _($table.find('span.subtotal')).reduce(function(m, n) { return m + Number($(n).text()); }, 0)
                        );
                    }
                });
            });
            return this;
        },
        saved: function() {
            humane.success('Saved');
        },
        resetme: function(e) {
            e.preventDefault();
            e.stopPropagation();
            this.model.fetch({
                url: '/api/course/'+this.model.get('id')
            });
        }
    });
    // The course editing interface
    var CourseEditor = Backbone.View.extend({
        initialize: function(options) {
            if (options.mbus) {
                this.mbus = options.mbus;
            }
            _.bindAll(this, 'render', 'additem', 'backtolist');
            this.view_cache = {};
            this.collection.bind('reset', this.render);
        },
        events: {
            'click .add': 'additem',
            'click .backtolist': 'backtolist'
        },
        backtolist: function(e) {
            e.preventDefault();
            e.stopPropagation();
            this.mbus.trigger('backtolist', this.model);
        },
        additem: function(e) {
            e.preventDefault();
            this.collection.add(new Course({facility_id: this.collection.facility_id}));
            this.render();
        },
        render: function() {
            this.el.html('<a href="#" class="backtolist">Back to list</a><a href="#" class="add">Add</a>');
            this.collection.each(function(course) {
                var id = course.isNew() ? course.cid : course.get('id');
                if ( ! this.view_cache[id] ) {
                    this.view_cache[id] = new CourseRow({model: course, mbus: this.mbus});
                }
                this.el.append(this.view_cache[id].render().el);
            }, this);
            return this;
        }

    });


    /* Player management
    */

    // A single row in the player list
    var PlayerRow = MGModelRowView.extend({
        tagName: 'tr',
        template: $('#player-row-template')
    });
    // A paged list of players
    var PlayerAdminList = PaginatedView.extend({
        collection: new PlayerCollection(),
        model_view: PlayerRow
    });
    // The player editing interface
    var PlayerEditor = MGFormView.extend({
        template: $('#player-edit-template'),
        initialize: function(options) {
            MGFormView.prototype.initialize.call(this, options);
            this.mbus.bind('resetting', this.render);
        },
        render: function() {
            MGFormView.prototype.render.call(this);

            var self = this;
            this.$('#facility_picker').addClass('noautoset').autocomplete({
                source: '/api/facility_search',
                select: function(e, ui) {
                    self.model.set({
                        facility_id: ui.item.id,
                        facility: new Facility(ui.item.facility),
                        facility_picker: ui.item.value
                    }, {silent: true});
                },
                change: function(e, ui) {
                    if ( ui.item === null ) {
                        self.model.unset('facility');
                        self.model.set({
                            facility_id: null,
                            facility_verified: null,
                            facility_picker: ''
                        }, {silent: true});
                    }
                }
            });

            return this;
        }

    });

    var PlayerAdminController = function(options) {
        var self = this;
        this.mymodel = Player;
        this.mbus = _.extend({}, Backbone.Events);

        this.init = function() {
            self.editarea = $('#editform');
            self.playereditor = new PlayerEditor({
                mbus: this.mbus,
                el: self.editarea
            });
            self.playerlist = new PlayerAdminList({
                el: $('#playerlist')
            });
            self.playerlist.mbus = this.mbus;
        };
        this.init();

        this.mbus.bind('backtolist', function(model) {
            this._list_mode();
        }, this);

        this.playerlist.collection.bind('add', function(model) {
            this.edit(model);
        }, this);
        this.playerlist.mbus.bind('editing', function(model) {
            this.edit(model);
        }, this);
        this.playereditor.mbus.bind('canceling', function(model) {
            if (model.isNew()) {
                model.destroy();
            }
            this.playereditor.model = {};
            this._list_mode();
        }, this);
        this.playerlist.mbus.bind('saving', function(model) {
            var isnew = model.isNew();

            model.save(model, {}, {
                success: function(model, resp) {
                    var p = model.get('page');
                    if (self.playerlist.collection.page !== p || isnew) {
                        self.playerlist.collection.page = p;
                        self.playerlist.collection.fetch();
                    }
                    humane.success('Saved');
                }
            });
        }, this);
    };
    _.extend(PlayerAdminController.prototype, {
        _list_mode: function() {
            var page = this.playerlist.collection.page > 1 ? '/'+this.playerlist.collection.page : '';
            window.router.navigate('users/'+this.playerlist.collection.model.prototype.label.pluralize()+page);
            this.playerlist.showme();
            this.editarea.hide();
        },
        _edit_mode: function() {
            this.playerlist.hideme();
            window.router.navigate('users/player/'+(this.playereditor.model.get('id') || 'new'));
            this.playereditor.render();
            this.editarea.show();
        },

        list: function(page) {
            var self = this;
            if (this.playerlist.render_to.children().length > 0) {
                this.init();
            }
            this.playerlist.collection.page = (page ? page : 1);
            this.playerlist.collection.fetch({success: function(collection, resp) {
                self._list_mode();
            }});
        },
        edit: function(id) {
            if (id instanceof this.mymodel) {
                this.playereditor.model = id;
                this._edit_mode();
            } else {
                var self = this;
                $.getJSON('/api/pageof/player/'+id,
                    {perpage: this.playerlist.collection.perPage},
                    function(data) {
                        self.playerlist.collection.page = data.page;
                        self.playerlist.collection.fetch({
                            success: function(coll, resp) {
                                if (id === 'new') {
                                    self.playerlist.collection.add({});
                                } else {
                                    self.playereditor.model = coll.find(function(item) {
                                        return item.get('id') === id;
                                    });
                                }
                                self._edit_mode();
                            }
                        });
                    }
                );
            }
        }
    });

    // A single row in the player list
    var FacilityPlayerRow = MGModelRowView.extend({
        tagName: 'tr',
        template: $('#facility-player-row-template')
    });
    // A paged list of players
    var FacilityPlayerAdminList = PaginatedView.extend({
        collection: new PlayerCollection(),
        model_view: FacilityPlayerRow
    });
    // The player editing interface
    var FacilityPlayerEditor = MGFormView.extend({
        template: $('#facility-player-edit-template')
    });

    var FacilityPlayerAdminController = function(options) {
        var self = this;
        this.mymodel = Player;
        this.mbus = _.extend({}, Backbone.Events);

        this.init = function() {
            self.editarea = $('#editform');
            self.playereditor = new FacilityPlayerEditor({
                mbus: this.mbus,
                el: self.editarea
            });
            self.playerlist = new FacilityPlayerAdminList({
                mbus: this.mbus,
                el: $('#playerlist')
            });
        };
        this.init();

        this.mbus.bind('backtolist', function(model) {
            this._list_mode();
        }, this);

        this.playerlist.collection.bind('add', function(model) {
            this.edit(model);
        }, this);
        this.playerlist.mbus.bind('editing', function(model) {
            this.edit(model);
        }, this);
        this.playereditor.mbus.bind('canceling', function(model) {
            if (model.isNew()) {
                model.destroy();
            }
            this.playereditor.model = {};
            this._list_mode();
        }, this);
        this.playerlist.mbus.bind('saving', function(model) {
            var self = this,
                isnew = model.isNew();

            model.save(model, {}, {
                success: function(model, resp) {
                    var p = model.get('page');
                    if (self.playerlist.collection.page !== p || isnew) {
                        self.playerlist.collection.page = p;
                        self.playerlist.collection.fetch();
                    }
                    self._list_mode();
                }
            });
        }, this);
    };
    _.extend(FacilityPlayerAdminController.prototype, {
        _list_mode: function() {
            var page = this.playerlist.collection.page > 1 ? '/'+this.playerlist.collection.page : '';
            window.router.navigate(this.playerlist.collection.model.prototype.label.pluralize()+page);
            this.playerlist.showme();
            this.editarea.hide();
        },
        _edit_mode: function() {
            var self = this;
            this.playerlist.hideme();
            window.router.navigate('player/'+(this.playereditor.model.get('id') || 'new'));
            this.playereditor.render();
            this.editarea.show();
        },

        list: function(page) {
            var self = this;
            if (this.playerlist.render_to.children().length > 0) {
                this.init();
            }
            this.playerlist.collection.page = (page ? page : 1);
            this.playerlist.collection.fetch({success: function(collection, resp) {
                self._list_mode();
            }});
        },
        edit: function(id) {
            if (id instanceof this.mymodel) {
                this.playereditor.model = id;
                this._edit_mode();
            } else {
                var self = this;
                $.getJSON('/api/pageof/player/'+id,
                    {perpage: this.playerlist.collection.perPage},
                    function(data) {
                        self.playerlist.collection.page = data.page;
                        self.playerlist.collection.fetch({
                            success: function(coll, resp) {
                                self.playereditor.model = coll.find(function(item) {
                                    return item.get('id') === id;
                                });
                                self._edit_mode();
                            }
                        });
                    }
                );
            }
        }
    });

    /* Facility admin management
    */

    // A single row in the player list
    var FacilityAdminRow = MGModelRowView.extend({
        tagName: 'tr',
        template: $('#facility-admin-row-template')
    });
    // A paged list of players
    var FacilityAdminAdminList = PaginatedView.extend({
        template: '#player-list-template',
        collection: new FacilityAdminCollection(),
        model_view: FacilityAdminRow
    });
    // The player editing interface
    var FacilityAdminEditor = MGFormView.extend({
        template: $('#facility-admin-edit-template'),
        initialize: function(options) {
            MGFormView.prototype.initialize.call(this, options);
            this.mbus.bind('resetting', this.render);
        },
        render: function() {
            MGFormView.prototype.render.call(this);

            var self = this;
            this.$('#facility_picker').addClass('noautoset').autocomplete({
                source: '/api/facility_search',
                select: function(e, ui) {
                    self.model.set({
                        facility_id: ui.item.id,
                        facility: new Facility(ui.item.facility),
                        facility_verified: 1,
                        facility_picker: ui.item.value
                    }, {silent: true});
                },
                change: function(e, ui) {
                    if ( ui.item === null ) {
                        self.model.unset('facility');
                        self.model.set({
                            facility_id: null,
                            facility_verified: null,
                            facility_picker: ''
                        }, {silent: true});
                    }
                }
            });

            return this;
        }

    });

    var FacilityAdminAdminController = function(options) {
        var self = this;
        this.mymodel = FacilityAdmin;
        this.mbus = _.extend({}, Backbone.Events);

        this.init = function() {
            self.editarea = $('#editform');
            self.playereditor = new FacilityAdminEditor({
                mbus: this.mbus,
                el: self.editarea
            });
            self.playerlist = new FacilityAdminAdminList({
                mbus: this.mbus,
                el: $('#playerlist')
            });
        };
        this.init();

        this.mbus.bind('backtolist', function(model) {
            this._list_mode();
        }, this);

        this.playerlist.collection.bind('add', function(model) {
            this.edit(model);
        }, this);
        this.playerlist.mbus.bind('editing', function(model) {
            this.edit(model);
        }, this);
        this.playereditor.mbus.bind('canceling', function(model) {
            if (model.isNew()) {
                model.destroy();
            }
            this.playereditor.model = {};
            this._list_mode();
        }, this);
        this.playerlist.mbus.bind('saving', function(model) {
            var self = this,
                isnew = model.isNew();

            model.save(model, {}, {
                success: function(model, resp) {
                    var p = model.get('page');
                    if (self.playerlist.collection.page !== p || isnew) {
                        self.playerlist.collection.page = p;
                        self.playerlist.collection.fetch();
                    }
                    humane.success('Saving');
                }
            });
        }, this);
    };
    _.extend(FacilityAdminAdminController.prototype, {
        _list_mode: function() {
            var page = this.playerlist.collection.page > 1 ? '/'+this.playerlist.collection.page : '';
            window.router.navigate('users/facility_admins'+page);
            this.playerlist.showme();
            this.editarea.hide();
        },
        _edit_mode: function() {
            var self = this;
            this.playerlist.hideme();
            window.router.navigate('users/facility_admin/'+(this.playereditor.model.get('id') || 'new'));
            this.playereditor.render();
            this.editarea.show();
        },

        list: function(page) {
            var self = this;
            if (this.playerlist.render_to.children().length > 0) {
                this.init();
            }
            this.playerlist.collection.page = (page ? page : 1);
            this.playerlist.collection.fetch({success: function(collection, resp) {
                self._list_mode();
            }});
        },
        edit: function(id) {
            if (id instanceof this.mymodel) {
                this.playereditor.model = id;
                this._edit_mode();
            } else {
                var self = this;
                $.getJSON('/api/pageof/facility_admin/'+id,
                    {perpage: this.playerlist.collection.perPage},
                    function(data) {
                        self.playerlist.collection.page = data.page;
                        self.playerlist.collection.fetch({
                            success: function(coll, resp) {
                                if (id === 'new') {
                                    self.playerlist.collection.add({});
                                } else {
                                    self.playereditor.model = coll.find(function(item) {
                                        return item.get('id') === id;
                                    });
                                }
                                self._edit_mode();
                            }
                        });
                    }
                );
            }
        }
    });

    /* Tournament management
    */

    // A single row in the tournament list
    var TournamentRow = MGModelRowView.extend({
        tagName: 'tr',
        template: $('#tournament-row-template')
    });
    // A paged list of tournaments
    var TournamentAdminList = PaginatedView.extend({
        el: $('#tournamentlist'),
        collection: new TournamentCollection(),
        model_view: TournamentRow
    });
    // The tournament editing interface
    var TournamentEditor = MGFormView.extend({
        el: $('#editform'),
        template: $('#tournament-edit-template'),
        //render_on_change: false,
        initialize: function(options) {
            MGFormView.prototype.initialize.call(this, options);
            _.bindAll(this, 'render', 'showme', 'hideme');
        },
        showme: function() {
            MGFormView.prototype.showme.call(this);
        },
        hideme: function() {
            MGFormView.prototype.hideme.call(this);
        },
        render: function() {
            MGFormView.prototype.render.call(this);
            var self = this;

            _.each(this.model.get('pros'), function(pro) {
                this.$('#pros_list').append($.mustache($('#pro-upload-entry-template').html(), pro)).closest('td').show();
            }, this);
            this.$('.playdatepicker, .seldatepicker').datepicker({
                'dateFormat': 'yy-mm-dd',
                'onSelect': function(selectedDate) {
                    var clicked = $(this).attr('id');
                    if (clicked === 'start_date') {
                        $('.seldatepicker').datepicker('option', 'maxDate', selectedDate);
                        $('#end_date, .datepicker').datepicker('option', 'minDate', selectedDate);
                    } else if (clicked === 'end_date') {
                        $('#start_date, .datepicker').datepicker('option', 'maxDate', selectedDate);
                    } else if (clicked === 'selection_start_date') {
                        $('#selection_end_date').datepicker('option', 'minDate', selectedDate);
                    } else if (clicked === 'selection_end_date') {
                        $('#selection_start_date').datepicker('option', 'maxDate', selectedDate);
                    }
                    $(this).trigger('change');
                }
            });

            return this;
        }

    });

    // A single row in the pro list
    var ProRow = MGModelRowView.extend({
        tagName: 'tr',
        template: $('#pro-upload-entry-template'),
        initialize: function(options) {
            MGModelRowView.prototype.initialize.call(this, options);
            if (this.model.isNew()) {
                $(this.el).addClass('pro_to_fix');
            }
            _.bindAll(this, 'render');
        },
        render: function() {
            MGModelRowView.prototype.render.call(this);
            var self = this;
            this.$('input.profinder').autocomplete({
                source: window.allpros,
                select: function(e, ui) {
                    var $row= $(e.target).closest('tr'),
                        data = ui.item;

                    data.id_orig = data.id;
                    data.country_id_orig = data.country_id;
                    self.model.set(data);
                    $row.removeClass('pro_to_fix');
                },
                change: function(e, ui) {
                    if ( ui.item === null ) {
                        var $text = $(e.target),
                            $row = $text.closest('tr');

                        self.model.unset('id');
                        if ($text.val() === '') {
                            self.model.unset('first_name');
                            self.model.unset('last_name');
                        } else {
                            var name = $text.val().match(/\|/) ? $text.val().split(/\|/) : $text.val().split(/ /);
                            self.model.set({
                                first_name: $.trim(_(name).first()),
                                last_name: $.trim(_(name).rest().join(' '))
                            });
                        }
                        $row.addClass('pro_to_fix');
                    }
                }
            });
            this.$('input.country_selection').autocomplete({
                source: window.allcountries,
                select: function(e, ui) {
                    var $cell = $(e.target).closest('td'),
                        data = {};

                    $cell.removeClass('country_to_fix');
                    data.country = ui.item.label;
                    data.country_id = ui.item.id;
                    if (ui.item.id === self.model.get('country_id_orig')) {
                        data.id = self.model.get('id_orig');
                    } else {
                        self.model.unset('id', {silent: true});
                    }
                    self.model.set(data);
                },
                change: function(e, ui) {
                    if ( ui.item === null ) {
                        var $cell = $(e.target).closest('td');

                        $cell.addClass('country_to_fix');
                        self.model.unset('country_id');
                    }
                }
            });
            return this;
        }
    });
    // A list of pros for a tournament
    var ProRows = MGCollectionView.extend({
        model: Pro,
        model_view: ProRow
    });
    var ProEditor = Backbone.View.extend({
        initialize: function(options) {
            if (options.mbus) {
                this.mbus = options.mbus;
            }
            _.bindAll(this, 'render', 'saved', 'upload_pros', 'resetme', 'backtolist');
            this.view_cache = {};
        },
        events: {
            'click .save': 'saveme',
            'click .reset': 'resetme',
            'click .backtolist': 'backtolist',
            'change #pros_file': 'upload_pros',
            'click :file': 'ie_upload'
        },
        saveme: function(e) {
            e.preventDefault();
            var tosave = new ProCollection();
            tosave.add(this.pros.toJSON(), {silent: true});
            tosave.add(this.unknowns.toJSON(), {silent: true});
            tosave.save(tosave, {url: '/api/tournament/'+this.tournament_id+'/pros'});
            tosave.unbind('saved').bind('saved', this.saved);
        },
        saved: function() {
            this.collection.each(function(item) {
                item.set({country_editable: false, editable: false}, {silent: true});
            });
            this.collection.fetch({
                url: '/api/tournament/'+this.tournament_id+'/pros',
                success: function() {
                    humane.success('Saved');
                }
            });
        },
        resetme: function(e) {
            e.preventDefault();
            e.stopPropagation();
            this.collection.each(function(item) {
                item.set({country_editable: false}, {silent: true});
            });
            this.collection.fetch({
                url: '/api/tournament/'+this.tournament_id+'/pros'
            });
        },
        backtolist: function(e) {
            e.preventDefault();
            e.stopPropagation();
            this.mbus.trigger('backtolist', this.model);
        },
        upload_pros: function() {
            var self = this;
            $('#pros_form').ajaxSubmit({
                url: '/api/check_pros',
                dataType: 'json',
                beforeSubmit: function() {
                    self.$('#upload_status').show();
                },
                success: function(resp, status) {
                    if (resp.success) {
                        self.pros = new ProCollection();
                        self.unknowns= new ProCollection();

                        window.allpros = resp.allpros;
                        window.allcountries = resp.allcountries;

                        _(resp.pros).each(function(pro) {
                            if (pro.id) {
                                self.pros.add(pro, {silent: true});
                            } else {
                                self.unknowns.add(pro, {silent: true});
                            }
                        });

                        pro_views = new ProRows({
                            el: self.$('#prolist'),
                            collection: self.pros
                        });
                        pro_views.render().showme();

                        unknown_views = new ProRows({
                            el: self.$('#unknownlist'),
                            collection: self.unknowns
                        });
                        unknown_views.render().showme();

                        self.$('#pros_instructions').show();
                        self.$('#pros_list').closest('table').find('span.current-pros').hide().siblings('span').show();
                        self.$('#pros_file').val('');

                        self.$('span.new-pros').show().siblings('span').hide();

                        self.$('#invalid_list').html('');
                        _.each(resp.invalid, function(line) {
                            self.$('#invalid_list').append('<li>'+line+'</li>').closest('td').show();
                        });

                        self.$('#upload_status').hide();
                    } else {
                        alert(resp.message);
                        self.$('#logo_file').val('');
                        $('#upload_status').hide();
                    }
                }
            });
        },
        render: function() {
            this.collection.unbind('reset').bind('reset', this.render);
            $(this.el).html($.mustache($('#pros-admin-template').html()));
            pro_views = new ProRows({
                mbus: this.mbus,
                el: self.$('#prolist'),
                collection: this.collection
            });
            pro_views.render().showme();

            this.delegateEvents();
            return this;
        }
    });

    // A single row in the prize list
    var PrizeRow = MGFormView.extend({
        tagName: 'tr',
        template: $('#prize-edit-row-template'),
        initialize: function(options) {
            if (options.mbus) {
                this.mbus = options.mbus;
            }
            _.bindAll(this, 'update_total');
            this.model.bind('change:amount', this.update_total);
        },
        update_total: function() {
            this.mbus.trigger('change:amount');
        }
    });
    // Edit prizes for a tournament
    var PrizeEditor = Backbone.View.extend({
        initialize: function(options) {
            if (options.mbus) {
                this.mbus = options.mbus;
            }
            _.bindAll(this, 'render', 'saved', 'add_blank', 'render_blank', 'update_total', 'resetme', 'backtolist');
            this.view_cache = {};
            this.mbus.bind('change:amount', this.update_total);
        },
        saved: function() {
            this.render();
            humane.success('Saved');
        },
        events: {
            'keyup input.prize_amount': 'add_blank',
            'click .reset': 'resetme',
            'click .backtolist': 'backtolist',
            'click .save': 'saveme'
        },
        saveme: function(e) {
            e.preventDefault();
            this.collection.save(this.collection, {url: '/api/tournament/'+this.tournament_id+'/prizes'});
        },
        resetme: function(e) {
            e.preventDefault();
            e.stopPropagation();
            var self = this;
            this.collection.fetch({
                url: '/api/tournament/'+this.tournament_id+'/prizes',
                success: function(coll, resp) {
                    self.render();
                }
            });
        },
        backtolist: function(e) {
            e.preventDefault();
            e.stopPropagation();
            this.mbus.trigger('backtolist', this.model);
        },
        add_blank: function(e) {
            e.preventDefault();
            var data = {},
                $amounts = this.$('.prize_amount'),
                $element = $(e.target);

            if ($element.closest('tr').is(':last-child')) {
                data.place = $element.closest('tr').index() + 2;
                if ($element.val() > 0) {
                    this.collection.add(new Prize(data));
                }
            }
        },
        render_blank: function(model) {
            this.prizelist.append((new PrizeRow({model: model, mbus: this.mbus})).render().el);
        },
        update_total: function() {
            var data = {};
            data.total_prize = this.collection.length > 0 ?
                currencify(this.collection.at(0).get('tournament').get('total_prize')) :
                '0.00';

            data.total = currencify(this.collection.reduce(function(memo, prize) {
                return (prize.get('amount') > 0) ? memo + Number(prize.get('amount')) : memo;
            }, 0));
            var over = Number(data.total.replace(/[^0-9.]/g, '')) > Number(data.total_prize.replace(/[^0-9.]/g, '')) ? true : false;
            this.$('#running_total').html($.mustache($('#running-total-template').html(), data)).css({
                color: (over ? 'red' : 'black'),
                "font-weight": (over ? 'bold' : 'normal')
            });
        },
        render: function() {
            this.collection.unbind('add').bind('add', this.render_blank);
            this.collection.unbind('saved').bind('saved', this.saved);
            if (this.collection.length === 0 || Number(this.collection.last().get('amount')) > 0) {
                this.collection.add(
                    {place: Number(this.collection.length) + 1},
                    {silent: true}
                );
            }
            $(this.el).html($.mustache($('#prize-admin-template').html()));

            this.prizelist = $(this.el).find('#prizelist');
            this.collection.each(function(prize) {
                var id = prize.isNew() ? prize.cid : prize.get('id');
                if ( ! this.view_cache[id] ) {
                    this.view_cache[id] = new PrizeRow({model: prize, mbus: this.mbus});
                }
                this.prizelist.append(this.view_cache[id].render().el);
            }, this);

            this.update_total();
            this.delegateEvents();
            return this;
        }
    });

    // A single row in the score list
    var ScoreRow = MGFormView.extend({
        tagName: 'tr',
        template: $('#round-row-template')
    });
    var ScoreEditor = Backbone.View.extend({
        initialize: function(options) {
            if (options.mbus) {
                this.mbus = options.mbus;
            }
            _.bindAll(this, 'render', 'saved', 'change_day', 'resetme', 'backtolist');
            this.view_cache = {};
            delete this.current_date;
            this.collection.unbind('saved').bind('saved', this.saved);
        },
        events: {
            'click .save': 'saveme',
            'click .reset': 'resetme',
            'click .backtolist': 'backtolist',
            'change #tday': 'change_day'
        },
        saveme: function(e) {
            e.preventDefault();
            this.collection.save(this.collection, {url: '/api/tournament/'+this.tournament.get('id')+'/scores'});
        },
        saved: function() {
            humane.success('Saved');
        },
        resetme: function(e) {
            e.preventDefault();
            e.stopPropagation();
            var self = this;
            this.collection.fetch({
                url: '/api/tournament/'+this.tournament_id+'/scores',
                success: function(coll, resp) {
                    self.tournament.set({pro_rounds: resp}, {silent: true});
                    self.$('#tday').change();
                }
            });
        },
        backtolist: function(e) {
            e.preventDefault();
            e.stopPropagation();
            this.mbus.trigger('backtolist', this.model);
        },
        change_day: function(e) {
            e.preventDefault();
            this.current_date = this.$('#tday').val();
            this.collection.reset(this.tournament.get('pro_rounds')[this.current_date]);
            this.render();
        },
        render: function() {
            this.scorelist = $(this.el).find('#scorelist').html('');

            this.collection.each(function(score) {
                var id = score.isNew() ? score.cid : score.get('id');
                if ( ! this.view_cache[id] ) {
                    this.view_cache[id] = new ScoreRow({model: score, mbus: this.mbus});
                }
                this.scorelist.append(this.view_cache[id].render().el);
            }, this);

            this.delegateEvents();
            return this;
        }
    });


    var TournamentAdminController = function(options) {
        var self = this;
        this.mymodel = Tournament;
        this.tabs = ['Info', 'Pros', 'Prize Division', 'Pro Scores'];
        this.mbus = _.extend({}, Backbone.Events);

        this.init = function() {
            self.editarea = $('#editform');
            self.editarea.html($.mustache($('#tab-structure-template').html(), {tabs: self.tabs}));
            self.editarea.find('ul.tabs li:first').addClass('current');
            self.editarea.find('.tabs').click(function(e){
                e.preventDefault();
                var $clicked = $(e.target).closest('li');
                $clicked.addClass('current').siblings().removeClass('current');
                $(self.editarea.find('.tabs-wrapper .tab-contents')[$clicked.index()]).siblings().hide().end().show();
            });

            self.tournamenteditor = new TournamentEditor({
                mbus: this.mbus,
                el: self.editarea.find('.tab-contents:first')
            });
            self.proeditor = new ProEditor({
                mbus: this.mbus,
                el: self.editarea.find('.tab-contents')[1],
                collection: new ProCollection()
            });
            self.prizeeditor = new PrizeEditor({
                mbus: this.mbus,
                el: self.editarea.find('.tab-contents')[2],
                collection: new PrizeCollection()
            });
            self.scoreeditor = new ScoreEditor({
                mbus: this.mbus,
                el: self.editarea.find('.tab-contents:last'),
                collection: new RoundCollection()
            });

            self.tournamentlist = new TournamentAdminList({
                collection: new TournamentCollection(),
                el: $('#tournamentlist')
            });
            self.tournamentlist.mbus = this.mbus;
        };
        this.init();

        this.tournamentlist.collection.bind('add', function(model) {
            this.edit(model);
        }, this);
        this.tournamentlist.mbus.bind('editing', function(model) {
            this.edit(model);
        }, this);
        this.mbus.bind('backtolist', function(model) {
            this.editarea.find('a:first').click();
            this._list_mode();
        }, this);
        this.tournamentlist.mbus.bind('canceling', function(model) {
            this.tournamenteditor.model = {};
            this._list_mode();
        }, this);
        this.tournamentlist.mbus.bind('saving', function(model) {
            var self = this,
                isnew = model.isNew();

            model.save(model, {}, {
                success: function(model, resp) {
                    var p = model.get('page');
                    if (self.tournamentlist.collection.page !== p || isnew) {
                        self.tournamentlist.collection.page = p;
                        self.tournamentlist.collection.fetch();
                    }
                    humane.success('Saved');
                }
            });
        }, this);
    };
    _.extend(TournamentAdminController.prototype, {
        _list_mode: function() {
            var page = this.tournamentlist.collection.page > 1 ? '/'+this.tournamentlist.collection.page : '';
            window.router.navigate(this.tournamentlist.collection.model.prototype.label.pluralize()+page);
            this.editarea.find('.tabs').hide();
            this.tournamentlist.showme();
            this.editarea.hide();
        },
        _edit_mode: function() {
            var self = this, data = {};
            this.tournamentlist.hideme();
            this.editarea.find('.tabs').show();
            window.router.navigate('tournament/'+(this.tournamenteditor.model.get('id') || 'new'));
            this.tournamenteditor.render().showme();
            this.proeditor.render();
            this.prizeeditor.render();
            data.dates = _(this.scoreeditor.tournament.get('pro_rounds')).keys();
            $(this.scoreeditor.el).html($.mustache($('#score-admin-template').html(), data));
            $(this.scoreeditor.el).find('#tday').change();
            this.editarea.show();
        },

        list: function(page) {
            var self = this;
            this.init();
            this.tournamentlist.collection.page = (page ? page : 1);
            this.tournamentlist.collection.fetch({success: function(collection, resp) {
                self._list_mode();
            }});
        },
        edit: function(id) {
            if (id instanceof this.mymodel) {
                this.tournamenteditor.model = id;
                this.proeditor.tournament_id = id.get('id');
                this.proeditor.collection = id.get('pros');
                this.prizeeditor.tournament_id = id.get('id');
                this.prizeeditor.collection = id.get('prizes');
                this.scoreeditor.tournament_id = id.get('id');
                this.scoreeditor.tournament = id;
                this._edit_mode();
            } else {
                var self = this;
                $.getJSON('/api/pageof/tournament/'+id,
                    {perpage: this.tournamentlist.collection.perPage},
                    function(data) {
                        self.tournamentlist.collection.page = data.page;
                        self.tournamentlist.collection.fetch({
                            success: function(coll, resp) {
                                if (id === 'new') {
                                    self.tournamentlist.collection.add({});
                                } else {
                                    self.tournamenteditor.model = coll.find(function(item) {
                                        return item.get('id') === id;
                                    });
                                    self.proeditor.tournament_id = id;
                                    self.prizeeditor.tournament_id = id;
                                    self.scoreeditor.tournament_id = self.tournamenteditor.model.get('id');
                                }
                                self.proeditor.collection = self.tournamenteditor.model.get('pros');
                                self.prizeeditor.collection = self.tournamenteditor.model.get('prizes');
                                self.scoreeditor.tournament = self.tournamenteditor.model;
                                self._edit_mode();
                            }
                        });
                    }
                );
            }
        }
    });


    // A single row in the player list
    var LeaderRow = MGModelRowView.extend({
        tagName: 'tr',
        template: $('#leader_'+pmode+'-row-template')
    });
    var LeaderboardsController = function(options) {
        var self = this;
        this.mymodel = Round;
        this.mbus = _.extend({}, Backbone.Events);

        this.init = function() {
            self.scorelist = new PaginatedView({
                mbus: this.mbus,
                model_view: LeaderRow,
                controls_template: '#leader-pagination-template',
                el: $('#scorelist'),
                collection: new ScoreCollection()
            });
        };
        this.init();

        self.scorelist.$('.pmode').unbind('click').bind('click', function(e) {
            var clicked = $(e.target);
            $.post('/api/pmode', {}, function(data) {
                var tournament_id = self.scorelist.$('#tournament_selector').val();
                window.pmode = data.pmode;
                self.scorelist.$('span.pmode_label').text(pmode.capitalize());
                self.scorelist.$('span.pmode_other_label').text(data.pmode === 'reality' ? 'Fantasy' : 'Reality');
                self.scorelist.collection.fetch({
                    url: '/api/results/'+tournament_id+'/'+self.scorelist.collection.page
                });
            }, 'json');
        });

        this.scorelist.$('#tournament_selector').bind('change', function() {
            self.list();
        });
    };
    _.extend(LeaderboardsController.prototype, {
        list: function(tournament, page) {
            var self = this,
                tournament_id = '';
            this.init();
            this.scorelist.collection.page = page ? page : '0';
            this.scorelist.collection.results_type = pmode;
            if (tournament) {
                tournament_id = tournament;
                this.scorelist.$('#tournament_selector').val(tournament);
            } else {
                tournament_id = this.scorelist.$('#tournament_selector').val();
            }
            this.scorelist.collection.tournament_id = tournament_id;
            this.scorelist.collection.fetch({
                url: '/api/results/'+tournament_id+'/'+this.scorelist.collection.page,
                success: function(collection, resp) {
                    var page = self.scorelist.collection.page > 1 ? '/'+self.scorelist.collection.page : '';
                    window.router.navigate('leaderboard/'+tournament_id+'/'+self.scorelist.collection.page);
                }
            });
        }
    });


    /* Ad management
    */

    // A single row in the ad list
    var AdRow = MGModelRowView.extend({
        tagName: 'tr',
        template: $('#ads-row-template')
    });
    // A paged list of ads
    var AdAdminList = PaginatedView.extend({
        template: '#ads-list-template',
        collection: new AdCollection(),
        model_view: AdRow
    });
    // The ad editing interface
    var AdEditor = MGFormView.extend({
        template: $('#ads-edit-template'),
        render: function() {
            var self = this;
            this.model.set({slots: this.model.collection.slots}, {silent: true});

            MGFormView.prototype.render.call(this);
            this.model.set({slot: this.$('#slot').val()});

            // set up datepicker restricted to tournament play dates
            this.$('#date_expires').datepicker({
                dateFormat: 'yy-mm-dd'
            });

            var countries = {};
            $('#country_picker').addClass('noautoset').autocomplete({
                source: countries,
                select: function(e, ui) {
                    //self.change_province();
                    self.change_province(ui);
                },
                change: function(e, ui) {
                    //self.change_province();
                }
            });
            $.getJSON('/api/country_search/', {}, function(data) {
                countries = data;
                $('#country_picker').autocomplete("option", "source", countries);
            });


            this.resize_image();
            this.updateTags();
            return this;
        },
        initialize: function(options) {
            MGFormView.prototype.initialize.call(this, options);
            _.bindAll(this, 'upload_file');
            this.el.show();
            this.mbus.bind('resetting', this.render);
        },
        events: _.extend(_.clone(MGFormView.prototype.events), {
            'change #file': 'upload_file',
            'change #slot': 'resize_image',
            'change #prov_picker': 'change_cities',
            'click #addTags': 'addTags',
            'click #removeTag': 'removeTag',
            'click :file': 'ie_upload'
        }),
        upload_file: function() {
            var self = this;
            $('#file_form').ajaxSubmit({
                url: '/api/ad_file/'+(this.model.get('id') || ''),
                dataType: 'json',
                beforeSubmit: function(arr) {
                    $('#upload_status').show();
                },
                success: function(resp, status) {
                    self.render();
                }
            });
        },
        resize_image: function() {
            $.getJSON('/api/ad_slot/'+$("#slot").val(), {}, function(data) {
                data = data[0];
                $("#file_image").animate({
                    width: data.width,
                    height: data.height
                }, 1000);
            });
        },
        change_province: function(country) {
            country = country.item;

            var html = _(country.provinces).map(function(province) {
                return '<option value="'+province.value+'">'+province.label+'</option>';
            });

            $("#prov_picker").html( html.join("") );

        },
        change_cities: function() {
            var prov = $("#prov_picker").val();
            $.getJSON('/api/get_cities/'+prov, {}, function(data) {

                var html = [];

                html.push(" <option value='Public'>Public</option><option disabled>------</option> ");
                _(data).each(function(item) {
                    html.push("<option value='"+item.name+"'>"+item.name+"</option>");
                });

                $("#city_picker").html( html.join("") );

            });
        },
        addTags: function() {
            //console.log( $("#city_picker").val() );
            var cities = $("#city_picker").val();

            $("#chosentags option").attr("selected", "selected");
            var existing = $("#chosentags").val(); // because its select box we gotta parse the raw html

            var html = [];
            var tags = [];

            // First lets add in the existing stuff
            _(existing).each(function(e) {
                tags.push(e);
                html.push( "<option value='"+e+"'>"+e+"</option>" );
            });

            _(cities).each(function(city) {
                if (_.indexOf(existing, city) < 0) {
                    tags.push(city);
                    html.push('<option value="'+city+'">'+city+'</option>');
                }
            });
            $("#chosentags").html( html.join("") );
            $("#tags").val(tags.join(",")).change();
        },
        removeTag: function() {
            var selected = $("#chosentags").val(); // because its select box we gotta parse the raw html

            if(selected.length > 0){

                $("#chosentags option").attr("selected", "selected");
                var everything = $("#chosentags").val(); // because its select box we gotta parse the raw html

                var html = [];
                var tags = [];

                _(everything).each(function(item) {
                    if (_.indexOf(selected, item) < 0) {
                        tags.push(item);
                        html.push('<option value="'+item+'">'+item+'</option>');
                    }
                });

                $("#chosentags").html( html.join("") );
                $("#tags").val(tags.join(",")).change();

            } else {
                return false;
            }

        },
        updateTags: function() {
            if($.trim($("#tags").val()) !== "") {
                var tags = $("#tags").val().split(",");
                var html = _(tags).map(function(tag) {
                    return "<option value='"+tag+"'>"+tag+"</option>";
                });

                $("#chosentags").html( html.join("") );
            }
        }
    });

    /*
    * Ad Controller
    */
    var AdAdminController = function(options) {
        var self = this;
        this.mymodel = Ad;
        this.mbus = _.extend({}, Backbone.Events);

        this.init = function() {
            self.editarea = $('#editform');
            self.adeditor = new AdEditor({
                mbus: this.mbus,
                el: self.editarea
            });
            self.adlist = new AdAdminList({
                el: $('#adlist')
            });
            self.adlist.mbus = this.mbus;
        };
        this.init();

        this.mbus.bind('backtolist', function(model) {
            this._list_mode();
        }, this);

        this.adlist.collection.bind('add', function(model) {
            model.set({slots: this.adlist.collection.slots}, {silent: true});
            this.edit(model);
        }, this);
        this.adlist.mbus.bind('editing', function(model) {
            model.set({slots: this.adlist.collection.slots}, {silent: true});
            this.edit(model);
        }, this);
        this.adlist.mbus.bind('canceling', function(model) {
            if (model.isNew()) {
                model.destroy();
            }
            this.adeditor.model = {};
            this._list_mode();
        }, this);
        this.adlist.mbus.bind('saving', function(model) {
            var self = this,
                isnew = model.isNew();

            model.save(model, {}, {
                success: function(model, resp) {
                    var p = model.get('page');
                    if (self.adlist.collection.page !== p || isnew) {
                        self.adlist.collection.page = p;
                        self.adlist.collection.fetch();
                    }
                    humane.success('Saving');
                }
            });
        }, this);
    };
    _.extend(AdAdminController.prototype, {
        _list_mode: function() {
            var page = this.adlist.collection.page > 1 ? '/'+this.adlist.collection.page : '';
            window.router.navigate('admin/'+this.adlist.collection.model.prototype.label.pluralize()+page);
            this.adlist.showme();
            this.editarea.hide();
        },
        _edit_mode: function() {
            var self = this;
            this.adlist.hideme();
            window.router.navigate('admin/ad/'+(this.adeditor.model.get('id') || 'new'));
            this.adeditor.render();
            this.editarea.show();
        },

        list: function(page) {
            var self = this;
            if (this.adlist.render_to.children().length > 0) {
                this.init();
            }
            this.adlist.collection.page = (page ? page : 1);
            this.adlist.collection.fetch({success: function(collection, resp) {
                self._list_mode();
            }});
        },
        edit: function(id) {
            if (id instanceof this.mymodel) {
                this.adeditor.model = id;
                this._edit_mode();
            } else {
                var self = this;
                $.getJSON('/api/pageof/ad/'+id,
                    {perpage: this.adlist.collection.perPage},
                    function(data) {
                        self.adlist.collection.page = data.page;
                        self.adlist.collection.fetch({
                            success: function(coll, resp) {
                                if (id === 'new') {
                                    self.adlist.collection.add({});
                                } else {
                                    self.adeditor.model = coll.find(function(item) {
                                        return item.get('id') === id;
                                    });
                                    self.adeditor.model.set({slots: self.adlist.collection.slots}, {silent: true});
                                }
                                self._edit_mode();
                            }
                        });
                    }
                );
            }
        }
    });


    /* App
    *  Sets up the urls used to navigate the app, and loads the right
    *  controllers
    */
    var App = Backbone.Router.extend({
        routes: {
            "facilities": "facility_list",
            "facilities/:page": "facility_list",
            "facility/:id": "facility_edit",
            "facilities/new": "facility_edit",

            "admin/ads": "ads_list",
            "admin/ads/:page": "ads_list",
            "admin/ad/:id": "ads_edit",
            "admin/ads/:id": "ads_edit",
            "admin/ads/new": "ads_edit",

            "facility": "single_facility_edit",

            "players": "facility_player_list",
            "players/:page": "facility_player_list",
            "player/:id": "facility_player_edit",
            "player/new": "facility_player_edit",

            "users/players": "player_list",
            "users/players/:page": "player_list",
            "users/player/:id": "player_edit",
            "users/player/new": "player_edit",

            "users/facility_admins": "facility_admin_list",
            "users/facility_admins/:page": "facility_admin_list",
            "users/facility_admin/:id": "facility_admin_edit",
            "users/facility_admin/new": "facility_admin_edit",

            "tournaments": "tournament_list",
            "tournaments/:page": "tournament_list",
            "tournament/:id": "tournament_edit",
            "tournaments/new": "tournament_edit",

            "leaderboard": "leaderboard",
            "leaderboard/:mode": "leaderboard_m",
            "leaderboard/:tournament/:page": "leaderboard",

            "choose_facility": "choose_facility",
            "signup/choose_facility": "signup_choose_facility",
            "search_courses": "scores_no_facility"
        },
        controllers : {},
        choose_facility: function() {
            if (! this.controllers.facilitychooser) {
                $('.content_home').html($.mustache($('#facility-picker-wrapper-template').html()));
                this.controllers.facilitychooser = new FacilityPickerController();
            }
            this.controllers.facilitychooser.list();
        },
        signup_choose_facility: function() {
            if (! this.controllers.facilitychooser) {
                $('.content_home').html($.mustache($('#facility-picker-signup-wrapper-template').html()));
                this.controllers.facilitychooser = new FacilityPickerController();
            }
            this.controllers.facilitychooser.list();
        },
        scores_no_facility: function() {
            if (! this.controllers.facilitychooser) {
                $('#facility_picker').html($.mustache($('#facility-picker-template').html()));
                this.controllers.facilitychooser = new FacilityPickerController();
            }
            this.controllers.facilitychooser.list();
        },
        facility_list: function(page) {
            $('.content_home').html($.mustache($('#facilities-admin-template').html()));
            if (!this.controllers.facilityadmin) {
                this.controllers.facilityadmin = new FacilityAdminController();
            }
            this.controllers.facilityadmin.list(page);
        },
        facility_edit: function(id) {
            $('.content_home').html($.mustache($('#facilities-admin-template').html()));
            if (!this.controllers.facilityadmin) {
                this.controllers.facilityadmin = new FacilityAdminController();
            }
            this.controllers.facilityadmin.edit(id);
        },
        single_facility_edit: function() {
            $('.content_home').html($.mustache($('#facilities-admin-template').html()));
            if (!this.controllers.singlefacilityadmin) {
                this.controllers.singlefacilityadmin = new SingleFacilityAdminController();
            }
            this.controllers.singlefacilityadmin.edit();
        },
        ads_list: function(page) {
            $('.content_home').html($.mustache($('#ads-admin-template').html()));
            if (!this.controllers.adsadmin) {
                this.controllers.adsadmin = new AdAdminController();
            }
            this.controllers.adsadmin.list(page);
        },
        ads_edit: function(id) {
            $('.content_home').html($.mustache($('#ads-admin-template').html()));
            if (!this.controllers.adsadmin) {
                this.controllers.adsadmin = new AdAdminController();
            }
            this.controllers.adsadmin.edit(id);
        },
        player_list: function(page) {
            $('.content_home').html($.mustache($('#players-admin-template').html()));
            if (!this.controllers.playeradmin) {
                this.controllers.playeradmin = new PlayerAdminController();
            }
            this.controllers.playeradmin.list(page);
        },
        player_edit: function(id) {
            $('.content_home').html($.mustache($('#players-admin-template').html()));
            if (!this.controllers.playeradmin) {
                this.controllers.playeradmin = new PlayerAdminController();
            }
            this.controllers.playeradmin.edit(id);
        },
        facility_player_list: function(page) {
            $('.content_home').html($.mustache($('#facility-players-admin-template').html()));
            this.Controller = new FacilityPlayerAdminController();
            this.Controller.list(page);
        },
        facility_player_edit: function(id) {
            $('.content_home').html($.mustache($('#facility-players-admin-template').html()));
            this.Controller = new FacilityPlayerAdminController();
            this.Controller.edit(id);
        },
        facility_admin_list: function(page) {
            $('.content_home').html($.mustache($('#players-admin-template').html()));
            this.Controller = new FacilityAdminAdminController();
            this.Controller.list(page);
        },
        facility_admin_edit: function(id) {
            $('.content_home').html($.mustache($('#players-admin-template').html()));
            this.Controller = new FacilityAdminAdminController();
            this.Controller.edit(id);
        },
        tournament_list: function(page) {
            $('.content_home').html($.mustache($('#tournaments-admin-template').html()));
            if (!this.controllers.tournamentadmin) {
                this.controllers.tournamentadmin = new TournamentAdminController();
            }
            this.controllers.tournamentadmin.list(page);
        },
        tournament_edit: function(id) {
            $('.content_home').html($.mustache($('#tournaments-admin-template').html()));
            if (!this.controllers.tournamentadmin) {
                this.controllers.tournamentadmin = new TournamentAdminController();
            }
            this.controllers.tournamentadmin.edit(id);
        },
        leaderboard: function(tournament, page) {
            if (tournaments.length === 0) {
                $('.content_home').html($.mustache($('#leaderboards_none-template').html()));
            } else {
                $('.content_home').html($.mustache($('#leaderboards_'+pmode+'-template').html(), {
                    tournaments: tournaments,
                    pmode_label: pmode.capitalize(),
                    pmode_other_label: pmode === 'reality' ? 'Fantasy' : 'Reality'
                }));
                this.Controller = new LeaderboardsController();
                this.Controller.list(tournament, page);
            }
        },
        leaderboard_m: function(mode) {
            if ($('#noleaderboards').length === 0) {
                pmode = mode;
                $('.content_home').html($.mustache($('#leaderboards_'+pmode+'-template').html(), {
                    tournaments: tournaments,
                    pmode_label: pmode.capitalize(),
                    pmode_other_label: pmode === 'reality' ? 'Fantasy' : 'Reality'
                }));
                this.Controller = new LeaderboardsController();
                this.Controller.list();
            }
        }
    });

    var bburls = /(?:\/|#)(?:search_courses|(?:signup\/)?choose_facility|admin\/ads?|leaderboard|tournaments?|(?:users\/)?players?|users\/facility_admins?|facility|facilities)/;

    window.has_pushstate = history.pushState ? true : false;

    window.router = new App();
    if (window.location.pathname.match(bburls) || window.location.hash.match(bburls)) {
        Backbone.history.start({pushState: true, root: "/"});
    } else if (has_pushstate) {
        Backbone.history.start({pushState: true, root: "/", silent: true});
    }
    var submenus = $('#subnav').find('a');
    if (!has_pushstate && window.location.hash.length > 1) {
        submenus.removeClass('selected');
        var h = window.location.hash.replace(/^#([^\/]+)(?:\/.+)?/, "$1");
        _(submenus).each(function(m) {
            var $m = $(m),
                str = $m.attr('href').replace(/^\//, '');
            if (str === h || str === h.pluralize()) {
                $m.addClass('selected');
                $m.addClass('selected').closest('ul').siblings('a').addClass('selected');
            }
        });
        var $mlink = $('#modeswitch a');
        if ($mlink.length > 0) {
            $mlink.attr('href', $mlink.attr('href')+'?hash='+window.location.hash.replace(/^#/, ''));
            $(window).bind('pushstate', function() {
                $mlink.attr('href', $mlink.attr('href').replace(/\?.+$/, '')+'?hash='+window.location.hash.replace(/^#/, ''));
            });
        }
    }
    $('#subnav a').bind('click', function(e) {
        var $clicked = $(e.target);
        if ($clicked.siblings('ul.children').length > 0) {
            e.preventDefault();
            return;
        }
        if ($clicked.hasClass('bb')) {
            e.preventDefault();
            if (!has_pushstate && window.location.pathname !== '/') {
                window.location.href = 'http://'+document.domain+'#'+$clicked.attr('href').replace(/^\//, '');
            } else {
                submenus.removeClass('selected');
                $clicked.addClass('selected').closest('ul').siblings('a').addClass('selected');
                window.router.navigate($clicked.attr('href').replace(/^\//, ''), true);
            }
        }
    });

}());

