<commit_before>define(["js/core/EventDispatcher", "js/core/Bindable", "underscore", "js/data/Query"], function (EventDispatcher, Bindable, _, Query) {

    var List = Bindable.inherit("js.core.List", {
        /**
         * List constructor
         * @param [Object] items to add
         * @param Object attributes to set
         */
        ctor: function (items, options) {
            options = options || {};

            this.$items = [];
            this.$itemEventMap = {};

            this.callBase(options);

            if (items) {
                this.add(items);
            }

            var self = this;

            function updateLength() {
                self.length = self.size();
            }

            // TODO: allow array here to give multiple event hin
            this.bind('add', updateLength);
            this.bind('remove', updateLength);
            this.bind('reset', updateLength);

            this.length = this.size();
        },
        /**
         *
         * @return {Boolean} true if list has items
         */
        isEmpty: function () {
            return this.$items.length === 0;
        }.on('*'),
        /**
         * Pushes one item to the list
         * @param item
         */
        push: function (item) {
            this.add(item);
        },
        /**
         * Removes last item  of the list
         * @return {*}
         */
        pop: function () {
            return this.removeAt(this.$items.length);
        },
        /**
         * Removes first item of list
         * @return {*}
         */
        shift: function () {
            return this.removeAt(0, {});
        },
        /**
         * Adds item to first position of list
         * @param item
         */
        unshift: function (item) {
            this.add(item, {
                index: 0
            });
        },
        /**
         * This method adds one ore items to the array.
         * @param items
         * @param options
         */
        add: function (items, options) {

            options = options || {};
            _.defaults(options, {silent: false, index: this.$items.length});

            var index = options.index;

            if (!_.isArray(items)) {
                items = [items];
            }
            var item, itemIndex;
            for (var i = 0; i < items.length; i++) {
                item = items[i];
                if (item instanceof Bindable) {
                    item.bind('change', this._onItemChange, this);
                    item.bind('*', this._onItemEvent, this);
                }
                itemIndex = index + i;
                if(_.isUndefined(this.$items[itemIndex]) || itemIndex >= this.$items.length){
                    this.$items[itemIndex] = item;
                } else {
                    this.$items.splice(itemIndex,0,item);
                }
                if (options.silent !== true) {
                    this.trigger('add', {item: item, index: itemIndex})
                }
            }
        },
        /**
         *
         * @param e
         * @param item
         * @private
         */
        _onItemChange: function (e, item) {
            this.trigger('change', {item: item, index: this.indexOf(item), changedAttributes: e.$});
        },
        _onItemEvent: function (e, item) {
            if (this.$itemEventMap[e.type]) {
                var listeners = this.$itemEventMap[e.type],
                    listener;
                for (var i = 0; i < listeners.length; i++) {
                    listener = listeners[i];
                    this.trigger(listener.eventType, {item: item, index: this.indexOf(item), itemEvent: e}, listener.callback, listener.scope);
                }
            }
        },
        /**
         * Removes an Array or just one item from the list. Triggers remove events.
         * @param Object | [Object] items
         * @param options
         */
        remove: function (items, options) {
            var removed = [];

            if (!_.isArray(items)) {
                items = [items];
            }
            var item;
            for (var i = 0; i < items.length; i++) {
                item = this.removeAt(this.indexOf(items[i]), options);
                item && removed.push(item);
            }
            return removed;
        },
        /**
         * Returns the index of the item
         * @param item
         * @return {*}
         */
        indexOf: function (item) {
            return this.$items.indexOf(item);
        },
        /**
         * Removes one item a specific index and triggers remove event
         * @param index
         * @param options
         * @return {*}
         */
        removeAt: function (index, options) {
            options = options || {};

            if (index > -1 && index < this.$items.length) {
                var items = this.$items.splice(index, 1), item = items[0];
                if (options.silent !== true) {
                    this.trigger('remove', {item: item, index: index});
                }
                if (item instanceof Bindable) {
                    item.unbind('*', this._onItemEvent, this);
                    item.unbind('change', this._onItemChange, this);
                }
                return items[0];
            }
            return null;
        },
        /**
         * Resets the list with the given items and triggers reset event
         * @param items
         */
        reset: function (items, options) {
            items = items || [];
            var self = this;
            this.each(function (item) {
                if (item instanceof EventDispatcher) {
                    item.unbind('*', self._onItemEvent, self);
                    item.unbind('change', self._onItemChange, self);
                }
            });

            this.$items = items || [];
            this.each(function (item) {
                if (item instanceof EventDispatcher) {
                    item.bind('*', self._onItemEvent, self);
                    item.bind('change', self._onItemChange, self);
                }
            });

            options = options || {};
            if (!options.silent) {
                this.trigger('reset', {items: items});
            }
        },

        /**
         * Clears all items and triggers reset event
         */
        clear: function (options) {
            this.reset([], options);
        },
        /**
         * Returns the size of the list
         */
        size: function () {
            return this.$items.length;
        }.on('*'),

        /**
         * Returns item at a specific index
         * @param index
         * @return {*}
         */
        at: function (index) {
            if (index < this.$items.length && index >= 0) {
                return this.$items[index];
            }
            return null;
        }.on('*'),
        /**
         * Iterates over all items with given callback
         * @param Function callback with signature function(item, index)
         * @param Object The call scope of the callback
         */
        each: function (fnc, scope) {
            scope = scope || this;

            if (scope['break']) {
                throw "each would overwrite break";
            }

            if (scope['return']) {
                throw "each would overwrite return";
            }

            var b = false,
                r,
                error;

            scope['break'] = function () {
                b = true;
            };

            scope['return'] = function (value) {
                b = true;
                r = value;
            };

            for (var i = 0; i < this.$items.length; i++) {
                try {
                    fnc.call(scope, this.$items[i], i, this.$items);
                } catch(e) {
                    error = e;
                    b = true;
                }

                if (b) {
                    break;
                }
            }

            scope['break'] = undefined;
            scope['return'] = undefined;

            if (error) {
                throw error;
            }

            return r;
        },
        /**
         * Checks if item is included in List
         * @return Boolean
         */
        includes: function (item) {
            var ret = this.each(function (innerItem) {
                if (innerItem === item) {
                    this["return"](true);
                }
            });
            if (ret) {
                return ret;
            } else {
                return false;
            }
        }.on('*'),

        /**
         * Returns a fresh copy of the List
         * @return List a fresh copy of the list
         */
        clone: function () {
            var attributes = this._cloneAttribute(this.$);
            var items = this._cloneAttribute(this.$items);
            var ret = new this.factory(items, attributes);
            ret._$source = this;
            return ret;
        },

        sync: function () {
            if (this._$source) {
                var item, items = [];
                for (var i = 0; i < this.$items.length; i++) {
                    item = this.$items[i];
                    if (item instanceof Bindable && item.sync()) {
                        item = item._$source;
                    }
                    items.push(item);
                }
                this._$source.reset(items);
            }
            return this.callBase();
        },

        _innerDestroy: function () {
            this.$itemEventMap = {};
            this.callBase();
        },

        bind: function (eventType, callback, scope) {
            var i = eventType.indexOf("item:");
            if (i === 0) {
                var itemEvent = eventType.substr("item:".length);
                this.$itemEventMap[itemEvent] = this.$itemEventMap[itemEvent] || [];
                this.$itemEventMap[itemEvent].push({
                    eventType: eventType,
                    callback: callback,
                    scope: scope
                });
            }
            this.callBase();
        },

        unbind: function (eventType, callback, scope) {
            var i = eventType.indexOf("item:");
            if (i === 0) {
                var itemEvent = eventType.substr("item:".length),
                    listeners = this.$itemEventMap[itemEvent] || [],
                    listener;

                for (var j = listeners.length - 1; j > -1; j--) {
                    listener = listeners[j];
                    if (listener.callback === callback && listener.scope === scope) {
                        listeners.splice(i, 1);
                    }
                }
            } else {
                this.callBase();
            }
        },

        /***
         * Transforms List to an array
         * You can specify a transform fnc
         * @param {Function} transformFnc
         * @return {Array}
         */
        toArray: function (transformFnc) {
            var ret = [];
            transformFnc = transformFnc || function (item, index) {
                return item;
            };

            this.each(function (item, index) {
                ret.push(transformFnc(item, index))
            });

            return ret;
        },

        /**
         * Sorts the list by the given function and triggers sort event
         * @param {Function|js.data.Query} fnc
         */
        sort: function (fnc) {
            if (fnc instanceof Function) {
                // TODO:
                this.trigger('sort', {items: this.$items.sort(fnc), sortFnc: fnc});
            }
            return this;
        },

        isDeepEqual: function (list) {
            if (list.size() !== this.size()) {
                return false;
            }
            var isEqual = true,
                a, b;
            for (var i = 0; i < this.$items.length; i++) {
                a = this.$items[i];
                b = list.at(i);
                if (a instanceof Bindable && b instanceof Bindable) {
                    if (!a.isDeepEqual(b)) {
                        return false;
                    }
                } else if (a instanceof Bindable || b instanceof Bindable) {
                    return false;
                } else {
                    isEqual = _.isEqual(a, b);
                }
            }

            return isEqual;
        }
    });

    return List;
});<commit_msg>fixed jsdoc for List<commit_after>define(["js/core/EventDispatcher", "js/core/Bindable", "underscore", "js/data/Query"], function (EventDispatcher, Bindable, _, Query) {

    var List = Bindable.inherit("js.core.List", {
        /**
         * List constructor
         * @param {Array} items to add
         * @param {Object} options - attributes to set
         */
        ctor: function (items, options) {
            options = options || {};

            this.$items = [];
            this.$itemEventMap = {};

            this.callBase(options);

            if (items) {
                this.add(items);
            }

            var self = this;

            function updateLength() {
                self.length = self.size();
            }

            // TODO: allow array here to give multiple event hin
            this.bind('add', updateLength);
            this.bind('remove', updateLength);
            this.bind('reset', updateLength);

            this.length = this.size();
        },
        /**
         *
         * @return {bool} true if list has items
         */
        isEmpty: function () {
            return this.$items.length === 0;
        }.on('*'),
        /**
         * Pushes one item to the list
         * @param item
         */
        push: function (item) {
            this.add(item);
        },
        /**
         * Removes last item  of the list
         * @return {*}
         */
        pop: function () {
            return this.removeAt(this.$items.length);
        },
        /**
         * Removes first item of list
         * @return {*}
         */
        shift: function () {
            return this.removeAt(0, {});
        },
        /**
         * Adds item to first position of list
         * @param item
         */
        unshift: function (item) {
            this.add(item, {
                index: 0
            });
        },
        /**
         * This method adds one ore items to the array.
         * @param {Array|Object} items
         * @param {Object} options
         */
        add: function (items, options) {

            options = options || {};
            _.defaults(options, {silent: false, index: this.$items.length});

            var index = options.index;

            if (!_.isArray(items)) {
                items = [items];
            }
            var item, itemIndex;
            for (var i = 0; i < items.length; i++) {
                item = items[i];
                if (item instanceof Bindable) {
                    item.bind('change', this._onItemChange, this);
                    item.bind('*', this._onItemEvent, this);
                }
                itemIndex = index + i;
                if(_.isUndefined(this.$items[itemIndex]) || itemIndex >= this.$items.length){
                    this.$items[itemIndex] = item;
                } else {
                    this.$items.splice(itemIndex,0,item);
                }
                if (options.silent !== true) {
                    this.trigger('add', {item: item, index: itemIndex})
                }
            }
        },
        /**
         *
         * @param {Event} e
         * @param {Object} item
         * @private
         */
        _onItemChange: function (e, item) {
            this.trigger('change', {item: item, index: this.indexOf(item), changedAttributes: e.$});
        },
        _onItemEvent: function (e, item) {
            if (this.$itemEventMap[e.type]) {
                var listeners = this.$itemEventMap[e.type],
                    listener;
                for (var i = 0; i < listeners.length; i++) {
                    listener = listeners[i];
                    this.trigger(listener.eventType, {item: item, index: this.indexOf(item), itemEvent: e}, listener.callback, listener.scope);
                }
            }
        },
        /**
         * Removes an Array or just one item from the list. Triggers remove events.
         * @param {Object|Array} items
         * @param {Object} options
         */
        remove: function (items, options) {
            var removed = [];

            if (!_.isArray(items)) {
                items = [items];
            }
            var item;
            for (var i = 0; i < items.length; i++) {
                item = this.removeAt(this.indexOf(items[i]), options);
                item && removed.push(item);
            }
            return removed;
        },
        /**
         * Returns the index of the item
         * @param {Object} item
         * @return {Number} index
         */
        indexOf: function (item) {
            return this.$items.indexOf(item);
        },
        /**
         * Removes one item a specific index and triggers remove event
         * @param {Number} index
         * @param {Object} options
         * @return {Object} removed item
         */
        removeAt: function (index, options) {
            options = options || {};

            if (index > -1 && index < this.$items.length) {
                var items = this.$items.splice(index, 1), item = items[0];
                if (options.silent !== true) {
                    this.trigger('remove', {item: item, index: index});
                }
                if (item instanceof Bindable) {
                    item.unbind('*', this._onItemEvent, this);
                    item.unbind('change', this._onItemChange, this);
                }
                return items[0];
            }
            return null;
        },
        /**
         * Resets the list with the given items and triggers reset event
         * @param {Array} items
         * @param {Object} options
         */
        reset: function (items, options) {
            items = items || [];
            var self = this;
            this.each(function (item) {
                if (item instanceof EventDispatcher) {
                    item.unbind('*', self._onItemEvent, self);
                    item.unbind('change', self._onItemChange, self);
                }
            });

            this.$items = items || [];
            this.each(function (item) {
                if (item instanceof EventDispatcher) {
                    item.bind('*', self._onItemEvent, self);
                    item.bind('change', self._onItemChange, self);
                }
            });

            options = options || {};
            if (!options.silent) {
                this.trigger('reset', {items: items});
            }
        },

        /**
         * Clears all items and triggers reset event
         * @param {Object} options
         */
        clear: function (options) {
            this.reset([], options);
        },
        /**
         * Returns the size of the list
         */
        size: function () {
            return this.$items.length;
        }.on('*'),

        /**
         * Returns item at a specific index
         * @param index
         * @return {*}
         */
        at: function (index) {
            if (index < this.$items.length && index >= 0) {
                return this.$items[index];
            }
            return null;
        }.on('*'),
        /**
         * Iterates over all items with given callback
         * @param {Function} fnc callback with signature function(item, index)
         * @param {Object} scope The call scope of the callback
         */
        each: function (fnc, scope) {
            scope = scope || this;

            if (scope['break']) {
                throw "each would overwrite break";
            }

            if (scope['return']) {
                throw "each would overwrite return";
            }

            var b = false,
                r,
                error;

            scope['break'] = function () {
                b = true;
            };

            scope['return'] = function (value) {
                b = true;
                r = value;
            };

            for (var i = 0; i < this.$items.length; i++) {
                try {
                    fnc.call(scope, this.$items[i], i, this.$items);
                } catch(e) {
                    error = e;
                    b = true;
                }

                if (b) {
                    break;
                }
            }

            scope['break'] = undefined;
            scope['return'] = undefined;

            if (error) {
                throw error;
            }

            return r;
        },
        /**
         * Checks if item is included in List
         * @return {Boolean}
         */
        includes: function (item) {
            var ret = this.each(function (innerItem) {
                if (innerItem === item) {
                    this["return"](true);
                }
            });
            if (ret) {
                return ret;
            } else {
                return false;
            }
        }.on('*'),

        /**
         * Returns a fresh copy of the List
         * @return {List} a fresh copy of the list
         */
        clone: function () {
            var attributes = this._cloneAttribute(this.$);
            var items = this._cloneAttribute(this.$items);
            var ret = new this.factory(items, attributes);
            ret._$source = this;
            return ret;
        },
        /**
         * Syncs the items back to the source
         * @return {*}
         */
        sync: function () {
            if (this._$source) {
                var item, items = [];
                for (var i = 0; i < this.$items.length; i++) {
                    item = this.$items[i];
                    if (item instanceof Bindable && item.sync()) {
                        item = item._$source;
                    }
                    items.push(item);
                }
                this._$source.reset(items);
            }
            return this.callBase();
        },

        _innerDestroy: function () {
            this.$itemEventMap = {};
            this.callBase();
        },

        bind: function (eventType, callback, scope) {
            var i = eventType.indexOf("item:");
            if (i === 0) {
                var itemEvent = eventType.substr("item:".length);
                this.$itemEventMap[itemEvent] = this.$itemEventMap[itemEvent] || [];
                this.$itemEventMap[itemEvent].push({
                    eventType: eventType,
                    callback: callback,
                    scope: scope
                });
            }
            this.callBase();
        },

        unbind: function (eventType, callback, scope) {
            var i = eventType.indexOf("item:");
            if (i === 0) {
                var itemEvent = eventType.substr("item:".length),
                    listeners = this.$itemEventMap[itemEvent] || [],
                    listener;

                for (var j = listeners.length - 1; j > -1; j--) {
                    listener = listeners[j];
                    if (listener.callback === callback && listener.scope === scope) {
                        listeners.splice(i, 1);
                    }
                }
            } else {
                this.callBase();
            }
        },

        /***
         * Transforms List to an array
         * You can specify a transform fnc
         * @param {Function} transformFnc
         * @return {Array}
         */
        toArray: function (transformFnc) {
            var ret = [];
            transformFnc = transformFnc || function (item, index) {
                return item;
            };

            this.each(function (item, index) {
                ret.push(transformFnc(item, index))
            });

            return ret;
        },

        /**
         * Sorts the list by the given function and triggers sort event
         * @param {Function|js.data.Query} fnc
         */
        sort: function (fnc) {
            if (fnc instanceof Function) {
                // TODO:
                this.trigger('sort', {items: this.$items.sort(fnc), sortFnc: fnc});
            }
            return this;
        },
        /**
         * Checks if items are deep equal
         * @param {js.core.List} list
         * @return {boolean}
         */
        isDeepEqual: function (list) {
            if (list.size() !== this.size()) {
                return false;
            }
            var isEqual = true,
                a, b;
            for (var i = 0; i < this.$items.length; i++) {
                a = this.$items[i];
                b = list.at(i);
                if (a instanceof Bindable && b instanceof Bindable) {
                    if (!a.isDeepEqual(b)) {
                        return false;
                    }
                } else if (a instanceof Bindable || b instanceof Bindable) {
                    return false;
                } else {
                    isEqual = _.isEqual(a, b);
                }
            }

            return isEqual;
        }
    });

    return List;
});<|endoftext|>


<commit_before>var parseQueryString = function(url) {
  var urlParams = {};
  url.replace(
    new RegExp("([^?=&]+)(=([^&]*))?", "g"),
    function($0, $1, $2, $3) {
      urlParams[$1] = $3;
    }
  );
  return urlParams;
};
var urlToParse = location.search;
var result = parseQueryString(urlToParse);
var _base_url = location.origin;
var _carrier = result.carrier;
if (_carrier == undefined) {
  _carrier = '9E';
}
// Chart.js scripts
// -- Set new default font family and font color to mimic Bootstrap's default styling
Chart.defaults.global.defaultFontFamily = '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
Chart.defaults.global.defaultFontColor = '#000';
// Start Chart1
var chart1data = {
  labels: [],
  data: []
};
$.ajax({
  url: _base_url + '/chart1',
  dataType: 'JSON',
  success: function(data) {
    chart1data = data;
    var seq = palette('tol-rainbow', 14);
    var ctx = document.getElementById("myPieChart");
    var myPieChart = new Chart(ctx, {
      type: 'pie',
      data: {
        labels: chart1data.labels,
        datasets: [{
          data: chart1data.data,
          backgroundColor: palette('tol-rainbow', chart1data.data.length).map(function(hex) {
            return '#' + hex;
          })
        }],
      },
      options: {
        pieceLabel: {
          // render 'label', 'value', 'percentage', 'image' or custom function, default is 'percentage'
          render: 'label',
          // precision for percentage, default is 0
          precision: 0,
          // identifies whether or not labels of value 0 are displayed, default is false
          showZero: true,
          // font size, default is defaultFontSize
          fontSize: 12,
          // font color, can be color array for each data or function for dynamic color, default is defaultFontColor
          fontColor: '#fff',
          // font style, default is defaultFontStyle
          fontStyle: 'normal',
          // font family, default is defaultFontFamily
          fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
          // draw label in arc, default is false
          arc: true,
          // position to draw label, available value is 'default', 'border' and 'outside'
          // default is 'default'
          position: 'border',
          // draw label even it's overlap, default is false
          overlap: false,
          // set images when `render` is 'image'
          images: [{
            src: 'image.png',
            width: 16,
            height: 16
          }]
        },
        legend: {
          display: false
        }
      }
    });
    ctx.onclick = function(evt) {
      var activePoints = myPieChart.getElementsAtEvent(evt);
      if (activePoints[0]) {
        var chartData = activePoints[0]._chart.config.data;
        var idx = activePoints[0]._index;
        var label = chartData.labels[idx];
        var url = location.origin + location.pathname + "?carrier=" + label;
        location.href = url;
      }
    };
  },
  error: function() {
    console.log('Error getting chart data');
  }
});
// End Chart1
// Start Chart2
var chart2data = {
  labels: [],
  data: []
};
var _url = _base_url + '/chart2?carrier=' + _carrier;
$.ajax({
  url: _url,
  dataType: 'JSON',
  success: function(data) {
    chart2data = data;
    var ctx = document.getElementById("myBarChart1");
    var myLineChart = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: chart2data.labels,
        datasets: [{
          label: "% Flight Count",
          backgroundColor: "rgba(2,117,216,1)",
          borderColor: "rgba(2,117,216,1)",
          data: chart2data.data,
        }],
      },
      options: {
        title: {
          display: true,
          text: 'Airline: ' + _carrier
        },
        layout: {
          padding: {
            left: 0,
            right: 0,
            top: 0,
            bottom: -12
          }
        },
        scales: {
          xAxes: [{
            time: {
              unit: 'route'
            },
            gridLines: {
              display: false
            },
            ticks: {
              fontSize: 12,
              maxTicksLimit: 7
            }
          }],
          yAxes: [{
            ticks: {
              beginAtZero: true
            },
            gridLines: {
              display: true
            }
          }],
        },
        legend: {
          display: false
        }
      }
    });
  },
  error: function() {
    console.log('Error getting chart data');
  }
});
// End Chart2
// Start Chart3
var chart3data = {
  labels: [],
  data: []
};
$.ajax({
  url: _base_url + '/chart3',
  dataType: 'JSON',
  success: function(data) {
    chart3data = data;
    var ctx = document.getElementById("myBarChart2");
    var myLineChart = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: chart3data.labels,
        datasets: [{
          label: "% Flight Count",
          backgroundColor: "rgba(2,117,216,1)",
          borderColor: "rgba(2,117,216,1)",
          data: chart3data.data,
        }],
      },
      options: {
        scales: {
          xAxes: [{
            time: {
              unit: 'airline'
            },
            gridLines: {
              display: false
            },
            ticks: {
              maxTicksLimit: 10
            }
          }],
          yAxes: [{
            ticks: {
              beginAtZero: true
            },
            gridLines: {
              display: true
            }
          }],
        },
        legend: {
          display: false
        }
      }
    });
  },
  error: function() {
    console.log('Error getting chart data');
  }
});
// End Chart3
// Start Chart4
var chart4data = {
  labels: [],
  data: []
};
$.ajax({
  url: _base_url + '/chart4',
  dataType: 'JSON',
  success: function(data) {
    chart4data = data;
    var ctx = document.getElementById("myBarChart3");
    var myLineChart = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: chart4data.labels,
        datasets: [{
          label: "% Flight Count",
          backgroundColor: "rgba(2,117,216,1)",
          borderColor: "rgba(2,117,216,1)",
          data: chart4data.data,
        }],
      },
      options: {
        scales: {
          xAxes: [{
            time: {
              unit: 'origin'
            },
            gridLines: {
              display: false
            },
            ticks: {
              maxTicksLimit: 10
            }
          }],
          yAxes: [{
            ticks: {
              beginAtZero: true
            },
            gridLines: {
              display: true
            }
          }],
        },
        legend: {
          display: false
        }
      }
    });
  },
  error: function() {
    console.log('Error getting chart data');
  }
});
// End Chart4
// Start Chart5
var chart5data = {
  labels: [],
  data: []
};
$.ajax({
  url: _base_url + '/chart5',
  dataType: 'JSON',
  success: function(data) {
    chart5data = data;
    var ctx = document.getElementById("myBarChart4");
    var myLineChart = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: chart5data.labels,
        datasets: [{
          label: "% Flight Count",
          backgroundColor: "rgba(2,117,216,1)",
          borderColor: "rgba(2,117,216,1)",
          data: chart5data.data,
        }],
      },
      options: {
        scales: {
          xAxes: [{
            time: {
              unit: 'airline'
            },
            gridLines: {
              display: false
            },
            ticks: {
              maxTicksLimit: 10
            }
          }],
          yAxes: [{
            ticks: {
              beginAtZero: true
            },
            gridLines: {
              display: true
            }
          }],
        },
        legend: {
          display: false
        }
      }
    });
  },
  error: function() {
    console.log('Error getting chart data');
  }
});
// End Chart5
// Start Chart6
var chart6data = {
  labels: [],
  data: []
};
$.ajax({
  url: _base_url + '/chart6',
  dataType: 'JSON',
  success: function(data) {
    chart6data = data;
    var ctx = document.getElementById("myBarChart5");
    var myLineChart = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: chart6data.labels,
        datasets: [{
          label: "% Flight Count",
          backgroundColor: "rgba(2,117,216,1)",
          borderColor: "rgba(2,117,216,1)",
          data: chart6data.data,
        }],
      },
      options: {
        scales: {
          xAxes: [{
            time: {
              unit: 'origin'
            },
            gridLines: {
              display: false
            },
            ticks: {
              maxTicksLimit: 10
            }
          }],
          yAxes: [{
            ticks: {
              beginAtZero: true
            },
            gridLines: {
              display: true
            }
          }],
        },
        legend: {
          display: false
        }
      }
    });
  },
  error: function() {
    console.log('Error getting chart data');
  }
});
//End Chart6
<commit_msg>Minor changes<commit_after>var parseQueryString = function(url) {
  var urlParams = {};
  url.replace(
    new RegExp("([^?=&]+)(=([^&]*))?", "g"),
    function($0, $1, $2, $3) {
      urlParams[$1] = $3;
    }
  );
  return urlParams;
};
var urlToParse = location.search;
var result = parseQueryString(urlToParse);
var _base_url = location.origin;
var _carrier = result.carrier;
if (_carrier == undefined) {
  _carrier = '9E';
}
// Chart.js scripts
// -- Set new default font family and font color to mimic Bootstrap's default styling
Chart.defaults.global.defaultFontFamily = '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
Chart.defaults.global.defaultFontColor = '#000';
// Start Chart1
var chart1data = {
  labels: [],
  data: []
};
$.ajax({
  url: _base_url + '/chart1',
  dataType: 'JSON',
  success: function(data) {
    chart1data = data;
    var seq = palette('tol-rainbow', chart1data.labels.length);
    var ctx = document.getElementById("myPieChart");
    var myPieChart = new Chart(ctx, {
      type: 'pie',
      data: {
        labels: chart1data.labels,
        datasets: [{
          data: chart1data.data,
          backgroundColor: palette('tol-rainbow', chart1data.data.length).map(function(hex) {
            return '#' + hex;
          })
        }],
      },
      options: {
        pieceLabel: {
          // render 'label', 'value', 'percentage', 'image' or custom function, default is 'percentage'
          render: 'label',
          // precision for percentage, default is 0
          precision: 0,
          // identifies whether or not labels of value 0 are displayed, default is false
          showZero: true,
          // font size, default is defaultFontSize
          fontSize: 12,
          // font color, can be color array for each data or function for dynamic color, default is defaultFontColor
          fontColor: '#fff',
          // font style, default is defaultFontStyle
          fontStyle: 'normal',
          // font family, default is defaultFontFamily
          fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
          // draw label in arc, default is false
          arc: true,
          // position to draw label, available value is 'default', 'border' and 'outside'
          // default is 'default'
          position: 'border',
          // draw label even it's overlap, default is false
          overlap: false,
          // set images when `render` is 'image'
          images: [{
            src: 'image.png',
            width: 16,
            height: 16
          }]
        },
        legend: {
          display: false
        }
      }
    });
    ctx.onclick = function(evt) {
      var activePoints = myPieChart.getElementsAtEvent(evt);
      if (activePoints[0]) {
        var chartData = activePoints[0]._chart.config.data;
        var idx = activePoints[0]._index;
        var label = chartData.labels[idx];
        var url = location.origin + location.pathname + "?carrier=" + label;
        location.href = url;
      }
    };
  },
  error: function() {
    console.log('Error getting chart data');
  }
});
// End Chart1
// Start Chart2
var chart2data = {
  labels: [],
  data: []
};
var _url = _base_url + '/chart2?carrier=' + _carrier;
$.ajax({
  url: _url,
  dataType: 'JSON',
  success: function(data) {
    chart2data = data;
    var ctx = document.getElementById("myBarChart1");
    var myLineChart = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: chart2data.labels,
        datasets: [{
          label: "% Flight Count",
          backgroundColor: "rgba(2,117,216,1)",
          borderColor: "rgba(2,117,216,1)",
          data: chart2data.data,
        }],
      },
      options: {
        title: {
          display: true,
          text: 'Airline: ' + _carrier
        },
        layout: {
          padding: {
            left: 0,
            right: 0,
            top: 0,
            bottom: -12
          }
        },
        scales: {
          xAxes: [{
            time: {
              unit: 'route'
            },
            gridLines: {
              display: false
            },
            ticks: {
              fontSize: 12,
              maxTicksLimit: 7
            }
          }],
          yAxes: [{
            ticks: {
              beginAtZero: true
            },
            gridLines: {
              display: true
            }
          }],
        },
        legend: {
          display: false
        }
      }
    });
  },
  error: function() {
    console.log('Error getting chart data');
  }
});
// End Chart2
// Start Chart3
var chart3data = {
  labels: [],
  data: []
};
$.ajax({
  url: _base_url + '/chart3',
  dataType: 'JSON',
  success: function(data) {
    chart3data = data;
    var ctx = document.getElementById("myBarChart2");
    var myLineChart = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: chart3data.labels,
        datasets: [{
          label: "% Flight Count",
          backgroundColor: "rgba(2,117,216,1)",
          borderColor: "rgba(2,117,216,1)",
          data: chart3data.data,
        }],
      },
      options: {
        scales: {
          xAxes: [{
            time: {
              unit: 'airline'
            },
            gridLines: {
              display: false
            },
            ticks: {
              maxTicksLimit: 10
            }
          }],
          yAxes: [{
            ticks: {
              beginAtZero: true
            },
            gridLines: {
              display: true
            }
          }],
        },
        legend: {
          display: false
        }
      }
    });
  },
  error: function() {
    console.log('Error getting chart data');
  }
});
// End Chart3
// Start Chart4
var chart4data = {
  labels: [],
  data: []
};
$.ajax({
  url: _base_url + '/chart4',
  dataType: 'JSON',
  success: function(data) {
    chart4data = data;
    var ctx = document.getElementById("myBarChart3");
    var myLineChart = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: chart4data.labels,
        datasets: [{
          label: "% Flight Count",
          backgroundColor: "rgba(2,117,216,1)",
          borderColor: "rgba(2,117,216,1)",
          data: chart4data.data,
        }],
      },
      options: {
        scales: {
          xAxes: [{
            time: {
              unit: 'origin'
            },
            gridLines: {
              display: false
            },
            ticks: {
              maxTicksLimit: 10
            }
          }],
          yAxes: [{
            ticks: {
              beginAtZero: true
            },
            gridLines: {
              display: true
            }
          }],
        },
        legend: {
          display: false
        }
      }
    });
  },
  error: function() {
    console.log('Error getting chart data');
  }
});
// End Chart4
// Start Chart5
var chart5data = {
  labels: [],
  data: []
};
$.ajax({
  url: _base_url + '/chart5',
  dataType: 'JSON',
  success: function(data) {
    chart5data = data;
    var ctx = document.getElementById("myBarChart4");
    var myLineChart = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: chart5data.labels,
        datasets: [{
          label: "% Flight Count",
          backgroundColor: "rgba(2,117,216,1)",
          borderColor: "rgba(2,117,216,1)",
          data: chart5data.data,
        }],
      },
      options: {
        scales: {
          xAxes: [{
            time: {
              unit: 'airline'
            },
            gridLines: {
              display: false
            },
            ticks: {
              maxTicksLimit: 10
            }
          }],
          yAxes: [{
            ticks: {
              beginAtZero: true
            },
            gridLines: {
              display: true
            }
          }],
        },
        legend: {
          display: false
        }
      }
    });
  },
  error: function() {
    console.log('Error getting chart data');
  }
});
// End Chart5
// Start Chart6
var chart6data = {
  labels: [],
  data: []
};
$.ajax({
  url: _base_url + '/chart6',
  dataType: 'JSON',
  success: function(data) {
    chart6data = data;
    var ctx = document.getElementById("myBarChart5");
    var myLineChart = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: chart6data.labels,
        datasets: [{
          label: "% Flight Count",
          backgroundColor: "rgba(2,117,216,1)",
          borderColor: "rgba(2,117,216,1)",
          data: chart6data.data,
        }],
      },
      options: {
        scales: {
          xAxes: [{
            time: {
              unit: 'origin'
            },
            gridLines: {
              display: false
            },
            ticks: {
              maxTicksLimit: 10
            }
          }],
          yAxes: [{
            ticks: {
              beginAtZero: true
            },
            gridLines: {
              display: true
            }
          }],
        },
        legend: {
          display: false
        }
      }
    });
  },
  error: function() {
    console.log('Error getting chart data');
  }
});
//End Chart6
<|endoftext|>


<commit_before>/*
 * Plantalytics
 *     Copyright (c) 2016 Sapphire Becker, Katy Brimm, Scott Ewing,
 *       Matt Fraser, Kelly Ledford, Michael Limb, Steven Ngo, Eric Turley.
 *     This project is licensed under the MIT License.
 *     Please see the file LICENSE in this distribution for license terms.
 * Contact: plantalytics.capstone@gmail.com
 */

$.getScript('http://www.mapquestapi.com/sdk/leaflet/v2.2/mq-map.js?key=' + mapQuestKey, readyMap);

/////////////////////////////////// On Load ///////////////////////////////////
$(function() {
    // Get information for our current user.
    // TODO: $.ajax({...})

    // Make sure the user has a login token before continuing.
    if (!localStorage.accessToken) {
      window.location = "index.html";
    }

    ({
        "url": "",
        "dataType": "json",
        "success": function (data) {
            if (mapsReady) {
                createMap(data);
            } else {
                delayMapCreation(data);
            }
        },
    }).success({
        // Vineyard center [lat, lon]
        "center": {
            "lat": 45.281041,
            "lng": -123.061896
        },
        // Node locations & values [lat, lon, val], ...
        'nodeLoc': [
            [45.281679, -123.062533, 58],
            [45.281554, -123.061838, 17],
            [45.281423, -123.061124, 55],
            [45.281249, -123.062615, 25],
            [45.281123, -123.061897, 42],
            [45.280989, -123.061145, 51],
            [45.280800, -123.062877, 17],
            [45.280680, -123.062076, 21],
            [45.280511, -123.061333, 66]
        ],
        // Boundary coords [lat, lon], ...
        // Must be listed in drawing order
        'propertyBoundary': [
            [45.282053, -123.062833],
            [45.281605, -123.060469],
            [45.280016, -123.061092],
            [45.280269, -123.061468],
            [45.280390, -123.062234],
            [45.280377, -123.063102],
            [45.280464, -123.063369]
        ]
    });
});

////////////////////////////////// Map code ///////////////////////////////////
var map, mapsReady = false, mapsData = null;
function createMap(data) {
    // Calculate map bounds
    var latOffset = .00145; // Perhaps calculate these offsets based
    var lngOffset = .00525; //   on the surface area of the vineyard.
    var southWest = {'lat': data.center.lat - latOffset, 'lng': data.center.lng - lngOffset};
    var northEast = {'lat': data.center.lat + latOffset, 'lng': data.center.lng + lngOffset};

    // Initialize map
    mapsData = data;
    map = L.map('map', {
        center: data.center,
        doubleClickZoom: false,
        layers: MQ.satelliteLayer(),
        maxBounds: [southWest, northEast],
        minZoom: 17,
        maxZoom: 19, // Lose tiles at 20 and up
        scrollWheelZoom: 'center',
        zoom: 18,
        zoomControl: false
    });
    var zoom = L.control.zoom({'position': 'topright'});
    map.addControl(zoom);

    // Set default map view upon map creation
    if (localStorage) {
        var defaultDataView = localStorage.defaultDataView;
        if (defaultDataView) {
            $(".data-buttons " + defaultDataView).click();
            $(".data-view-defaults " + defaultDataView + " input").prop("checked", true);
        }
    }

    //////////////////////// Generate IDW Interpolation ////////////////////////
    var idw = L.idwLayer(data.nodeLoc , {
        opacity: 0.4,  // Interpolation layer opacity
        maxZoom: 20,
        cellSize: 5,   // CellSize determines interpolation granularity
        exp: 3,        // Exponent used for weighting
        // Max varies based on condition/value
        max: 66,       // Point value ceiling
        gradient: {    // Gradient assignment red (hi) -> violet (lo)
            0.0: 'violet',
            0.1: 'blueviolet',
            0.2: 'blue',
            0.3: 'darkcyan',
            0.4: 'green',
            0.5: 'greenyellow',
            0.6: 'yellowgreen',
            0.7: 'yellow',
            0.8: 'orange',
            0.9: 'orangered',
            1.0: 'red'
        }
    }).addTo(map);

    ////////////////////// Set Property Boundary Vertices //////////////////////
    var propertyBoundary = L.polygon(data.propertyBoundary, {
        fillOpacity: 0
    }).addTo(map);
}

function readyMap() {
    mapsReady = true;
    if (mapsData !== null) {
        createMap(mapsData);
    }
}

function delayMapCreation(data) {
    mapsData = data;
}

// Re-centers the map on double-click
$(window).dblclick(function() {
    map.panTo(mapsData.center);
});

//////////////////////////////// Controls code ////////////////////////////////
$(function() {
    $("#menu").click(function() {
        var $menu = $(".menu");
        var visible = $menu.is(":visible");
        $menu.show();
        var width = $menu.width();
        var height = $menu.height();
        if (!$menu.is(':animated')) {
            if (visible) {
                // Animate hiding menu.
                $menu.animate({
                    "height": 10,
                }, {
                    "duration": 150,
                }).animate({
                    "width": 0,
                }, {
                    "duration": 300,
                    "queue": true,
                    "complete": function() {
                        $menu.width(width);
                        $menu.height(height);
                        $menu.hide();
                        isAnimating = false;
                    }
                });
            } else {
                // Animate showing menu.
                $menu.width(0).height(10);
                $menu.animate({
                    "width": width,
                }, {
                    "duration": 150,
                }).animate({
                    "height": height,
                }, {
                    "duration": 300,
                    "queue": true
                });
            }
        }
    });

    $(".data-view-defaults .data-button-label div").click(function(e) {
        var parent = $(this).parent();
        var selector = "." + parent.attr("class").replace(/ /g, ".");
        if (localStorage.defaultDataView == selector) {
            // Double tap to turn off.
            delete localStorage.defaultDataView;
            setTimeout(function() {
                parent.find("input").prop("checked", false);
            }, 10);
        } else {
            // Set new default.
            localStorage.defaultDataView = selector;
        }
    });

    $('#leafwetness-button').click(function() {
				$.ajax({
            url: backendIpAddress + 'env_data?vineyard_id=0&env_variable=leafwetness',
            type: "GET"
        }).done(function(json) {
            if (json.env_data) {
                window.location.href = backendIpAddress + "env_data?vineyard_id=0&env_variable=leafwetness";
            } else {
                window.location.href = "dashboard.html";
                /* TODO: add error handing message*/
            }
        }).error( function() {
            window.location.href = "dashboard.html";
            /* TODO: add error handing message*/
        });
    });

    $('#temperature-button').click(function() {
				$.ajax({
            url: backendIpAddress + 'env_data?vineyard_id=0&env_variable=temperature',
            type: "GET"
        }).done(function(json) {
            if (json.env_data) {
                window.location.href = backendIpAddress + "env_data?vineyard_id=0&env_variable=temperature";
            } else {
                window.location.href = "dashboard.html";
                /* TODO: add error handing message*/
            }
        }).error( function() {
            window.location.href = "dashboard.html";
            /* TODO: add error handing message*/
        });
    });

    $('#humidity-button').click(function() {
				$.ajax({
            url: backendIpAddress + 'env_data?vineyard_id=0&env_variable=humidity',
            type: "GET"
        }).done(function(json) {
            if (json.env_data) {
                window.location.href = backendIpAddress + "env_data?vineyard_id=0&env_variable=humidity";
            } else {
                window.location.href = "dashboard.html";
                /* TODO: add error handing message*/
            }
        }).error( function() {
            window.location.href = "dashboard.html";
            /* TODO: add error handing message*/
        });
    });

    $('#menu-leafwetness').click(function() {
				$.ajax({
            url: backendIpAddress + 'env_data?vineyard_id=0&env_variable=leafwetness',
            type: "GET"
        }).done(function(json) {
            if (json.env_data) {
                window.location.href = backendIpAddress + "env_data?vineyard_id=0&env_variable=leafwetness";
            } else {
                window.location.href = "dashboard.html";
                /* TODO: add error handing message*/
            }
        }).error( function() {
            window.location.href = "dashboard.html";
            /* TODO: add error handing message*/
        });
    });

    $('#menu-temperature').click(function() {
				$.ajax({
            url: backendIpAddress + 'env_data?vineyard_id=0&env_variable=temperature',
            type: "GET"
        }).done(function(json) {
            if (json.env_data) {
                window.location.href = backendIpAddress + "env_data?vineyard_id=0&env_variable=temperature";
            } else {
                window.location.href = "dashboard.html";
                /* TODO: add error handing message*/
            }
        }).error( function() {
            window.location.href = "dashboard.html";
            /* TODO: add error handing message*/
        });
    });

    $('#menu-humidity').click(function() {
				$.ajax({
            url: backendIpAddress + 'env_data?vineyard_id=0&env_variable=humidity',
            type: "GET"
        }).done(function(json) {
            if (json.env_data) {
                window.location.href = backendIpAddress + "env_data?vineyard_id=0&env_variable=humidity";
            } else {
                window.location.href = "dashboard.html";
                /* TODO: add error handing message*/
            }
        }).error( function() {
            window.location.href = "dashboard.html";
            /* TODO: add error handing message*/
        });
    });

    $("#menu-logout").click(function() {
        // Delete access token from local storage.
        delete localStorage.accessToken;

        // Redirect to login page
        window.location = "index.html";
    });
});
<commit_msg>changed temperature to default view if no default is found in local storage<commit_after>/*
 * Plantalytics
 *     Copyright (c) 2016 Sapphire Becker, Katy Brimm, Scott Ewing,
 *       Matt Fraser, Kelly Ledford, Michael Limb, Steven Ngo, Eric Turley.
 *     This project is licensed under the MIT License.
 *     Please see the file LICENSE in this distribution for license terms.
 * Contact: plantalytics.capstone@gmail.com
 */

$.getScript('http://www.mapquestapi.com/sdk/leaflet/v2.2/mq-map.js?key=' + mapQuestKey, readyMap);

/////////////////////////////////// On Load ///////////////////////////////////

$(function() {
    // Get information for our current user.
    // TODO: $.ajax({...})

    // Make sure the user has a login token before continuing.
    if (!localStorage.accessToken) {
      window.location = "index.html";
    }

    ({
        "url": "",
        "dataType": "json",
        "done": function (data) {
            if (mapsReady) {
                createMap(data);
            } else {
                delayMapCreation(data);
            }
        }
    }).done({
        // Vineyard center [lat, lon]
        "center": {
            "lat": 45.281041,
            "lng": -123.061896
        },
        // Node locations & values [lat, lon, val], ...
        'nodeLoc': [
            [45.281679, -123.062533, 58],
            [45.281554, -123.061838, 17],
            [45.281423, -123.061124, 55],
            [45.281249, -123.062615, 25],
            [45.281123, -123.061897, 42],
            [45.280989, -123.061145, 51],
            [45.280800, -123.062877, 17],
            [45.280680, -123.062076, 21],
            [45.280511, -123.061333, 66]
        ],
        // Boundary coords [lat, lon], ...
        // Must be listed in drawing order
        'propertyBoundary': [
            [45.282053, -123.062833],
            [45.281605, -123.060469],
            [45.280016, -123.061092],
            [45.280269, -123.061468],
            [45.280390, -123.062234],
            [45.280377, -123.063102],
            [45.280464, -123.063369]
        ]
    });
});

////////////////////////////////// Map code ///////////////////////////////////

var map, mapsReady = false, mapsData = null;
function createMap(data) {
    // Calculate map bounds
    var latOffset = .00145; // Perhaps calculate these offsets based
    var lngOffset = .00525; //   on the surface area of the vineyard.
    var southWest = {'lat': data.center.lat - latOffset, 'lng': data.center.lng - lngOffset};
    var northEast = {'lat': data.center.lat + latOffset, 'lng': data.center.lng + lngOffset};

    // Initialize map
    mapsData = data;
    map = L.map('map', {
        center: data.center,
        doubleClickZoom: false,
        layers: MQ.satelliteLayer(),
        maxBounds: [southWest, northEast],
        minZoom: 17,
        maxZoom: 19, // Lose tiles at 20 and up
        scrollWheelZoom: 'center',
        zoom: 18,
        zoomControl: false
    });
    var zoom = L.control.zoom({'position': 'topright'});
    map.addControl(zoom);

    // Set default map view upon map creation
    if (localStorage) {
        if (!localStorage.defaultDataView) {
            localStorage.defaultDataView = '.data-button-label.temperature';
        }
        $(".data-buttons " + localStorage.defaultDataView).click();
        $(".data-view-defaults " + localStorage.defaultDataView + " input").prop("checked", true);
    }

    //////////////////////// Generate IDW Interpolation ////////////////////////
    var idw = L.idwLayer(data.nodeLoc , {
        opacity: 0.4,  // Interpolation layer opacity
        maxZoom: 20,
        cellSize: 5,   // CellSize determines interpolation granularity
        exp: 3,        // Exponent used for weighting
        // Max varies based on condition/value
        max: 66,       // Point value ceiling
        gradient: {    // Gradient assignment red (hi) -> violet (lo)
            0.0: 'violet',
            0.1: 'blueviolet',
            0.2: 'blue',
            0.3: 'darkcyan',
            0.4: 'green',
            0.5: 'greenyellow',
            0.6: 'yellowgreen',
            0.7: 'yellow',
            0.8: 'orange',
            0.9: 'orangered',
            1.0: 'red'
        }
    }).addTo(map);

    ////////////////////// Set Property Boundary Vertices //////////////////////
    var propertyBoundary = L.polygon(data.propertyBoundary, {
        fillOpacity: 0
    }).addTo(map);
}

function readyMap() {
    mapsReady = true;
    if (mapsData !== null) {
        createMap(mapsData);
    }
}

function delayMapCreation(data) {
    mapsData = data;
}

// Re-centers the map on double-click
$(window).dblclick(function() {
    map.panTo(mapsData.center);
});
<|endoftext|>


<commit_before>$(document).ready(function() {
    var info;
    //maybe pass data as variable since everything is moved inside asynchronous data call
    //added d3 data load to try new data format
    d3.json("data/datnewnew.json", function(error, data) {
       info = data.data;

       //svg dims and margin
		var margin = {top: 20, right: 20, bottom: 20, left: 20},
            mini_margin = {top: 2, right: 0, bottom: 2, left: 0},
			width = 600,
			height = 1500,
			mini_width = 300,
			mini_height = 144;
	//create svg
		svg = d3.select("#timeline").append("svg").attr({
			width: width,
			height: height,
			id: "svg"
		});
		mini_svg = d3.select("#mini_timeline").append("svg").attr({
			width: mini_width,
			height: mini_height,
			id: "mini_svg"
		});
	//function for scale
	plotnames = []
		function startFunction() {
			plotnames = []
			setdate = ""
			$('.tier').each(function (index) {
				if ($(this).find(':first-child').hasClass("glyphicon-ok")) {
					if (this.id == 11) {
						plotnames.push(1);
						plotnames.push(2);
						setdate = "15 March 2010"
					} else if (this.id == 22) {
						plotnames.push(3);
						setdate = "15 March 2005"
					} else if (this.id == 33) {
						plotnames.push(4);
						plotnames.push(5);
						setdate = "15 March 2000"
					} else if (this.id == 44) {
						plotnames.push(6);
						setdate = "15 March 1995"
					} else if (this.id == 55) {
						plotnames.push(7);
						plotnames.push(8);
						setdate = "15 March 1990"
					} else {

					}
				} else {

				}
			});
		};
	startFunction();
	//plotting
		var axisPadding = 45;
		var col_width = 50;
		var mini_col_width = 12;
		var counter = 0;
		var colPadding = 10;
		var mini_colPadding = 2;

		var events = d3.select("#svg")
		var mini_events = d3.select("#mini_svg")
		//might be mini_svg
		mini_events.append("mini_svg:g")
			.attr("id", "mini_barchart")

		events.append("svg:g")
			.attr("id", "barchart")

		var vis = d3.select("#barchart")
		var mini_vis = d3.select("#mini_barchart")

	//scale
		var timeScale = d3.time.scale()
			.domain([new Date(setdate), new Date])
			.nice(d3.time.year)
			.rangeRound([height - margin.bottom, margin.top]);
			//might need mini margin
		var mini_timeScale = d3.time.scale()
			.domain([new Date(setdate), new Date])
			.nice(d3.time.year)
			.rangeRound([mini_width - mini_margin.bottom, mini_margin.top]);
	//axis
		var yAxis = d3.svg.axis().scale(timeScale).orient("left")
			.ticks(d3.time.years, 1);

		var mini_yAxis = d3.svg.axis().scale(mini_timeScale).orient("top")
			.ticks(d3.time.years, 2);

		var yAxisGroup = svg.append("g").attr({
			"class": "axis",
			transform: "translate(" + [axisPadding, 0] + ")"
			}).call(yAxis);
			//might need custom mini axis padding
		var mini_yAxisGroup = mini_svg.append("g").attr({
			"class": "mini_axis",
			transform: "translate(" + [0, (mini_height-margin.bottom)] + ")"
			}).call(mini_yAxis);

	function plot(data) {
		timeScale.domain([new Date(setdate), new Date]).nice(d3.time.year).rangeRound([height - margin.bottom, margin.top]);
		yAxisGroup.transition().ease("linear").duration(500).call(yAxis);
		var eventAttrs = {
				x: function (d, i) {
					return col_width * (d.set - 1) + colPadding * d.set + axisPadding
				},
				y: function (d, i) {
					if (d.end == "Current") {
						return timeScale(new Date)
					} else {
						return timeScale(new Date(d.end))
					}
				},
				width: col_width,
				height: function (d, i) {
						if (d.end == "Current") {
							return (timeScale(new Date(d.start)) - timeScale(new Date))
						} else {
							return (timeScale(new Date(d.start)) - timeScale(new Date(d.end)))
						}
				},
				fill: function (d, i) {
                    return d.color;
				}

		};

		var rects = vis.selectAll("rect.bar")
			.data(data)

			//enter
			rects.enter()
				.append("svg:rect")
				.attr("class", "bar")
				.attr("id", function (d, i) {
                    return "bar" + d.id
                })
				.attr(eventAttrs)
				.on("click", function (d, i) {
					$("h5#timelineTitle").text(d.title);
					$("h5#timelineTitle").css('border-left-color', $(this).css('fill'));
					$("p#timelineDes").text(d.description);
                    $("p#timelineDes").css('border-left-color', $(this).css('fill'));
					d3.selectAll("rect").classed("mini_selected", false);
                    d3.selectAll("rect").classed("selected", false);
					d3.select(this).classed("selected", true);
                    d3.select("#mini_bar"+d.id).classed("mini_selected", true);
					return false;

				});

			//exit
			rects.exit()
				.transition()
				.ease("linear")
					.attr("height", 0)
					.duration(1000)
					.remove()

			//update
			rects
				.data(data)
				.transition()
				.ease("linear")
					.duration(1000)
				.attr(eventAttrs)
				.attr("class", "bar")
				.attr("id", function (d, i) {
                    return "bar" + d.id
                })
	};

//////////mini plot
// mini map bug for axis ticks, not "nicely" rounding the range..margins need to use proper names as well..
	function mini_plot(data) {
		mini_timeScale.domain([new Date(setdate), new Date]).nice(d3.time.year).rangeRound([mini_width - mini_margin.bottom, mini_margin.top]);
		mini_yAxisGroup.transition().ease("linear").duration(500).call(mini_yAxis)

		var mini_eventAttrs = {
				x: function (d, i) {
					if (d.end == "Current") {
						return mini_timeScale(new Date)
					} else {
						return mini_timeScale(new Date(d.end))
					}
				},
				y: function (d, i) {
					return mini_col_width * (d.set - 1) + mini_colPadding * d.set
				},
				width: function (d, i) {
						if (d.end == "Current") {
							return (mini_timeScale(new Date(d.start)) - mini_timeScale(new Date))
						} else {
							return (mini_timeScale(new Date(d.start)) - mini_timeScale(new Date(d.end)))
						}
				},
				height: mini_col_width,

				fill: function (d, i) {
					return d.color;
				}
		};

		var mini_rects = mini_vis.selectAll("rect.mini_bar")
			.data(data)

			//enter
			mini_rects.enter()
				.append("mini_svg:rect")
				.attr("class", "mini_bar")
				.attr("id", function (d, i) {
                    return "mini" + d.id
                })
				.attr(mini_eventAttrs)
				.on("click", function (d, i) {
					$("h5#timelineTitle").text(d.title);
					$("h5#timelineTitle").css('border-left-color', $(this).css('fill'));
					$("p#timelineDes").text(d.description);
					$("p#timelineDes").css('border-left-color', $(this).css('fill'));
					d3.selectAll("rect").classed("mini_selected", false);
                    d3.selectAll("rect").classed("selected", false);
					d3.select(this).classed("mini_selected", true);
                    d3.select("#bar"+d.id).classed("selected", true);
					return false;

				});

			//exit
			mini_rects.exit()
				.transition()
				.ease("linear")
					.attr("height", 0)
					.duration(1000)
					.remove()

			//update
			mini_rects
				.data(data)
				.transition()
				.ease("linear")
					.duration(1000)
				.attr(mini_eventAttrs)
				.attr("class", "mini_bar")
				.attr("id", function (d, i) {
                    return "mini_bar" + d.id
                })
	};

	//other
	function findSet(){
		var inputdata = [];
		plotnames.forEach(function(entry) {
			 setcut = $.grep(info, function(n, i){
				return n.set == entry
			});
			inputdata = inputdata.concat(setcut)
		});
		return inputdata
	};

	$(".tier").click(function() {
		$( "#tier_" + this.id + "_span" ).toggleClass('glyphicon-ok glyphicon-remove');

		startFunction();
        plot(findSet(info));
        mini_plot(findSet(info));
	});
	plot(findSet(info.data));
	mini_plot(findSet(info.data));

    });
});
<commit_msg>min max dates for domain and scale definition calculated from data set<commit_after>$(document).ready(function() {
    var info;
    //maybe pass data as variable since everything is moved inside asynchronous data call
    //added d3 data load to try new data format
    d3.json("data/datnewnew.json", function(error, data) {
       info = data.data;

       //svg dims and margin
		var margin = {top: 20, right: 20, bottom: 20, left: 20},
            mini_margin = {top: 2, right: 0, bottom: 2, left: 0},
			width = 600,
			height = 1500,
			mini_width = 300,
			mini_height = 144;
	//create svg
		svg = d3.select("#timeline").append("svg").attr({
			width: width,
			height: height,
			id: "svg"
		});
		mini_svg = d3.select("#mini_timeline").append("svg").attr({
			width: mini_width,
			height: mini_height,
			id: "mini_svg"
		});
	//function for scale
	plotnames = []
		function startFunction() {
			plotnames = []
			setdate = ""
			$('.tier').each(function (index) {
				if ($(this).find(':first-child').hasClass("glyphicon-ok")) {
					if (this.id == 11) {
						plotnames.push(1);
						plotnames.push(2);

					} else if (this.id == 22) {
						plotnames.push(3);

					} else if (this.id == 33) {
						plotnames.push(4);
						plotnames.push(5);

					} else if (this.id == 44) {
						plotnames.push(6);

					} else if (this.id == 55) {
						plotnames.push(7);
						plotnames.push(8);

					} else {

					}
				} else {

				}
			});
		};
	startFunction();
	//plotting
		var axisPadding = 45;
		var col_width = 50;
		var mini_col_width = 12;
		var counter = 0;
		var colPadding = 10;
		var mini_colPadding = 2;

		var events = d3.select("#svg")
		var mini_events = d3.select("#mini_svg")
		//might be mini_svg
		mini_events.append("mini_svg:g")
			.attr("id", "mini_barchart")

		events.append("svg:g")
			.attr("id", "barchart")

		var vis = d3.select("#barchart")
		var mini_vis = d3.select("#mini_barchart")

	//scale
		var timeScale = d3.time.scale()
			.domain([new Date(setdate), new Date])
			.nice(d3.time.year)
			.rangeRound([height - margin.bottom, margin.top]);
			//might need mini margin
		var mini_timeScale = d3.time.scale()
			.domain([new Date(setdate), new Date])
			.nice(d3.time.year)
			.rangeRound([mini_width - mini_margin.bottom, mini_margin.top]);
	//axis
		var yAxis = d3.svg.axis().scale(timeScale).orient("left")
			.ticks(d3.time.years, 1);

		var mini_yAxis = d3.svg.axis().scale(mini_timeScale).orient("top")
			.ticks(d3.time.years, 2);

		var yAxisGroup = svg.append("g").attr({
			"class": "axis",
			transform: "translate(" + [axisPadding, 0] + ")"
			}).call(yAxis);
			//might need custom mini axis padding
		var mini_yAxisGroup = mini_svg.append("g").attr({
			"class": "mini_axis",
			transform: "translate(" + [0, (mini_height-margin.bottom)] + ")"
			}).call(mini_yAxis);

	function plot(data) {
        var min_date = d3.min(data, function (d) {
            temp = new Date(d.start);
            return temp
        });

        var max_date = d3.max(data, function (d) {
            if (d.end == "Current") {
                temp = new Date;
                return temp
            } else {
                temp = new Date(d.end);
                return temp
            }
        });

		timeScale.domain([min_date, max_date]).nice(d3.time.year).rangeRound([height - margin.bottom, margin.top]);
		yAxisGroup.transition().ease("linear").duration(500).call(yAxis);
		var eventAttrs = {
				x: function (d, i) {
					return col_width * (d.set - 1) + colPadding * d.set + axisPadding
				},
				y: function (d, i) {
					if (d.end == "Current") {
						return timeScale(new Date)
					} else {
						return timeScale(new Date(d.end))
					}
				},
				width: col_width,
				height: function (d, i) {
						if (d.end == "Current") {
							return (timeScale(new Date(d.start)) - timeScale(new Date))
						} else {
							return (timeScale(new Date(d.start)) - timeScale(new Date(d.end)))
						}
				},
				fill: function (d, i) {
                    return d.color;
				}

		};

		var rects = vis.selectAll("rect.bar")
			.data(data)

			//enter
			rects.enter()
				.append("svg:rect")
				.attr("class", "bar")
				.attr("id", function (d, i) {
                    return "bar" + d.id
                })
				.attr(eventAttrs)
				.on("click", function (d, i) {
					$("h5#timelineTitle").text(d.title);
					$("h5#timelineTitle").css('border-left-color', $(this).css('fill'));
					$("p#timelineDes").text(d.description);
                    $("p#timelineDes").css('border-left-color', $(this).css('fill'));
					d3.selectAll("rect").classed("mini_selected", false);
                    d3.selectAll("rect").classed("selected", false);
					d3.select(this).classed("selected", true);
                    d3.select("#mini_bar"+d.id).classed("mini_selected", true);
					return false;

				});

			//exit
			rects.exit()
				.transition()
				.ease("linear")
					.attr("height", 0)
					.duration(1000)
					.remove()

			//update
			rects
				.data(data)
				.transition()
				.ease("linear")
					.duration(1000)
				.attr(eventAttrs)
				.attr("class", "bar")
				.attr("id", function (d, i) {
                    return "bar" + d.id
                })
	};

//////////mini plot
// mini map bug for axis ticks, not "nicely" rounding the range..margins need to use proper names as well..
	function mini_plot(data) {
        var min_date = d3.min(data, function (d) {
            temp = new Date(d.start);
            return temp
        });

        var max_date = d3.max(data, function (d) {
            if (d.end == "Current") {
                temp = new Date;
                return temp
            } else {
                temp = new Date(d.end);
                return temp
            }
        });

		mini_timeScale.domain([min_date, max_date]).nice(d3.time.year).rangeRound([mini_width - mini_margin.bottom, mini_margin.top]);
		mini_yAxisGroup.transition().ease("linear").duration(500).call(mini_yAxis)

		var mini_eventAttrs = {
				x: function (d, i) {
					if (d.end == "Current") {
						return mini_timeScale(new Date)
					} else {
						return mini_timeScale(new Date(d.end))
					}
				},
				y: function (d, i) {
					return mini_col_width * (d.set - 1) + mini_colPadding * d.set
				},
				width: function (d, i) {
						if (d.end == "Current") {
							return (mini_timeScale(new Date(d.start)) - mini_timeScale(new Date))
						} else {
							return (mini_timeScale(new Date(d.start)) - mini_timeScale(new Date(d.end)))
						}
				},
				height: mini_col_width,

				fill: function (d, i) {
					return d.color;
				}
		};

		var mini_rects = mini_vis.selectAll("rect.mini_bar")
			.data(data)

			//enter
			mini_rects.enter()
				.append("mini_svg:rect")
				.attr("class", "mini_bar")
				.attr("id", function (d, i) {
                    return "mini" + d.id
                })
				.attr(mini_eventAttrs)
				.on("click", function (d, i) {
					$("h5#timelineTitle").text(d.title);
					$("h5#timelineTitle").css('border-left-color', $(this).css('fill'));
					$("p#timelineDes").text(d.description);
					$("p#timelineDes").css('border-left-color', $(this).css('fill'));
					d3.selectAll("rect").classed("mini_selected", false);
                    d3.selectAll("rect").classed("selected", false);
					d3.select(this).classed("mini_selected", true);
                    d3.select("#bar"+d.id).classed("selected", true);
					return false;

				});

			//exit
			mini_rects.exit()
				.transition()
				.ease("linear")
					.attr("height", 0)
					.duration(1000)
					.remove()

			//update
			mini_rects
				.data(data)
				.transition()
				.ease("linear")
					.duration(1000)
				.attr(mini_eventAttrs)
				.attr("class", "mini_bar")
				.attr("id", function (d, i) {
                    return "mini_bar" + d.id
                })
	};

	//other
	function findSet(){
		var inputdata = [];
		plotnames.forEach(function(entry) {
			 setcut = $.grep(info, function(n, i){
				return n.set == entry
			});
			inputdata = inputdata.concat(setcut)
		});
		return inputdata
	};

	$(".tier").click(function() {
		$( "#tier_" + this.id + "_span" ).toggleClass('glyphicon-ok glyphicon-remove');

		startFunction();
        plot(findSet(info));
        mini_plot(findSet(info));
	});
	plot(findSet(info.data));
	mini_plot(findSet(info.data));

    });
});
<|endoftext|>


<commit_before>"use strict";

/**
 * Responsible for keeping track of downloaded content.
 *   - Maintains a manifest of what's downloaded
 *   - Actually performs downloads
 *   - Provides the ability to delete those downloads
 */

define(["util", "storage", "models", "apiclient"],
        function(Util, Storage, models, APIClient) {

    var Downloads = {
        /**
         * Initializes download manager
         */
        init: function() {
            var d = $.Deferred();
            this.contentList = new models.ContentList();
            this._readManifest().always(() => {
                d.resolve();
            });
            return d.promise();
        },
        /**
         * Writes out the manifest file, which keeps track of downloaded data
         */
        _writeManifest: function() {
            var contentListIds = this.contentList.models.map(function(model) {
                return model.getId();
            });
            var jsonManifest = JSON.stringify(contentListIds);
            return Storage.writeText(this.manifestFilename, jsonManifest);
        },
        /**
         * Reads in a manifest file, which keeps track of downloaded data
         */
        _readManifest: function() {
            var d = $.Deferred();
            Storage.readText(this.manifestFilename).done((data) => {
                var contentListIds;
                if (data) {
                    contentListIds = JSON.parse(data);
                }
                var contentListModels = models.TopicTree.getContentItemsByIds(contentListIds);
                _(contentListModels).each((model) => {
                    this._setDownloaded(model, true);
                });
                this.contentList = new models.ContentList(contentListModels);
                d.resolve();
            }).fail(() => {
                d.reject();
            });
            return d.promise();
        },
        _setDownloaded: function(model, downloaded) {
            model.setDownloaded(downloaded);
            while (model = model.getParent()) {
                var downloadCount = model.get("downloadCount");
                if (downloaded) {
                    downloadCount++;
                } else {
                    downloadCount--;
                }
                model.set("downloadCount", downloadCount);
            }
        },
        /**
         * Cancels downloading if it's in progress.
         */
        cancelDownloading: function() {
            models.TempAppState.set("isDownloadingTopic", false);
            var currentRequest = models.TempAppState.get("currentDownloadRequest");
            // Cancel the XHR if it exists (it may not, in the case of article downloads).
            if (currentRequest) {
                currentRequest.abort();
                models.TempAppState.set("currentDownloadRequest", null);
            }
            if (this.currentProgress) {
                this.currentProgress(undefined, true);
            }
        },
        /**
         * Used to download either a single content item for all content
         * items underneath the specified topic.
         */
        download: function(model, onProgress) {
            if (model.isContent()) {
                return this.downloadContent(model, onProgress);
            } else if (model.isTopic()) {
                return this.downloadTopic(model, onProgress);
            }
            return $.Deferred().resolve().pormise();
        },
        /**
         * Downloads all content items recursively one at a time for
         * the current topic
         */
        downloadTopic: function(topic, onProgress) {
            this.currentProgress = onProgress;
            var d = $.Deferred();
            var downloadedCount = 0;
            models.TempAppState.set("isDownloadingTopic", true);
            if (onProgress) {
                onProgress(0);
            }
            var predicate = (model) => !model.isDownloaded();
            var seq = topic.enumChildrenGenerator(predicate);
            var downloadOneAtATime = () => {
                try {
                    var contentItem = seq.next().value;
                    // Allow at most one download at a time.
                    this.downloadContent(contentItem).done(() => {
                        downloadedCount++;
                        if (onProgress) {
                            onProgress(downloadedCount);
                        }
                        setTimeout(downloadOneAtATime, 1000);
                    }).fail((isCancel) => {
                        if (isCancel) {
                            delete this.currentProgress;
                        }
                        d.reject(isCancel);
                    });
                } catch (e) {
                    // done, no more items in the generator
                    models.TempAppState.set("isDownloadingTopic", false);
                    d.resolve(topic, downloadedCount);
                    delete this.currentProgress;
                }
            };
            downloadOneAtATime();
            return d.promise();
        },
        /**
         * Downloads the file at the specified URL and stores it to the
         * specified filename.
         */
        downloadContent: function(contentItem, onProgress) {
            var d = $.Deferred();
            if (onProgress) {
                onProgress(0);
            }

            var filename = contentItem.getId();
            var handleContentLoaded = (contentData) => {
                var blob = new Blob([contentData], {type: contentItem.getContentMimeType()});
                Storage.writeBlob(filename, blob).done(() => {
                    this._addDownloadToManifest(contentItem);
                    if (onProgress) {
                        onProgress(1);
                    }
                    d.resolve(contentItem, 1);
                }).fail(() => {
                    d.reject();
                });
            };
            if (contentItem.isVideo()) {
                var url = contentItem.getDownloadUrl();
                var req = new XMLHttpRequest({mozSystem: true});
                req.open("GET", url, true);
                req.responseType = "arraybuffer";
                req.onload = () => {
                    handleContentLoaded(req.response);
                };
                req.onabort = (e) => {
                    d.reject(true);
                };
                req.onerror = (e) => {
                    d.reject(false);
                };
                req.send();
                models.TempAppState.set("currentDownloadRequest", req);
            } else if (contentItem.isArticle()) {
                if (contentItem.get("content")) {
                    // Articles have a content property with the html we want to
                    // download already. It's not loaded in by the topic tree but
                    // when the article is actually loaded.
                    handleContentLoaded(contentItem.get("content"));
                } else {
                    // Sometimes articles are downloaded before they are viewed,
                    // so try to download it here.
                    APIClient.getArticle(contentItem.getId()).done((result) => {
                        contentItem.set("content", result.translated_html_content);
                        handleContentLoaded(contentItem.get("content"));
                    }).fail(() => {
                        return d.reject().promise();
                    });
                }
            }
            return d.promise();
        },
        /**
         * Removes a download from the list of downloaded files and
         * removes the file on disk.
         */
        deleteContent: function(contentItem) {
            var d = $.Deferred();
            var filename = contentItem.getId();
            Storage.delete(filename).done(() => {
                this._removeDownloadFromManifest(contentItem);
                d.resolve();
            }).fail(() => {
                d.reject();
            });
            return d.promise();
        },
        /**
         * Adds the specified model to the list of downloaded files
         */
        _addDownloadToManifest: function(model) {
            this._setDownloaded(model, true);
            Util.log('adding model to manifest: ');
            Util.log(model);
            this.contentList.push(model);
            this._writeManifest();
        },
        /**
         * Remove the specified model from the list of downloaded files
         */
        _removeDownloadFromManifest: function(model) {
            this._setDownloaded(model, false);
            Util.log('removing model from manifest: ');
            Util.log(model);
            this.contentList.remove(model);
            this._writeManifest();
        },
        manifestFilename: "download-manifest.json"
    }

    return Downloads;

});
<commit_msg>Force onProgress to be called in every invocation of downloadContent<commit_after>"use strict";

/**
 * Responsible for keeping track of downloaded content.
 *   - Maintains a manifest of what's downloaded
 *   - Actually performs downloads
 *   - Provides the ability to delete those downloads
 */

define(["util", "storage", "models", "apiclient"],
        function(Util, Storage, models, APIClient) {

    var Downloads = {
        /**
         * Initializes download manager
         */
        init: function() {
            var d = $.Deferred();
            this.contentList = new models.ContentList();
            this._readManifest().always(() => {
                d.resolve();
            });
            return d.promise();
        },
        /**
         * Writes out the manifest file, which keeps track of downloaded data
         */
        _writeManifest: function() {
            var contentListIds = this.contentList.models.map(function(model) {
                return model.getId();
            });
            var jsonManifest = JSON.stringify(contentListIds);
            return Storage.writeText(this.manifestFilename, jsonManifest);
        },
        /**
         * Reads in a manifest file, which keeps track of downloaded data
         */
        _readManifest: function() {
            var d = $.Deferred();
            Storage.readText(this.manifestFilename).done((data) => {
                var contentListIds;
                if (data) {
                    contentListIds = JSON.parse(data);
                }
                var contentListModels = models.TopicTree.getContentItemsByIds(contentListIds);
                _(contentListModels).each((model) => {
                    this._setDownloaded(model, true);
                });
                this.contentList = new models.ContentList(contentListModels);
                d.resolve();
            }).fail(() => {
                d.reject();
            });
            return d.promise();
        },
        _setDownloaded: function(model, downloaded) {
            model.setDownloaded(downloaded);
            while (model = model.getParent()) {
                var downloadCount = model.get("downloadCount");
                if (downloaded) {
                    downloadCount++;
                } else {
                    downloadCount--;
                }
                model.set("downloadCount", downloadCount);
            }
        },
        /**
         * Cancels downloading if it's in progress.
         */
        cancelDownloading: function() {
            models.TempAppState.set("isDownloadingTopic", false);
            var currentRequest = models.TempAppState.get("currentDownloadRequest");
            // Cancel the XHR if it exists (it may not, in the case of article downloads).
            if (currentRequest) {
                currentRequest.abort();
                models.TempAppState.set("currentDownloadRequest", null);
            }
            if (this.currentProgress) {
                this.currentProgress(undefined, true);
            }
        },
        /**
         * Used to download either a single content item for all content
         * items underneath the specified topic.
         */
        download: function(model, onProgress) {
            if (model.isContent()) {
                return this.downloadContent(model, onProgress, 0);
            } else if (model.isTopic()) {
                return this.downloadTopic(model, onProgress);
            }
            return $.Deferred().resolve().pormise();
        },
        /**
         * Downloads all content items recursively one at a time for
         * the current topic
         */
        downloadTopic: function(topic, onProgress) {
            this.currentProgress = onProgress;
            var d = $.Deferred();
            var downloadedCount = 0;
            models.TempAppState.set("isDownloadingTopic", true);
            var predicate = (model) => !model.isDownloaded();
            var seq = topic.enumChildrenGenerator(predicate);
            var downloadOneAtATime = () => {
                try {
                    var contentItem = seq.next().value;
                    // Allow at most one download at a time.
                    this.downloadContent(contentItem, onProgress, downloadedCount).done(() => {
                        downloadedCount++;
                        setTimeout(downloadOneAtATime, 1000);
                    }).fail((isCancel) => {
                        if (isCancel) {
                            delete this.currentProgress;
                        }
                        d.reject(isCancel);
                    });
                } catch (e) {
                    // done, no more items in the generator
                    models.TempAppState.set("isDownloadingTopic", false);
                    d.resolve(topic, downloadedCount);
                    delete this.currentProgress;
                }
            };
            downloadOneAtATime();
            return d.promise();
        },
        /**
         * Downloads the file at the specified URL and stores it to the
         * specified filename.
         */
        downloadContent: function(contentItem, onProgress, downloadNumber) {
            var d = $.Deferred();
            if (onProgress) {
                onProgress(downloadNumber);
            }

            var filename = contentItem.getId();
            var handleContentLoaded = (contentData) => {
                var blob = new Blob([contentData], {type: contentItem.getContentMimeType()});
                Storage.writeBlob(filename, blob).done(() => {
                    this._addDownloadToManifest(contentItem);
                    if (onProgress) {
                        onProgress(downloadNumber + 1);
                    }
                    d.resolve(contentItem, 1);
                }).fail(() => {
                    d.reject();
                });
            };
            if (contentItem.isVideo()) {
                var url = contentItem.getDownloadUrl();
                var req = new XMLHttpRequest({mozSystem: true});
                req.open("GET", url, true);
                req.responseType = "arraybuffer";
                req.onload = () => {
                    handleContentLoaded(req.response);
                };
                req.onabort = (e) => {
                    d.reject(true);
                };
                req.onerror = (e) => {
                    d.reject(false);
                };
                req.send();
                models.TempAppState.set("currentDownloadRequest", req);
            } else if (contentItem.isArticle()) {
                if (contentItem.get("content")) {
                    // Articles have a content property with the html we want to
                    // download already. It's not loaded in by the topic tree but
                    // when the article is actually loaded.
                    handleContentLoaded(contentItem.get("content"));
                } else {
                    // Sometimes articles are downloaded before they are viewed,
                    // so try to download it here.
                    APIClient.getArticle(contentItem.getId()).done((result) => {
                        contentItem.set("content", result.translated_html_content);
                        handleContentLoaded(contentItem.get("content"));
                    }).fail(() => {
                        return d.reject().promise();
                    });
                }
            }
            return d.promise();
        },
        /**
         * Removes a download from the list of downloaded files and
         * removes the file on disk.
         */
        deleteContent: function(contentItem) {
            var d = $.Deferred();
            var filename = contentItem.getId();
            Storage.delete(filename).done(() => {
                this._removeDownloadFromManifest(contentItem);
                d.resolve();
            }).fail(() => {
                d.reject();
            });
            return d.promise();
        },
        /**
         * Adds the specified model to the list of downloaded files
         */
        _addDownloadToManifest: function(model) {
            this._setDownloaded(model, true);
            Util.log('adding model to manifest: ');
            Util.log(model);
            this.contentList.push(model);
            this._writeManifest();
        },
        /**
         * Remove the specified model from the list of downloaded files
         */
        _removeDownloadFromManifest: function(model) {
            this._setDownloaded(model, false);
            Util.log('removing model from manifest: ');
            Util.log(model);
            this.contentList.remove(model);
            this._writeManifest();
        },
        manifestFilename: "download-manifest.json"
    }

    return Downloads;

});
<|endoftext|>