<commit_before>import React, { Component } from 'react'
import {connect} from 'react-redux'
import {fetchProfile,createBuyRequestFetch,createSellRequestFetch} from '../../actions/index.js'
import Sidebar from '../Sidebar'
import Topbar from '../Topbar'
import defaultImageRequest from '../../../public/assets/img/box-outline-filled.png'
const compId = localStorage.getItem('companyId')
import $ from 'jquery'

let requestInfo = {
  showTitleMessage: function (from, align) {
    $.notify({
      icon: 'pe-7s-close',
      message: '
Please input the title
'
    }, {
      type: 'danger',
      timer: 4000,
      placement: {
        from: from,
        align: align
      }
    })
  },
  showRequestMessage: function (from, align) {
    $.notify({
      icon: 'pe-7s-close',
      message: '
Request message is required
'
    }, {
      type: 'danger',
      timer: 4000,
      placement: {
        from: from,
        align: align
      }
    })
  },
  showTypeMessage: function (from, align) {
    $.notify({
      icon: 'pe-7s-close',
      message: '
Please update your company profile type
'
    }, {
      type: 'danger',
      timer: 4000,
      placement: {
        from: from,
        align: align
      }
    })
  },
  showSubmitMessage: function (from, align) {
    $.notify({
      icon: 'pe-7s-cloud-download',
      message: '
Request has been sent
'
    }, {
      type: 'info',
      timer: 4000,
      placement: {
        from: from,
        align: align
      }
    })
  }
}

class CreateRequest extends Component {
  constructor(){
    super()
    this.state = {
      topbarTitle: 'Create Request',
      activeNavigation: ['','','active','', ''],
      requestData:{
        request: '',
        title: '',
        price: '',
        image: defaultImageRequest
      }
    }
  }

  componentDidMount(){
    this.props.fetchProfile(compId)
  }

  onHandleSubmitRequest(data,id,companyType){
    console.log(data,id,companyType);
    if (this.state.requestData.title === '') {
      requestInfo.showTitleMessage('top','center')
    } else if (this.state.requestData.request === '') {
      requestInfo.showRequestMessage('top','center')
    } else {
      if (companyType === 'ukm'){
        this.props.createSellRequestFetch(data,id)
      } else if (companyType === 'corporate'){
        this.props.createBuyRequestFetch(data,id)
      } else {
        requestInfo.showTypeMessage('top','center')
      }
      requestInfo.showSubmitMessage('top','center')
      this.setState({
        requestData:{
          request: '',
          title: '',
          price: '',
          image: ''
        }
      })
    }

  }

  onHandleChange (e) {
    let newState = {}
    newState[e.target.name] = e.target.value
    const newData = Object.assign({}, this.state.requestData, newState);
    this.setState({requestData: newData})
  }

  render () {
    return (

        <Sidebar activeNavigation={this.state.activeNavigation} />

          <Topbar title={this.state.topbarTitle} />

                      <form>

                                Title

                              <input
                                type='text'
                                className='form-control'
                                name='title'
                                value={this.state.requestData.title}
                                placeholder='Request title'
                                onChange={this.onHandleChange.bind(this)} />

                                Request

                              <textarea
                                rows='3'
                                name='request'
                                className='form-control'
                                value={this.state.requestData.request}
                                placeholder='Request message goes in here. Post a message to all companies within your category'
                                onChange={this.onHandleChange.bind(this)} />

                                Image (Optional)

                              <input
                                type='text'
                                className='form-control'
                                name='image'
                                value={this.state.requestData.image}
                                placeholder='Request image URL'
                                onChange={this.onHandleChange.bind(this)} />

                                Price (Optional)

                              <input
                                type='text'
                                className='form-control'
                                name='price'
                                value={this.state.requestData.price}
                                placeholder='Price Range'
                                onChange={this.onHandleChange.bind(this)} />

                        <button
                          type='submit'
                          className='btn btn-primary btn-fill'
                          style={{marginRight: 20}}
                          onClick={(e) => {
                                     e.preventDefault()
                                     this.onHandleSubmitRequest(this.state.requestData,compId,this.props.profile.type)}}>
                          Submit Request
                        </button>

                      </form>

    )
  }
}

const mapStateToProps = (state) => {
  return {
    profile: state.profile
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    fetchProfile: (id) => dispatch(fetchProfile(id)),
    createBuyRequestFetch: (data,id) => dispatch(createBuyRequestFetch(data,id)),
    createSellRequestFetch: (data,id) => dispatch(createSellRequestFetch(data,id)),
  }
}

export default connect(mapStateToProps,mapDispatchToProps)(CreateRequest)
<commit_msg>pull development<commit_after>import React, { Component } from 'react'
import {connect} from 'react-redux'
import {fetchProfile,createBuyRequestFetch,createSellRequestFetch} from '../../actions/index.js'
import Sidebar from '../Sidebar'
import Topbar from '../Topbar'
import defaultImageRequest from '../../../public/assets/img/box-outline-filled.png'
const compId = localStorage.getItem('companyId')
import $ from 'jquery'

// image upload
import Dropzone from 'react-dropzone'
import upload from 'superagent'
import superagent from 'superagent'

// generateRandomString
function generateRandomString() {
    var length = 5,
        charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
        retVal = "";
    for (var i = 0, n = charset.length; i < length; ++i) {
        retVal += charset.charAt(Math.floor(Math.random() * n));
    }
    return retVal;
}

let requestInfo = {
  showTitleMessage: function (from, align) {
    $.notify({
      icon: 'pe-7s-close',
      message: '
Please input the title
'
    }, {
      type: 'danger',
      timer: 4000,
      placement: {
        from: from,
        align: align
      }
    })
  },
  showRequestMessage: function (from, align) {
    $.notify({
      icon: 'pe-7s-close',
      message: '
Request message is required
'
    }, {
      type: 'danger',
      timer: 4000,
      placement: {
        from: from,
        align: align
      }
    })
  },
  showTypeMessage: function (from, align) {
    $.notify({
      icon: 'pe-7s-close',
      message: '
Please update your company profile type
'
    }, {
      type: 'danger',
      timer: 4000,
      placement: {
        from: from,
        align: align
      }
    })
  },
  showSubmitMessage: function (from, align) {
    $.notify({
      icon: 'pe-7s-cloud-download',
      message: '
Request has been sent
'
    }, {
      type: 'info',
      timer: 4000,
      placement: {
        from: from,
        align: align
      }
    })
  }
}

class CreateRequest extends Component {
  constructor(){
    super()
    this.state = {
      topbarTitle: 'Create Request',
      activeNavigation: ['','','active','', ''],
      requestData:{
        request: '',
        title: '',
        price: '',
        image: defaultImageRequest
      },
      files: []
    }
  }

  componentDidMount(){
    this.props.fetchProfile(compId)
  }

  onHandleSubmitRequest(data,id,companyType){
    console.log(data,id,companyType);
    if (this.state.requestData.title === '') {
      requestInfo.showTitleMessage('top','center')
    } else if (this.state.requestData.request === '') {
      requestInfo.showRequestMessage('top','center')
    } else {
      if (companyType === 'ukm'){
        this.props.createSellRequestFetch(data,id)
      } else if (companyType === 'corporate'){
        this.props.createBuyRequestFetch(data,id)
      } else {
        requestInfo.showTypeMessage('top','center')
      }
      requestInfo.showSubmitMessage('top','center')
      this.setState({
        requestData:{
          request: '',
          title: '',
          price: '',
          image: ''
        }
      })
    }

  }

  onHandleChange (e) {
    let newState = {}
    newState[e.target.name] = e.target.value
    const newData = Object.assign({}, this.state.requestData, newState);
    this.setState({requestData: newData})
  }

  /*
    image upload
  */
  onDrop(acceptedFiles) {
    const that = this

    let randomString = generateRandomString()
    let newState = {
      image: 'https://s3-ap-southeast-1.amazonaws.com/ukm-hub/images/'+randomString + acceptedFiles[0].name
    }
    const newImage = Object.assign({}, this.state.data, newState);

    setTimeout(function(){
      that.setState({
        files: acceptedFiles,
        randomImageKey: randomString
      });
    }, 1000)
  }

  render () {
    return (

        <Sidebar activeNavigation={this.state.activeNavigation} />

          <Topbar title={this.state.topbarTitle} />

                      <form>

                                Title

                              <input
                                type='text'
                                className='form-control'
                                name='title'
                                value={this.state.requestData.title}
                                placeholder='Request title'
                                onChange={this.onHandleChange.bind(this)} />

                                Request

                              <textarea
                                rows='3'
                                name='request'
                                className='form-control'
                                value={this.state.requestData.request}
                                placeholder='Request message goes in here. Post a message to all companies within your category'
                                onChange={this.onHandleChange.bind(this)} />

                                Image (Optional)

                              {/* file upload */}
                              <Dropzone style={{width:'100%', border:'1px dotted rgb(50,50,50)', height:200, cursor:'pointer', display:'flex',justifyContent:'center',alignItems:'center',fontFamily:'open sans'}} ref={(node) => { this.dropzone = node; }} onDrop={this.onDrop.bind(this)}>

Try dropping some files here, or click to select files to upload.

                              </Dropzone>
                              {
                                this.state.files.length > 0 ?

Uploading {this.state.files.length} files...

{this.state.files.map((file,index) =>  )}

 : null
                              }
                              {/* file upload */}

                                Price (Optional)

                              <input
                                type='text'
                                className='form-control'
                                name='price'
                                value={this.state.requestData.price}
                                placeholder='Price Range'
                                onChange={this.onHandleChange.bind(this)} />

                        <button
                          type='submit'
                          className='btn btn-primary btn-fill'
                          style={{marginRight: 20}}
                          onClick={(e) => {
                                     e.preventDefault()
                                     this.onHandleSubmitRequest(this.state.requestData,compId,this.props.profile.type)}}>
                          Submit Request
                        </button>

                      </form>

    )
  }
}

const mapStateToProps = (state) => {
  return {
    profile: state.profile
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    fetchProfile: (id) => dispatch(fetchProfile(id)),
    createBuyRequestFetch: (data,id) => dispatch(createBuyRequestFetch(data,id)),
    createSellRequestFetch: (data,id) => dispatch(createSellRequestFetch(data,id)),
  }
}

export default connect(mapStateToProps,mapDispatchToProps)(CreateRequest)
<|endoftext|>


<commit_before>require('c3/c3.min.css');
require('../../css/share-search.css');

var m = require('mithril');
var $osf = require('osfHelpers');
var Stats = require('../share/stats.js');
var Results = require('../share/results.js');
var SearchBar = require('../share/searchBar.js');
var MESSAGES = JSON.parse(require('raw!../share/messages.json'));

var ShareApp = {};

ShareApp.ViewModel = function() {
    var self = this;

    self.time = 0;
    self.page = 0;
    self.count = 0;
    self.results = [];
    self.query = m.prop($osf.urlParams().q || '');
};

ShareApp.view = function(ctrl) {
    return m('.row', [
        m('.col-md-offset-1.col-md-10', [
            SearchBar.view(ctrl.searchBarController),
            Stats.view(ctrl.statsController),
            m('br'),
            Results.view(ctrl.resultsController),
	    m('.row', m('.col-md-12', {style: "padding-top: 30px;"},m('span', m.trust(MESSAGES.ABOUTSHARE))))
        ])
    ]);
};

ShareApp.controller = function() {
    var self = this;

    self.vm = new ShareApp.ViewModel(self.vm);
    self.statsController = new Stats.controller(self.vm);
    self.resultsController = new Results.controller(self.vm);
    self.searchBarController = new SearchBar.controller(self.vm);
};

module.exports = ShareApp;
<commit_msg>Import the unminified c3 css rather than the minified version<commit_after>require('c3/c3.css');
require('../../css/share-search.css');

var m = require('mithril');
var $osf = require('osfHelpers');
var Stats = require('../share/stats.js');
var Results = require('../share/results.js');
var SearchBar = require('../share/searchBar.js');
var MESSAGES = JSON.parse(require('raw!../share/messages.json'));

var ShareApp = {};

ShareApp.ViewModel = function() {
    var self = this;

    self.time = 0;
    self.page = 0;
    self.count = 0;
    self.results = [];
    self.query = m.prop($osf.urlParams().q || '');
};

ShareApp.view = function(ctrl) {
    return m('.row', [
        m('.col-md-offset-1.col-md-10', [
            SearchBar.view(ctrl.searchBarController),
            Stats.view(ctrl.statsController),
            m('br'),
            Results.view(ctrl.resultsController),
	    m('.row', m('.col-md-12', {style: "padding-top: 30px;"},m('span', m.trust(MESSAGES.ABOUTSHARE))))
        ])
    ]);
};

ShareApp.controller = function() {
    var self = this;

    self.vm = new ShareApp.ViewModel(self.vm);
    self.statsController = new Stats.controller(self.vm);
    self.resultsController = new Results.controller(self.vm);
    self.searchBarController = new SearchBar.controller(self.vm);
};

module.exports = ShareApp;
<|endoftext|>


<commit_before>var c3 = require('c3');
var m = require('mithril');
var $osf = require('osfHelpers');
var utils = require('./utils.js');

var Stats = {};

function get_source_length(elastic_data) {

    sources = elastic_data.raw_aggregations.sources.buckets;
    source_names = [];
    for (i = 0; i < sources.length; i++) {
        source_names.push(sources[i]);
    }

    return source_names.length;
}

function donutGraph (data) {
    return c3.generate({
        bindto: '#shareDonutGraph',
        size: {
            height: 200
        },
        data: data.charts.shareDonutGraph,
        donut: {
            title: get_source_length(data) + ' Providers'
        },
        legend: {
            show: false
        }
    });
}

function timeGraph (data) {
    return c3.generate({
        bindto: '#shareTimeGraph',
        size: {
            height: 200
        },
        data: data.charts.shareTimeGraph,
        axis: {
            x: {
                type: 'category',
            }
        },
        legend: {
            show: false
        }
    });
}

Stats.view = function(ctrl) {
    return [
        m('.row', {style: {color: 'darkgrey'}}, [
            m('.col-md-4', m('p.text-center', ctrl.vm.latestDate ? utils.formatNumber(ctrl.vm.totalCount) + ' events as of ' + ctrl.vm.latestDate : '')),
            m('.col-md-4', m('p.text-center', ctrl.vm.query().length > 0 ? 'Found ' + utils.formatNumber(ctrl.vm.count) + ' events in ' + ctrl.vm.time + ' seconds' : '')),
            m('.col-md-4', m('p.text-center', ctrl.vm.providers + ' content providers'))
        ]),
        m('.row', ctrl.vm.showStats ? [
            m('.col-md-12', [
                m('.row', m('.col-md-12', [
                    !ctrl.vm.statsLoaded() ? m('img[src=/static/img/loading.gif]') : [
                        m('.row', [
                            m('.col-sm-3', ctrl.drawGraph('shareDonutGraph', donutGraph)),
                            m('.col-sm-9', ctrl.drawGraph('shareTimeGraph', timeGraph))
                        ])
                    ]
                ]))
            ]),
        ] : []),
        m('.row', [
            m('col-md-12', m('a.stats-expand', {
                onclick: function() {ctrl.vm.showStats = !ctrl.vm.showStats;}
            },
                ctrl.vm.showStats ? m('i.icon-angle-up') : m('i.icon-angle-down')
            ))
        ])
    ];
};

Stats.controller = function(vm) {
    var self = this;

    self.vm = vm;
    self.vm.providers = 26;

    self.graphs = {};

    self.vm.totalCount = 0;
    self.vm.showStats = true;
    self.vm.latestDate = undefined;
    self.vm.statsLoaded = m.prop(false);

    self.drawGraph = function(divId, graphFunction) {
        return m('div', {id: divId, config: function(e, i) {
            if (i) return;
            self.graphs[divId] = graphFunction(self.vm.statsData);
        }});
    };

    self.loadStats = function() {
        self.vm.statsLoaded(false);

        m.request({
            method: 'GET',
            url: '/api/v1/share/stats/?' + $.param({q: self.vm.query()}),
            background: true
        }).then(function(data) {
            self.vm.statsData = data;
            Object.keys(self.graphs).map(function(type) {
                self.vm.statsData.charts[type].unload = true;
                if(type === 'shareDonutGraph') {
                    $('.c3-chart-arcs-title').text(data.raw_aggregations.sources.buckets.length + ' Provider');
                }
                self.graphs[type].load(self.vm.statsData.charts[type]);
            });
            self.vm.statsLoaded(true);
        }).then(m.redraw);
    };

    utils.onSearch(self.loadStats);

    m.request({
        method: 'GET',
        background: true,
        url: '/api/v1/share/?size=1',
    }).then(function(data) {
        self.vm.totalCount = data.count;
        self.vm.latestDate = new $osf.FormattableDate(data.results[0].dateUpdated).local;
    }).then(m.redraw);

    self.loadStats();
};

module.exports = Stats;
<commit_msg>Update for provider count<commit_after>var c3 = require('c3');
var m = require('mithril');
var $osf = require('osfHelpers');
var utils = require('./utils.js');

var Stats = {};

function get_source_length(elastic_data) {

    sources = elastic_data.raw_aggregations.sources.buckets;
    source_names = [];
    for (i = 0; i < sources.length; i++) {
        source_names.push(sources[i]);
    }

    return source_names.length;
}

function donutGraph (data) {
    return c3.generate({
        bindto: '#shareDonutGraph',
        size: {
            height: 200
        },
        data: data.charts.shareDonutGraph,
        donut: {
            title: get_source_length(data) + ' Providers'
        },
        legend: {
            show: false
        }
    });
}

function timeGraph (data) {
    return c3.generate({
        bindto: '#shareTimeGraph',
        size: {
            height: 200
        },
        data: data.charts.shareTimeGraph,
        axis: {
            x: {
                type: 'category',
            }
        },
        legend: {
            show: false
        }
    });
}

Stats.view = function(ctrl) {
    return [
        m('.row', {style: {color: 'darkgrey'}}, [
            m('.col-md-4', m('p.text-center', ctrl.vm.latestDate ? utils.formatNumber(ctrl.vm.totalCount) + ' events as of ' + ctrl.vm.latestDate : '')),
            m('.col-md-4', m('p.text-center', ctrl.vm.query().length > 0 ? 'Found ' + utils.formatNumber(ctrl.vm.count) + ' events in ' + ctrl.vm.time + ' seconds' : '')),
            m('.col-md-4', m('p.text-center', ctrl.vm.providers + ' content providers'))
        ]),
        m('.row', ctrl.vm.showStats ? [
            m('.col-md-12', [
                m('.row', m('.col-md-12', [
                    !ctrl.vm.statsLoaded() ? m('img[src=/static/img/loading.gif]') : [
                        m('.row', [
                            m('.col-sm-3', ctrl.drawGraph('shareDonutGraph', donutGraph)),
                            m('.col-sm-9', ctrl.drawGraph('shareTimeGraph', timeGraph))
                        ])
                    ]
                ]))
            ]),
        ] : []),
        m('.row', [
            m('col-md-12', m('a.stats-expand', {
                onclick: function() {ctrl.vm.showStats = !ctrl.vm.showStats;}
            },
                ctrl.vm.showStats ? m('i.icon-angle-up') : m('i.icon-angle-down')
            ))
        ])
    ];
};

Stats.controller = function(vm) {
    var self = this;

    self.vm = vm;
    self.vm.providers = 26;

    self.graphs = {};

    self.vm.totalCount = 0;
    self.vm.showStats = true;
    self.vm.latestDate = undefined;
    self.vm.statsLoaded = m.prop(false);

    self.drawGraph = function(divId, graphFunction) {
        return m('div', {id: divId, config: function(e, i) {
            if (i) return;
            self.graphs[divId] = graphFunction(self.vm.statsData);
        }});
    };

    self.loadStats = function() {
        self.vm.statsLoaded(false);

        m.request({
            method: 'GET',
            url: '/api/v1/share/stats/?' + $.param({q: self.vm.query()}),
            background: true
        }).then(function(data) {
            self.vm.statsData = data;
            Object.keys(self.graphs).map(function(type) {
                self.vm.statsData.charts[type].unload = true;
                if(type === 'shareDonutGraph') {
                    var count = data.charts.shareDonutGraph.columns.filter(function(val){return val[1] > 0;}).length;
                    $('.c3-chart-arcs-title').text(count + ' Provider' + (count > 1 ? 's' : ''));
                }
                self.graphs[type].load(self.vm.statsData.charts[type]);
            });
            self.vm.statsLoaded(true);
        }).then(m.redraw);
    };

    utils.onSearch(self.loadStats);

    m.request({
        method: 'GET',
        background: true,
        url: '/api/v1/share/?size=1',
    }).then(function(data) {
        self.vm.totalCount = data.count;
        self.vm.latestDate = new $osf.FormattableDate(data.results[0].dateUpdated).local;
    }).then(m.redraw);

    self.loadStats();
};

module.exports = Stats;
<|endoftext|>


<commit_before>// Pseudocode

// Create a variable that takes an array of number elements (it can be contain any number of elements).
// Create three separate operations that can all be run on your list of numbers.

// Operation 1 should take all of the elements in your numbers variable and add them up to get the sum.

// Operation 2 should find the average (mean) of the numbers variable.  To do so:
  // First calculate the sum of all of the numbers.
  // Then divide the sum by the number of elements in the variable.

// Operation 3 (has two senarios depending on the number of elements in the numbers variable) you should find the middle (median) value of the elements in the numbers variable.  To do so:
  // If there is an odd number of elements in the numbers variable:
  //First order the numbers either from lowest to highest or highest to lowest.
  // Then find the middle value of the numbers variable which is the median.

  // If there is an even number of elements in the numbers variable:
  // First order the numbers either from lowest to highest or highest to lowest.
  // Find find the two middle values.
  // Add the two middle values together then divide them by two.
  // That should give you the median

<commit_msg>Submit group assignment<commit_after>// Accountability Group 12
// 7.8 JavaScript Telephone
// Due January 4, 2016

// RELEASE 1: TESTS TO USER STORIES by Jim O'Neal

// Ok, I'm going to do my best to follow the instructions and write my user
// stories without using any code language at all, although the instructions
// do say that I can use the name of the functions that the tests intend for
// us to write.  Here it goes:

// As a user, I want to perform three different mathematical operations on a
// list of numbers.  The list of numbers could be any quantity of numbers.  The
// three things are three separate operations and don't need to be done all at
// once.  I just want to be able to use whichever operation I want on any list
// of numbers.  The three things I want to be able to do are:
// 1) I want to add up (sum) the list of numbers.
// 2) I want to find the average (mean) of the list of numbers.
// 3) I want to find the middle value (median) of the list of numbers.

// RELEASE 2: USER STORIES TO PSEUDOCODE by LeeAnne Hawley

// Create a variable that takes an array of number elements (it can be contain any number of elements).
// Create three separate operations that can all be run on your list of numbers.

// Operation 1 should take all of the elements in your numbers variable and add them up to get the sum.

// Operation 2 should find the average (mean) of the numbers variable.  To do so:
  // First calculate the sum of all of the numbers.
  // Then divide the sum by the number of elements in the variable.

// Operation 3 (has two senarios depending on the number of elements in the numbers variable) you should find the middle (median) value of the elements in the numbers variable.  To do so:
  // If there is an odd number of elements in the numbers variable:
  //First order the numbers either from lowest to highest or highest to lowest.
  // Then find the middle value of the numbers variable which is the median.

  // If there is an even number of elements in the numbers variable:
  // First order the numbers either from lowest to highest or highest to lowest.
  // Find find the two middle values.
  // Add the two middle values together then divide them by two.
  // That should give you the median

// RELEASE #3: PSEUDOCODE TO CODE by Gary Wong

//Any integers can be in the array
var number_array = [2,6,4,5,5,1]

//sum
var operation_1_draft = function(){
  var total = 0;
  for (var i = 0; i < number_array.length; i++){
    total += number_array[i];
  }
  console.log(total)
}

//average
var operation_2_draft = function(){
  var total = 0;
  var average = 0;
  for (var i = 0; i < number_array.length; i++){
  total += number_array[i];
    average = total/(i+1);
  }
  console.log(average)
}

//median
var operation_3_draft = function() {
  var median = 0;
  var middle = Math.floor(number_array.length / 2);
  if (number_array.length % 2 == 0) {
    //even
    number_array.sort(function(a,b){return a - b})
    median = (number_array[middle-1]  + number_array[middle]) /2
  }else {
    //odd
    number_array.sort(function(a,b){return a - b})
    median = number_array[middle]
  }
  console.log(median)
}

//testing to make sure the function works
// operation_1_draft()
// operation_2_draft()
// operation_3_draft()
// console.log(number_array)

// RELEASE 4: REFACTOR AND TRANSLATE TO USER STORIES by Chris Wong

//Any integers can be in the array
var number_array = [2,6,4,5,5,1]

// Refactor

//sum refactor
function sum(number_array){
  var total = 0;
  for (var i = 0; i < number_array.length; i++){total += number_array[i]}
  return total
}

//average refactor
function mean(number_array){
  var total = 0;
  for (var i = 0; i < number_array.length; i++){
    total += number_array[i];
    var average = total/(i+1);
  }
  return average
}

//median refactor
function median(number_array){
  var middle = Math.floor(number_array.length/2);
  number_array.sort( function(a,b) {return a - b;} );

  if(number_array.length % 2 == 0)
    //even
    return ((number_array[middle-1] + number_array[middle]) / 2);
  else
    //odd
    return number_array[middle]
}

/* User Stories
operation_1
As a user, I want to take a group of numbers and add them all together to get the total.
operation_2
As a user, I want to get the average of a group of numbers.
operation_3
As a user, I want to get the median of a group of numbers.
If the group of numbers is even, I want to get the average of the two closest numbers in the middle.
*/

// RELEASE 5: PULL IT ALL TOGETHER by Kurt Schlueter

// Thanks to everyone for being patient with me. I went ahead and refactored Chris' solution so that I could run the tests from the tests.js file. I inserted an input into each function, changed the name of each function, and returned a value in each function. Everything worked as expected. Below I took the tests.js file and summarized it so that everyone can run it on this file and see that every function is working like it is supposed to.

//testing to make sure the function works
oddLengthArray  = [1, 2, 3, 4, 5, 5, 7]
evenLengthArray = [4, 4, 5, 5, 6, 6, 6, 7]

console.log(sum(oddLengthArray))
console.log('Needs to be 27')
console.log(sum(evenLengthArray))
console.log('Needs to be 43')
console.log(mean(oddLengthArray))
console.log('Needs to be 3.857142857142857')
console.log(mean(evenLengthArray))
console.log('Needs to be 5.375')
console.log(median(oddLengthArray))
console.log('Needs to be 4')
console.log(median(evenLengthArray))
console.log('Needs to be 5.5')

// Thanks everyone!<|endoftext|>


<commit_before>(function () {
  'use strict';

  angular
    .module('app')
    .controller('SpotsController', Spots);

  Spots.$inject = ['$cordovaDevice', '$cordovaFile', '$document', '$ionicHistory', '$ionicLoading', '$ionicModal',
    '$ionicPopover', '$ionicPopup', '$location', '$log', '$scope', '$window', 'HelpersFactory', 'ProjectFactory',
    'SpotFactory', 'SpotsFactory', 'UserFactory'];

  function Spots($cordovaDevice, $cordovaFile, $document, $ionicHistory, $ionicLoading, $ionicModal, $ionicPopover,
                 $ionicPopup, $location, $log, $scope, $window, HelpersFactory, ProjectFactory, SpotFactory,
                 SpotsFactory, UserFactory) {
    var vm = this;

    var visibleDatasets = [];

    vm.activeDatasets = [];
    vm.checkedDataset = checkedDataset;
    vm.closeDetailModal = closeDetailModal;
    vm.closeModal = closeModal;
    vm.deleteAllActiveSpots = deleteAllActiveSpots;
    vm.deleteSelected = false;
    vm.deleteSpot = deleteSpot;
    vm.exportToCSV = exportToCSV;
    vm.getTagNames = getTagNames;
    vm.filter = filter;
    vm.filterModal = {};
    vm.filterOn = false;
    vm.goToSpot = goToSpot;
    vm.hasTags = hasTags;
    vm.isDatasetChecked = isDatasetChecked;
    vm.isOnlineLoggedIn = isOnlineLoggedIn;
    vm.loadMoreSpots = loadMoreSpots;
    vm.moreSpotsCanBeLoaded = moreSpotsCanBeLoaded;
    vm.newSpot = newSpot;
    vm.resetFilters = resetFilters;
    vm.setListDetail = setListDetail;
    vm.showDetail = SpotsFactory.getSpotsListDetail();
    vm.spots = [];
    vm.spotsDisplayed = [];

    HelpersFactory.setBackView($ionicHistory.currentView().url);
    activate();

    /**
     * Private Functions
     */

    function activate() {
      SpotFactory.clearCurrentSpot();           // Make sure the current spot is empty
      visibleDatasets = SpotFactory.getVisibleDatasets();
      setVisibleSpots();
      createPopover();

      $ionicModal.fromTemplateUrl('app/spot/spots-filter-modal.html', {
        'scope': $scope,
        'animation': 'slide-in-up',
        'backdropClickToClose': false
      }).then(function (modal) {
        vm.filterModal = modal;
      });

      $ionicModal.fromTemplateUrl('app/spot/spots-list-detail-modal.html', {
        'scope': $scope,
        'animation': 'slide-in-up',
        'backdropClickToClose': false
      }).then(function (modal) {
        vm.detailModal = modal;
      });

      // Cleanup the modal when we're done with it!
      $scope.$on('$destroy', function () {
        vm.filterModal.remove();
        vm.detailModal.remove();
      });
    }

    function createPopover() {
      $ionicPopover.fromTemplateUrl('app/spot/spots-popover.html', {
        'scope': $scope
      }).then(function (popover) {
        vm.popover = popover;
      });

      // Cleanup the popover when we're done with it!
      $scope.$on('$destroy', function () {
        vm.popover.remove();
      });
    }

    function setVisibleSpots() {
      var activeSpots = SpotFactory.getActiveSpots();
      if (_.isEmpty(visibleDatasets)) {
        vm.spots = activeSpots;
        vm.filterOn = false;
      }
      else {
        var datasetIdsToSpotIds = ProjectFactory.getSpotIds();
        var visibleSpotsIds = [];
        _.each(visibleDatasets, function (visibleDataset) {
          visibleSpotsIds.push(datasetIdsToSpotIds[visibleDataset]);
        });
        visibleSpotsIds = _.flatten(visibleSpotsIds);
        vm.spots = _.filter(activeSpots, function (activeSpot) {
          return _.contains(visibleSpotsIds, activeSpot.properties.id);
        });
        vm.filterOn = true;
      }
      vm.spots = _.sortBy(vm.spots, function (spot) {
        return spot.properties.modified_timestamp;
      }).reverse();
      vm.spotsDisplayed = vm.spots.slice(0, 25);
    }

    /**
     * Public Functions
     */

    function checkedDataset(dataset) {
      $log.log('visibleDatasets:', visibleDatasets);
      var i = _.indexOf(visibleDatasets, dataset);
      if (i === -1) visibleDatasets.push(dataset);
      else visibleDatasets.splice(i, 1);
      SpotFactory.setVisibleDatasets(visibleDatasets);
      $log.log('visibleDatasets after:', visibleDatasets);
    }

    function closeModal(modal) {
      setVisibleSpots();
      vm[modal].hide();
    }

    function closeDetailModal() {
      SpotsFactory.setSpotsListDetail(vm.showDetail);
      vm.detailModal.hide();
    }

    // clears all spots
    function deleteAllActiveSpots() {
      vm.popover.hide();
      var confirmPopup = $ionicPopup.confirm({
        'title': 'Delete Spots',
        'template': 'Are you sure you want to delete ALL active spots? This will also delete any associated image basemaps.'
      });
      confirmPopup.then(
        function (res) {
          if (res) {
            $ionicLoading.show({
              'template': '<ion-spinner></ion-spinner>'
            });
            SpotFactory.clearActiveSpots().then(function () {
              // update the spots list
              vm.spots = [];
              vm.spotsDisplayed = [];
              $ionicLoading.hide();
            });
          }
        }
      );
    }

    function deleteSpot(spot) {
      vm.deleteSelected = true;

      if (SpotFactory.isSafeDelete(spot)) {
        var confirmPopup = $ionicPopup.confirm({
          'title': 'Delete Spot',
          'template': 'Are you sure you want to delete Spot ' + spot.properties.name + '?'
        });
        confirmPopup.then(function (res) {
          if (res) {
            SpotFactory.destroy(spot.properties.id).then(function () {
              vm.spots = SpotFactory.getActiveSpots();
              vm.spotsDisplayed = angular.fromJson(angular.toJson(vm.spots)).slice(0, 20);
            });
          }
          vm.deleteSelected = false;
        });
      }
      else {
        var alertPopup = $ionicPopup.alert({
          'title': 'Spot Deletion Prohibited!',
          'template': 'This Spot has at least one image being used as an image basemap. Remove any image basemaps' +
          ' from this Spot before deleting.'
        });
        alertPopup.then(function () {
          vm.deleteSelected = false;
        });
      }
    }

    // Export data to CSV
    function exportToCSV() {
      vm.popover.hide();
      // Convert the spot objects to a csv format
      function convertToCSV() {
        // Get all the fields for the csv header row
        var allHeaders = [];
        _.each(vm.spots,
          function (spot) {
            var headers = _.keys(spot.properties);
            // If there are custom fields, loop through the custom object grabbing those fields
            var i = _.indexOf(headers, 'custom');
            if (i !== -1) {
              headers = _.without(headers, 'custom');
              var customHeaders = _.keys(spot.properties.custom);
              customHeaders = _.map(customHeaders,
                function (header) {
                  return 'custom_' + header;
                }
              );
              allHeaders = _.union(headers, allHeaders, customHeaders);
            }
            else {
              allHeaders = _.union(headers, allHeaders);
            }
          }
        );
        // Add the two fields for the geometry
        allHeaders.push('geometry_type');
        allHeaders.push('geometry_coordinates');

        var allHeadersQuoted = _.map(allHeaders,
          function (ele) {
            return '\'' + ele + '\'';
          }
        );
        var csv = allHeadersQuoted.toString() + '\r\n';

        // Get all the values for each csv data row
        _.each(vm.spots,
          function (spot) {
            var row = new Array(allHeaders.length);
            row = _.map(row,
              function (ele) {
                return '';
              }
            );
            _.each(spot.properties,
              function (value, key) {
                // If the value is actually an object
                var i = _.indexOf(allHeaders, key);
                if (_.isObject(value)) {
                  // Separate date parts
                  if (value instanceof Date) {
                    if (key === 'time') {
                      row[i] = value.toLocaleTimeString();
                    }
                    else if (key === 'date') {
                      row[i] = value.toLocaleDateString();
                    }
                    else {
                      row[i] = value.toJSON();
                    }
                  }
                  // Flatten the child values
                  else {
                    row[i] = '\'' + _.flatten(value).join(', ') + '\'';
                  }
                }
                else {
                  row[i] = '\'' + value + '\'';
                }
              }
            );
            // Add the values for the geometry fields
            if (angular.isDefined(spot.geometry) && angular.isDefined(spot.geometry.type)) {
              var i = _.indexOf(allHeaders, 'geometry_type');
              row[i] = '\'' + spot.geometry.type + '\'';
              if (angular.isDefined(spot.geometry.coordinates)) {
                i = _.indexOf(allHeaders, 'geometry_coordinates');
                row[i] = '\'' + _.flatten(spot.geometry.coordinates).join(', ') + '\'';
              }
            }
            csv += row.toString() + '\r\n';
          }
        );
        return csv;
      }

      var spotData = convertToCSV(vm.spots);

      // If this is a web browser and not using cordova
      if ($document[0].location.protocol !== 'file:') { // Phonegap is not present }
        spotData = spotData.replace(/\r\n/g, '
');
        var win = $window.open();
        win.document.body.innerHTML = spotData;
        return;
      }

      var d = new Date();
      d = d.toLocaleDateString() + '-' + d.toLocaleTimeString();
      d = d.replace(/\//g, '-');
      d = d.replace(/:/g, '');
      d = d.replace(/ /g, '');
      var fileName = d + '-' + 'strabo-data.csv';

      var devicePath;
      switch ($cordovaDevice.getPlatform()) {
        case 'Android':
          devicePath = cordova.file.externalRootDirectory;
          break;
        case 'iOS':
          devicePath = cordova.file.documentsDirectory;
          break;
        default:
          // uh oh?  TODO: what about windows and blackberry?
          devicePath = cordova.file.externalRootDirectory;
          break;
      }

      var directory = 'strabo';

      function writeFile(dir) {
        $cordovaFile.writeFile(devicePath + dir, fileName, spotData, true).then(
          function (success) {
            $log.log(success);
            $ionicPopup.alert({
              'title': 'Success!',
              'template': 'CSV written to ' + devicePath + dir + fileName
            });
          },
          function (error) {
            $log.log(error);
          }
        );
      }

      $cordovaFile.checkDir(devicePath, directory).then(
        function (success) {
          $log.log(success);
          writeFile(success.fullPath);
        },
        function (error) {
          $cordovaFile.createDir(devicePath, directory, false).then(
            function (success) {
              $log.log(success);
              writeFile(success.fullPath);
            },
            function (error) {
              $log.log(error);
            }
          );
        }
      );
    }

    function filter() {
      vm.activeDatasets = ProjectFactory.getActiveDatasets();
      vm.filterModal.show();
    }

    function getTagNames(spotId) {
      var tags = ProjectFactory.getTagsBySpotId(spotId);
      return _.pluck(tags, 'name').join(', ');
    }

    function goToSpot(id) {
      if (!vm.deleteSelected) {
        $location.path('/app/spotTab/' + id + '/spot');
      }
    }

    function hasTags(spotId) {
      return !_.isEmpty(ProjectFactory.getTagsBySpotId(spotId));
    }

    function isDatasetChecked(id) {
      return _.find(visibleDatasets, function (visibleDataset) {
        return visibleDataset === id;
      });
    }

    // Is the user online and logged in
    function isOnlineLoggedIn() {
      return navigator.onLine && UserFactory.getUser();
    }

    function loadMoreSpots() {
      vm.spotsDisplayed = vm.spots.slice(0, vm.spotsDisplayed.length + 20);
      $scope.$broadcast('scroll.infiniteScrollComplete');
    }

    function moreSpotsCanBeLoaded() {
      return vm.spotsDisplayed.length !== vm.spots.length;
    }

    // Create a new Spot
    function newSpot() {
      SpotFactory.setNewSpot({}).then(function (id) {
        $location.path('/app/spotTab/' + id + '/spot');
      });
    }

    function resetFilters() {
      visibleDatasets = [];
      SpotFactory.setVisibleDatasets(visibleDatasets);
      setVisibleSpots();
    }

    function setListDetail() {
      vm.popover.hide();
      vm.detailModal.show();
    }
  }
}());
<commit_msg>fixed bug when deleting a spot in the spots list<commit_after>(function () {
  'use strict';

  angular
    .module('app')
    .controller('SpotsController', Spots);

  Spots.$inject = ['$cordovaDevice', '$cordovaFile', '$document', '$ionicHistory', '$ionicLoading', '$ionicModal',
    '$ionicPopover', '$ionicPopup', '$location', '$log', '$scope', '$window', 'HelpersFactory', 'ProjectFactory',
    'SpotFactory', 'SpotsFactory', 'UserFactory'];

  function Spots($cordovaDevice, $cordovaFile, $document, $ionicHistory, $ionicLoading, $ionicModal, $ionicPopover,
                 $ionicPopup, $location, $log, $scope, $window, HelpersFactory, ProjectFactory, SpotFactory,
                 SpotsFactory, UserFactory) {
    var vm = this;

    var visibleDatasets = [];

    vm.activeDatasets = [];
    vm.checkedDataset = checkedDataset;
    vm.closeDetailModal = closeDetailModal;
    vm.closeModal = closeModal;
    vm.deleteAllActiveSpots = deleteAllActiveSpots;
    vm.deleteSelected = false;
    vm.deleteSpot = deleteSpot;
    vm.exportToCSV = exportToCSV;
    vm.getTagNames = getTagNames;
    vm.filter = filter;
    vm.filterModal = {};
    vm.filterOn = false;
    vm.goToSpot = goToSpot;
    vm.hasTags = hasTags;
    vm.isDatasetChecked = isDatasetChecked;
    vm.isOnlineLoggedIn = isOnlineLoggedIn;
    vm.loadMoreSpots = loadMoreSpots;
    vm.moreSpotsCanBeLoaded = moreSpotsCanBeLoaded;
    vm.newSpot = newSpot;
    vm.resetFilters = resetFilters;
    vm.setListDetail = setListDetail;
    vm.showDetail = SpotsFactory.getSpotsListDetail();
    vm.spots = [];
    vm.spotsDisplayed = [];

    HelpersFactory.setBackView($ionicHistory.currentView().url);
    activate();

    /**
     * Private Functions
     */

    function activate() {
      SpotFactory.clearCurrentSpot();           // Make sure the current spot is empty
      visibleDatasets = SpotFactory.getVisibleDatasets();
      setVisibleSpots();
      createPopover();

      $ionicModal.fromTemplateUrl('app/spot/spots-filter-modal.html', {
        'scope': $scope,
        'animation': 'slide-in-up',
        'backdropClickToClose': false
      }).then(function (modal) {
        vm.filterModal = modal;
      });

      $ionicModal.fromTemplateUrl('app/spot/spots-list-detail-modal.html', {
        'scope': $scope,
        'animation': 'slide-in-up',
        'backdropClickToClose': false
      }).then(function (modal) {
        vm.detailModal = modal;
      });

      // Cleanup the modal when we're done with it!
      $scope.$on('$destroy', function () {
        vm.filterModal.remove();
        vm.detailModal.remove();
      });
    }

    function createPopover() {
      $ionicPopover.fromTemplateUrl('app/spot/spots-popover.html', {
        'scope': $scope
      }).then(function (popover) {
        vm.popover = popover;
      });

      // Cleanup the popover when we're done with it!
      $scope.$on('$destroy', function () {
        vm.popover.remove();
      });
    }

    function setVisibleSpots() {
      var activeSpots = SpotFactory.getActiveSpots();
      if (_.isEmpty(visibleDatasets)) {
        vm.spots = activeSpots;
        vm.filterOn = false;
      }
      else {
        var datasetIdsToSpotIds = ProjectFactory.getSpotIds();
        var visibleSpotsIds = [];
        _.each(visibleDatasets, function (visibleDataset) {
          visibleSpotsIds.push(datasetIdsToSpotIds[visibleDataset]);
        });
        visibleSpotsIds = _.flatten(visibleSpotsIds);
        vm.spots = _.filter(activeSpots, function (activeSpot) {
          return _.contains(visibleSpotsIds, activeSpot.properties.id);
        });
        vm.filterOn = true;
      }
      vm.spots = _.sortBy(vm.spots, function (spot) {
        return spot.properties.modified_timestamp;
      }).reverse();
      vm.spotsDisplayed = vm.spots.slice(0, 25);
    }

    /**
     * Public Functions
     */

    function checkedDataset(dataset) {
      $log.log('visibleDatasets:', visibleDatasets);
      var i = _.indexOf(visibleDatasets, dataset);
      if (i === -1) visibleDatasets.push(dataset);
      else visibleDatasets.splice(i, 1);
      SpotFactory.setVisibleDatasets(visibleDatasets);
      $log.log('visibleDatasets after:', visibleDatasets);
    }

    function closeModal(modal) {
      setVisibleSpots();
      vm[modal].hide();
    }

    function closeDetailModal() {
      SpotsFactory.setSpotsListDetail(vm.showDetail);
      vm.detailModal.hide();
    }

    // clears all spots
    function deleteAllActiveSpots() {
      vm.popover.hide();
      var confirmPopup = $ionicPopup.confirm({
        'title': 'Delete Spots',
        'template': 'Are you sure you want to delete ALL active spots? This will also delete any associated image basemaps.'
      });
      confirmPopup.then(
        function (res) {
          if (res) {
            $ionicLoading.show({
              'template': '<ion-spinner></ion-spinner>'
            });
            SpotFactory.clearActiveSpots().then(function () {
              // update the spots list
              vm.spots = [];
              vm.spotsDisplayed = [];
              $ionicLoading.hide();
            });
          }
        }
      );
    }

    function deleteSpot(spot) {
      vm.deleteSelected = true;

      if (SpotFactory.isSafeDelete(spot)) {
        var confirmPopup = $ionicPopup.confirm({
          'title': 'Delete Spot',
          'template': 'Are you sure you want to delete Spot ' + spot.properties.name + '?'
        });
        confirmPopup.then(function (res) {
          if (res) {
            SpotFactory.destroy(spot.properties.id).then(function () {
              vm.spots = SpotFactory.getActiveSpots();
              vm.spots = _.sortBy(vm.spots, function (spot) {
                return spot.properties.modified_timestamp;
              }).reverse();
              vm.spotsDisplayed = vm.spots.slice(0, 25);
            });
          }
          vm.deleteSelected = false;
        });
      }
      else {
        var alertPopup = $ionicPopup.alert({
          'title': 'Spot Deletion Prohibited!',
          'template': 'This Spot has at least one image being used as an image basemap. Remove any image basemaps' +
          ' from this Spot before deleting.'
        });
        alertPopup.then(function () {
          vm.deleteSelected = false;
        });
      }
    }

    // Export data to CSV
    function exportToCSV() {
      vm.popover.hide();
      // Convert the spot objects to a csv format
      function convertToCSV() {
        // Get all the fields for the csv header row
        var allHeaders = [];
        _.each(vm.spots,
          function (spot) {
            var headers = _.keys(spot.properties);
            // If there are custom fields, loop through the custom object grabbing those fields
            var i = _.indexOf(headers, 'custom');
            if (i !== -1) {
              headers = _.without(headers, 'custom');
              var customHeaders = _.keys(spot.properties.custom);
              customHeaders = _.map(customHeaders,
                function (header) {
                  return 'custom_' + header;
                }
              );
              allHeaders = _.union(headers, allHeaders, customHeaders);
            }
            else {
              allHeaders = _.union(headers, allHeaders);
            }
          }
        );
        // Add the two fields for the geometry
        allHeaders.push('geometry_type');
        allHeaders.push('geometry_coordinates');

        var allHeadersQuoted = _.map(allHeaders,
          function (ele) {
            return '\'' + ele + '\'';
          }
        );
        var csv = allHeadersQuoted.toString() + '\r\n';

        // Get all the values for each csv data row
        _.each(vm.spots,
          function (spot) {
            var row = new Array(allHeaders.length);
            row = _.map(row,
              function (ele) {
                return '';
              }
            );
            _.each(spot.properties,
              function (value, key) {
                // If the value is actually an object
                var i = _.indexOf(allHeaders, key);
                if (_.isObject(value)) {
                  // Separate date parts
                  if (value instanceof Date) {
                    if (key === 'time') {
                      row[i] = value.toLocaleTimeString();
                    }
                    else if (key === 'date') {
                      row[i] = value.toLocaleDateString();
                    }
                    else {
                      row[i] = value.toJSON();
                    }
                  }
                  // Flatten the child values
                  else {
                    row[i] = '\'' + _.flatten(value).join(', ') + '\'';
                  }
                }
                else {
                  row[i] = '\'' + value + '\'';
                }
              }
            );
            // Add the values for the geometry fields
            if (angular.isDefined(spot.geometry) && angular.isDefined(spot.geometry.type)) {
              var i = _.indexOf(allHeaders, 'geometry_type');
              row[i] = '\'' + spot.geometry.type + '\'';
              if (angular.isDefined(spot.geometry.coordinates)) {
                i = _.indexOf(allHeaders, 'geometry_coordinates');
                row[i] = '\'' + _.flatten(spot.geometry.coordinates).join(', ') + '\'';
              }
            }
            csv += row.toString() + '\r\n';
          }
        );
        return csv;
      }

      var spotData = convertToCSV(vm.spots);

      // If this is a web browser and not using cordova
      if ($document[0].location.protocol !== 'file:') { // Phonegap is not present }
        spotData = spotData.replace(/\r\n/g, '
');
        var win = $window.open();
        win.document.body.innerHTML = spotData;
        return;
      }

      var d = new Date();
      d = d.toLocaleDateString() + '-' + d.toLocaleTimeString();
      d = d.replace(/\//g, '-');
      d = d.replace(/:/g, '');
      d = d.replace(/ /g, '');
      var fileName = d + '-' + 'strabo-data.csv';

      var devicePath;
      switch ($cordovaDevice.getPlatform()) {
        case 'Android':
          devicePath = cordova.file.externalRootDirectory;
          break;
        case 'iOS':
          devicePath = cordova.file.documentsDirectory;
          break;
        default:
          // uh oh?  TODO: what about windows and blackberry?
          devicePath = cordova.file.externalRootDirectory;
          break;
      }

      var directory = 'strabo';

      function writeFile(dir) {
        $cordovaFile.writeFile(devicePath + dir, fileName, spotData, true).then(
          function (success) {
            $log.log(success);
            $ionicPopup.alert({
              'title': 'Success!',
              'template': 'CSV written to ' + devicePath + dir + fileName
            });
          },
          function (error) {
            $log.log(error);
          }
        );
      }

      $cordovaFile.checkDir(devicePath, directory).then(
        function (success) {
          $log.log(success);
          writeFile(success.fullPath);
        },
        function (error) {
          $cordovaFile.createDir(devicePath, directory, false).then(
            function (success) {
              $log.log(success);
              writeFile(success.fullPath);
            },
            function (error) {
              $log.log(error);
            }
          );
        }
      );
    }

    function filter() {
      vm.activeDatasets = ProjectFactory.getActiveDatasets();
      vm.filterModal.show();
    }

    function getTagNames(spotId) {
      var tags = ProjectFactory.getTagsBySpotId(spotId);
      return _.pluck(tags, 'name').join(', ');
    }

    function goToSpot(id) {
      if (!vm.deleteSelected) {
        $location.path('/app/spotTab/' + id + '/spot');
      }
    }

    function hasTags(spotId) {
      return !_.isEmpty(ProjectFactory.getTagsBySpotId(spotId));
    }

    function isDatasetChecked(id) {
      return _.find(visibleDatasets, function (visibleDataset) {
        return visibleDataset === id;
      });
    }

    // Is the user online and logged in
    function isOnlineLoggedIn() {
      return navigator.onLine && UserFactory.getUser();
    }

    function loadMoreSpots() {
      vm.spotsDisplayed = vm.spots.slice(0, vm.spotsDisplayed.length + 20);
      $scope.$broadcast('scroll.infiniteScrollComplete');
    }

    function moreSpotsCanBeLoaded() {
      return vm.spotsDisplayed.length !== vm.spots.length;
    }

    // Create a new Spot
    function newSpot() {
      SpotFactory.setNewSpot({}).then(function (id) {
        $location.path('/app/spotTab/' + id + '/spot');
      });
    }

    function resetFilters() {
      visibleDatasets = [];
      SpotFactory.setVisibleDatasets(visibleDatasets);
      setVisibleSpots();
    }

    function setListDetail() {
      vm.popover.hide();
      vm.detailModal.show();
    }
  }
}());
<|endoftext|>


<commit_before>var courierChat = angular.module('courierChat', ['ui.router', 'ngResource']);

courierChat.config(function($stateProvider, $urlRouterProvider, $locationProvider) {
	'use strict';

	var appBase = 'js/courierchat/';
	var components = appBase + 'components/';

	$locationProvider.html5Mode(true);

	$stateProvider
	.state('home', {
		url: '/',
		templateUrl: components + 'login/login.html',
  		controller: 'loginController'
	})
    .state('login', {
  		url: '/login',
  		templateUrl: components + 'login/login.html',
  		controller: 'loginController'
    })
	.state('about',  {
		url: '/about',
		templateUrl: components + 'about/about.html'
  	})
    .state('rooms', {
  		url: '/rooms',
  		templateUrl: components + 'rooms/rooms.html'
  	})
});
<commit_msg>Changed the appBase variable to be an absolute path.<commit_after>var courierChat = angular.module('courierChat', ['ui.router', 'ngResource']);

courierChat.config(function($stateProvider, $urlRouterProvider, $locationProvider) {
	'use strict';

	var appBase = '/js/courierchat/';
	var components = appBase + 'components/';

	$locationProvider.html5Mode(true);

	$stateProvider
	.state('home', {
		url: '/',
		templateUrl: components + 'login/login.html',
  		controller: 'loginController'
	})
    .state('login', {
  		url: '/login',
  		templateUrl: components + 'login/login.html',
  		controller: 'loginController'
    })
	.state('about',  {
		url: '/about',
		templateUrl: components + 'about/about.html'
  	})
    .state('rooms', {
  		url: '/rooms',
  		templateUrl: components + 'rooms/rooms.html'
  	})
});
<|endoftext|>


<commit_before>app.controller('AlertsController', function ($scope, alertsService) {

    /**
     * Alert message storage.
     *
     * @type {Array}
     */
    $scope.alerts = [];

    /**
     * Adds new alert message.
     *
     * @param message
     * @param type
     */
    $scope.addAlert = function(message, type) {
        $scope.alerts.push({
            msg: message,
            type: type
        });
    };

    /**
     * Removes alert message from storage.
     *
     * @param index
     */
    $scope.removeAlert = function(index) {
        $scope.alerts.splice(index, 1);
    };

    /**
     * Listens for alertMessages emitted by alerts service.
     */
    function addMessageListener() {
        var _unregister;
        _unregister = $scope.$on('alertMessage', function (event, message, type) {
            $scope.addAlert(message, type);
        });
        $scope.$on("$destroy", _unregister);
    }

    /**
     * Displays queued alert message.
     */
    function showQueuedAlert() {
        var queuedAlert = alertsService.getQueuedAlert();
        console.log(queuedAlert);
        if (queuedAlert !== '') {
            $scope.addAlert(queuedAlert.message, queuedAlert.type);
        }
    }

    addMessageListener();
    showQueuedAlert();
});<commit_msg>Remove debug message<commit_after>app.controller('AlertsController', function ($scope, alertsService) {

    /**
     * Alert message storage.
     *
     * @type {Array}
     */
    $scope.alerts = [];

    /**
     * Adds new alert message.
     *
     * @param message
     * @param type
     */
    $scope.addAlert = function(message, type) {
        $scope.alerts.push({
            msg: message,
            type: type
        });
    };

    /**
     * Removes alert message from storage.
     *
     * @param index
     */
    $scope.removeAlert = function(index) {
        $scope.alerts.splice(index, 1);
    };

    /**
     * Listens for alertMessages emitted by alerts service.
     */
    function addMessageListener() {
        var _unregister;
        _unregister = $scope.$on('alertMessage', function (event, message, type) {
            $scope.addAlert(message, type);
        });
        $scope.$on("$destroy", _unregister);
    }

    /**
     * Displays queued alert message.
     */
    function showQueuedAlert() {
        var queuedAlert = alertsService.getQueuedAlert();
        if (queuedAlert !== '') {
            $scope.addAlert(queuedAlert.message, queuedAlert.type);
        }
    }

    addMessageListener();
    showQueuedAlert();
});<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Kagilum SAS.
 *
 * This file is part of iceScrum.
 *
 * iceScrum is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License.
 *
 * iceScrum is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with iceScrum.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors:
 *
 * Vincent Barrier (vbarrier@kagilum.com)
 * Nicolas Noullet (nnoullet@kagilum.com)
 *
 */

controllers.controller('releaseCtrl', ['$scope', '$state', 'Session', 'ReleaseService', 'SprintService', function($scope, $state, Session, ReleaseService, SprintService) {
    // Functions
    $scope.authorizedRelease = function(action, release) {
        return ReleaseService.authorizedRelease(action, release);
    };
    $scope.authorizedSprint = function(action, sprint) {
        return SprintService.authorizedSprint(action, sprint);
    };
    $scope.showReleaseMenu = function() {
        return Session.poOrSm();
    };
    $scope.activate = function(release) {
        ReleaseService.activate(release).then(function() {
            $scope.notifySuccess('todo.is.ui.release.activated');
        });
    };
    $scope.close = function(release) {
        ReleaseService.close(release).then(function() {
            $scope.notifySuccess('todo.is.ui.release.closed');
        });
    };
    $scope.generateSprints = function(release) {
        SprintService.generateSprints(release).then(function() {
            $scope.notifySuccess('todo.is.ui.release.generatedSprints');
        });
    };
    $scope.autoPlan = function(release, capacity) {
        $rootScope.uiWorking();
        ReleaseService.autoPlan(release, capacity).then(function() {
            $rootScope.uiReady();
            $scope.notifySuccess('todo.is.ui.release.autoPlanned');
        });
    };
    $scope.unPlan = function(release) {
        $rootScope.uiWorking();
        ReleaseService.unPlan(release).then(function() {
            $rootScope.uiReady();
            $scope.notifySuccess('todo.is.ui.release.unPlanned');
        });
    };
    $scope['delete'] = function(release) {
        ReleaseService.delete(release, $scope.project)
            .then(function() {
                $scope.notifySuccess('todo.is.ui.deleted');
            });
    };
    $scope.menus = [
        {
            name: 'is.ui.timeline.menu.activate',
            visible: function(release) { return $scope.authorizedRelease('activate', release); },
            action: function(release) { $scope.confirm({message: $scope.message('is.ui.timeline.menu.activate.confirm'), callback: $scope.activate, args: [release]}); }
        },
        {
            name: 'todo.is.ui.sprint.new',
            visible: function(release) { return $scope.authorizedSprint('create'); },
            url: function(release) { return '#' + $scope.viewName + '/' + release.id + '/sprint/new'; }
        },
        {
            name: 'todo.is.ui.release.new',
            priority: function(release, defaultPriority, viewType) { return viewType !== 'details' ? 100 : defaultPriority; },
            visible: function(release) { return $scope.authorizedRelease('create'); },
            url: function(release) { return '#' + $scope.viewName + '/new'; }
        },
        {
            name: 'is.ui.releasePlan.toolbar.generateSprints',
            visible: function(release) { return $scope.authorizedSprint('create'); },
            action: function(release) { $scope.generateSprints(release); }
        },
        {
            name: 'is.ui.releasePlan.toolbar.autoPlan',
            visible: function(release) { return $scope.authorizedRelease('autoPlan', release); },
            action: function(release) { $scope.showAutoPlanModal({callback: $scope.autoPlan, args: [release]}); }
        },
        {
            name: 'is.ui.releasePlan.toolbar.dissociateAll',
            visible: function(release) { return $scope.authorizedRelease('unPlan', release); },
            action: function(release) { $scope.confirm({message: $scope.message('is.ui.releasePlan.toolbar.warning.dissociateAll'), callback: $scope.unPlan, args: [release]}); }
        },
        {
            name: 'is.ui.timeline.menu.delete',
            visible: function(release) { return $scope.authorizedRelease('delete', release); },
            action: function(release) { $scope.confirm({message: $scope.message('is.ui.timeline.menu.delete.confirm'), callback: $scope.delete, args: [release]}); }
        },
        {
            name: 'is.ui.timeline.menu.close',
            visible: function(release) { return $scope.authorizedRelease('close', release); },
            action: function(release) { $scope.confirm({message: $scope.message('is.ui.timeline.menu.close.confirm'), callback: $scope.close, args: [release]}); }
        }
    ];
    // Init
    $scope.project = Session.getProject();
    $scope.startDateOptions = {
        opened: false
    };
    $scope.endDateOptions = angular.copy($scope.startDateOptions);
}]);

controllers.controller('releaseNewCtrl', ['$scope', '$controller', '$state', 'DateService', 'ReleaseService', 'hotkeys', function($scope, $controller, $state, DateService, ReleaseService, hotkeys) {
    $controller('releaseCtrl', {$scope: $scope}); // inherit from releaseCtrl
    // Functions
    $scope.resetReleaseForm = function() {
        $scope.release = {};
        $scope.resetFormValidation($scope.formHolder.releaseForm);
    };
    $scope.save = function(release, andContinue) {
        ReleaseService.save(release, $scope.project).then(function(release) {
            if (andContinue) {
                $scope.resetReleaseForm();
                initReleaseDates($scope.project.releases);
            } else {
                $scope.setInEditingMode(true);
                $state.go('^.release.details', {releaseId: release.id});
            }
            $scope.notifySuccess('todo.is.ui.release.saved');
        });
    };
    var initReleaseDates = function(releases) {
        if (!_.isUndefined(releases)) {
            if (_.isEmpty(releases)) {
                $scope.startDateOptions.minDate = $scope.project.startDate;
            } else {
                $scope.startDateOptions.minDate = DateService.immutableAddDaysToDate(_.max(_.map($scope.project.releases, 'endDate')), 1);
            }
            $scope.release.startDate = $scope.startDateOptions.minDate;
            $scope.release.endDate = DateService.immutableAddMonthsToDate($scope.release.startDate, 3);
        }
    };
    // Init
    $scope.$watchCollection('project.releases', initReleaseDates);
    $scope.$watchCollection('[release.startDate, release.endDate]', function(newValues) {
        var startDate = newValues[0];
        var endDate = newValues[1];
        if (startDate) {
            $scope.endDateOptions.minDate = DateService.immutableAddDaysToDate(startDate, 1);
        }
        if (endDate) {
            $scope.startDateOptions.maxDate = DateService.immutableAddDaysToDate(endDate, -1);
        }
    });
    $scope.formHolder = {};
    $scope.resetReleaseForm();
    hotkeys.bindTo($scope).add({
        combo: 'esc',
        allowIn: ['INPUT'],
        callback: $scope.resetReleaseForm
    });
}]);

controllers.controller('releaseDetailsCtrl', ['$scope', '$controller', 'ReleaseStatesByName', 'DateService', 'ReleaseService', 'FormService', 'detailsRelease', function($scope, $controller, ReleaseStatesByName, DateService, ReleaseService, FormService, detailsRelease) {
    $controller('releaseCtrl', {$scope: $scope}); // inherit from releaseCtrl
    $controller('attachmentCtrl', {$scope: $scope, attachmentable: detailsRelease, clazz: 'release'});
    // Functions
    $scope.update = function(release) {
        $scope.formHolder.submitting = true;
        ReleaseService.update(release).then(function() {
            $scope.resetReleaseForm();
            $scope.notifySuccess('todo.is.ui.release.updated');
        });
    };
    // Init
    $scope.$watchCollection('project.releases', function(releases) {
        if (!_.isUndefined(releases)) {
            if (_.isEmpty($scope.previousRelease)) {
                $scope.startDateOptions.minDate = $scope.project.startDate;
            } else {
                $scope.startDateOptions.minDate = DateService.immutableAddDaysToDate($scope.previousRelease.endDate, 1);
            }
        }
    });
    $scope.$watchCollection('[editableRelease.startDate, editableRelease.endDate]', function(newValues) {
        var startDate = newValues[0];
        var endDate = newValues[1];
        if (startDate) {
            $scope.endDateOptions.minDate = DateService.immutableAddDaysToDate(startDate, 1);
        }
        if (endDate) {
            $scope.startDateOptions.maxDate = DateService.immutableAddDaysToDate(endDate, -1);
        }
    });
    $controller('updateFormController', {$scope: $scope, item: detailsRelease, type: 'release'});
    $scope.releaseStatesByName = ReleaseStatesByName;
    $scope.previousRelease = FormService.previous($scope.project.releases, $scope.release);
    $scope.nextRelease = FormService.next($scope.project.releases, $scope.release);
}]);

controllers.controller('releaseTimelineCtrl', ['$scope', 'DateService', function($scope, DateService) {
    // Functions
    $scope.computeReleaseParts = function(release) {
        var parts = [];
        var latestDate = release.startDate;
        _.each(release.sprints, function(sprint) {
            var gapDuration = DateService.daysBetweenDates(latestDate, sprint.startDate);
            if (gapDuration > 1) {
                parts.push({duration: gapDuration - 1});
            }
            latestDate = sprint.endDate;
            parts.push(sprint);
        });
        return parts;
    };
    // Init
    $scope.releaseParts = [];
    $scope.$watch('release', function(newRelease) {
        if (newRelease) {
            $scope.releaseParts = $scope.computeReleaseParts(newRelease);
        }
    }, true);
}]);<commit_msg>Fix autoplanning / unplan on release<commit_after>/*
 * Copyright (c) 2015 Kagilum SAS.
 *
 * This file is part of iceScrum.
 *
 * iceScrum is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License.
 *
 * iceScrum is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with iceScrum.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors:
 *
 * Vincent Barrier (vbarrier@kagilum.com)
 * Nicolas Noullet (nnoullet@kagilum.com)
 *
 */

controllers.controller('releaseCtrl', ['$scope', '$state', '$rootScope', 'Session', 'ReleaseService', 'SprintService', function($scope, $state, $rootScope, Session, ReleaseService, SprintService) {
    // Functions
    $scope.authorizedRelease = function(action, release) {
        return ReleaseService.authorizedRelease(action, release);
    };
    $scope.authorizedSprint = function(action, sprint) {
        return SprintService.authorizedSprint(action, sprint);
    };
    $scope.showReleaseMenu = function() {
        return Session.poOrSm();
    };
    $scope.activate = function(release) {
        ReleaseService.activate(release).then(function() {
            $scope.notifySuccess('todo.is.ui.release.activated');
        });
    };
    $scope.close = function(release) {
        ReleaseService.close(release).then(function() {
            $scope.notifySuccess('todo.is.ui.release.closed');
        });
    };
    $scope.generateSprints = function(release) {
        SprintService.generateSprints(release).then(function() {
            $scope.notifySuccess('todo.is.ui.release.generatedSprints');
        });
    };
    $scope.autoPlan = function(release, capacity) {
        $rootScope.uiWorking();
        ReleaseService.autoPlan(release, capacity).then(function() {
            $rootScope.uiReady();
            $scope.notifySuccess('todo.is.ui.release.autoPlanned');
        });
    };
    $scope.unPlan = function(release) {
        $rootScope.uiWorking();
        ReleaseService.unPlan(release).then(function() {
            $rootScope.uiReady();
            $scope.notifySuccess('todo.is.ui.release.unPlanned');
        });
    };
    $scope['delete'] = function(release) {
        ReleaseService.delete(release, $scope.project)
            .then(function() {
                $scope.notifySuccess('todo.is.ui.deleted');
            });
    };
    $scope.menus = [
        {
            name: 'is.ui.timeline.menu.activate',
            visible: function(release) { return $scope.authorizedRelease('activate', release); },
            action: function(release) { $scope.confirm({message: $scope.message('is.ui.timeline.menu.activate.confirm'), callback: $scope.activate, args: [release]}); }
        },
        {
            name: 'todo.is.ui.sprint.new',
            visible: function(release) { return $scope.authorizedSprint('create'); },
            url: function(release) { return '#' + $scope.viewName + '/' + release.id + '/sprint/new'; }
        },
        {
            name: 'todo.is.ui.release.new',
            priority: function(release, defaultPriority, viewType) { return viewType !== 'details' ? 100 : defaultPriority; },
            visible: function(release) { return $scope.authorizedRelease('create'); },
            url: function(release) { return '#' + $scope.viewName + '/new'; }
        },
        {
            name: 'is.ui.releasePlan.toolbar.generateSprints',
            visible: function(release) { return $scope.authorizedSprint('create'); },
            action: function(release) { $scope.generateSprints(release); }
        },
        {
            name: 'is.ui.releasePlan.toolbar.autoPlan',
            visible: function(release) { return $scope.authorizedRelease('autoPlan', release); },
            action: function(release) { $scope.showAutoPlanModal({callback: $scope.autoPlan, args: [release]}); }
        },
        {
            name: 'is.ui.releasePlan.toolbar.dissociateAll',
            visible: function(release) { return $scope.authorizedRelease('unPlan', release); },
            action: function(release) { $scope.confirm({message: $scope.message('is.ui.releasePlan.toolbar.warning.dissociateAll'), callback: $scope.unPlan, args: [release]}); }
        },
        {
            name: 'is.ui.timeline.menu.delete',
            visible: function(release) { return $scope.authorizedRelease('delete', release); },
            action: function(release) { $scope.confirm({message: $scope.message('is.ui.timeline.menu.delete.confirm'), callback: $scope.delete, args: [release]}); }
        },
        {
            name: 'is.ui.timeline.menu.close',
            visible: function(release) { return $scope.authorizedRelease('close', release); },
            action: function(release) { $scope.confirm({message: $scope.message('is.ui.timeline.menu.close.confirm'), callback: $scope.close, args: [release]}); }
        }
    ];
    // Init
    $scope.project = Session.getProject();
    $scope.startDateOptions = {
        opened: false
    };
    $scope.endDateOptions = angular.copy($scope.startDateOptions);
}]);

controllers.controller('releaseNewCtrl', ['$scope', '$controller', '$state', 'DateService', 'ReleaseService', 'hotkeys', function($scope, $controller, $state, DateService, ReleaseService, hotkeys) {
    $controller('releaseCtrl', {$scope: $scope}); // inherit from releaseCtrl
    // Functions
    $scope.resetReleaseForm = function() {
        $scope.release = {};
        $scope.resetFormValidation($scope.formHolder.releaseForm);
    };
    $scope.save = function(release, andContinue) {
        ReleaseService.save(release, $scope.project).then(function(release) {
            if (andContinue) {
                $scope.resetReleaseForm();
                initReleaseDates($scope.project.releases);
            } else {
                $scope.setInEditingMode(true);
                $state.go('^.release.details', {releaseId: release.id});
            }
            $scope.notifySuccess('todo.is.ui.release.saved');
        });
    };
    var initReleaseDates = function(releases) {
        if (!_.isUndefined(releases)) {
            if (_.isEmpty(releases)) {
                $scope.startDateOptions.minDate = $scope.project.startDate;
            } else {
                $scope.startDateOptions.minDate = DateService.immutableAddDaysToDate(_.max(_.map($scope.project.releases, 'endDate')), 1);
            }
            $scope.release.startDate = $scope.startDateOptions.minDate;
            $scope.release.endDate = DateService.immutableAddMonthsToDate($scope.release.startDate, 3);
        }
    };
    // Init
    $scope.$watchCollection('project.releases', initReleaseDates);
    $scope.$watchCollection('[release.startDate, release.endDate]', function(newValues) {
        var startDate = newValues[0];
        var endDate = newValues[1];
        if (startDate) {
            $scope.endDateOptions.minDate = DateService.immutableAddDaysToDate(startDate, 1);
        }
        if (endDate) {
            $scope.startDateOptions.maxDate = DateService.immutableAddDaysToDate(endDate, -1);
        }
    });
    $scope.formHolder = {};
    $scope.resetReleaseForm();
    hotkeys.bindTo($scope).add({
        combo: 'esc',
        allowIn: ['INPUT'],
        callback: $scope.resetReleaseForm
    });
}]);

controllers.controller('releaseDetailsCtrl', ['$scope', '$controller', 'ReleaseStatesByName', 'DateService', 'ReleaseService', 'FormService', 'detailsRelease', function($scope, $controller, ReleaseStatesByName, DateService, ReleaseService, FormService, detailsRelease) {
    $controller('releaseCtrl', {$scope: $scope}); // inherit from releaseCtrl
    $controller('attachmentCtrl', {$scope: $scope, attachmentable: detailsRelease, clazz: 'release'});
    // Functions
    $scope.update = function(release) {
        $scope.formHolder.submitting = true;
        ReleaseService.update(release).then(function() {
            $scope.resetReleaseForm();
            $scope.notifySuccess('todo.is.ui.release.updated');
        });
    };
    // Init
    $scope.$watchCollection('project.releases', function(releases) {
        if (!_.isUndefined(releases)) {
            if (_.isEmpty($scope.previousRelease)) {
                $scope.startDateOptions.minDate = $scope.project.startDate;
            } else {
                $scope.startDateOptions.minDate = DateService.immutableAddDaysToDate($scope.previousRelease.endDate, 1);
            }
        }
    });
    $scope.$watchCollection('[editableRelease.startDate, editableRelease.endDate]', function(newValues) {
        var startDate = newValues[0];
        var endDate = newValues[1];
        if (startDate) {
            $scope.endDateOptions.minDate = DateService.immutableAddDaysToDate(startDate, 1);
        }
        if (endDate) {
            $scope.startDateOptions.maxDate = DateService.immutableAddDaysToDate(endDate, -1);
        }
    });
    $controller('updateFormController', {$scope: $scope, item: detailsRelease, type: 'release'});
    $scope.releaseStatesByName = ReleaseStatesByName;
    $scope.previousRelease = FormService.previous($scope.project.releases, $scope.release);
    $scope.nextRelease = FormService.next($scope.project.releases, $scope.release);
}]);

controllers.controller('releaseTimelineCtrl', ['$scope', 'DateService', function($scope, DateService) {
    // Functions
    $scope.computeReleaseParts = function(release) {
        var parts = [];
        var latestDate = release.startDate;
        _.each(release.sprints, function(sprint) {
            var gapDuration = DateService.daysBetweenDates(latestDate, sprint.startDate);
            if (gapDuration > 1) {
                parts.push({duration: gapDuration - 1});
            }
            latestDate = sprint.endDate;
            parts.push(sprint);
        });
        return parts;
    };
    // Init
    $scope.releaseParts = [];
    $scope.$watch('release', function(newRelease) {
        if (newRelease) {
            $scope.releaseParts = $scope.computeReleaseParts(newRelease);
        }
    }, true);
}]);<|endoftext|>