<commit_before>
import React from 'react/addons';
import AutoSuggest from '../src/AutoSuggest';
import Asserter from './assertions/Asserter';
import chai from 'chai';

const { TestUtils } = React.addons;
const expect = chai.expect;

describe('AutoSuggest', () => {

    const fetchSuggestions = function(value, callback) {
        callback(['one', 'two', 'three']);
    };

    const onSuggestion = function(suggestion) {
        console.info('onSuggestion', suggestion);
    };

    it('should render', function() {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest);
    });

    it('should not display the DropDown', function() {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .assertDropDownNotDisplayed()
        ;
    });

    it('should display the DropDown when text is entered', function() {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .assertDropDownNotDisplayed()
            .enterNewValue('C').assertDropDownDisplayed()
        ;
    });

    it('should display the suggestions when the DropDown is displayed', function() {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C').assertSuggestions(['one', 'two', 'three'])
        ;
    });

    it('should select the next suggestion when the down key is pressed', function() {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C').assertSelectedSuggestion()
            .pressArrowDown().assertSelectedSuggestion('one')
            .pressArrowDown().assertSelectedSuggestion('two')
            .pressArrowDown().assertSelectedSuggestion('three')
            .pressArrowDown().assertSelectedSuggestion()
            .pressArrowDown().assertSelectedSuggestion('one')
        ;
    });

    it('should select the previous suggestion when the up key is pressed', function() {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C').assertSelectedSuggestion()
            .pressArrowUp().assertSelectedSuggestion('three')
            .pressArrowUp().assertSelectedSuggestion('two')
            .pressArrowUp().assertSelectedSuggestion('one')
            .pressArrowUp().assertSelectedSuggestion()
            .pressArrowUp().assertSelectedSuggestion('three')
        ;
    });

    it('should display entered text', function() {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('cat').assertValue('cat');
    });

    it('should pass entered text to suggestions function', function(done) {
        const fetchSuggestions = function(suggestion) {
            expect(suggestion).to.equal('cat');
            done();
        };

        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('cat').assertValue('cat');
    });

    it('should hide the dropdown when the esc key is pressed', function() {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('cat').assertDropDownDisplayed()
            .pressEscape().assertDropDownNotDisplayed()
    });

    it('should allow custom dropdowns', function() {
        const CustomSuggestionRenderer = React.createClass({
            render() {
                return
{this.props.suggestion}
;
            }
        });

        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}>
                <CustomSuggestionRenderer />
            </AutoSuggest>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C').assertSuggestions(['one', 'two', 'three'])
        ;
    });

    it('should allow custom dropdowns for arrays of json', function() {
        const fetchSuggestions = function(value, callback) {
            callback([{title:'one'}, {title:'two'}, {title:'three'}]);
        };

        const CustomSuggestionRenderer = React.createClass({
            render() {
                return
{this.props.suggestion.title}
;
            }
        });

        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}>
                <CustomSuggestionRenderer />
            </AutoSuggest>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C').assertSuggestions(['one', 'two', 'three'])
        ;
    });

    it('should handle displaying term from custom dropdowns', function() {
        const fetchSuggestions = function(value, callback) {
            callback([{title:'cow'}, {title:'cat'}, {title:'chicken'}]);
        };

        const onSelect = function(suggestion) {
            return suggestion.title;
        };

        const CustomSuggestionRenderer = React.createClass({
            render() {
                let classes = ['Suggestion', this.props.selected?'selected':''];
                const suggestion = this.props.suggestion;
                return
{suggestion.title}
;
            }
        });

        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion} onSelect={onSelect}>
                <CustomSuggestionRenderer />
            </AutoSuggest>
        );

        new Asserter(autoSuggest)
            .enterNewValue('c').assertValue('c').assertSuggestions(['cow', 'cat', 'chicken'])
            .pressArrowDown().assertValue('cow')
            .pressArrowDown().assertValue('cat')
            .pressArrowDown().assertValue('chicken')
            .pressArrowDown().assertValue('c')
        ;
    });
});
<commit_msg>es6 all the way<commit_after>
import React from 'react/addons';
import AutoSuggest from '../src/AutoSuggest';
import Asserter from './assertions/Asserter';
import chai from 'chai';

const { TestUtils } = React.addons;
const expect = chai.expect;

describe('AutoSuggest', () => {

    const fetchSuggestions = (value, callback) => {
        callback(['one', 'two', 'three']);
    };

    const onSuggestion = (suggestion) => {
        console.info('onSuggestion', suggestion);
    };

    it('should render', () => {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest);
    });

    it('should not display the DropDown', () => {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .assertDropDownNotDisplayed()
        ;
    });

    it('should display the DropDown when text is entered', () => {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .assertDropDownNotDisplayed()
            .enterNewValue('C').assertDropDownDisplayed()
        ;
    });

    it('should display the suggestions when the DropDown is displayed', () => {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C').assertSuggestions(['one', 'two', 'three'])
        ;
    });

    it('should select the next suggestion when the down key is pressed', () => {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C').assertSelectedSuggestion()
            .pressArrowDown().assertSelectedSuggestion('one')
            .pressArrowDown().assertSelectedSuggestion('two')
            .pressArrowDown().assertSelectedSuggestion('three')
            .pressArrowDown().assertSelectedSuggestion()
            .pressArrowDown().assertSelectedSuggestion('one')
        ;
    });

    it('should select the previous suggestion when the up key is pressed', () => {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C').assertSelectedSuggestion()
            .pressArrowUp().assertSelectedSuggestion('three')
            .pressArrowUp().assertSelectedSuggestion('two')
            .pressArrowUp().assertSelectedSuggestion('one')
            .pressArrowUp().assertSelectedSuggestion()
            .pressArrowUp().assertSelectedSuggestion('three')
        ;
    });

    it('should display entered text', () => {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('cat').assertValue('cat');
    });

    it('should pass entered text to suggestions function', (done) => {
        const fetchSuggestions = function(suggestion) {
            expect(suggestion).to.equal('cat');
            done();
        };

        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('cat').assertValue('cat');
    });

    it('should hide the dropdown when the esc key is pressed', () => {
        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('cat').assertDropDownDisplayed()
            .pressEscape().assertDropDownNotDisplayed()
    });

    it('should allow custom dropdowns', () => {
        const CustomSuggestionRenderer = React.createClass({
            render() {
                return
{this.props.suggestion}
;
            }
        });

        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}>
                <CustomSuggestionRenderer />
            </AutoSuggest>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C').assertSuggestions(['one', 'two', 'three'])
        ;
    });

    it('should allow custom dropdowns for arrays of json', () => {
        const fetchSuggestions = function(value, callback) {
            callback([{title:'one'}, {title:'two'}, {title:'three'}]);
        };

        const CustomSuggestionRenderer = React.createClass({
            render() {
                return
{this.props.suggestion.title}
;
            }
        });

        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}>
                <CustomSuggestionRenderer />
            </AutoSuggest>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C').assertSuggestions(['one', 'two', 'three'])
        ;
    });

    it('should handle displaying term from custom dropdowns', () => {
        const fetchSuggestions = function(value, callback) {
            callback([{title:'cow'}, {title:'cat'}, {title:'chicken'}]);
        };

        const onSelect = function(suggestion) {
            return suggestion.title;
        };

        const CustomSuggestionRenderer = React.createClass({
            render() {
                let classes = ['Suggestion', this.props.selected?'selected':''];
                const suggestion = this.props.suggestion;
                return
{suggestion.title}
;
            }
        });

        const autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion} onSelect={onSelect}>
                <CustomSuggestionRenderer />
            </AutoSuggest>
        );

        new Asserter(autoSuggest)
            .enterNewValue('c').assertValue('c').assertSuggestions(['cow', 'cat', 'chicken'])
            .pressArrowDown().assertValue('cow')
            .pressArrowDown().assertValue('cat')
            .pressArrowDown().assertValue('chicken')
            .pressArrowDown().assertValue('c')
        ;
    });
});
<|endoftext|>


<commit_before>command: 'curl -s "http://legacy.cafebonappetit.com/api/2/menus?cafe=261"',
refreshFrequency: 300000,

render: function(output) {
  return [
      '<link rel="stylesheet" href="BonApp-Widget/styles/bonAppStyle.css">',
      '<link rel="stylesheet" href="BonApp-widget/styles/jquery/jquery-ui.css">',
      '',

      '
',
        //'
Could not update
',
        '

',
        '
',
          '

',
          '
',
            '
',
              '<input type="text" class="queryBox" id="search" placeholder="Search...">',
            '
',
          '
',
          '
',
            '',
          '
',
        '
',
        '
',
          '

',
          '

',
          '
',
            '<select class="myDiet">',
              '<option value="-1">All</option>',
              '<option value="6">Farm to Fork</option>',
              '<option value="18">Humane</option>',
              '<option value="7">In-Balance</option>',
              '<option value="9">Made Without Gluten</option>',
              '<option value="57">Contains Nuts</option>',
              '<option value="3">Seafood Watch</option>',
              '<option value="1">Vegetarian</option>',
              '<option value="4">Vegan</option>',
            '</select>',
          '
',
          '

',
          '

',
        '
',
      '
'
    ].join('')
  },

  update: function(output, domEl) {
    var theMenu = null

    var dietSelection = domEl.querySelector('.myDiet')

    var parseCafe = function(json) {
      var id = Object.keys(json['days'][0]['cafes'])[0]
      var theTitle = json['days'][0]['cafes'][id]['name']

      return theTitle
    }

    var parseDate = function(x) {
      var y = x.substr(0, 4)
      var m = x.substr(5, 2)
      var d = x.substr(8, 2)

      var postedDate = new Date(y, m - 1, d).toString()
      postedDate = postedDate.substr(0, 10)

      return postedDate.toString()
    }

    var widgetSelect = 'Find Your Café'
    var jstringObj   = JSON.parse(output)
    var theDate      = parseDate(jstringObj.days[0].date)
    var title        = parseCafe(jstringObj)

    var createOutputString = function(okItems) {
      return okItems.join('
')
    }

    var parseMenu = function(menu, diet) {
      var preferredDiet = parseInt(diet)
      var okItems = []
      var prevStation = ''
      var item = ''
      var desc = ''

      for (var prop in menu) {
        if (menu.hasOwnProperty(prop)) {
          if (menu[prop].cor_icon.hasOwnProperty(preferredDiet) || preferredDiet === -1) {
            if (menu[prop].station !== prevStation) {
              item = '
' + cleanUpStationLabel(menu[prop].station + '
')
              item += '⚬ ' + capitalizeStr(menu[prop].label, 0, 1)
              if(menu[prop].description != '') {
                  desc = menu[prop].description.replace(/
/g, '
 ⚬ ')
                  item += "
" + '⚬ ' + desc
              }
              prevStation = menu[prop].station
            }
            else {
                item = '⚬ ' + capitalizeStr(menu[prop].label, 0, 1)
              if(menu[prop].description != '') {
                  desc = menu[prop].description.replace(/
/g, '
 ⚬ ')
                  item += "
" + '⚬ ' + desc
              }
                prevStation = menu[prop].station
            }
            okItems.push(item)
          }
        }
      }
      if (okItems.length === 0) {
        okItems.push('0 menu items found...')
      }

      return okItems
    }

    var lastUpdated = function() {
      var time = new Date
      var hours = time.getHours()
      var minutes = time.getMinutes()
      var timeOfDay = ''

      if (hours > 12) {
        timeOfDay = 'pm'
        hours -= 12
      }
      else if (hours == 12) {
        timeOfDay = 'pm'
      }
      else if (hours === 0) {
        timeOfDay = 'am'
        hours = 12
      }
      else {
        timeOfDay = 'am'
      }

      return function() { return 'Updated at ' + pad(hours) + ':' + pad(minutes) + timeOfDay}
    }

    var pad = function(number) {
      return (number < 10 ? '0' : '') + number
    }

    var capitalizeStr = function(theStr, upperCaseLetter, removeLetter) {
      var newCapitalStr = theStr[upperCaseLetter].toUpperCase() + theStr.slice(removeLetter)
      return newCapitalStr
    }

    var sortByProperty = function(property) {
      //'use strict'
      return function(a, b) {
        var sortStatus = 0
        if (a[property]  b[property]) {
          sortStatus = 1
        }

        return sortStatus
      }
    }

    var updateTheData = (function(_this) {
      return function(cafeId) {
        var theCommand = 'curl -s \'http://legacy.cafebonappetit.com/api/2/menus?cafe=' + cafeId + "'"

        return _this.run(theCommand, function(err, myOut) {
          makeItAllHappen(myOut)
          return changeTheView()
        })
      }
    })(this)

    var initSearch = function() {
      return jQuery.get('BonApp-Widget/scripts/data.json', function(data) {
        return $('#search').autocomplete({
          source: data.sort(sortByProperty('label')),
          minLength: 2,

          select: function(event, ui) {
            $("#spinner").show()
            $('#search').val(ui.item.label)

            updateTheData(ui.item.id)

            return false
          },

          open: function() {
            return $('.ui-autocomplete:visible').css({
              top: '+=0',
              left: '-=0'
            })
          }

        }).autocomplete('instance')._renderItem = function(ul, item) {
          return $('
').append('
' +
                                  '' + item.label + '' +
                                  '' + item.desc + '' + '
')
                          .appendTo(ul)
        }
      })
    }

    var cleanUpStationLabel = function(theStation) {
      var cleanString = theStation.replace('@', '')
      var capitalizedStr = capitalizeStr(cleanString, 8, 9)
      capitalizedStr = '' + capitalizedStr + ''

      return capitalizedStr
    }

    var updateView = function(dom, theDate, widgetSelect, title, outputString) {
      var err
      var theLastUpdate = lastUpdated

      try {
        $(document).find(widgetSelectTitle).html(widgetSelect)
      }
      catch (_error) {
        err = _error
        console.log('Error: "' + err.message + '" on line ' + err.line)
      }
      try {
        $(document).find(widgetTitle).html(title)
      }
      catch (_error) {
        err = _error
        console.log('Error: "' + err.message + '" on line ' + err.line)
      }
      try {
        $(document).find(date).html(theDate)
      }
      catch (_error) {
        err = _error
        console.log('Error: "' + err.message + '" on line ' + err.line)
      }
      try {
        $(document).find(footerBA).html(theLastUpdate)
      }
      catch (_error) {
        err = _error
        console.log('Error: "' + err.message + '" on line ' + err.line)
      }
      try {
        return $(document).find(food).html(outputString)
      }
      catch (_error) {
        err = _error
        console.log('Error: "' + err.message + '" on line ' + err.line)
      }
    }

    var initFromLocalStorage = function() {
      var theValue

      if (localStorage.getItem('BonAppSettings') !== null) {
        savedDiet = localStorage.getItem('BonAppSettings')
        var theValue = JSON.parse(savedDiet).diet
        document.querySelector('.myDiet').value = theValue
      }
      else {
        return -1
      }

      return theValue
    }

    var switchNum = 0

    var changeTheView = function() {

      if (switchNum % 2 == 0) {
        $('#cafeSelection').show()
        $('#main').hide()
        $("#spinner").hide()
        $('#topRightIcon').css('background-image', 'url(BonApp-Widget/img/arrow.png)')
        $('#bonApp').css('height', '200px')
        $('.queryBox').focus()

        initSearch()
      }
      else {
        $('#cafeSelection').hide()
        $('#main').show()
        $('#topRightIcon').css('background-image', 'url(BonApp-Widget/img/search.png)')
        $('#bonApp').css('height', 'auto')
      }

      return switchNum++
    }

    document.getElementById('topRightIcon').addEventListener('click', function() {
      return changeTheView()
    })

   var makeItAllHappen = function(output) {
      var dom          = $(domEl)
      var savedDiet    = initFromLocalStorage()
      var jstringObj   = JSON.parse(output)
      var title        = parseCafe(jstringObj)
      var theDate      = parseDate(jstringObj.days[0].date)
      var theMenu      = jstringObj.items
      var friendly     = parseMenu(theMenu, savedDiet)
      var outputString = createOutputString(friendly)
      var widgetSelect = 'Find Your Café'

      dietSelection.onchange = function() {
        return saveSettings(dietSelection.value)
      }

      var saveSettings = function(diet) {
        var friendly, userSettings

        userSettings = {
          diet: diet
        }

        localStorage.setItem('BonAppSettings', JSON.stringify(userSettings))
        friendly = parseMenu(theMenu, diet)
        outputString = createOutputString(friendly)

        return updateView(domEl, theDate, widgetSelect, title, outputString)
    }

      return updateView(dom, theDate, widgetSelect, title, outputString)
    }

    makeItAllHappen(output)
  }
<commit_msg>Removing conversion of string on string<commit_after>command: 'curl -s "http://legacy.cafebonappetit.com/api/2/menus?cafe=261"',
refreshFrequency: 300000,

render: function(output) {
  return [
      '<link rel="stylesheet" href="BonApp-Widget/styles/bonAppStyle.css">',
      '<link rel="stylesheet" href="BonApp-widget/styles/jquery/jquery-ui.css">',
      '',

      '
',
        //'
Could not update
',
        '

',
        '
',
          '

',
          '
',
            '
',
              '<input type="text" class="queryBox" id="search" placeholder="Search...">',
            '
',
          '
',
          '
',
            '',
          '
',
        '
',
        '
',
          '

',
          '

',
          '
',
            '<select class="myDiet">',
              '<option value="-1">All</option>',
              '<option value="6">Farm to Fork</option>',
              '<option value="18">Humane</option>',
              '<option value="7">In-Balance</option>',
              '<option value="9">Made Without Gluten</option>',
              '<option value="57">Contains Nuts</option>',
              '<option value="3">Seafood Watch</option>',
              '<option value="1">Vegetarian</option>',
              '<option value="4">Vegan</option>',
            '</select>',
          '
',
          '

',
          '

',
        '
',
      '
'
    ].join('')
  },

  update: function(output, domEl) {
    var theMenu = null

    var dietSelection = domEl.querySelector('.myDiet')

    var parseCafe = function(json) {
      var id = Object.keys(json['days'][0]['cafes'])[0]
      var theTitle = json['days'][0]['cafes'][id]['name']

      return theTitle
    }

    var parseDate = function(x) {
      var y = x.substr(0, 4)
      var m = x.substr(5, 2)
      var d = x.substr(8, 2)

      var postedDate = new Date(y, m - 1, d).toString()
      postedDate = postedDate.substr(0, 10)

      return postedDate
    }

    var widgetSelect = 'Find Your Café'
    var jstringObj   = JSON.parse(output)
    var theDate      = parseDate(jstringObj.days[0].date)
    var title        = parseCafe(jstringObj)

    var createOutputString = function(okItems) {
      return okItems.join('
')
    }

    var parseMenu = function(menu, diet) {
      var preferredDiet = parseInt(diet)
      var okItems = []
      var prevStation = ''
      var item = ''
      var desc = ''

      for (var prop in menu) {
        if (menu.hasOwnProperty(prop)) {
          if (menu[prop].cor_icon.hasOwnProperty(preferredDiet) || preferredDiet === -1) {
            if (menu[prop].station !== prevStation) {
              item = '
' + cleanUpStationLabel(menu[prop].station + '
')
              item += '⚬ ' + capitalizeStr(menu[prop].label, 0, 1)
              if(menu[prop].description != '') {
                  desc = menu[prop].description.replace(/
/g, '
 ⚬ ')
                  item += "
" + '⚬ ' + desc
              }
              prevStation = menu[prop].station
            }
            else {
                item = '⚬ ' + capitalizeStr(menu[prop].label, 0, 1)
              if(menu[prop].description != '') {
                  desc = menu[prop].description.replace(/
/g, '
 ⚬ ')
                  item += "
" + '⚬ ' + desc
              }
                prevStation = menu[prop].station
            }
            okItems.push(item)
          }
        }
      }
      if (okItems.length === 0) {
        okItems.push('0 menu items found...')
      }

      return okItems
    }

    var lastUpdated = function() {
      var time = new Date
      var hours = time.getHours()
      var minutes = time.getMinutes()
      var timeOfDay = ''

      if (hours > 12) {
        timeOfDay = 'pm'
        hours -= 12
      }
      else if (hours == 12) {
        timeOfDay = 'pm'
      }
      else if (hours === 0) {
        timeOfDay = 'am'
        hours = 12
      }
      else {
        timeOfDay = 'am'
      }

      return function() { return 'Updated at ' + pad(hours) + ':' + pad(minutes) + timeOfDay}
    }

    var pad = function(number) {
      return (number < 10 ? '0' : '') + number
    }

    var capitalizeStr = function(theStr, upperCaseLetter, removeLetter) {
      var newCapitalStr = theStr[upperCaseLetter].toUpperCase() + theStr.slice(removeLetter)
      return newCapitalStr
    }

    var sortByProperty = function(property) {
      //'use strict'
      return function(a, b) {
        var sortStatus = 0
        if (a[property]  b[property]) {
          sortStatus = 1
        }

        return sortStatus
      }
    }

    var updateTheData = (function(_this) {
      return function(cafeId) {
        var theCommand = 'curl -s \'http://legacy.cafebonappetit.com/api/2/menus?cafe=' + cafeId + "'"

        return _this.run(theCommand, function(err, myOut) {
          makeItAllHappen(myOut)
          return changeTheView()
        })
      }
    })(this)

    var initSearch = function() {
      return jQuery.get('BonApp-Widget/scripts/data.json', function(data) {
        return $('#search').autocomplete({
          source: data.sort(sortByProperty('label')),
          minLength: 2,

          select: function(event, ui) {
            $("#spinner").show()
            $('#search').val(ui.item.label)

            updateTheData(ui.item.id)

            return false
          },

          open: function() {
            return $('.ui-autocomplete:visible').css({
              top: '+=0',
              left: '-=0'
            })
          }

        }).autocomplete('instance')._renderItem = function(ul, item) {
          return $('
').append('
' +
                                  '' + item.label + '' +
                                  '' + item.desc + '' + '
')
                          .appendTo(ul)
        }
      })
    }

    var cleanUpStationLabel = function(theStation) {
      var cleanString = theStation.replace('@', '')
      var capitalizedStr = capitalizeStr(cleanString, 8, 9)
      capitalizedStr = '' + capitalizedStr + ''

      return capitalizedStr
    }

    var updateView = function(dom, theDate, widgetSelect, title, outputString) {
      var err
      var theLastUpdate = lastUpdated

      try {
        $(document).find(widgetSelectTitle).html(widgetSelect)
      }
      catch (_error) {
        err = _error
        console.log('Error: "' + err.message + '" on line ' + err.line)
      }
      try {
        $(document).find(widgetTitle).html(title)
      }
      catch (_error) {
        err = _error
        console.log('Error: "' + err.message + '" on line ' + err.line)
      }
      try {
        $(document).find(date).html(theDate)
      }
      catch (_error) {
        err = _error
        console.log('Error: "' + err.message + '" on line ' + err.line)
      }
      try {
        $(document).find(footerBA).html(theLastUpdate)
      }
      catch (_error) {
        err = _error
        console.log('Error: "' + err.message + '" on line ' + err.line)
      }
      try {
        return $(document).find(food).html(outputString)
      }
      catch (_error) {
        err = _error
        console.log('Error: "' + err.message + '" on line ' + err.line)
      }
    }

    var initFromLocalStorage = function() {
      var theValue

      if (localStorage.getItem('BonAppSettings') !== null) {
        savedDiet = localStorage.getItem('BonAppSettings')
        var theValue = JSON.parse(savedDiet).diet
        document.querySelector('.myDiet').value = theValue
      }
      else {
        return -1
      }

      return theValue
    }

    var switchNum = 0

    var changeTheView = function() {

      if (switchNum % 2 == 0) {
        $('#cafeSelection').show()
        $('#main').hide()
        $("#spinner").hide()
        $('#topRightIcon').css('background-image', 'url(BonApp-Widget/img/arrow.png)')
        $('#bonApp').css('height', '200px')
        $('.queryBox').focus()

        initSearch()
      }
      else {
        $('#cafeSelection').hide()
        $('#main').show()
        $('#topRightIcon').css('background-image', 'url(BonApp-Widget/img/search.png)')
        $('#bonApp').css('height', 'auto')
      }

      return switchNum++
    }

    document.getElementById('topRightIcon').addEventListener('click', function() {
      return changeTheView()
    })

   var makeItAllHappen = function(output) {
      var dom          = $(domEl)
      var savedDiet    = initFromLocalStorage()
      var jstringObj   = JSON.parse(output)
      var title        = parseCafe(jstringObj)
      var theDate      = parseDate(jstringObj.days[0].date)
      var theMenu      = jstringObj.items
      var friendly     = parseMenu(theMenu, savedDiet)
      var outputString = createOutputString(friendly)
      var widgetSelect = 'Find Your Café'

      dietSelection.onchange = function() {
        return saveSettings(dietSelection.value)
      }

      var saveSettings = function(diet) {
        var friendly, userSettings

        userSettings = {
          diet: diet
        }

        localStorage.setItem('BonAppSettings', JSON.stringify(userSettings))
        friendly = parseMenu(theMenu, diet)
        outputString = createOutputString(friendly)

        return updateView(domEl, theDate, widgetSelect, title, outputString)
    }

      return updateView(dom, theDate, widgetSelect, title, outputString)
    }

    makeItAllHappen(output)
  }
<|endoftext|>


<commit_before>/* global Module */

/* Magic Mirror
 * Module: CurrentWeather
 *
 * By Michael Teeuw http://michaelteeuw.nl
 * MIT Licensed.
 */

Module.register("currentweather",{

	// Default module config.
	defaults: {
		location: "",
		locationID: "",
		appid: "",
		units: config.units,
		updateInterval: 10 * 60 * 1000, // every 10 minutes
		animationSpeed: 1000,
		timeFormat: config.timeFormat,
		showPeriod: true,
		showPeriodUpper: false,
		showWindDirection: true,
		useBeaufort: true,
		lang: config.language,

		initialLoadDelay: 0, // 0 seconds delay
		retryDelay: 2500,

		apiVersion: "2.5",
		apiBase: "http://api.openweathermap.org/data/",
		weatherEndpoint: "weather",

		iconTable: {
			"01d": "wi-day-sunny",
			"02d": "wi-day-cloudy",
			"03d": "wi-cloudy",
			"04d": "wi-cloudy-windy",
			"09d": "wi-showers",
			"10d": "wi-rain",
			"11d": "wi-thunderstorm",
			"13d": "wi-snow",
			"50d": "wi-fog",
			"01n": "wi-night-clear",
			"02n": "wi-night-cloudy",
			"03n": "wi-night-cloudy",
			"04n": "wi-night-cloudy",
			"09n": "wi-night-showers",
			"10n": "wi-night-rain",
			"11n": "wi-night-thunderstorm",
			"13n": "wi-night-snow",
			"50n": "wi-night-alt-cloudy-windy"
		},
	},

	// Define required scripts.
	getScripts: function() {
		return ["moment.js"];
	},

	// Define required scripts.
	getStyles: function() {
		return ["weather-icons.css", "currentweather.css"];
	},

	// Define required translations.
	getTranslations: function() {
		// The translations for the defaut modules are defined in the core translation files.
		// Therefor we can just return false. Otherwise we should have returned a dictionairy.
		// If you're trying to build yiur own module including translations, check out the documentation.
		return false;
	},

	// Define start sequence.
	start: function() {
		Log.info("Starting module: " + this.name);

		// Set locale.
		moment.locale(config.language);

		this.windSpeed = null;
		this.windDirection = null;
		this.sunriseSunsetTime = null;
		this.sunriseSunsetIcon = null;
		this.temperature = null;
		this.weatherType = null;

		this.loaded = false;
		this.scheduleUpdate(this.config.initialLoadDelay);

		this.updateTimer = null;

	},

	// Override dom generator.
	getDom: function() {
		var wrapper = document.createElement("div");

		if (this.config.appid === "") {
			wrapper.innerHTML = "Please set the correct openweather appid in the config for module: " + this.name + ".";
			wrapper.className = "dimmed light small";
			return wrapper;
		}

		if (this.config.location === "") {
			wrapper.innerHTML = "Please set the openweather location in the config for module: " + this.name + ".";
			wrapper.className = "dimmed light small";
			return wrapper;
		}

		if (!this.loaded) {
			wrapper.innerHTML = this.translate('LOADING');
			wrapper.className = "dimmed light small";
			return wrapper;
		}

		var small = document.createElement("div");
		small.className = "normal medium";

		var windIcon = document.createElement("span");
		windIcon.className = "wi wi-strong-wind dimmed";
		small.appendChild(windIcon);

		var windSpeed = document.createElement("span");
		windSpeed.innerHTML = " " + this.windSpeed;
		small.appendChild(windSpeed);

		if (this.config.showWindDirection) {
			var windDirection = document.createElement("sup");
			windDirection.innerHTML = " " + this.translate(this.windDirection);
			small.appendChild(windDirection);
		}
		var spacer = document.createElement("span");
		spacer.innerHTML = " ";
		small.appendChild(spacer);

		var sunriseSunsetIcon = document.createElement("span");
		sunriseSunsetIcon.className = "wi dimmed " + this.sunriseSunsetIcon;
		small.appendChild(sunriseSunsetIcon);

		var sunriseSunsetTime = document.createElement("span");
		sunriseSunsetTime.innerHTML = " " + this.sunriseSunsetTime;
		small.appendChild(sunriseSunsetTime);

		var large = document.createElement("div");
		large.className = "large light";

		var weatherIcon = document.createElement("span");
		weatherIcon.className = "wi weathericon " + this.weatherType;
		large.appendChild(weatherIcon);

		var temperature = document.createElement("span");
		temperature.className = "bright";
		temperature.innerHTML = " " + this.temperature + "°";
		large.appendChild(temperature);

		wrapper.appendChild(small);
		wrapper.appendChild(large);
		return wrapper;
	},

	/* updateWeather(compliments)
	 * Requests new data from openweather.org.
	 * Calls processWeather on succesfull response.
	 */
	updateWeather: function() {
		var url = this.config.apiBase + this.config.apiVersion + "/" + this.config.weatherEndpoint + '/' + this.getParams();
		var self = this;
		var retry = true;

		var weatherRequest = new XMLHttpRequest();
		weatherRequest.open("GET", url, true);
		weatherRequest.onreadystatechange = function() {
			if (this.readyState === 4) {
				if (this.status === 200) {
					self.processWeather(JSON.parse(this.response));
				} else if (this.status === 401) {
					self.config.appid = "";
					self.updateDom(self.config.animationSpeed);

					Log.error(self.name + ": Incorrect APPID.");
					retry = false;
				} else {
					Log.error(self.name + ": Could not load weather.");
				}

				if (retry) {
					self.scheduleUpdate((self.loaded) ? -1 : self.config.retryDelay);
				}
			}
		};
		weatherRequest.send();
	},

	/* getParams(compliments)
	 * Generates an url with api parameters based on the config.
	 *
	 * return String - URL params.
	 */
	getParams: function() {
		var params = "?";
		if(this.config.locationID !== "") {
			params += "id=" + this.config.locationID;
		} else {
			params += "q=" + this.config.location;
		}
		params += "&units=" + this.config.units;
		params += "&lang=" + this.config.lang;
		params += "&APPID=" + this.config.appid;

		return params;
	},

	/* processWeather(data)
	 * Uses the received data to set the various values.
	 *
	 * argument data object - Weather information received form openweather.org.
	 */
	processWeather: function(data) {

		if (!data || !data.main || !data.main.temp) {
			// Did not receive usable new data.
			// Maybe this needs a better check?
			return;
		}

		this.temperature = this.roundValue(data.main.temp);

		if (this.config.useBeaufort){
			this.windSpeed = this.ms2Beaufort(this.roundValue(data.wind.speed));
		}else {
			this.windSpeed = parseFloat(data.wind.speed).toFixed(0);
		}

		this.windDirection = this.deg2Cardinal(data.wind.deg);
		this.weatherType = this.config.iconTable[data.weather[0].icon];

		var now = new Date();
		var sunrise = new Date(data.sys.sunrise * 1000);
		var sunset = new Date(data.sys.sunset * 1000);

		// The moment().format('h') method has a bug on the Raspberry Pi.
		// So we need to generate the timestring manually.
		// See issue: https://github.com/MichMich/MagicMirror/issues/181
		var sunriseSunsetDateObject = (sunrise < now && sunset > now) ? sunset : sunrise;
		var timeString = moment(sunriseSunsetDateObject).format('HH:mm');
		if (this.config.timeFormat !== 24) {
			//var hours = sunriseSunsetDateObject.getHours() % 12 || 12;
			if (this.config.showPeriod) {
				if (this.config.showPeriodUpper) {
					//timeString = hours + moment(sunriseSunsetDateObject).format(':mm A');
					timeString = moment(sunriseSunsetDateObject).format('h:mm A');
				} else {
					//timeString = hours + moment(sunriseSunsetDateObject).format(':mm a');
					timeString = moment(sunriseSunsetDateObject).format('h:mm a');
				}
			} else {
				//timeString = hours + moment(sunriseSunsetDateObject).format(':mm');
				timeString = moment(sunriseSunsetDateObject).format('h:mm');
			}
		}

		this.sunriseSunsetTime = timeString;
		this.sunriseSunsetIcon = (sunrise < now && sunset > now) ? "wi-sunset" : "wi-sunrise";

		this.loaded = true;
		this.updateDom(this.config.animationSpeed);
	},

	/* scheduleUpdate()
	 * Schedule next update.
	 *
	 * argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used.
	 */
	scheduleUpdate: function(delay) {
		var nextLoad = this.config.updateInterval;
		if (typeof delay !== "undefined" && delay >= 0) {
			nextLoad = delay;
		}

		var self = this;
		setTimeout(function() {
			self.updateWeather();
		}, nextLoad);
	},

	/* ms2Beaufort(ms)
	 * Converts m2 to beaufort (windspeed).
	 *
	 * argument ms number - Windspeed in m/s.
	 *
	 * return number - Windspeed in beaufort.
	 */
	ms2Beaufort: function(ms) {
		var kmh = ms * 60 * 60 / 1000;
		var speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
		for (var beaufort in speeds) {
			var speed = speeds[beaufort];
			if (speed > kmh) {
				return beaufort;
			}
		}
		return 12;
	},

	/* function(temperature)
	 * Rounds a temperature to 1 decimal.
	 *
	 * argument temperature number - Temperature.
	 *
	 * return number - Rounded Temperature.
	 */

	deg2Cardinal: function(deg) {
                if (deg>11.25 && deg<=33.75){
                        return "NNE";
                } else if (deg > 33.75 && deg <= 56.25) {
                        return "NE";
                } else if (deg > 56.25 && deg <= 78.75) {
                        return "ENE";
                } else if (deg > 78.75 && deg <= 101.25) {
                        return "E";
                } else if (deg > 101.25 && deg <= 123.75) {
                        return "ESE";
                } else if (deg > 123.75 && deg <= 146.25) {
                        return "SE";
                } else if (deg > 146.25 && deg <= 168.75) {
                        return "SSE";
                } else if (deg > 168.75 && deg <= 191.25) {
                        return "S";
                } else if (deg > 191.25 && deg <= 213.75) {
                        return "SSW";
                } else if (deg > 213.75 && deg <= 236.25) {
                        return "SW";
                } else if (deg > 236.25 && deg <= 258.75) {
                        return "WSW";
                } else if (deg > 258.75 && deg <= 281.25) {
                        return "W";
                } else if (deg > 281.25 && deg <= 303.75) {
                        return "WNW";
                } else if (deg > 303.75 && deg <= 326.25) {
                        return "NW";
                } else if (deg > 326.25 && deg <= 348.75) {
                        return "NNW";
                } else {
                         return "N";
                }
	},

	roundValue: function(temperature) {
		return parseFloat(temperature).toFixed(1);
	}
});
<commit_msg>Worked around weather giving back a Kelvin response<commit_after>/* global Module */

/* Magic Mirror
 * Module: CurrentWeather
 *
 * By Michael Teeuw http://michaelteeuw.nl
 * MIT Licensed.
 */

Module.register("currentweather",{

	// Default module config.
	defaults: {
		location: "",
		locationID: "",
		appid: "",
		units: config.units,
		updateInterval: 10 * 60 * 1000, // every 10 minutes
		animationSpeed: 1000,
		timeFormat: config.timeFormat,
		showPeriod: true,
		showPeriodUpper: false,
		showWindDirection: true,
		useBeaufort: true,
		lang: config.language,

		initialLoadDelay: 0, // 0 seconds delay
		retryDelay: 2500,

		apiVersion: "2.5",
		apiBase: "http://api.openweathermap.org/data/",
		weatherEndpoint: "weather",

		iconTable: {
			"01d": "wi-day-sunny",
			"02d": "wi-day-cloudy",
			"03d": "wi-cloudy",
			"04d": "wi-cloudy-windy",
			"09d": "wi-showers",
			"10d": "wi-rain",
			"11d": "wi-thunderstorm",
			"13d": "wi-snow",
			"50d": "wi-fog",
			"01n": "wi-night-clear",
			"02n": "wi-night-cloudy",
			"03n": "wi-night-cloudy",
			"04n": "wi-night-cloudy",
			"09n": "wi-night-showers",
			"10n": "wi-night-rain",
			"11n": "wi-night-thunderstorm",
			"13n": "wi-night-snow",
			"50n": "wi-night-alt-cloudy-windy"
		},
	},

	// Define required scripts.
	getScripts: function() {
		return ["moment.js"];
	},

	// Define required scripts.
	getStyles: function() {
		return ["weather-icons.css", "currentweather.css"];
	},

	// Define required translations.
	getTranslations: function() {
		// The translations for the defaut modules are defined in the core translation files.
		// Therefor we can just return false. Otherwise we should have returned a dictionairy.
		// If you're trying to build yiur own module including translations, check out the documentation.
		return false;
	},

	// Define start sequence.
	start: function() {
		Log.info("Starting module: " + this.name);

		// Set locale.
		moment.locale(config.language);

		this.windSpeed = null;
		this.windDirection = null;
		this.sunriseSunsetTime = null;
		this.sunriseSunsetIcon = null;
		this.temperature = null;
		this.weatherType = null;

		this.loaded = false;
		this.scheduleUpdate(this.config.initialLoadDelay);

		this.updateTimer = null;

	},

	// Override dom generator.
	getDom: function() {
		var wrapper = document.createElement("div");

		if (this.config.appid === "") {
			wrapper.innerHTML = "Please set the correct openweather appid in the config for module: " + this.name + ".";
			wrapper.className = "dimmed light small";
			return wrapper;
		}

		if (this.config.location === "") {
			wrapper.innerHTML = "Please set the openweather location in the config for module: " + this.name + ".";
			wrapper.className = "dimmed light small";
			return wrapper;
		}

		if (!this.loaded) {
			wrapper.innerHTML = this.translate('LOADING');
			wrapper.className = "dimmed light small";
			return wrapper;
		}

		var small = document.createElement("div");
		small.className = "normal medium";

		var windIcon = document.createElement("span");
		windIcon.className = "wi wi-strong-wind dimmed";
		small.appendChild(windIcon);

		var windSpeed = document.createElement("span");
		windSpeed.innerHTML = " " + this.windSpeed;
		small.appendChild(windSpeed);

		if (this.config.showWindDirection) {
			var windDirection = document.createElement("sup");
			windDirection.innerHTML = " " + this.translate(this.windDirection);
			small.appendChild(windDirection);
		}
		var spacer = document.createElement("span");
		spacer.innerHTML = " ";
		small.appendChild(spacer);

		var sunriseSunsetIcon = document.createElement("span");
		sunriseSunsetIcon.className = "wi dimmed " + this.sunriseSunsetIcon;
		small.appendChild(sunriseSunsetIcon);

		var sunriseSunsetTime = document.createElement("span");
		sunriseSunsetTime.innerHTML = " " + this.sunriseSunsetTime;
		small.appendChild(sunriseSunsetTime);

		var large = document.createElement("div");
		large.className = "large light";

		var weatherIcon = document.createElement("span");
		weatherIcon.className = "wi weathericon " + this.weatherType;
		large.appendChild(weatherIcon);

		var temperature = document.createElement("span");
		temperature.className = "bright";
		temperature.innerHTML = " " + this.temperature + "°";
		large.appendChild(temperature);

		wrapper.appendChild(small);
		wrapper.appendChild(large);
		return wrapper;
	},

	/* updateWeather(compliments)
	 * Requests new data from openweather.org.
	 * Calls processWeather on succesfull response.
	 */
	updateWeather: function() {
		var url = this.config.apiBase + this.config.apiVersion + "/" + this.config.weatherEndpoint + '/' + this.getParams();
		var self = this;
		var retry = true;

		var weatherRequest = new XMLHttpRequest();
		weatherRequest.open("GET", url, true);
		weatherRequest.onreadystatechange = function() {
			if (this.readyState === 4) {
				if (this.status === 200) {
					self.processWeather(JSON.parse(this.response));
				} else if (this.status === 401) {
					self.config.appid = "";
					self.updateDom(self.config.animationSpeed);

					Log.error(self.name + ": Incorrect APPID.");
					retry = false;
				} else {
					Log.error(self.name + ": Could not load weather.");
				}

				if (retry) {
					self.scheduleUpdate((self.loaded) ? -1 : self.config.retryDelay);
				}
			}
		};
		weatherRequest.send();
	},

	/* getParams(compliments)
	 * Generates an url with api parameters based on the config.
	 *
	 * return String - URL params.
	 */
	getParams: function() {
		var params = "?";
		if(this.config.locationID !== "") {
			params += "id=" + this.config.locationID;
		} else {
			params += "q=" + this.config.location;
		}
		params += "&units=" + this.config.units;
		params += "&lang=" + this.config.lang;
		params += "&APPID=" + this.config.appid;

		return params;
	},

	/* processWeather(data)
	 * Uses the received data to set the various values.
	 *
	 * argument data object - Weather information received form openweather.org.
	 */
	processWeather: function(data) {

		if (!data || !data.main || !data.main.temp) {
			// Did not receive usable new data.
			// Maybe this needs a better check?
			return;
		}

        if (data.main.temp > 200) {
            if (config.units == "imperial") {
            this.temperature
                = this.roundValue((data.main.temp-273.15)*9/5 + 32);

            } else {
                this.temperature =this.roundValue(data.main.temp-273.15);

            }
        } else {
		    this.temperature = this.roundValue(data.main.temp);
        }

		if (this.config.useBeaufort){
			this.windSpeed = this.ms2Beaufort(this.roundValue(data.wind.speed));
		}else {
			this.windSpeed = parseFloat(data.wind.speed).toFixed(0);
		}

		this.windDirection = this.deg2Cardinal(data.wind.deg);
		this.weatherType = this.config.iconTable[data.weather[0].icon];

		var now = new Date();
		var sunrise = new Date(data.sys.sunrise * 1000);
		var sunset = new Date(data.sys.sunset * 1000);

		// The moment().format('h') method has a bug on the Raspberry Pi.
		// So we need to generate the timestring manually.
		// See issue: https://github.com/MichMich/MagicMirror/issues/181
		var sunriseSunsetDateObject = (sunrise < now && sunset > now) ? sunset : sunrise;
		var timeString = moment(sunriseSunsetDateObject).format('HH:mm');
		if (this.config.timeFormat !== 24) {
			//var hours = sunriseSunsetDateObject.getHours() % 12 || 12;
			if (this.config.showPeriod) {
				if (this.config.showPeriodUpper) {
					//timeString = hours + moment(sunriseSunsetDateObject).format(':mm A');
					timeString = moment(sunriseSunsetDateObject).format('h:mm A');
				} else {
					//timeString = hours + moment(sunriseSunsetDateObject).format(':mm a');
					timeString = moment(sunriseSunsetDateObject).format('h:mm a');
				}
			} else {
				//timeString = hours + moment(sunriseSunsetDateObject).format(':mm');
				timeString = moment(sunriseSunsetDateObject).format('h:mm');
			}
		}

		this.sunriseSunsetTime = timeString;
		this.sunriseSunsetIcon = (sunrise < now && sunset > now) ? "wi-sunset" : "wi-sunrise";

		this.loaded = true;
		this.updateDom(this.config.animationSpeed);
	},

	/* scheduleUpdate()
	 * Schedule next update.
	 *
	 * argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used.
	 */
	scheduleUpdate: function(delay) {
		var nextLoad = this.config.updateInterval;
		if (typeof delay !== "undefined" && delay >= 0) {
			nextLoad = delay;
		}

		var self = this;
		setTimeout(function() {
			self.updateWeather();
		}, nextLoad);
	},

	/* ms2Beaufort(ms)
	 * Converts m2 to beaufort (windspeed).
	 *
	 * argument ms number - Windspeed in m/s.
	 *
	 * return number - Windspeed in beaufort.
	 */
	ms2Beaufort: function(ms) {
		var kmh = ms * 60 * 60 / 1000;
		var speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
		for (var beaufort in speeds) {
			var speed = speeds[beaufort];
			if (speed > kmh) {
				return beaufort;
			}
		}
		return 12;
	},

	/* function(temperature)
	 * Rounds a temperature to 1 decimal.
	 *
	 * argument temperature number - Temperature.
	 *
	 * return number - Rounded Temperature.
	 */

	deg2Cardinal: function(deg) {
                if (deg>11.25 && deg<=33.75){
                        return "NNE";
                } else if (deg > 33.75 && deg <= 56.25) {
                        return "NE";
                } else if (deg > 56.25 && deg <= 78.75) {
                        return "ENE";
                } else if (deg > 78.75 && deg <= 101.25) {
                        return "E";
                } else if (deg > 101.25 && deg <= 123.75) {
                        return "ESE";
                } else if (deg > 123.75 && deg <= 146.25) {
                        return "SE";
                } else if (deg > 146.25 && deg <= 168.75) {
                        return "SSE";
                } else if (deg > 168.75 && deg <= 191.25) {
                        return "S";
                } else if (deg > 191.25 && deg <= 213.75) {
                        return "SSW";
                } else if (deg > 213.75 && deg <= 236.25) {
                        return "SW";
                } else if (deg > 236.25 && deg <= 258.75) {
                        return "WSW";
                } else if (deg > 258.75 && deg <= 281.25) {
                        return "W";
                } else if (deg > 281.25 && deg <= 303.75) {
                        return "WNW";
                } else if (deg > 303.75 && deg <= 326.25) {
                        return "NW";
                } else if (deg > 326.25 && deg <= 348.75) {
                        return "NNW";
                } else {
                         return "N";
                }
	},

	roundValue: function(temperature) {
		return parseFloat(temperature).toFixed(1);
	}
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2018 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview The class representing a theme.
 */
'use strict';

goog.module('Blockly.Theme');
goog.module.declareLegacyNamespace();

const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');

/**
 * Class for a theme.
 * @param {string} name Theme name.
 * @param {!Object<string, Theme.BlockStyle>=} opt_blockStyles A map
 *     from style names (strings) to objects with style attributes for blocks.
 * @param {!Object<string, Theme.CategoryStyle>=} opt_categoryStyles A
 *     map from style names (strings) to objects with style attributes for
 *     categories.
 * @param {!Theme.ComponentStyle=} opt_componentStyles A map of Blockly
 *     component names to style value.
 * @constructor
 */
const Theme = function(name, opt_blockStyles, opt_categoryStyles,
    opt_componentStyles) {

  /**
   * The theme name. This can be used to reference a specific theme in CSS.
   * @type {string}
   */
  this.name = name;

  /**
   * The block styles map.
   * @type {!Object<string, !Theme.BlockStyle>}
   * @package
   */
  this.blockStyles = opt_blockStyles || Object.create(null);

  /**
   * The category styles map.
   * @type {!Object<string, Theme.CategoryStyle>}
   * @package
   */
  this.categoryStyles = opt_categoryStyles || Object.create(null);

  /**
   * The UI components styles map.
   * @type {!Theme.ComponentStyle}
   * @package
   */
  this.componentStyles = opt_componentStyles ||
    (/** @type {Theme.ComponentStyle} */ (Object.create(null)));

  /**
   * The font style.
   * @type {!Theme.FontStyle}
   * @package
   */
  this.fontStyle = /** @type {Theme.FontStyle} */ (Object.create(null));

  /**
   * Whether or not to add a 'hat' on top of all blocks with no previous or
   * output connections.
   * @type {?boolean}
   * @package
   */
  this.startHats = null;

  // Register the theme by name.
  registry.register(registry.Type.THEME, name, this);
};

/**
 * A block style.
 * @typedef {{
 *            colourPrimary:string,
 *            colourSecondary:string,
 *            colourTertiary:string,
 *            hat:string
 *          }}
 */
Theme.BlockStyle;

/**
 * A category style.
 * @typedef {{
 *            colour:string
 *          }}
 */
Theme.CategoryStyle;

/**
 * A component style.
 * @typedef {{
 *            workspaceBackgroundColour:?string,
 *            toolboxBackgroundColour:?string,
 *            toolboxForegroundColour:?string,
 *            flyoutBackgroundColour:?string,
 *            flyoutForegroundColour:?string,
 *            flyoutOpacity:?number,
 *            scrollbarColour:?string,
 *            scrollbarOpacity:?number,
 *            insertionMarkerColour:?string,
 *            insertionMarkerOpacity:?number,
 *            markerColour:?string,
 *            cursorColour:?string,
 *            selectedGlowColour:?string,
 *            selectedGlowOpacity:?number,
 *            replacementGlowColour:?string,
 *            replacementGlowOpacity:?number
 *          }}
 */
Theme.ComponentStyle;

/**
 * A font style.
 * @typedef {{
 *            family:?string,
 *            weight:?string,
 *            size:?number
 *          }}
 */
Theme.FontStyle;

/**
 * Gets the class name that identifies this theme.
 * @return {string} The CSS class name.
 * @package
 */
Theme.prototype.getClassName = function() {
  return this.name + '-theme';
};

/**
 * Overrides or adds a style to the blockStyles map.
 * @param {string} blockStyleName The name of the block style.
 * @param {Theme.BlockStyle} blockStyle The block style.
*/
Theme.prototype.setBlockStyle = function(blockStyleName, blockStyle) {
  this.blockStyles[blockStyleName] = blockStyle;
};

/**
 * Overrides or adds a style to the categoryStyles map.
 * @param {string} categoryStyleName The name of the category style.
 * @param {Theme.CategoryStyle} categoryStyle The category style.
*/
Theme.prototype.setCategoryStyle = function(categoryStyleName,
    categoryStyle) {
  this.categoryStyles[categoryStyleName] = categoryStyle;
};

/**
 * Gets the style for a given Blockly UI component.  If the style value is a
 * string, we attempt to find the value of any named references.
 * @param {string} componentName The name of the component.
 * @return {?string} The style value.
 */
Theme.prototype.getComponentStyle = function(componentName) {
  const style = this.componentStyles[componentName];
  if (style && typeof style == 'string' &&
      this.getComponentStyle(/** @type {string} */ (style))) {
    return this.getComponentStyle(/** @type {string} */ (style));
  }
  return style ? String(style) : null;
};

/**
 * Configure a specific Blockly UI component with a style value.
 * @param {string} componentName The name of the component.
 * @param {*} styleValue The style value.
*/
Theme.prototype.setComponentStyle = function(componentName,
    styleValue) {
  this.componentStyles[componentName] = styleValue;
};

/**
 * Configure a theme's font style.
 * @param {Theme.FontStyle} fontStyle The font style.
*/
Theme.prototype.setFontStyle = function(fontStyle) {
  this.fontStyle = fontStyle;
};

/**
 * Configure a theme's start hats.
 * @param {boolean} startHats True if the theme enables start hats, false
 *     otherwise.
*/
Theme.prototype.setStartHats = function(startHats) {
  this.startHats = startHats;
};

/**
 * Define a new Blockly theme.
 * @param {string} name The name of the theme.
 * @param {!Object} themeObj An object containing theme properties.
 * @return {!Theme} A new Blockly theme.
*/
Theme.defineTheme = function(name, themeObj) {
  const theme = new Theme(name);
  let base = themeObj['base'];
  if (base) {
    if (typeof base == "string") {
      base = registry.getObject(registry.Type.THEME, base);
    }
    if (base instanceof Theme) {
      object.deepMerge(theme, base);
      theme.name = name;
    }
  }

  object.deepMerge(theme.blockStyles,
      themeObj['blockStyles']);
  object.deepMerge(theme.categoryStyles,
      themeObj['categoryStyles']);
  object.deepMerge(theme.componentStyles,
      themeObj['componentStyles']);
  object.deepMerge(theme.fontStyle,
      themeObj['fontStyle']);
  if (themeObj['startHats'] != null) {
    theme.startHats = themeObj['startHats'];
  }

  return theme;
};

exports = Theme;
<commit_msg>clang-format core/theme.js<commit_after>/**
 * @license
 * Copyright 2018 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview The class representing a theme.
 */
'use strict';

goog.module('Blockly.Theme');
goog.module.declareLegacyNamespace();

const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');

/**
 * Class for a theme.
 * @param {string} name Theme name.
 * @param {!Object<string, Theme.BlockStyle>=} opt_blockStyles A map
 *     from style names (strings) to objects with style attributes for blocks.
 * @param {!Object<string, Theme.CategoryStyle>=} opt_categoryStyles A
 *     map from style names (strings) to objects with style attributes for
 *     categories.
 * @param {!Theme.ComponentStyle=} opt_componentStyles A map of Blockly
 *     component names to style value.
 * @constructor
 */
const Theme = function(
    name, opt_blockStyles, opt_categoryStyles, opt_componentStyles) {
  /**
   * The theme name. This can be used to reference a specific theme in CSS.
   * @type {string}
   */
  this.name = name;

  /**
   * The block styles map.
   * @type {!Object<string, !Theme.BlockStyle>}
   * @package
   */
  this.blockStyles = opt_blockStyles || Object.create(null);

  /**
   * The category styles map.
   * @type {!Object<string, Theme.CategoryStyle>}
   * @package
   */
  this.categoryStyles = opt_categoryStyles || Object.create(null);

  /**
   * The UI components styles map.
   * @type {!Theme.ComponentStyle}
   * @package
   */
  this.componentStyles = opt_componentStyles ||
      (/** @type {Theme.ComponentStyle} */ (Object.create(null)));

  /**
   * The font style.
   * @type {!Theme.FontStyle}
   * @package
   */
  this.fontStyle = /** @type {Theme.FontStyle} */ (Object.create(null));

  /**
   * Whether or not to add a 'hat' on top of all blocks with no previous or
   * output connections.
   * @type {?boolean}
   * @package
   */
  this.startHats = null;

  // Register the theme by name.
  registry.register(registry.Type.THEME, name, this);
};

/**
 * A block style.
 * @typedef {{
 *            colourPrimary:string,
 *            colourSecondary:string,
 *            colourTertiary:string,
 *            hat:string
 *          }}
 */
Theme.BlockStyle;

/**
 * A category style.
 * @typedef {{
 *            colour:string
 *          }}
 */
Theme.CategoryStyle;

/**
 * A component style.
 * @typedef {{
 *            workspaceBackgroundColour:?string,
 *            toolboxBackgroundColour:?string,
 *            toolboxForegroundColour:?string,
 *            flyoutBackgroundColour:?string,
 *            flyoutForegroundColour:?string,
 *            flyoutOpacity:?number,
 *            scrollbarColour:?string,
 *            scrollbarOpacity:?number,
 *            insertionMarkerColour:?string,
 *            insertionMarkerOpacity:?number,
 *            markerColour:?string,
 *            cursorColour:?string,
 *            selectedGlowColour:?string,
 *            selectedGlowOpacity:?number,
 *            replacementGlowColour:?string,
 *            replacementGlowOpacity:?number
 *          }}
 */
Theme.ComponentStyle;

/**
 * A font style.
 * @typedef {{
 *            family:?string,
 *            weight:?string,
 *            size:?number
 *          }}
 */
Theme.FontStyle;

/**
 * Gets the class name that identifies this theme.
 * @return {string} The CSS class name.
 * @package
 */
Theme.prototype.getClassName = function() {
  return this.name + '-theme';
};

/**
 * Overrides or adds a style to the blockStyles map.
 * @param {string} blockStyleName The name of the block style.
 * @param {Theme.BlockStyle} blockStyle The block style.
 */
Theme.prototype.setBlockStyle = function(blockStyleName, blockStyle) {
  this.blockStyles[blockStyleName] = blockStyle;
};

/**
 * Overrides or adds a style to the categoryStyles map.
 * @param {string} categoryStyleName The name of the category style.
 * @param {Theme.CategoryStyle} categoryStyle The category style.
 */
Theme.prototype.setCategoryStyle = function(categoryStyleName, categoryStyle) {
  this.categoryStyles[categoryStyleName] = categoryStyle;
};

/**
 * Gets the style for a given Blockly UI component.  If the style value is a
 * string, we attempt to find the value of any named references.
 * @param {string} componentName The name of the component.
 * @return {?string} The style value.
 */
Theme.prototype.getComponentStyle = function(componentName) {
  const style = this.componentStyles[componentName];
  if (style && typeof style == 'string' &&
      this.getComponentStyle(/** @type {string} */ (style))) {
    return this.getComponentStyle(/** @type {string} */ (style));
  }
  return style ? String(style) : null;
};

/**
 * Configure a specific Blockly UI component with a style value.
 * @param {string} componentName The name of the component.
 * @param {*} styleValue The style value.
 */
Theme.prototype.setComponentStyle = function(componentName, styleValue) {
  this.componentStyles[componentName] = styleValue;
};

/**
 * Configure a theme's font style.
 * @param {Theme.FontStyle} fontStyle The font style.
 */
Theme.prototype.setFontStyle = function(fontStyle) {
  this.fontStyle = fontStyle;
};

/**
 * Configure a theme's start hats.
 * @param {boolean} startHats True if the theme enables start hats, false
 *     otherwise.
 */
Theme.prototype.setStartHats = function(startHats) {
  this.startHats = startHats;
};

/**
 * Define a new Blockly theme.
 * @param {string} name The name of the theme.
 * @param {!Object} themeObj An object containing theme properties.
 * @return {!Theme} A new Blockly theme.
 */
Theme.defineTheme = function(name, themeObj) {
  const theme = new Theme(name);
  let base = themeObj['base'];
  if (base) {
    if (typeof base == 'string') {
      base = registry.getObject(registry.Type.THEME, base);
    }
    if (base instanceof Theme) {
      object.deepMerge(theme, base);
      theme.name = name;
    }
  }

  object.deepMerge(theme.blockStyles, themeObj['blockStyles']);
  object.deepMerge(theme.categoryStyles, themeObj['categoryStyles']);
  object.deepMerge(theme.componentStyles, themeObj['componentStyles']);
  object.deepMerge(theme.fontStyle, themeObj['fontStyle']);
  if (themeObj['startHats'] != null) {
    theme.startHats = themeObj['startHats'];
  }

  return theme;
};

exports = Theme;
<|endoftext|>


<commit_before>import {Subject, merge, timer} from 'rxjs'
import {connect, select} from 'store'
import {delay, filter, map, mergeMap} from 'rxjs/operators'
import {msg} from 'translate'
import {v4 as uuid} from 'uuid'
import PropTypes from 'prop-types'
import React from 'react'
import actionBuilder from 'action-builder'
import hash from 'object-hash'
import styles from './notifications.module.css'

const PATH = 'Notifications'
const DISMISS_DELAY_MS = 500

const publish$ = new Subject()
const manualDismiss$ = new Subject()
const autoDismiss$ = publish$
    .pipe(
        filter(notification => notification.timeout),
        mergeMap(notification =>
            timer(notification.timeout).pipe(
                map(() => notification.id)
            )
        )
    )
const dismiss$ = merge(manualDismiss$, autoDismiss$)
const remove$ = dismiss$.pipe(delay(DISMISS_DELAY_MS))

const group = ({group = false, id, ...notification}) =>
    group === false
        ? id
        : group === true
            ? hash(notification) // id is excluded
            : group

const publish = notification => {
    const publish = notification =>
        publish$.next({
            ...notification,
            group: group(notification)
        })

    const defaultTitle = {
        'error': msg('widget.notification.error.title'),
        'warning': msg('widget.notification.warning.title')
    }

    const applyDefaults = ({
        id = uuid(),
        level = 'info',
        title = defaultTitle[level],
        timeout = 10000,
        dismissable = true,
        ...notification
    }) => ({id, level, title, timeout, dismissable, ...notification})

    publish(applyDefaults(notification))
}
const dismiss = notificationId =>
    manualDismiss$.next(notificationId)

const mapStateToProps = () => ({
    notifications: select(PATH) || []
})

class _Notifications extends React.Component {
    subscriptions = []

    renderTitle(title) {
        return (

                {title}

        )
    }

    renderMessage(message) {
        return (

                {message}

        )
    }

    renderError(error) {
        const errorMessage = typeof error === 'string' ? error : error.message
        return (

                {errorMessage}

        )
    }

    renderContent(content, dismiss) {
        return (

                {content(dismiss)}

        )
    }

    renderDismissMessage() {
        return (

                {msg('widget.notification.dismiss')}

        )
    }

    renderNotification({id, level, title, message, error, content, timeout, dismissable, dismissing}) {
        const dismiss = () => manualDismiss$.next(id)
        return (
            <div
                key={id}
                className={[
                    styles.notification,
                    styles[level],
                    dismissable ? styles.dismissable : null,
                    dismissing ? styles.dismissing : null
                ].join(' ')}
                style={{'--dismiss-time-ms': `${DISMISS_DELAY_MS}ms`}}
                onClick={() => dismissable && dismiss()}
            >
                {title ? this.renderTitle(title) : null}
                {message ? this.renderMessage(message) : null}
                {error ? this.renderError(error) : null}
                {content ? this.renderContent(content, dismiss) : null}
                {timeout === 0 ? this.renderDismissMessage() : null}

        )
    }

    renderNotifications() {
        const {notifications} = this.props
        return notifications.map(notification => this.renderNotification(notification))
    }

    render() {
        return (

                {this.renderNotifications()}

        )
    }

    componentDidMount() {
        this.subscriptions.push(
            publish$.subscribe(notification =>
                actionBuilder('PUBLISH_NOTIFICATION')
                    .pushIfMissing(PATH, notification, 'group')
                    .dispatch()
            )
        )
        this.subscriptions.push(
            dismiss$.subscribe(notificationId =>
                actionBuilder('DISMISS_NOTIFICATION')
                    .assignValueByTemplate(PATH, {id: notificationId}, {dismissing: true})
                    .dispatch()
            )
        )
        this.subscriptions.push(
            remove$.subscribe(notificationId =>
                actionBuilder('REMOVE_NOTIFICATION')
                    .delValueByTemplate(PATH, {id: notificationId})
                    .dispatch()
            )
        )
    }

    componentWillUnmount() {
        this.subscriptions.forEach(subscription => subscription.unsubscribe())
    }
}

const Notifications = connect(mapStateToProps)(_Notifications)

Notifications.success = notification =>
    publish({...notification, level: 'success'})

Notifications.info = notification =>
    publish({...notification, level: 'info'})

Notifications.warning = notification =>
    publish({...notification, level: 'warning'})

Notifications.error = notification =>
    publish({...notification, level: 'error'})

Notifications.dismiss = notificationId =>
    dismiss(notificationId)

export default Notifications

Notifications.propTypes = {
    content: PropTypes.func,
    dismissable: PropTypes.any,
    error: PropTypes.string,
    group: PropTypes.oneOf([true, false, PropTypes.string]),
    id: PropTypes.string,
    level: PropTypes.string,
    message: PropTypes.string,
    timeout: PropTypes.number,
    title: PropTypes.string,
}
<commit_msg>Restored default 3s notification timeout.<commit_after>import {Subject, merge, timer} from 'rxjs'
import {connect, select} from 'store'
import {delay, filter, map, mergeMap} from 'rxjs/operators'
import {msg} from 'translate'
import {v4 as uuid} from 'uuid'
import PropTypes from 'prop-types'
import React from 'react'
import actionBuilder from 'action-builder'
import hash from 'object-hash'
import styles from './notifications.module.css'

const PATH = 'Notifications'
const DISMISS_DELAY_MS = 500

const publish$ = new Subject()
const manualDismiss$ = new Subject()
const autoDismiss$ = publish$
    .pipe(
        filter(notification => notification.timeout),
        mergeMap(notification =>
            timer(notification.timeout).pipe(
                map(() => notification.id)
            )
        )
    )
const dismiss$ = merge(manualDismiss$, autoDismiss$)
const remove$ = dismiss$.pipe(delay(DISMISS_DELAY_MS))

const group = ({group = false, id, ...notification}) =>
    group === false
        ? id
        : group === true
            ? hash(notification) // id is excluded
            : group

const publish = notification => {
    const publish = notification =>
        publish$.next({
            ...notification,
            group: group(notification)
        })

    const defaultTitle = {
        'error': msg('widget.notification.error.title'),
        'warning': msg('widget.notification.warning.title')
    }

    const applyDefaults = ({
        id = uuid(),
        level = 'info',
        title = defaultTitle[level],
        timeout = 3000,
        dismissable = true,
        ...notification
    }) => ({id, level, title, timeout, dismissable, ...notification})

    publish(applyDefaults(notification))
}
const dismiss = notificationId =>
    manualDismiss$.next(notificationId)

const mapStateToProps = () => ({
    notifications: select(PATH) || []
})

class _Notifications extends React.Component {
    subscriptions = []

    renderTitle(title) {
        return (

                {title}

        )
    }

    renderMessage(message) {
        return (

                {message}

        )
    }

    renderError(error) {
        const errorMessage = typeof error === 'string' ? error : error.message
        return (

                {errorMessage}

        )
    }

    renderContent(content, dismiss) {
        return (

                {content(dismiss)}

        )
    }

    renderDismissMessage() {
        return (

                {msg('widget.notification.dismiss')}

        )
    }

    renderNotification({id, level, title, message, error, content, timeout, dismissable, dismissing}) {
        const dismiss = () => manualDismiss$.next(id)
        return (
            <div
                key={id}
                className={[
                    styles.notification,
                    styles[level],
                    dismissable ? styles.dismissable : null,
                    dismissing ? styles.dismissing : null
                ].join(' ')}
                style={{'--dismiss-time-ms': `${DISMISS_DELAY_MS}ms`}}
                onClick={() => dismissable && dismiss()}
            >
                {title ? this.renderTitle(title) : null}
                {message ? this.renderMessage(message) : null}
                {error ? this.renderError(error) : null}
                {content ? this.renderContent(content, dismiss) : null}
                {timeout === 0 ? this.renderDismissMessage() : null}

        )
    }

    renderNotifications() {
        const {notifications} = this.props
        return notifications.map(notification => this.renderNotification(notification))
    }

    render() {
        return (

                {this.renderNotifications()}

        )
    }

    componentDidMount() {
        this.subscriptions.push(
            publish$.subscribe(notification =>
                actionBuilder('PUBLISH_NOTIFICATION')
                    .pushIfMissing(PATH, notification, 'group')
                    .dispatch()
            )
        )
        this.subscriptions.push(
            dismiss$.subscribe(notificationId =>
                actionBuilder('DISMISS_NOTIFICATION')
                    .assignValueByTemplate(PATH, {id: notificationId}, {dismissing: true})
                    .dispatch()
            )
        )
        this.subscriptions.push(
            remove$.subscribe(notificationId =>
                actionBuilder('REMOVE_NOTIFICATION')
                    .delValueByTemplate(PATH, {id: notificationId})
                    .dispatch()
            )
        )
    }

    componentWillUnmount() {
        this.subscriptions.forEach(subscription => subscription.unsubscribe())
    }
}

const Notifications = connect(mapStateToProps)(_Notifications)

Notifications.success = notification =>
    publish({...notification, level: 'success'})

Notifications.info = notification =>
    publish({...notification, level: 'info'})

Notifications.warning = notification =>
    publish({...notification, level: 'warning'})

Notifications.error = notification =>
    publish({...notification, level: 'error'})

Notifications.dismiss = notificationId =>
    dismiss(notificationId)

export default Notifications

Notifications.propTypes = {
    content: PropTypes.func,
    dismissable: PropTypes.any,
    error: PropTypes.string,
    group: PropTypes.oneOf([true, false, PropTypes.string]),
    id: PropTypes.string,
    level: PropTypes.string,
    message: PropTypes.string,
    timeout: PropTypes.number,
    title: PropTypes.string,
}
<|endoftext|>


<commit_before>describe("This:", function() {
	it("should hold value of undefined in global functions and anonymous functions  if not bound to objects in strict mode", function() {
		window.color = 'blue';
		function thisUseStrict() {
			'use strict';
			return this.color;
		}
		expect(function() {thisUseStrict() }).toThrow(new TypeError("Cannot read property 'color' of undefined"));
	});

	it("is not defined until an object invokes the this function at runtime");

	it("If this is used in a global function, it refers to the window object", function() {
		var name = "Abby";
		function showName() {
			return this.name; // this will have value of window object
		};
		var person = {
			name: "Barry",
			showName: function () {
				return this.name; // refers to the person object, because showName will be invoked by person object
			}
		};
		expect(showName()).toMatch("Abby");
		expect(person.showName()).toMatch("Barry");
	});

	describe("Tricky uses of this:", function() {
		it("In a method passed as a callback, use call/bind/apply for proper context", function() {
			var user = {
				name: "Charlie",
				showName: function() {
					return this.name;
				}
			};
			function callShowName() {
				user.showName();
			};
			expect(callShowName()).toBe(undefined);

			function bindShowName() {
				user.showName.bind(user);
			};
			// bindShowName();
			// console.log(bindShowName());
// THIS DOESNT WORK, FIX LATER TODO
		});

		// it("Closures can't access the outer function's this variable because it is only accessible by the function itself, not inner functions", function() {
		// 	var user = {
		// 		data: [{name: "Delta"}, {name: "Emily"}],
		// 		showName: function () {
		// 			this.data.forEach(function(person) {
		// 				console.log(this.name);
		// 			});
		// 		}
		// 	};

		// 	console.log(user.showName());

		// });

	});

}); // this end<commit_msg>add - this.inside closures<commit_after>describe("This:", function() {
	it("should hold value of undefined in global functions and anonymous functions  if not bound to objects in strict mode", function() {
		window.color = 'blue';
		function thisUseStrict() {
			'use strict';
			return this.color;
		}
		expect(function() {thisUseStrict() }).toThrow(new TypeError("Cannot read property 'color' of undefined"));
	});

	it("is not defined until an object invokes the this function at runtime");

	it("If this is used in a global function, it refers to the window object", function() {
		var name = "Abby";
		function showName() {
			return this.name; // this will have value of window object
		};
		var person = {
			name: "Barry",
			showName: function () {
				return this.name; // refers to the person object, because showName will be invoked by person object
			}
		};
		expect(showName()).toMatch("Abby");
		expect(person.showName()).toMatch("Barry");
	});

	describe("Tricky uses of this:", function() {
		describe("1.  Callbacks", function() {
			it("In a method passed as a callback, use call/bind/apply for proper context", function() {
				var user = {
					name: "Charlie",
					returnName: function() {
						return this.name;
					}
				};
				function callReturnName() {
					user.returnName();
				};
				expect(callReturnName()).toBe(undefined);

				// var bindedUserReturnName = user.returnName.bind(user);
				// function callBindedUserReturnName () {
				// 	bindedUserReturnName();
				// };
				// function bindReturnName() {
				// 	var binded = user.returnName.bind(user);
				// 	binded();
				// };
				// console.log(callBindedUserReturnName());
	// THIS DOESNT WORK, FIX LATER TODO
			});
		});
		describe("2. Inside Closures", function() {
			it("A closure can't access the outer function's this var because this is accessible only by the function itself, not inner function");
			it("Inside an anonymous function, set the this value to another variable before entering the method", function() {
				var animals = {
					data: [{species: 'Giraffe'}, {species: 'Elephant'}],
					listOfSpecies: [],
					logEachAnimal: function() {
						var that = this; // we set the value of "this" to "that" variable, so we can use later
						this.data.forEach(function(animal) {
							that.listOfSpecies.push(animal.species);
						});
					}
				};
				var list = animals.listOfSpecies;
				expect(list.length).toBe(0);
				animals.logEachAnimal();
				expect(list.length).toBe(2);
			});

		});

	});

}); // this end<|endoftext|>