//     Backbone.js 1.3.3
"use strict";
//     (c) 2010-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
//     Backbone may be freely distributed under the MIT license.
//     For all details and documentation:
//     http://backbonejs.org
var _ = require("underscore");
var $ = require("jquery");
var events_1 = require("./events");
// Backbone.Model
// --------------
// Backbone **Models** are the basic data object in the framework --
// frequently representing a row in a table in a database on your server.
// A discrete chunk of data and a bunch of useful, related methods for
// performing computations and transformations on that data.
// Create a new model with the specified attributes.
exports.Model = function (attributes, options) {
    var attrs = attributes || {};
    options || (options = {});
    this.attributes = {};
    this.setv(attrs, options);
    this.changed = {};
    this.initialize.apply(this, arguments);
};
// Attach all inheritable methods to the Model prototype.
_.extend(exports.Model.prototype, events_1.Events, {
    // A hash of attributes whose current and previous value differ.
    changed: null,
    // Initialize is an empty function by default. Override it with your own
    // initialization logic.
    initialize: function () { },
    // Get the value of an attribute.
    getv: function (attr) {
        return this.attributes[attr];
    },
    // Set a hash of model attributes on the object, firing `"change"`. This is
    // the core primitive operation of a model, updating the data and notifying
    // anyone who needs to know about the change in state. The heart of the beast.
    setv: function (key, val, options) {
        if (key == null)
            return this;
        // Handle both `"key", value` and `{key: value}` -style arguments.
        var attrs;
        if (typeof key === 'object') {
            attrs = key;
            options = val;
        }
        else {
            (attrs = {})[key] = val;
        }
        options || (options = {});
        // Extract attributes and options.
        var silent = options.silent;
        var changes = [];
        var changing = this._changing;
        this._changing = true;
        if (!changing) {
            this._previousAttributes = _.clone(this.attributes);
            this.changed = {};
        }
        var current = this.attributes;
        var changed = this.changed;
        var prev = this._previousAttributes;
        // For each `set` attribute, update or delete the current value.
        for (var attr in attrs) {
            val = attrs[attr];
            if (!_.isEqual(current[attr], val))
                changes.push(attr);
            if (!_.isEqual(prev[attr], val)) {
                changed[attr] = val;
            }
            else {
                delete changed[attr];
            }
            current[attr] = val;
        }
        // Trigger all relevant attribute changes.
        if (!silent) {
            if (changes.length)
                this._pending = true;
            for (var i = 0; i < changes.length; i++) {
                this.trigger('change:' + changes[i], this, current[changes[i]]);
            }
        }
        // You might be wondering why there's a `while` loop here. Changes can
        // be recursively nested within `"change"` events.
        if (changing)
            return this;
        if (!silent) {
            while (this._pending) {
                this._pending = false;
                this.trigger('change', this);
            }
        }
        this._pending = false;
        this._changing = false;
        return this;
    },
    destroy: function () {
        this.stopListening();
        this.trigger('destroy', this);
    },
    // Create a new model with identical attributes to this one.
    clone: function () {
        return new this.constructor(this.attributes);
    }
});
// Backbone.View
// -------------
// Backbone Views are almost more convention than they are actual code. A View
// is simply a JavaScript object that represents a logical chunk of UI in the
// DOM. This might be a single item, an entire list, a sidebar or panel, or
// even the surrounding frame which wraps your whole app. Defining a chunk of
// UI as a **View** allows you to define your DOM events declaratively, without
// having to worry about render order ... and makes it easy for the view to
// react to specific changes in the state of your models.
// Creating a Backbone.View creates its initial element outside of the DOM,
// if an existing element is not provided...
exports.View = function (options) {
    _.extend(this, _.pick(options, viewOptions));
    this._ensureElement();
    this.initialize.apply(this, arguments);
};
// Cached regex to split keys for `delegate`.
var delegateEventSplitter = /^(\S+)\s*(.*)$/;
// List of view options to be set as properties.
var viewOptions = ['model', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
// Set up all inheritable **Backbone.View** properties and methods.
_.extend(exports.View.prototype, events_1.Events, {
    // The default `tagName` of a View's element is `"div"`.
    tagName: 'div',
    // Initialize is an empty function by default. Override it with your own
    // initialization logic.
    initialize: function () { },
    // **render** is the core function that your view should override, in order
    // to populate its element (`this.el`), with the appropriate HTML. The
    // convention is for **render** to always return `this`.
    render: function () {
        return this;
    },
    // Remove this view by taking the element out of the DOM, and removing any
    // applicable Backbone.Events listeners.
    remove: function () {
        this._removeElement();
        this.stopListening();
        return this;
    },
    // Remove this view's element from the document and all event listeners
    // attached to it. Exposed for subclasses using an alternative DOM
    // manipulation API.
    _removeElement: function () {
        this.$el.remove();
    },
    // Change the view's element (`this.el` property) and re-delegate the
    // view's events on the new element.
    setElement: function (element) {
        this.undelegateEvents();
        this._setElement(element);
        this.delegateEvents();
        return this;
    },
    // Creates the `this.el` and `this.$el` references for this view using the
    // given `el`. `el` can be a CSS selector or an HTML string, a jQuery
    // context or an element. Subclasses can override this to utilize an
    // alternative DOM manipulation API and are only required to set the
    // `this.el` property.
    _setElement: function (el) {
        this.$el = el instanceof $ ? el : $(el);
        this.el = this.$el[0];
    },
    // Set callbacks, where `this.events` is a hash of
    //
    // *{"event selector": "callback"}*
    //
    //     {
    //       'mousedown .title':  'edit',
    //       'click .button':     'save',
    //       'click .open':       function(e) { ... }
    //     }
    //
    // pairs. Callbacks will be bound to the view, with `this` set properly.
    // Uses event delegation for efficiency.
    // Omitting the selector binds the event to `this.el`.
    delegateEvents: function (events) {
        events || (events = _.result(this, 'events'));
        if (!events)
            return this;
        this.undelegateEvents();
        for (var key in events) {
            var method = events[key];
            if (!_.isFunction(method))
                method = this[method];
            if (!method)
                continue;
            var match = key.match(delegateEventSplitter);
            this.delegate(match[1], match[2], method.bind(this));
        }
        return this;
    },
    // Add a single event listener to the view's element (or a child element
    // using `selector`). This only works for delegate-able events: not `focus`,
    // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
    delegate: function (eventName, selector, listener) {
        this.$el.on(eventName + '.delegateEvents' + this.id, selector, listener);
        return this;
    },
    // Clears all callbacks previously bound to the view by `delegateEvents`.
    // You usually don't need to use this, but may wish to if you have multiple
    // Backbone views attached to the same DOM element.
    undelegateEvents: function () {
        if (this.$el)
            this.$el.off('.delegateEvents' + this.id);
        return this;
    },
    // A finer-grained `undelegateEvents` for removing a single delegated event.
    // `selector` and `listener` are both optional.
    undelegate: function (eventName, selector, listener) {
        this.$el.off(eventName + '.delegateEvents' + this.id, selector, listener);
        return this;
    },
    // Produces a DOM element to be assigned to your view. Exposed for
    // subclasses using an alternative DOM manipulation API.
    _createElement: function (tagName) {
        return document.createElement(tagName);
    },
    // Ensure that the View has a DOM element to render into.
    // If `this.el` is a string, pass it through `$()`, take the first
    // matching element, and re-assign it to `el`. Otherwise, create
    // an element from the `id`, `className` and `tagName` properties.
    _ensureElement: function () {
        if (!this.el) {
            var attrs = _.extend({}, _.result(this, 'attributes'));
            if (this.id)
                attrs.id = _.result(this, 'id');
            if (this.className)
                attrs['class'] = _.result(this, 'className');
            this.setElement(this._createElement(_.result(this, 'tagName')));
            this._setAttributes(attrs);
        }
        else {
            this.setElement(_.result(this, 'el'));
        }
    },
    // Set attributes from a hash on this view's element.  Exposed for
    // subclasses using an alternative DOM manipulation API.
    _setAttributes: function (attributes) {
        this.$el.attr(attributes);
    }
});
exports.Model.getter = exports.View.getter = function (name, get) {
    Object.defineProperty(this.prototype, name, { get: get });
};
exports.Model.getters = exports.View.getters = function (specs) {
    for (var name in specs) {
        this.getter(name, specs[name]);
    }
};
