<commit_before>/*!
 *
 *  Web Starter Kit
 *  Copyright 2015 Google Inc. All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License
 *
 */
/* eslint-env browser */
(function() {
  'use strict';

  // Check to make sure service workers are supported in the current browser,
  // and that the current page is accessed from a secure origin. Using a
  // service worker from an insecure origin will trigger JS console errors. See
  // http://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features
  var isLocalhost = Boolean(window.location.hostname === 'localhost' ||
      // [::1] is the IPv6 localhost address.
      window.location.hostname === '[::1]' ||
      // 127.0.0.1/8 is considered localhost for IPv4.
      window.location.hostname.match(
        /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
      )
    );

  if ('serviceWorker' in navigator &&
      (window.location.protocol === 'https:' || isLocalhost)) {
    navigator.serviceWorker.register('service-worker.js')
    .then(function(registration) {
      // updatefound is fired if service-worker.js changes.
      registration.onupdatefound = function() {
        // updatefound is also fired the very first time the SW is installed,
        // and there's no need to prompt for a reload at that point.
        // So check here to see if the page is already controlled,
        // i.e. whether there's an existing service worker.
        if (navigator.serviceWorker.controller) {
          // The updatefound event implies that registration.installing is set:
          // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
          var installingWorker = registration.installing;

          installingWorker.onstatechange = function() {
            switch (installingWorker.state) {
              case 'installed':
                // At this point, the old content will have been purged and the
                // fresh content will have been added to the cache.
                // It's the perfect time to display a "New content is
                // available; please refresh." message in the page's interface.
                break;

              case 'redundant':
                throw new Error('The installing ' +
                                'service worker became redundant.');

              default:
                // Ignore
            }
          };
        }
      };
    }).catch(function(e) {
      console.error('Error during service worker registration:', e);
    });
  }

  // Your custom JavaScript goes here
})();

/* dialog
(function() {
  'use strict';
  var dialog = document.querySelector('#modal-example');
  var closeButton = dialog.querySelector('button');
  var showButton = document.querySelector('#show-modal-example');
  if (! dialog.showModal) {
    dialogPolyfill.registerDialog(dialog);
  }
  var closeClickHandler = function(event) {
    dialog.close();
  };
  var showClickHandler = function(event) {
    dialog.showModal();
  };
  showButton.addEventListener('click', showClickHandler);
  closeButton.addEventListener('click', closeClickHandler);
}());
*/

var map,
    defaultPosition,
    Restaurants = {},
    userLocation
    data = {
      location:{
        lat:"",
        lng:""
      }
    };

/* Services */
var RestaurantFactory = {
  getRestaurants: function(data, output) {
    $.ajax({
      url: 'http://localhost:1234/api' + '/restaurant ',
      method: 'GET',
      data: {},
      success: function(response) {
        output(response);
      }
    })
  }
};

function initMap() {
  defaultPosition = new google.maps.LatLng(46.051343, 14.506293);
  map = new google.maps.Map(document.getElementById('map'), {
    zoom: 16,
    center: defaultPosition,
    mapTypeId: 'roadmap'
  });

}

function init() {

  /* Get restaurants */
  RestaurantFactory.getRestaurants("", function(response){
    Restaurants = response;

    $.Mustache.load('templates/restaurants.html')
      .done(function () {
        $('#test-restaurants').mustache('restavracije', Restaurants);
      });

  });

  /* scoll header */
  window.addEventListener('scroll', function(e){
    var distanceY = window.pageYOffset || document.documentElement.scrollTop,
      shrinkOn = 150,
      header = document.querySelector("header");

    if (distanceY > shrinkOn) {
      classie.add(header,"smaller");
    } else {
      if (classie.has(header,"smaller")) {
        classie.remove(header,"smaller");
      }
    }
  });

  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(showOnMap);
  } else {
    console.log("Geolocation is not supported by this browser.");
  }

  // else 1 default to city center, Ljubljana
  // else 2 ask use for location
  //46.051343, 14.506293
  function showOnMap(position) {
    console.log("User: " + "lat: " + position.coords.latitude +
      " lng: " + position.coords.longitude);

    userLocation = new google.maps.LatLng(position.coords.latitude,  position.coords.longitude);
    map.setCenter(userLocation);

    var marker = new google.maps.Marker({
      position: userLocation,
      map: map,
      icon: '../app/images/me-marker.png'
    });

    // add markers
    var infowindow = new google.maps.InfoWindow(),
        marker,
        i;

    for (i = 0; i < Restaurants.length; i++) {

       marker = new google.maps.Marker({
         position: new google.maps.LatLng(Restaurants[i].location.lat, Restaurants[i].location.lng),
         map: map,
         icon: '../app/images/map-marker.png'
       });

       google.maps.event.addListener(marker, 'click', (
         function(marker, i) {
           return function() {
             infowindow.setContent(Restaurants[i].name);
             infowindow.open(map, marker);
           }
        })(marker, i));
     }

    data.location.lat = position.coords.latitude;
    data.location.lng = position.coords.longitude;
    console.log(data);

    RestaurantFactory.getRestaurants(data, function(response){
      Restaurants = response;

      $.Mustache.load('templates/restaurants.html')
        .done(function () {
          $('#test-restaurants').mustache('restavracije', Restaurants);
        });

    });
  }
}

window.onload = init();
<commit_msg>google maps, restaurant factory<commit_after>/*!
 *
 *  Web Starter Kit
 *  Copyright 2015 Google Inc. All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License
 *
 */
/* eslint-env browser */
(function() {
  'use strict';

  // Check to make sure service workers are supported in the current browser,
  // and that the current page is accessed from a secure origin. Using a
  // service worker from an insecure origin will trigger JS console errors. See
  // http://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features
  var isLocalhost = Boolean(window.location.hostname === 'localhost' ||
      // [::1] is the IPv6 localhost address.
      window.location.hostname === '[::1]' ||
      // 127.0.0.1/8 is considered localhost for IPv4.
      window.location.hostname.match(
        /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
      )
    );

  if ('serviceWorker' in navigator &&
      (window.location.protocol === 'https:' || isLocalhost)) {
    navigator.serviceWorker.register('service-worker.js')
    .then(function(registration) {
      // updatefound is fired if service-worker.js changes.
      registration.onupdatefound = function() {
        // updatefound is also fired the very first time the SW is installed,
        // and there's no need to prompt for a reload at that point.
        // So check here to see if the page is already controlled,
        // i.e. whether there's an existing service worker.
        if (navigator.serviceWorker.controller) {
          // The updatefound event implies that registration.installing is set:
          // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
          var installingWorker = registration.installing;

          installingWorker.onstatechange = function() {
            switch (installingWorker.state) {
              case 'installed':
                // At this point, the old content will have been purged and the
                // fresh content will have been added to the cache.
                // It's the perfect time to display a "New content is
                // available; please refresh." message in the page's interface.
                break;

              case 'redundant':
                throw new Error('The installing ' +
                                'service worker became redundant.');

              default:
                // Ignore
            }
          };
        }
      };
    }).catch(function(e) {
      console.error('Error during service worker registration:', e);
    });
  }

  // Your custom JavaScript goes here
})();

/* dialog
(function() {
  'use strict';
  var dialog = document.querySelector('#modal-example');
  var closeButton = dialog.querySelector('button');
  var showButton = document.querySelector('#show-modal-example');
  if (! dialog.showModal) {
    dialogPolyfill.registerDialog(dialog);
  }
  var closeClickHandler = function(event) {
    dialog.close();
  };
  var showClickHandler = function(event) {
    dialog.showModal();
  };
  showButton.addEventListener('click', showClickHandler);
  closeButton.addEventListener('click', closeClickHandler);
}());
*/

var map,
    defaultPosition,
    Restaurants = {},
    userLocation
    data = {
      location:{
        lat:"",
        lng:""
      }
    };

/* Services */
var RestaurantFactory = {
  getRestaurants: function(data, output) {
    $.ajax({
      url: 'http://localhost:1234/api' + '/restaurant ',
      method: 'GET',
      data: {lat: data.location.lat,
            lng: data.location.lng},
      success: function(response) {
        output(response);
      }
    })
  }
};

function initMap() {
  defaultPosition = new google.maps.LatLng(46.051343, 14.506293);
  map = new google.maps.Map(document.getElementById('map'), {
    zoom: 16,
    center: defaultPosition,
    mapTypeId: 'roadmap'
  });

}

function init() {

  /* Get restaurants */
  RestaurantFactory.getRestaurants("", function(response){
    Restaurants = response;

    $.Mustache.load('templates/restaurants.html')
      .done(function () {
        $('#test-restaurants').mustache('restavracije', Restaurants);
      });

  });

  /* scoll header */
  window.addEventListener('scroll', function(e){
    var distanceY = window.pageYOffset || document.documentElement.scrollTop,
      shrinkOn = 150,
      header = document.querySelector("header");

    if (distanceY > shrinkOn) {
      classie.add(header,"smaller");
    } else {
      if (classie.has(header,"smaller")) {
        classie.remove(header,"smaller");
      }
    }
  });

  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(showOnMap);
  } else {
    console.log("Geolocation is not supported by this browser.");
  }

  // else 1 default to city center, Ljubljana
  // else 2 ask use for location
  //46.051343, 14.506293
  function showOnMap(position) {
    data.location.lat = position.coords.latitude;
    data.location.lng = position.coords.longitude;

    /*
    console.log("User: " + "lat: " + position.coords.latitude +
      " lng: " + position.coords.longitude);
    */

    userLocation = new google.maps.LatLng(position.coords.latitude,  position.coords.longitude);
    map.setCenter(userLocation);

    var marker = new google.maps.Marker({
      position: userLocation,
      map: map,
      icon: '../app/images/me-marker.png'
    });

    // add markers
    var infowindow = new google.maps.InfoWindow(),
        marker,
        i;

    for (i = 0; i < Restaurants.length; i++) {

       marker = new google.maps.Marker({
         position: new google.maps.LatLng(Restaurants[i].location.lat, Restaurants[i].location.lng),
         map: map,
         icon: '../app/images/map-marker.png'
       });

       google.maps.event.addListener(marker, 'click', (
         function(marker, i) {
           return function() {
             infowindow.setContent(Restaurants[i].name);
             infowindow.open(map, marker);
           }
        })(marker, i));
     }

    RestaurantFactory.getRestaurants(data, function(response){
      Restaurants = response;

      $.Mustache.load('templates/restaurants.html')
        .done(function () {
          $('#test-restaurants').mustache('restavracije', Restaurants);
        });

    });
  }
}

window.onload = init();
<|endoftext|>


<commit_before>/* global build_map, getAPIData, Awesomplete */
/* exported SuburbKmFilter */

// Globals
var arrSuburbs = [];
var userResults = {};
var arrAnchors = []; // Stores anchor strings for FullPage.js nav
var arrToolTips = []; // Stores tooltips for FullPage.js nav
var SuburbKmFilter = 5.0; // (decimal) filter active suburbs by this
var arrMapColours = ['#fafa6e', '#2A4858', '#000'];
var suburbcombo;

var build_results = function() {
  $('#questions .slide').each(function(index, item) {
    if (this === undefined) {
    console.log("Missing data-contect on slide");
    } else {
      userResults[$(this).attr('data-context')] = null;
    }
  });
};

var hide_preloader = function() {
  $('body').removeClass('loading');
  $('#pre-loader').addClass('hidden');
};

(function() {
  'use strict';

  // Check to make sure service workers are supported in the current browser,
  // and that the current page is accessed from a secure origin. Using a
  // service worker from an insecure origin will trigger JS console errors. See
  // http://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features
  var isLocalhost = Boolean(window.location.hostname === 'localhost' ||
      // [::1] is the IPv6 localhost address.
      window.location.hostname === '[::1]' ||
      // 127.0.0.1/8 is considered localhost for IPv4.
      window.location.hostname.match(
        /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
      )
    );

  if ('serviceWorker' in navigator &&
      (window.location.protocol === 'https:' || isLocalhost)) {
    navigator.serviceWorker.register('service-worker.js')
    .then(function(registration) {
      // updatefound is fired if service-worker.js changes.
      registration.onupdatefound = function() {
        // updatefound is also fired the very first time the SW is installed,
        // and there's no need to prompt for a reload at that point.
        // So check here to see if the page is already controlled,
        // i.e. whether there's an existing service worker.
        if (navigator.serviceWorker.controller) {
          // The updatefound event implies that registration.installing is set:
          // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
          var installingWorker = registration.installing;

          installingWorker.onstatechange = function() {
            switch (installingWorker.state) {
              case 'installed':
                // At this point, the old content will have been purged and the
                // fresh content will have been added to the cache.
                // It's the perfect time to display a "New content is
                // available; please refresh." message in the page's interface.
                break;

              case 'redundant':
                throw new Error('The installing ' +
                                'service worker became redundant.');

              default:
                // Ignore
            }
          };
        }
      };
    }).catch(function(e) {
      console.error('Error during service worker registration:', e);
    });
  }

  var get_suburb_data = function() {
     getAPIData('/suburbs?filter=5', function(jqXHR, settings) {
       /* beforeSend */
     }, function(data, textStatus, jqXHR) {
       /* success */
       $.each(data, function(index, item) {
         var obj = new Object();
         obj.SuburbName = item.Suburb;
         obj.Postcode = item.Postcode;
         obj.HousePrice = item.HousePrice;
         obj.UnitPrice = item.UnitPrice;
         if (obj.HousePrice === 0 || obj.UnitPrice === 0) {
          obj.MedianPrice = toInteger(parseInt(obj.HousePrice, 10) + parseInt(obj.UnitPrice, 10));
         } else {
          obj.MedianPrice = toInteger((parseInt(obj.HousePrice, 10) + parseInt(obj.UnitPrice, 10)) / 2);
         }
         obj.Distance = item.Distance;
         obj.Latitude = item.Latitude;
         obj.Longitude = item.Longitude;
         obj.active = false;
         obj.color = "#000";
         arrSuburbs.push(obj);
       });
       // Dropdown validation
       setup_validation();
       build_map(false); // init Map
       // hide_preloader();
     });
   };

   function setup_validation() {
    // Input suburb list into
    var strPattern = "";
    $.each(arrSuburbs, function(index, item) {
      $("#questions .input-container #listsuburbs").append("
" + item.SuburbName + "
");
      strPattern = strPattern + item.SuburbName + "|";
    });
    strPattern = strPattern.slice(0, -1); // trim last |
    var suburbInput = document.getElementById("input_suburbname");
    $(suburbInput).attr("pattern", strPattern);
    suburbcombo = new Awesomplete(suburbInput, {
      list: "#listsuburbs",
      minChars: 1
    });
    // on selection force event fire
    $('#input_suburbname').on('awesomplete-selectcomplete', function() {
      $('#input_suburbname').trigger('input');
    });
   }

   // Nav Generator
   var generate_nav = function(arrAnchors, arrToolTips) {
     var sectionCount = $('.section').length; // Int of how many sections
     var i;
     for (i = 0; i < sectionCount; i++) {
       var newElement = $('
<button type="button" value="' + (i + 1) + '"></button>' + $('.section:nth-of-type(' + (i + 1) + ')').attr('data-tooltip') + '
');
       $('nav ul').append(newElement);
       arrAnchors.push("Section-" + (i + 1)); // Add to string array
       arrToolTips.push($('.section:nth-of-type(' + (i + 1) + ')').attr('data-tooltip'));
       var navBtnColor = $('.section:nth-of-type(' + (i + 1) + ')').attr('data-navcolour'); // get data attribute value
       if (navBtnColor) {
         newElement.css('border-color', navBtnColor); // add border color to the parent.
       } else {
         console.log("var navBtnColor undefined");
       }
     }
     $('nav li:first-of-type').addClass('active');
   };

    var build_sliders = function() {
    var rangeSalary = document.getElementById('rangeSalary');
    var rangeCoffee = document.getElementById('rangeCoffee');
    var rangeAvo = document.getElementById('rangeAvo');

    noUiSlider.create(rangeSalary, {
      start: 5000,
      step: 5000,
      range: {
        min: 5000,
        max: 200000
      },
      pips: {
        mode: 'range',
        density: 3,
        format: wNumb({
          decimals: 0,
          prefix: '$'
        })
      },
      format: wNumb({
        decimals: 0,
        thousand: ',',
        prefix: '$ '
      })
    });
      rangeSalary.noUiSlider.on('update', function(values, handle) {
        var parent = $(rangeSalary).parent();
        $(parent).find('.value').html(values[handle]);
      });

      noUiSlider.create(rangeCoffee, {
        start: 0,
        step: 1,
        range: {
          min: 0,
          max: 20
        },
        pips: {
          mode: 'range',
          density: 3,
          format: wNumb({
            decimals: 0,
          })
        },
        format: wNumb({
          decimals: 0,
          thousand: ','
        })
      });
        rangeCoffee.noUiSlider.on('update', function(values, handle) {
          var parent = $(rangeCoffee).parent();
          $(parent).find('.value').html(values[handle]);
        });

      noUiSlider.create(rangeAvo, {
        start: 0,
        step: 1,
        range: {
          min: 0,
          max: 20
        },
        pips: {
          mode: 'range',
          density: 3,
          format: wNumb({
            decimals: 0,
          })
        },
        format: wNumb({
          decimals: 0,
          thousand: ','
        })
      });
        rangeAvo.noUiSlider.on('update', function(values, handle) {
          var parent = $(rangeAvo).parent();
          $(parent).find('.value').html(values[handle]);
        });
      };

    // slider for coffee

   function toInteger(number) {
     return Math.round(  // round to nearest integer
       Number(number)    // type cast your input
     );
   }

  $(document).ready(function() { // loading done
    generate_nav(arrAnchors, arrToolTips);
    // Init Fullpage JS
    $('#fullpage').fullpage({
        lockAnchors: true,
        menu: '#main-nav',
        anchors: arrAnchors,
        navigationTooltips: arrToolTips,
        scrollBar: true,
        slidesNavigation: true,
        controlArrows: false,
        loopHorizontal: true
    });
    get_suburb_data();
    build_results();
    build_sliders();
  });
})();
<commit_msg>enable hide again<commit_after>/* global build_map, getAPIData, Awesomplete */
/* exported SuburbKmFilter */

// Globals
var arrSuburbs = [];
var userResults = {};
var arrAnchors = []; // Stores anchor strings for FullPage.js nav
var arrToolTips = []; // Stores tooltips for FullPage.js nav
var SuburbKmFilter = 5.0; // (decimal) filter active suburbs by this
var arrMapColours = ['#fafa6e', '#2A4858', '#000'];
var suburbcombo;

var build_results = function() {
  $('#questions .slide').each(function(index, item) {
    if (this === undefined) {
    console.log("Missing data-contect on slide");
    } else {
      userResults[$(this).attr('data-context')] = null;
    }
  });
};

var hide_preloader = function() {
  $('body').removeClass('loading');
  $('#pre-loader').addClass('hidden');
};

(function() {
  'use strict';

  // Check to make sure service workers are supported in the current browser,
  // and that the current page is accessed from a secure origin. Using a
  // service worker from an insecure origin will trigger JS console errors. See
  // http://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features
  var isLocalhost = Boolean(window.location.hostname === 'localhost' ||
      // [::1] is the IPv6 localhost address.
      window.location.hostname === '[::1]' ||
      // 127.0.0.1/8 is considered localhost for IPv4.
      window.location.hostname.match(
        /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
      )
    );

  if ('serviceWorker' in navigator &&
      (window.location.protocol === 'https:' || isLocalhost)) {
    navigator.serviceWorker.register('service-worker.js')
    .then(function(registration) {
      // updatefound is fired if service-worker.js changes.
      registration.onupdatefound = function() {
        // updatefound is also fired the very first time the SW is installed,
        // and there's no need to prompt for a reload at that point.
        // So check here to see if the page is already controlled,
        // i.e. whether there's an existing service worker.
        if (navigator.serviceWorker.controller) {
          // The updatefound event implies that registration.installing is set:
          // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
          var installingWorker = registration.installing;

          installingWorker.onstatechange = function() {
            switch (installingWorker.state) {
              case 'installed':
                // At this point, the old content will have been purged and the
                // fresh content will have been added to the cache.
                // It's the perfect time to display a "New content is
                // available; please refresh." message in the page's interface.
                break;

              case 'redundant':
                throw new Error('The installing ' +
                                'service worker became redundant.');

              default:
                // Ignore
            }
          };
        }
      };
    }).catch(function(e) {
      console.error('Error during service worker registration:', e);
    });
  }

  var get_suburb_data = function() {
     getAPIData('/suburbs?filter=5', function(jqXHR, settings) {
       /* beforeSend */
     }, function(data, textStatus, jqXHR) {
       /* success */
       $.each(data, function(index, item) {
         var obj = new Object();
         obj.SuburbName = item.Suburb;
         obj.Postcode = item.Postcode;
         obj.HousePrice = item.HousePrice;
         obj.UnitPrice = item.UnitPrice;
         if (obj.HousePrice === 0 || obj.UnitPrice === 0) {
          obj.MedianPrice = toInteger(parseInt(obj.HousePrice, 10) + parseInt(obj.UnitPrice, 10));
         } else {
          obj.MedianPrice = toInteger((parseInt(obj.HousePrice, 10) + parseInt(obj.UnitPrice, 10)) / 2);
         }
         obj.Distance = item.Distance;
         obj.Latitude = item.Latitude;
         obj.Longitude = item.Longitude;
         obj.active = false;
         obj.color = "#000";
         arrSuburbs.push(obj);
       });
       // Dropdown validation
       setup_validation();
       build_map(false); // init Map
       hide_preloader();
     });
   };

   function setup_validation() {
    // Input suburb list into
    var strPattern = "";
    $.each(arrSuburbs, function(index, item) {
      $("#questions .input-container #listsuburbs").append("
" + item.SuburbName + "
");
      strPattern = strPattern + item.SuburbName + "|";
    });
    strPattern = strPattern.slice(0, -1); // trim last |
    var suburbInput = document.getElementById("input_suburbname");
    $(suburbInput).attr("pattern", strPattern);
    suburbcombo = new Awesomplete(suburbInput, {
      list: "#listsuburbs",
      minChars: 1
    });
    // on selection force event fire
    $('#input_suburbname').on('awesomplete-selectcomplete', function() {
      $('#input_suburbname').trigger('input');
    });
   }

   // Nav Generator
   var generate_nav = function(arrAnchors, arrToolTips) {
     var sectionCount = $('.section').length; // Int of how many sections
     var i;
     for (i = 0; i < sectionCount; i++) {
       var newElement = $('
<button type="button" value="' + (i + 1) + '"></button>' + $('.section:nth-of-type(' + (i + 1) + ')').attr('data-tooltip') + '
');
       $('nav ul').append(newElement);
       arrAnchors.push("Section-" + (i + 1)); // Add to string array
       arrToolTips.push($('.section:nth-of-type(' + (i + 1) + ')').attr('data-tooltip'));
       var navBtnColor = $('.section:nth-of-type(' + (i + 1) + ')').attr('data-navcolour'); // get data attribute value
       if (navBtnColor) {
         newElement.css('border-color', navBtnColor); // add border color to the parent.
       } else {
         console.log("var navBtnColor undefined");
       }
     }
     $('nav li:first-of-type').addClass('active');
   };

    var build_sliders = function() {
    var rangeSalary = document.getElementById('rangeSalary');
    var rangeCoffee = document.getElementById('rangeCoffee');
    var rangeAvo = document.getElementById('rangeAvo');

    noUiSlider.create(rangeSalary, {
      start: 5000,
      step: 5000,
      range: {
        min: 5000,
        max: 200000
      },
      pips: {
        mode: 'range',
        density: 3,
        format: wNumb({
          decimals: 0,
          prefix: '$'
        })
      },
      format: wNumb({
        decimals: 0,
        thousand: ',',
        prefix: '$ '
      })
    });
      rangeSalary.noUiSlider.on('update', function(values, handle) {
        var parent = $(rangeSalary).parent();
        $(parent).find('.value').html(values[handle]);
      });

      noUiSlider.create(rangeCoffee, {
        start: 0,
        step: 1,
        range: {
          min: 0,
          max: 20
        },
        pips: {
          mode: 'range',
          density: 3,
          format: wNumb({
            decimals: 0,
          })
        },
        format: wNumb({
          decimals: 0,
          thousand: ','
        })
      });
        rangeCoffee.noUiSlider.on('update', function(values, handle) {
          var parent = $(rangeCoffee).parent();
          $(parent).find('.value').html(values[handle]);
        });

      noUiSlider.create(rangeAvo, {
        start: 0,
        step: 1,
        range: {
          min: 0,
          max: 20
        },
        pips: {
          mode: 'range',
          density: 3,
          format: wNumb({
            decimals: 0,
          })
        },
        format: wNumb({
          decimals: 0,
          thousand: ','
        })
      });
        rangeAvo.noUiSlider.on('update', function(values, handle) {
          var parent = $(rangeAvo).parent();
          $(parent).find('.value').html(values[handle]);
        });
      };

    // slider for coffee

   function toInteger(number) {
     return Math.round(  // round to nearest integer
       Number(number)    // type cast your input
     );
   }

  $(document).ready(function() { // loading done
    generate_nav(arrAnchors, arrToolTips);
    // Init Fullpage JS
    $('#fullpage').fullpage({
        lockAnchors: true,
        menu: '#main-nav',
        anchors: arrAnchors,
        navigationTooltips: arrToolTips,
        scrollBar: true,
        slidesNavigation: true,
        controlArrows: false,
        loopHorizontal: true
    });
    get_suburb_data();
    build_results();
    build_sliders();
  });
})();
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict'

// Session store in Brave works as follows:
// - Electron sends a ‘before-quit’ event
// - Brave sends REQUEST_WINDOW_STATE to each renderer process
// - Each renderer responds with its window state with a RESPONSE_WINDOW_STATE IPC message
// - When all state is collected save it to a JSON file and close the app
// - NODE_ENV of ‘test’ bypassing session state or else they all fail.

const fs = require('fs')
const path = require('path')
const electron = require('electron')
const app = electron.app
const UpdateStatus = require('../js/constants/updateStatus')
const settings = require('../js/constants/settings')
const downloadStates = require('../js/constants/downloadStates')
const sessionStorageVersion = 1
let suffix = ''
if (process.env.NODE_ENV === 'development') {
  suffix = '-dev'
}
const sessionStorageName = `session-store-${sessionStorageVersion}${suffix}`
const storagePath = process.env.NODE_ENV !== 'test'
  ? path.join(app.getPath('userData'), sessionStorageName)
  : path.join(process.env.HOME, '.brave-test-session-store-1')
const getSetting = require('../js/settings').getSetting
const promisify = require('../js/lib/promisify')

/**
 * Saves the specified immutable browser state to storage.
 *
 * @param {object} payload - Application state as per
 *   https://github.com/brave/browser/wiki/Application-State
 *   (not immutable data)
 * @return a promise which resolves when the state is saved
 */
module.exports.saveAppState = (payload) => {
  return new Promise((resolve, reject) => {
    // Don't persist private frames
    let startupModeSettingValue
    if (require('../js/stores/appStore').getState()) {
      startupModeSettingValue = getSetting(settings.STARTUP_MODE)
    }
    const savePerWindowState = startupModeSettingValue === undefined ||
      startupModeSettingValue === 'lastTime'
    if (payload.perWindowState && savePerWindowState) {
      payload.perWindowState.forEach((wndPayload) => {
        wndPayload.frames = wndPayload.frames.filter((frame) => !frame.isPrivate)
      })
    } else {
      delete payload.perWindowState
    }

    try {
      module.exports.cleanAppData(payload)
      payload.cleanedOnShutdown = true
    } catch (e) {
      payload.cleanedOnShutdown = false
    }

    const epochTimestamp = (new Date()).getTime().toString()
    const tmpStoragePath = process.env.NODE_ENV !== 'test'
      ? path.join(app.getPath('userData'), 'session-store-tmp-' + epochTimestamp)
      : path.join(process.env.HOME, '.brave-test-session-store-tmp-' + epochTimestamp)

    promisify(fs.writeFile, tmpStoragePath, JSON.stringify(payload))
      .then(() => promisify(fs.rename, tmpStoragePath, storagePath))
      .then(() => resolve())
      .catch((err) => reject(err))
  })
}

/**
 * Cleans session data from unwanted values.
 */
module.exports.cleanSessionData = (sessionData) => {
  if (!sessionData) {
    sessionData = {}
  }
  // Hide the context menu when we restore.
  delete sessionData.contextMenuDetail
  // Don't save preview frame since they are only related to hovering on a tab
  delete sessionData.previewFrameKey
  // Don't restore add/edit dialog
  delete sessionData.bookmarkDetail
  // Don't restore bravery panel
  delete sessionData.braveryPanelDetail
  // Don't restore drag data
  if (sessionData.ui) {
    delete sessionData.ui.dragging
  }
  sessionData.frames = sessionData.frames || []
  let newKey = 0
  const cleanFrame = (frame) => {
    newKey++
    // Reset the ids back to sequential numbers
    if (frame.key === sessionData.activeFrameKey) {
      sessionData.activeFrameKey = newKey
    } else {
      // For now just set everything to unloaded unless it's the active frame
      frame.unloaded = true
    }
    frame.key = newKey
    // Full history is not saved yet
    frame.canGoBack = false
    frame.canGoForward = false

    // Set the frame src to the last visited location
    // or else users will see the first visited URL.
    // Pinned location always get reset to what they are
    frame.src = frame.pinnedLocation || frame.location

    // If a blob is present for the thumbnail, create the object URL
    if (frame.thumbnailBlob) {
      try {
        frame.thumbnailUrl = window.URL.createObjectURL(frame.thumbnailBlob)
      } catch (e) {
        delete frame.thumbnailUrl
      }
    }

    // Delete lists of blocked sites
    delete frame.trackingProtection
    delete frame.httpsEverywhere
    delete frame.adblock
    delete frame.noScript
    delete frame.trackingProtection

    // Guest instance ID's are not valid after restarting.
    // Electron won't know about them.
    delete frame.guestInstanceId

    // Tab ids are per-session and should not be persisted
    delete frame.tabId

    // Do not show the audio indicator until audio starts playing
    delete frame.audioMuted
    delete frame.audioPlaybackActive
    // Let's not assume wknow anything about loading
    delete frame.loading
    // Always re-determine the security data
    delete frame.security
    // Value is only used for local storage
    delete frame.isActive
    // Hide modal prompts.
    delete frame.modalPromptDetail
    // Remove HTTP basic authentication requests.
    delete frame.basicAuthDetail
    // Remove open search details
    delete frame.searchDetail
    // Remove find in page details
    if (frame.findDetail) {
      delete frame.findDetail.numberOfMatches
      delete frame.findDetail.activeMatchOrdinal
    }
    delete frame.findbarShown
    // Don't restore full screen state
    delete frame.isFullScreen
    delete frame.showFullScreenWarning
    // Don't store child tab open ordering since keys
    // currently get re-generated when session store is
    // restored.  We will be able to keep this once we
    // don't regenerate new frame keys when opening storage.
    delete frame.parentFrameKey
    // Delete the active shortcut details
    delete frame.activeShortcutDetails

    if (frame.navbar && frame.navbar.urlbar) {
      frame.navbar.urlbar.urlPreview = null
      if (frame.navbar.urlbar.suggestions) {
        frame.navbar.urlbar.suggestions.selectedIndex = null
        frame.navbar.urlbar.suggestions.suggestionList = null
      }
    }
  }

  // Clean closed frame data before frames because the keys are re-ordered
  // and the new next key is calculated in windowStore.js based on
  // the max frame key ID.
  if (sessionData.closedFrames) {
    sessionData.closedFrames.forEach(cleanFrame)
  }
  if (sessionData.frames) {
    // Don't restore pinned locations because they will be auto created by the app state change event
    sessionData.frames = sessionData.frames
      .filter((frame) => !frame.pinnedLocation)
    sessionData.frames.forEach(cleanFrame)
  }
}

/**
 * Cleans app data before it's written to disk.
 * @param {Object} data - top-level app data
 */
module.exports.cleanAppData = (data) => {
  if (data.settings) {
    // useragent value gets recalculated on restart
    data.settings[settings.USERAGENT] = undefined
  }
  // Don't show notifications from the last session
  data.notifications = []
  // Delete temp site settings
  data.temporarySiteSettings = {}
  // Delete Flash state since this is checked on startup
  delete data.flashInstalled
  // We used to store a huge list of IDs but we didn't use them.
  // Get rid of them here.
  delete data.windows
  if (data.perWindowState) {
    data.perWindowState.forEach(module.exports.cleanSessionData)
  }
}

/**
 * Loads the browser state from storage.
 *
 * @return a promise which resolves with the immutable browser state or
 * rejects if the state cannot be loaded.
 */
module.exports.loadAppState = () => {
  return new Promise((resolve, reject) => {
    let data
    try {
      data = fs.readFileSync(storagePath)
    } catch (e) {
    }

    if (!data) {
      reject()
      return
    }

    try {
      data = Object.assign(module.exports.defaultAppState(), JSON.parse(data))
    } catch (e) {
      // TODO: Session state is corrupted, maybe we should backup this
      // corrupted value for people to report into support.
      console.log('could not parse data: ', data)
      reject(e)
      return
    }
    // Clean app data here if it wasn't cleared on shutdown
    if (data.cleanedOnShutdown !== true) {
      module.exports.cleanAppData(data)
    }
    data.cleanedOnShutdown = false
    // Always recalculate the update status
    if (data.updates) {
      const updateStatus = data.updates.status
      delete data.updates.status
      // The process always restarts after an update so if the state
      // indicates that a restart isn't wanted, close right away.
      if (updateStatus === UpdateStatus.UPDATE_APPLYING_NO_RESTART) {
        module.exports.saveAppState(data).then(() => {
          // Exit immediately without doing the session store saving stuff
          // since we want the same state saved except for the update status
          app.exit(0)
        })
        return
      }
    }
    // Delete downloaded items older than a week
    if (data.downloads) {
      const dateOffset = 7 * 24 * 60 * 60 * 1000
      const lastWeek = new Date().getTime() - dateOffset
      Object.keys(data.downloads).forEach((downloadId) => {
        if (data.downloads[downloadId].startTime < lastWeek) {
          delete data.downloads[downloadId]
        } else {
          const state = data.downloads[downloadId].state
          if (state === downloadStates.IN_PROGRESS || state === downloadStates.PAUSED) {
            data.downloads[downloadId].state = downloadStates.INTERRUPTED
          }
        }
      })
    }
    resolve(data)
  })
}

/**
 * Obtains the default application level state
 */
module.exports.defaultAppState = () => {
  return {
    sites: [],
    visits: [],
    settings: {},
    siteSettings: {},
    passwords: [],
    notifications: [],
    temporarySiteSettings: {},
    dictionary: {
      addedWords: [],
      ignoredWords: []
    }
  }
}
<commit_msg>Simplify resolve/catch calls<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict'

// Session store in Brave works as follows:
// - Electron sends a ‘before-quit’ event
// - Brave sends REQUEST_WINDOW_STATE to each renderer process
// - Each renderer responds with its window state with a RESPONSE_WINDOW_STATE IPC message
// - When all state is collected save it to a JSON file and close the app
// - NODE_ENV of ‘test’ bypassing session state or else they all fail.

const fs = require('fs')
const path = require('path')
const electron = require('electron')
const app = electron.app
const UpdateStatus = require('../js/constants/updateStatus')
const settings = require('../js/constants/settings')
const downloadStates = require('../js/constants/downloadStates')
const sessionStorageVersion = 1
let suffix = ''
if (process.env.NODE_ENV === 'development') {
  suffix = '-dev'
}
const sessionStorageName = `session-store-${sessionStorageVersion}${suffix}`
const storagePath = process.env.NODE_ENV !== 'test'
  ? path.join(app.getPath('userData'), sessionStorageName)
  : path.join(process.env.HOME, '.brave-test-session-store-1')
const getSetting = require('../js/settings').getSetting
const promisify = require('../js/lib/promisify')

/**
 * Saves the specified immutable browser state to storage.
 *
 * @param {object} payload - Application state as per
 *   https://github.com/brave/browser/wiki/Application-State
 *   (not immutable data)
 * @return a promise which resolves when the state is saved
 */
module.exports.saveAppState = (payload) => {
  return new Promise((resolve, reject) => {
    // Don't persist private frames
    let startupModeSettingValue
    if (require('../js/stores/appStore').getState()) {
      startupModeSettingValue = getSetting(settings.STARTUP_MODE)
    }
    const savePerWindowState = startupModeSettingValue === undefined ||
      startupModeSettingValue === 'lastTime'
    if (payload.perWindowState && savePerWindowState) {
      payload.perWindowState.forEach((wndPayload) => {
        wndPayload.frames = wndPayload.frames.filter((frame) => !frame.isPrivate)
      })
    } else {
      delete payload.perWindowState
    }

    try {
      module.exports.cleanAppData(payload)
      payload.cleanedOnShutdown = true
    } catch (e) {
      payload.cleanedOnShutdown = false
    }

    const epochTimestamp = (new Date()).getTime().toString()
    const tmpStoragePath = process.env.NODE_ENV !== 'test'
      ? path.join(app.getPath('userData'), 'session-store-tmp-' + epochTimestamp)
      : path.join(process.env.HOME, '.brave-test-session-store-tmp-' + epochTimestamp)

    promisify(fs.writeFile, tmpStoragePath, JSON.stringify(payload))
      .then(() => promisify(fs.rename, tmpStoragePath, storagePath))
      .then(resolve)
      .catch(reject)
  })
}

/**
 * Cleans session data from unwanted values.
 */
module.exports.cleanSessionData = (sessionData) => {
  if (!sessionData) {
    sessionData = {}
  }
  // Hide the context menu when we restore.
  delete sessionData.contextMenuDetail
  // Don't save preview frame since they are only related to hovering on a tab
  delete sessionData.previewFrameKey
  // Don't restore add/edit dialog
  delete sessionData.bookmarkDetail
  // Don't restore bravery panel
  delete sessionData.braveryPanelDetail
  // Don't restore drag data
  if (sessionData.ui) {
    delete sessionData.ui.dragging
  }
  sessionData.frames = sessionData.frames || []
  let newKey = 0
  const cleanFrame = (frame) => {
    newKey++
    // Reset the ids back to sequential numbers
    if (frame.key === sessionData.activeFrameKey) {
      sessionData.activeFrameKey = newKey
    } else {
      // For now just set everything to unloaded unless it's the active frame
      frame.unloaded = true
    }
    frame.key = newKey
    // Full history is not saved yet
    frame.canGoBack = false
    frame.canGoForward = false

    // Set the frame src to the last visited location
    // or else users will see the first visited URL.
    // Pinned location always get reset to what they are
    frame.src = frame.pinnedLocation || frame.location

    // If a blob is present for the thumbnail, create the object URL
    if (frame.thumbnailBlob) {
      try {
        frame.thumbnailUrl = window.URL.createObjectURL(frame.thumbnailBlob)
      } catch (e) {
        delete frame.thumbnailUrl
      }
    }

    // Delete lists of blocked sites
    delete frame.trackingProtection
    delete frame.httpsEverywhere
    delete frame.adblock
    delete frame.noScript
    delete frame.trackingProtection

    // Guest instance ID's are not valid after restarting.
    // Electron won't know about them.
    delete frame.guestInstanceId

    // Tab ids are per-session and should not be persisted
    delete frame.tabId

    // Do not show the audio indicator until audio starts playing
    delete frame.audioMuted
    delete frame.audioPlaybackActive
    // Let's not assume wknow anything about loading
    delete frame.loading
    // Always re-determine the security data
    delete frame.security
    // Value is only used for local storage
    delete frame.isActive
    // Hide modal prompts.
    delete frame.modalPromptDetail
    // Remove HTTP basic authentication requests.
    delete frame.basicAuthDetail
    // Remove open search details
    delete frame.searchDetail
    // Remove find in page details
    if (frame.findDetail) {
      delete frame.findDetail.numberOfMatches
      delete frame.findDetail.activeMatchOrdinal
    }
    delete frame.findbarShown
    // Don't restore full screen state
    delete frame.isFullScreen
    delete frame.showFullScreenWarning
    // Don't store child tab open ordering since keys
    // currently get re-generated when session store is
    // restored.  We will be able to keep this once we
    // don't regenerate new frame keys when opening storage.
    delete frame.parentFrameKey
    // Delete the active shortcut details
    delete frame.activeShortcutDetails

    if (frame.navbar && frame.navbar.urlbar) {
      frame.navbar.urlbar.urlPreview = null
      if (frame.navbar.urlbar.suggestions) {
        frame.navbar.urlbar.suggestions.selectedIndex = null
        frame.navbar.urlbar.suggestions.suggestionList = null
      }
    }
  }

  // Clean closed frame data before frames because the keys are re-ordered
  // and the new next key is calculated in windowStore.js based on
  // the max frame key ID.
  if (sessionData.closedFrames) {
    sessionData.closedFrames.forEach(cleanFrame)
  }
  if (sessionData.frames) {
    // Don't restore pinned locations because they will be auto created by the app state change event
    sessionData.frames = sessionData.frames
      .filter((frame) => !frame.pinnedLocation)
    sessionData.frames.forEach(cleanFrame)
  }
}

/**
 * Cleans app data before it's written to disk.
 * @param {Object} data - top-level app data
 */
module.exports.cleanAppData = (data) => {
  if (data.settings) {
    // useragent value gets recalculated on restart
    data.settings[settings.USERAGENT] = undefined
  }
  // Don't show notifications from the last session
  data.notifications = []
  // Delete temp site settings
  data.temporarySiteSettings = {}
  // Delete Flash state since this is checked on startup
  delete data.flashInstalled
  // We used to store a huge list of IDs but we didn't use them.
  // Get rid of them here.
  delete data.windows
  if (data.perWindowState) {
    data.perWindowState.forEach(module.exports.cleanSessionData)
  }
}

/**
 * Loads the browser state from storage.
 *
 * @return a promise which resolves with the immutable browser state or
 * rejects if the state cannot be loaded.
 */
module.exports.loadAppState = () => {
  return new Promise((resolve, reject) => {
    let data
    try {
      data = fs.readFileSync(storagePath)
    } catch (e) {
    }

    if (!data) {
      reject()
      return
    }

    try {
      data = Object.assign(module.exports.defaultAppState(), JSON.parse(data))
    } catch (e) {
      // TODO: Session state is corrupted, maybe we should backup this
      // corrupted value for people to report into support.
      console.log('could not parse data: ', data)
      reject(e)
      return
    }
    // Clean app data here if it wasn't cleared on shutdown
    if (data.cleanedOnShutdown !== true) {
      module.exports.cleanAppData(data)
    }
    data.cleanedOnShutdown = false
    // Always recalculate the update status
    if (data.updates) {
      const updateStatus = data.updates.status
      delete data.updates.status
      // The process always restarts after an update so if the state
      // indicates that a restart isn't wanted, close right away.
      if (updateStatus === UpdateStatus.UPDATE_APPLYING_NO_RESTART) {
        module.exports.saveAppState(data).then(() => {
          // Exit immediately without doing the session store saving stuff
          // since we want the same state saved except for the update status
          app.exit(0)
        })
        return
      }
    }
    // Delete downloaded items older than a week
    if (data.downloads) {
      const dateOffset = 7 * 24 * 60 * 60 * 1000
      const lastWeek = new Date().getTime() - dateOffset
      Object.keys(data.downloads).forEach((downloadId) => {
        if (data.downloads[downloadId].startTime < lastWeek) {
          delete data.downloads[downloadId]
        } else {
          const state = data.downloads[downloadId].state
          if (state === downloadStates.IN_PROGRESS || state === downloadStates.PAUSED) {
            data.downloads[downloadId].state = downloadStates.INTERRUPTED
          }
        }
      })
    }
    resolve(data)
  })
}

/**
 * Obtains the default application level state
 */
module.exports.defaultAppState = () => {
  return {
    sites: [],
    visits: [],
    settings: {},
    siteSettings: {},
    passwords: [],
    notifications: [],
    temporarySiteSettings: {},
    dictionary: {
      addedWords: [],
      ignoredWords: []
    }
  }
}
<|endoftext|>


<commit_before>import React from 'react'

export default class BookList extends React.Component {
  renderBook(book) {
    const {title, author, year, description} = book

    return (

{title}

{author} - {year}

              {description}

            Show
            Edit
            Delete

    )
  }
  render() {
    return (

          { this.props.books.map((book) => this.renderBook(book)) }

    )
  }
}<commit_msg>Update BookList.js<commit_after>import React from 'react'

export default class BookList extends React.Component {
  renderBook(book) {
    const {title, author, year, description} = book

    return (

{title}

{author} - {year}

              {description}

            Show
            Edit
            Delete

    )
  }
  render() {
    return (

          { this.props.books.map((book) => this.renderBook(book)) }

    )
  }
}
<|endoftext|>


<commit_before>"use strict";

/**************************
 * Import important stuff *
 **************************/

const Discord = require("discord.js");
const md2html = require("./md2html");
const MessageMap = require("../MessageMap");
const LatestDiscordMessageIds = require("./LatestDiscordMessageIds");
const handleEmbed = require("./handleEmbed");
const relayOldMessages = require("./relayOldMessages");
const Bridge = require("../bridgestuff/Bridge");
const path = require("path");
const R = require("ramda");
const { sleepOneMinute } = require("../sleep");
const helpers = require("./helpers");
const fetchDiscordChannel = require("../fetchDiscordChannel");

/***********
 * Helpers *
 ***********/

/**
 * Creates a function to give to 'guildMemberAdd' or 'guildMemberRemove' on a Discord bot
 *
 * @param {Logger} logger	The Logger instance to log messages to
 * @param {String} verb	Either "joined" or "left"
 * @param {BridgeMap} bridgeMap	Map of existing bridges
 * @param {Telegraf} tgBot	The Telegram bot to send the messages to
 *
 * @returns {Function}	Function which can be given to the 'guildMemberAdd' or 'guildMemberRemove' events of a Discord bot
 *
 * @private
 */
function makeJoinLeaveFunc(logger, verb, bridgeMap, tgBot) {
	// Find out which setting property to check the bridges for
	const relaySetting = verb === "joined" ? "relayJoinMessages" : "relayLeaveMessages";
	return function (member) {
		// Get the bridges in the guild the member joined/left
		member.guild.channels
			// Get the bridges corresponding to the channels in this guild
			.map(({ id }) => bridgeMap.fromDiscordChannelId(id))
			// Remove the ones which are not bridged
			.filter(bridges => bridges !== undefined)
			// Flatten the bridge arrays
			.reduce((flattened, bridges) => flattened.concat(bridges))
			// Remove those which do not allow relaying join messages
			.filter(bridge => bridge.discord[relaySetting])
			// Ignore the T2D bridges
			.filter(bridge => bridge.direction !== Bridge.DIRECTION_TELEGRAM_TO_DISCORD)
			.forEach(async bridge => {
				// Make the text to send
				const text = `<b>${member.displayName} (@${member.user.username})</b> ${verb} the Discord side of the chat`;

				try {
					// Send it
					await tgBot.telegram.sendMessage(bridge.telegram.chatId, text, {
						parse_mode: "HTML"
					});
				} catch (err) {
					logger.error(
						`[${bridge.name}] Could not notify Telegram about a user that ${verb} Discord`,
						err
					);
				}
			});
	};
}

/**********************
 * The setup function *
 **********************/

/**
 * Sets up the receiving of Discord messages, and relaying them to Telegram
 *
 * @param {Logger} logger	The Logger instance to log messages to
 * @param {Discord.Client} dcBot	The Discord bot
 * @param {Telegraf} tgBot	The Telegram bot
 * @param {MessageMap} messageMap	Map between IDs of messages
 * @param {BridgeMap} bridgeMap	Map of the bridges to use
 * @param {Settings} settings	Settings to use
 * @param {String} datadirPath	Path to the directory to put data files in
 */
function setup(logger, dcBot, tgBot, messageMap, bridgeMap, settings, datadirPath) {
	// Create the map of latest message IDs and bridges
	const latestDiscordMessageIds = new LatestDiscordMessageIds(
		logger,
		path.join(datadirPath, "latestDiscordMessageIds.json")
	);
	const useNickname = settings.discord.useNickname;

	// Make a set to keep track of where the "This is an instance of TediCross..." message has been sent the last minute
	const antiInfoSpamSet = new Set();

	// Set of server IDs. Will be filled when the bot is ready
	const knownServerIds = new Set();

	// Listen for users joining the server
	dcBot.on("guildMemberAdd", makeJoinLeaveFunc(logger, "joined", bridgeMap, tgBot));

	// Listen for users joining the server
	dcBot.on("guildMemberRemove", makeJoinLeaveFunc(logger, "left", bridgeMap, tgBot));

	// Listen for Discord messages
	dcBot.on("message", async message => {
		// Ignore the bot's own messages
		if (message.author.id === dcBot.user.id) {
			return;
		}

		// Check if this is a request for server info
		if (message.channel.type === "text" && message.cleanContent === "/chatinfo") {
			// It is. Give it
			message.reply("\nchannelId: '" + message.channel.id + "'")
				.then(sleepOneMinute)
				.then(info => Promise.all([info.delete(), message.delete()]))
				.catch(helpers.ignoreAlreadyDeletedError);

			// Don't process the message any further
			return;
		}

		// Get info about the sender
		const senderName = R.compose(
			// Make it HTML safe
			helpers.escapeHTMLSpecialChars,
			// Add a colon if wanted
			R.when(R.always(settings.telegram.colonAfterSenderName), senderName => senderName + ":"),
			// Figure out what name to use
			R.ifElse(
				message => useNickname && !R.isNil(message.member),
				R.path(["member", "displayName"]),
				R.path(["author", "username"])
			)
		)(message);

		// Check if the message is from the correct chat
		const bridges = bridgeMap.fromDiscordChannelId(message.channel.id);
		if (!R.isEmpty(bridges)) {
			bridges.forEach(async bridge => {
				// Ignore it if this is a telegram-to-discord bridge
				if (bridge.direction === Bridge.DIRECTION_TELEGRAM_TO_DISCORD) {
					return;
				}

				// This is now the latest message for this bridge
				latestDiscordMessageIds.setLatest(message.id, bridge);

				// Check for attachments and pass them on
				message.attachments.forEach(async ({ url }) => {
					try {
						const textToSend = bridge.discord.sendUsernames
							? `<b>${senderName}</b>\n<a href="${url}">${url}</a>`
							: `<a href="${url}">${url}</a>`;
						const tgMessage = await tgBot.telegram.sendMessage(
							bridge.telegram.chatId,
							textToSend,
							{
								parse_mode: "HTML"
							}
						);
						messageMap.insert(
							MessageMap.DISCORD_TO_TELEGRAM,
							bridge,
							message.id,
							tgMessage.message_id
						);
					} catch (err) {
						logger.error(
							`[${bridge.name}] Telegram did not accept an attachment:`,
							err
						);
					}
				});

				// Check the message for embeds
				message.embeds.forEach(embed => {
					// Ignore it if it is not a "rich" embed (image, link, video, ...)
					if (embed.type !== "rich") {
						return;
					}

					// Convert it to something Telegram likes
					const text = handleEmbed(embed, senderName);

					try {
						// Send it
						tgBot.telegram.sendMessage(bridge.telegram.chatId, text, {
							parse_mode: "HTML",
							disable_web_page_preview: true
						});
					} catch (err) {
						logger.error(`[${bridge.name}] Telegram did not accept an embed:`, err);
					}
				});

				// Check if there is an ordinary text message
				if (message.cleanContent) {
					// Modify the message to fit Telegram
					const processedMessage = md2html(message.cleanContent);

					// Pass the message on to Telegram
					try {
						const textToSend = bridge.discord.sendUsernames
							? `<b>${senderName}</b>\n${processedMessage}`
							: processedMessage;
						const tgMessage = await tgBot.telegram.sendMessage(
							bridge.telegram.chatId,
							textToSend,
							{
								parse_mode: "HTML"
							}
						);

						// Make the mapping so future edits can work
						messageMap.insert(
							MessageMap.DISCORD_TO_TELEGRAM,
							bridge,
							message.id,
							tgMessage.message_id
						);
					} catch (err) {
						logger.error(
							`[${bridge.name}] Telegram did not accept a message:`,
							err
						);
						logger.error(`[${bridge.name}] Failed message:`, err);
					}
				}
			});
		} else if (R.isNil(message.channel.guild) || !knownServerIds.has(message.channel.guild.id)) {
			// Check if it is the correct server
			// The message is from the wrong chat. Inform the sender that this is a private bot, if they have not been informed the last minute
			if (!antiInfoSpamSet.has(message.channel.id)) {
				antiInfoSpamSet.add(message.channel.id);

				message.reply(
					"This is an instance of a TediCross bot, bridging a chat in Telegram with one in Discord. " +
						"If you wish to use TediCross yourself, please download and create an instance. " +
						"See https://github.com/TediCross/TediCross"
				)
					// Delete it again after some time
					.then(sleepOneMinute)
					.then(message => message.delete())
					.catch(helpers.ignoreAlreadyDeletedError)
					.then(() => antiInfoSpamSet.delete(message.channel.id));
			}
		}
	});

	// Listen for message edits
	dcBot.on("messageUpdate", async (_oldMessage, newMessage) => {
		// Don't do anything with the bot's own messages
		if (newMessage.author.id === dcBot.user.id) {
			return;
		}

		// Pass it on to the bridges
		bridgeMap.fromDiscordChannelId(newMessage.channel.id).forEach(async bridge => {
			try {
				// Get the corresponding Telegram message ID
				const [tgMessageId] = messageMap.getCorresponding(
					MessageMap.DISCORD_TO_TELEGRAM,
					bridge,
					newMessage.id
				);

				// Get info about the sender
				const senderName =
					(useNickname && newMessage.member
						? newMessage.member.displayName
						: newMessage.author.username) +
					(settings.telegram.colonAfterSenderName ? ":" : "");

				// Modify the message to fit Telegram
				const processedMessage = md2html(newMessage.cleanContent);

				// Send the update to Telegram
				const textToSend = bridge.discord.sendUsernames
					? `<b>${senderName}</b>\n${processedMessage}`
					: processedMessage;
				await tgBot.telegram.editMessageText(
					bridge.telegram.chatId,
					tgMessageId,
					null,
					textToSend,
					{
						parse_mode: "HTML"
					}
				);
			} catch (err) {
				logger.error(`[${bridge.name}] Could not edit Telegram message:`, err);
			}
		});
	});

	// Listen for deleted messages
	function onMessageDelete(message) {
		// Check if it is a relayed message
		const isFromTelegram = message.author.id === dcBot.user.id;

		// Hand it on to the bridges
		bridgeMap.fromDiscordChannelId(message.channel.id).forEach(async bridge => {
			// Ignore it if cross deletion is disabled
			if (!bridge.discord.crossDeleteOnTelegram) {
				return;
			}

			try {
				// Get the corresponding Telegram message IDs
				const tgMessageIds = isFromTelegram
					? messageMap.getCorrespondingReverse(
							MessageMap.DISCORD_TO_TELEGRAM,
							bridge,
							message.id
					  )
					: messageMap.getCorresponding(
							MessageMap.DISCORD_TO_TELEGRAM,
							bridge,
							message.id
					  );
				// Try to delete them
				await Promise.all(
					tgMessageIds.map(tgMessageId =>
						tgBot.telegram.deleteMessage(bridge.telegram.chatId, tgMessageId)
					)
				);
			} catch (err) {
				logger.error(`[${bridge.name}] Could not delete Telegram message:`, err);
				logger.warn(
					'If the previous message was a result of a message being "deleted" on Telegram, you can safely ignore it'
				);
			}
		});
	}
	dcBot.on("messageDelete", onMessageDelete);
	dcBot.on("messageDeleteBulk", messages => [...messages.values()].forEach(onMessageDelete));

	// Start the Discord bot
	dcBot.login(settings.discord.token)
		// Complain if it could not authenticate for some reason
		.catch(err => logger.error("Could not authenticate the Discord bot:", err));

	// Listen for the 'disconnected' event
	dcBot.on("disconnected", async evt => {
		logger.error("Discord bot disconnected!", evt);

		bridgeMap.bridges.forEach(async bridge => {
			try {
				await tgBot.telegram.sendMessage(
					bridge.telegram.chatId,
					"**TEDICROSS**\nThe discord side of the bot disconnected! Please check the log"
				);
			} catch (err) {
				logger.error(`[${bridge.name}] Could not send message to Telegram:`, err);
			}
		});
	});

	// Listen for errors
	dcBot.on("error", err => {
		if (err.code === "ECONNRESET") {
			// Lost connection to the discord servers
			logger.warn(
				"Lost connection to Discord's servers. The bot will resume when connection is reestablished, which should happen automatically. If it does not, please report this to the TediCross support channel"
			);
		} else {
			// Unknown error. Tell the user to tell the devs
			logger.error(
				"The Discord bot ran into an error. Please post the following error message in the TediCross support channel"
			);
			logger.error(err);
		}
	});

	// Listen for debug messages
	if (settings.debug) {
		dcBot.on("debug", str => {
			logger.log(str);
		});
	}

	// Make a promise which resolves when the dcBot is ready
	dcBot.ready = new Promise(resolve => {
		// Listen for the 'ready' event
		dcBot.once("ready", () => {
			// Log the event
			logger.info(`Discord: ${dcBot.user.username} (${dcBot.user.id})`);

			// Get the server IDs from the channels
			R.compose(
				// Mark the bot as ready
				R.andThen(() => resolve()),
				// Add them to the known server ID set
				R.andThen(
					R.reduce(
						(knownServerIds, serverId) => knownServerIds.add(serverId),
						knownServerIds
					)
				),
				// Remove the invalid channels
				R.andThen(R.filter(R.complement(R.isNil))),
				// Extract the server IDs from the channels
				R.andThen(R.map(R.path(["value", "guild", "id"]))),
				// Remove those which failed
				R.andThen(R.filter(R.propEq("status", "fulfilled"))),
				// Wait for the channels to be fetched
				Promise.allSettled.bind(Promise),
				// Get the channels
				R.map(bridge => fetchDiscordChannel(dcBot, bridge)),
				// Get the bridges
				R.prop("bridges")
			)(bridgeMap);
		});
	});

	// Relay old messages, if wanted by the user
	if (!settings.discord.skipOldMessages) {
		dcBot.ready = relayOldMessages(logger, dcBot, latestDiscordMessageIds, bridgeMap);
	}
}

/*****************************
 * Export the setup function *
 *****************************/

module.exports = setup;
<commit_msg>Fixed people joining/leaving Discord crashing the bot<commit_after>"use strict";

/**************************
 * Import important stuff *
 **************************/

const Discord = require("discord.js");
const md2html = require("./md2html");
const MessageMap = require("../MessageMap");
const LatestDiscordMessageIds = require("./LatestDiscordMessageIds");
const handleEmbed = require("./handleEmbed");
const relayOldMessages = require("./relayOldMessages");
const Bridge = require("../bridgestuff/Bridge");
const path = require("path");
const R = require("ramda");
const { sleepOneMinute } = require("../sleep");
const helpers = require("./helpers");
const fetchDiscordChannel = require("../fetchDiscordChannel");

/***********
 * Helpers *
 ***********/

/**
 * Creates a function to give to 'guildMemberAdd' or 'guildMemberRemove' on a Discord bot
 *
 * @param {Logger} logger	The Logger instance to log messages to
 * @param {String} verb	Either "joined" or "left"
 * @param {BridgeMap} bridgeMap	Map of existing bridges
 * @param {Telegraf} tgBot	The Telegram bot to send the messages to
 *
 * @returns {Function}	Function which can be given to the 'guildMemberAdd' or 'guildMemberRemove' events of a Discord bot
 *
 * @private
 */
function makeJoinLeaveFunc(logger, verb, bridgeMap, tgBot) {
	// Find out which setting property to check the bridges for
	const relaySetting = verb === "joined" ? "relayJoinMessages" : "relayLeaveMessages";
	return function (member) {
		// Get the bridges in the guild the member joined/left
		member.guild.channels.cache
			// Get the bridges corresponding to the channels in this guild
			.map(({ id }) => bridgeMap.fromDiscordChannelId(id))
			// Remove the ones which are not bridged
			.filter(bridges => bridges !== undefined)
			// Flatten the bridge arrays
			.reduce((flattened, bridges) => flattened.concat(bridges))
			// Remove those which do not allow relaying join messages
			.filter(bridge => bridge.discord[relaySetting])
			// Ignore the T2D bridges
			.filter(bridge => bridge.direction !== Bridge.DIRECTION_TELEGRAM_TO_DISCORD)
			.forEach(async bridge => {
				// Make the text to send
				const text = `<b>${member.displayName} (@${member.user.username})</b> ${verb} the Discord side of the chat`;

				try {
					// Send it
					await tgBot.telegram.sendMessage(bridge.telegram.chatId, text, {
						parse_mode: "HTML"
					});
				} catch (err) {
					logger.error(
						`[${bridge.name}] Could not notify Telegram about a user that ${verb} Discord`,
						err
					);
				}
			});
	};
}

/**********************
 * The setup function *
 **********************/

/**
 * Sets up the receiving of Discord messages, and relaying them to Telegram
 *
 * @param {Logger} logger	The Logger instance to log messages to
 * @param {Discord.Client} dcBot	The Discord bot
 * @param {Telegraf} tgBot	The Telegram bot
 * @param {MessageMap} messageMap	Map between IDs of messages
 * @param {BridgeMap} bridgeMap	Map of the bridges to use
 * @param {Settings} settings	Settings to use
 * @param {String} datadirPath	Path to the directory to put data files in
 */
function setup(logger, dcBot, tgBot, messageMap, bridgeMap, settings, datadirPath) {
	// Create the map of latest message IDs and bridges
	const latestDiscordMessageIds = new LatestDiscordMessageIds(
		logger,
		path.join(datadirPath, "latestDiscordMessageIds.json")
	);
	const useNickname = settings.discord.useNickname;

	// Make a set to keep track of where the "This is an instance of TediCross..." message has been sent the last minute
	const antiInfoSpamSet = new Set();

	// Set of server IDs. Will be filled when the bot is ready
	const knownServerIds = new Set();

	// Listen for users joining the server
	dcBot.on("guildMemberAdd", makeJoinLeaveFunc(logger, "joined", bridgeMap, tgBot));

	// Listen for users joining the server
	dcBot.on("guildMemberRemove", makeJoinLeaveFunc(logger, "left", bridgeMap, tgBot));

	// Listen for Discord messages
	dcBot.on("message", async message => {
		// Ignore the bot's own messages
		if (message.author.id === dcBot.user.id) {
			return;
		}

		// Check if this is a request for server info
		if (message.channel.type === "text" && message.cleanContent === "/chatinfo") {
			// It is. Give it
			message.reply("\nchannelId: '" + message.channel.id + "'")
				.then(sleepOneMinute)
				.then(info => Promise.all([info.delete(), message.delete()]))
				.catch(helpers.ignoreAlreadyDeletedError);

			// Don't process the message any further
			return;
		}

		// Get info about the sender
		const senderName = R.compose(
			// Make it HTML safe
			helpers.escapeHTMLSpecialChars,
			// Add a colon if wanted
			R.when(R.always(settings.telegram.colonAfterSenderName), senderName => senderName + ":"),
			// Figure out what name to use
			R.ifElse(
				message => useNickname && !R.isNil(message.member),
				R.path(["member", "displayName"]),
				R.path(["author", "username"])
			)
		)(message);

		// Check if the message is from the correct chat
		const bridges = bridgeMap.fromDiscordChannelId(message.channel.id);
		if (!R.isEmpty(bridges)) {
			bridges.forEach(async bridge => {
				// Ignore it if this is a telegram-to-discord bridge
				if (bridge.direction === Bridge.DIRECTION_TELEGRAM_TO_DISCORD) {
					return;
				}

				// This is now the latest message for this bridge
				latestDiscordMessageIds.setLatest(message.id, bridge);

				// Check for attachments and pass them on
				message.attachments.forEach(async ({ url }) => {
					try {
						const textToSend = bridge.discord.sendUsernames
							? `<b>${senderName}</b>\n<a href="${url}">${url}</a>`
							: `<a href="${url}">${url}</a>`;
						const tgMessage = await tgBot.telegram.sendMessage(
							bridge.telegram.chatId,
							textToSend,
							{
								parse_mode: "HTML"
							}
						);
						messageMap.insert(
							MessageMap.DISCORD_TO_TELEGRAM,
							bridge,
							message.id,
							tgMessage.message_id
						);
					} catch (err) {
						logger.error(
							`[${bridge.name}] Telegram did not accept an attachment:`,
							err
						);
					}
				});

				// Check the message for embeds
				message.embeds.forEach(embed => {
					// Ignore it if it is not a "rich" embed (image, link, video, ...)
					if (embed.type !== "rich") {
						return;
					}

					// Convert it to something Telegram likes
					const text = handleEmbed(embed, senderName);

					try {
						// Send it
						tgBot.telegram.sendMessage(bridge.telegram.chatId, text, {
							parse_mode: "HTML",
							disable_web_page_preview: true
						});
					} catch (err) {
						logger.error(`[${bridge.name}] Telegram did not accept an embed:`, err);
					}
				});

				// Check if there is an ordinary text message
				if (message.cleanContent) {
					// Modify the message to fit Telegram
					const processedMessage = md2html(message.cleanContent);

					// Pass the message on to Telegram
					try {
						const textToSend = bridge.discord.sendUsernames
							? `<b>${senderName}</b>\n${processedMessage}`
							: processedMessage;
						const tgMessage = await tgBot.telegram.sendMessage(
							bridge.telegram.chatId,
							textToSend,
							{
								parse_mode: "HTML"
							}
						);

						// Make the mapping so future edits can work
						messageMap.insert(
							MessageMap.DISCORD_TO_TELEGRAM,
							bridge,
							message.id,
							tgMessage.message_id
						);
					} catch (err) {
						logger.error(
							`[${bridge.name}] Telegram did not accept a message:`,
							err
						);
						logger.error(`[${bridge.name}] Failed message:`, err);
					}
				}
			});
		} else if (R.isNil(message.channel.guild) || !knownServerIds.has(message.channel.guild.id)) {
			// Check if it is the correct server
			// The message is from the wrong chat. Inform the sender that this is a private bot, if they have not been informed the last minute
			if (!antiInfoSpamSet.has(message.channel.id)) {
				antiInfoSpamSet.add(message.channel.id);

				message.reply(
					"This is an instance of a TediCross bot, bridging a chat in Telegram with one in Discord. " +
						"If you wish to use TediCross yourself, please download and create an instance. " +
						"See https://github.com/TediCross/TediCross"
				)
					// Delete it again after some time
					.then(sleepOneMinute)
					.then(message => message.delete())
					.catch(helpers.ignoreAlreadyDeletedError)
					.then(() => antiInfoSpamSet.delete(message.channel.id));
			}
		}
	});

	// Listen for message edits
	dcBot.on("messageUpdate", async (_oldMessage, newMessage) => {
		// Don't do anything with the bot's own messages
		if (newMessage.author.id === dcBot.user.id) {
			return;
		}

		// Pass it on to the bridges
		bridgeMap.fromDiscordChannelId(newMessage.channel.id).forEach(async bridge => {
			try {
				// Get the corresponding Telegram message ID
				const [tgMessageId] = messageMap.getCorresponding(
					MessageMap.DISCORD_TO_TELEGRAM,
					bridge,
					newMessage.id
				);

				// Get info about the sender
				const senderName =
					(useNickname && newMessage.member
						? newMessage.member.displayName
						: newMessage.author.username) +
					(settings.telegram.colonAfterSenderName ? ":" : "");

				// Modify the message to fit Telegram
				const processedMessage = md2html(newMessage.cleanContent);

				// Send the update to Telegram
				const textToSend = bridge.discord.sendUsernames
					? `<b>${senderName}</b>\n${processedMessage}`
					: processedMessage;
				await tgBot.telegram.editMessageText(
					bridge.telegram.chatId,
					tgMessageId,
					null,
					textToSend,
					{
						parse_mode: "HTML"
					}
				);
			} catch (err) {
				logger.error(`[${bridge.name}] Could not edit Telegram message:`, err);
			}
		});
	});

	// Listen for deleted messages
	function onMessageDelete(message) {
		// Check if it is a relayed message
		const isFromTelegram = message.author.id === dcBot.user.id;

		// Hand it on to the bridges
		bridgeMap.fromDiscordChannelId(message.channel.id).forEach(async bridge => {
			// Ignore it if cross deletion is disabled
			if (!bridge.discord.crossDeleteOnTelegram) {
				return;
			}

			try {
				// Get the corresponding Telegram message IDs
				const tgMessageIds = isFromTelegram
					? messageMap.getCorrespondingReverse(
							MessageMap.DISCORD_TO_TELEGRAM,
							bridge,
							message.id
					  )
					: messageMap.getCorresponding(
							MessageMap.DISCORD_TO_TELEGRAM,
							bridge,
							message.id
					  );
				// Try to delete them
				await Promise.all(
					tgMessageIds.map(tgMessageId =>
						tgBot.telegram.deleteMessage(bridge.telegram.chatId, tgMessageId)
					)
				);
			} catch (err) {
				logger.error(`[${bridge.name}] Could not delete Telegram message:`, err);
				logger.warn(
					'If the previous message was a result of a message being "deleted" on Telegram, you can safely ignore it'
				);
			}
		});
	}
	dcBot.on("messageDelete", onMessageDelete);
	dcBot.on("messageDeleteBulk", messages => [...messages.values()].forEach(onMessageDelete));

	// Start the Discord bot
	dcBot.login(settings.discord.token)
		// Complain if it could not authenticate for some reason
		.catch(err => logger.error("Could not authenticate the Discord bot:", err));

	// Listen for the 'disconnected' event
	dcBot.on("disconnected", async evt => {
		logger.error("Discord bot disconnected!", evt);

		bridgeMap.bridges.forEach(async bridge => {
			try {
				await tgBot.telegram.sendMessage(
					bridge.telegram.chatId,
					"**TEDICROSS**\nThe discord side of the bot disconnected! Please check the log"
				);
			} catch (err) {
				logger.error(`[${bridge.name}] Could not send message to Telegram:`, err);
			}
		});
	});

	// Listen for errors
	dcBot.on("error", err => {
		if (err.code === "ECONNRESET") {
			// Lost connection to the discord servers
			logger.warn(
				"Lost connection to Discord's servers. The bot will resume when connection is reestablished, which should happen automatically. If it does not, please report this to the TediCross support channel"
			);
		} else {
			// Unknown error. Tell the user to tell the devs
			logger.error(
				"The Discord bot ran into an error. Please post the following error message in the TediCross support channel"
			);
			logger.error(err);
		}
	});

	// Listen for debug messages
	if (settings.debug) {
		dcBot.on("debug", str => {
			logger.log(str);
		});
	}

	// Make a promise which resolves when the dcBot is ready
	dcBot.ready = new Promise(resolve => {
		// Listen for the 'ready' event
		dcBot.once("ready", () => {
			// Log the event
			logger.info(`Discord: ${dcBot.user.username} (${dcBot.user.id})`);

			// Get the server IDs from the channels
			R.compose(
				// Mark the bot as ready
				R.andThen(() => resolve()),
				// Add them to the known server ID set
				R.andThen(
					R.reduce(
						(knownServerIds, serverId) => knownServerIds.add(serverId),
						knownServerIds
					)
				),
				// Remove the invalid channels
				R.andThen(R.filter(R.complement(R.isNil))),
				// Extract the server IDs from the channels
				R.andThen(R.map(R.path(["value", "guild", "id"]))),
				// Remove those which failed
				R.andThen(R.filter(R.propEq("status", "fulfilled"))),
				// Wait for the channels to be fetched
				Promise.allSettled.bind(Promise),
				// Get the channels
				R.map(bridge => fetchDiscordChannel(dcBot, bridge)),
				// Get the bridges
				R.prop("bridges")
			)(bridgeMap);
		});
	});

	// Relay old messages, if wanted by the user
	if (!settings.discord.skipOldMessages) {
		dcBot.ready = relayOldMessages(logger, dcBot, latestDiscordMessageIds, bridgeMap);
	}
}

/*****************************
 * Export the setup function *
 *****************************/

module.exports = setup;
<|endoftext|>


<commit_before>// Native components
const cluster = require('cluster');

// Third party components
const Winston = require('winston');
require('winston-daily-rotate-file');

// Application components
const config = require('../../config/index');

// Setup
const verbose = config.get('verbose');
const silent = config.get('silent');
const environment = config.get('env');

function _parseError(error) {
  const jsonObj = {
    name: error.name,
    message: error.message,
    stack: error.stack,
    __error__: true
  };

  Object.keys(error).forEach((key) => {
    if (!jsonObj[key]) { jsonObj[key] = error[key]; }
  });

  return jsonObj;
}

class MasterLogger {
  constructor() {
    this.logger = Winston;

    // @todo File logging options should be fetched from config file
    // Add winston file logger, which rotates daily
    const fileLevel = 'silly';
    this.logger.configure({
      transports: [
        new (Winston.transports.Console)({
          colorize: true,
          level: silent ? 'error' : ['info', 'debug', 'verbose', 'silly'][verbose % 4]
        }),
        new (Winston.transports.DailyRotateFile)({
          filename: './log/app.log',
          json: false,
          handleExceptions: false,
          level: fileLevel,
          datePatter: '.yyyy-MM-dd_HH-mm.log'
        })
      ]
    });

    // Print current config
    this.logger.debug(`Using cfg: ${environment}.`);
  }
  set level(level) {
    this.logger.level = level;
  }

  handleWorkerLog(worker, data) {
    const level = data['level'] || 'debug';
    const args = data['args'] || [];
    args.unshift(`<Worker#${worker.id}>`);
    try {
      this.logger[level](...args);
    } catch (error) {
      this.logger.error(data);
      this.logger.error(error);
    }
  }
  log(level, ...args) {
    args.unshift('<Master>');
    this.logger.log(level, ...args);
  }
  error(...args) {
    this.log('error', ...args);
  }
  warn(...args) {
    this.log('warn', ...args);
  }
  info(...args) {
    this.log('info', ...args);
  }
  debug(...args) {
    this.log('debug', ...args);
  }
  verbose(...args) {
    this.log('verbose', ...args);
  }
  silly(...args) {
    this.log('silly', ...args);
  }
}

class WorkerLogger {
  constructor() {
    this._emitter = process;
  }

  _proxy(level, ...args) {
    const editedArgs = args;
    Object.keys(args).forEach((key) => {
      if (args[key] instanceof Error) {
        editedArgs[key] = _parseError(args[key]);
      }
    });
    if (process.connected) {
      // @todo better intercommunication..
      this._emitter.send({type: 'log', level, args: editedArgs});
    }
  }

  set level(level) {
    this.warn('Not implemented');
  }

  log(level, ...args) {
    this._proxy(level, ...args);
  }
  error(...args) {
    this._proxy('error', ...args);
  }
  debug(...args) {
    this._proxy('debug', ...args);
  }
  info(...args) {
    this._proxy('info', ...args);
  }
  warn(...args) {
    this._proxy('warn', ...args);
  }
  silly(...args) {
    this._proxy('silly', ...args);
  }
  verbose(...args) {
    this._proxy('verbose', ...args);
  }
}

module.exports = cluster.isMaster ? new MasterLogger() : new WorkerLogger();
<commit_msg>removed os specific + relative filename definition from logger.<commit_after>// Native components
const cluster = require('cluster');
const path = require('path');

// Third party components
const Winston = require('winston');
require('winston-daily-rotate-file');

// Application components
const config = require('../../config/index');

// Setup
const verbose = config.get('verbose');
const silent = config.get('silent');
const environment = config.get('env');

function _parseError(error) {
  const jsonObj = {
    name: error.name,
    message: error.message,
    stack: error.stack,
    __error__: true
  };

  Object.keys(error).forEach((key) => {
    if (!jsonObj[key]) { jsonObj[key] = error[key]; }
  });

  return jsonObj;
}

class MasterLogger {
  constructor() {
    this.logger = Winston;

    // @todo File logging options should be fetched from config file
    // Add winston file logger, which rotates daily
    const fileLevel = 'silly';
    this.logger.configure({
      transports: [
        new (Winston.transports.Console)({
          colorize: true,
          level: silent ? 'error' : ['info', 'debug', 'verbose', 'silly'][verbose % 4]
        }),
        new (Winston.transports.DailyRotateFile)({
          filename: path.resolve(__dirname, '..', '..', 'log', 'app.log'),
          json: false,
          handleExceptions: false,
          level: fileLevel,
          datePatter: '.yyyy-MM-dd_HH-mm.log'
        })
      ]
    });

    // Print current config
    this.logger.debug(`Using cfg: ${environment}.`);
  }
  set level(level) {
    this.logger.level = level;
  }

  handleWorkerLog(worker, data) {
    const level = data['level'] || 'debug';
    const args = data['args'] || [];
    args.unshift(`<Worker#${worker.id}>`);
    try {
      this.logger[level](...args);
    } catch (error) {
      this.logger.error(data);
      this.logger.error(error);
    }
  }
  log(level, ...args) {
    args.unshift('<Master>');
    this.logger.log(level, ...args);
  }
  error(...args) {
    this.log('error', ...args);
  }
  warn(...args) {
    this.log('warn', ...args);
  }
  info(...args) {
    this.log('info', ...args);
  }
  debug(...args) {
    this.log('debug', ...args);
  }
  verbose(...args) {
    this.log('verbose', ...args);
  }
  silly(...args) {
    this.log('silly', ...args);
  }
}

class WorkerLogger {
  constructor() {
    this._emitter = process;
  }

  _proxy(level, ...args) {
    const editedArgs = args;
    Object.keys(args).forEach((key) => {
      if (args[key] instanceof Error) {
        editedArgs[key] = _parseError(args[key]);
      }
    });
    if (process.connected) {
      // @todo better intercommunication..
      this._emitter.send({type: 'log', level, args: editedArgs});
    }
  }

  set level(level) {
    this.warn('Not implemented');
  }

  log(level, ...args) {
    this._proxy(level, ...args);
  }
  error(...args) {
    this._proxy('error', ...args);
  }
  debug(...args) {
    this._proxy('debug', ...args);
  }
  info(...args) {
    this._proxy('info', ...args);
  }
  warn(...args) {
    this._proxy('warn', ...args);
  }
  silly(...args) {
    this._proxy('silly', ...args);
  }
  verbose(...args) {
    this._proxy('verbose', ...args);
  }
}

module.exports = cluster.isMaster ? new MasterLogger() : new WorkerLogger();
<|endoftext|>


<commit_before>/* eslint-env node */
var fs = require('fs');

// Constructor
function DebugOutput(config) {
    this.enabled = config['enabled'];
    this.dbg_output = config['output'];
    this.dbg_level = config['level'];
    this.dbg_file = config['file'];

    this.prev_level = 0;
}

// Debug level to text
var dbg_level_text = ["debug", "info", "warn", "error"];

// Log function
DebugOutput.prototype.log = function(message, level) {
    if(this.enabled) {
        var log_text = new Date().toISOString();
        log_text = log_text + " " + dbg_level_text[level] + ":\t" + message;

        fs.open(this.dbg_file, 'a', function(err, fd) {
            if(!err) {
                fs.write(fd, log_text + "\n");
            }
            else {
                console.log("Error writing to file " + this.dbg_file);
            }
        });

        if(this.dbg_output) {
            if(level >= this.dbg_level) {
                console.log(log_text);
            }
        }
    }

    this.prev_level = level;
};

// Add to previous log function
DebugOutput.prototype.prev = function(message) {
    if(this.enabled) {
        var log_text = new Date().toISOString();
        log_text = log_text + "\t" + message;

        fs.open(this.dbg_file, 'a', function(err, fd) {
            if(!err) {
                fs.write(fd, log_text + "\n");
            }
            else {
                console.log("Error writing to file " + this.dbg_file);
            }
        });

        if(this.dbg_output) {
            if(this.prev_level >= this.dbg_level) {
                console.log(log_text);
            }
        }
    }
};

// Finish it
module.exports = DebugOutput;<commit_msg>Fixed deprecation warning with newer NodeJS versions<commit_after>/* eslint-env node */
var fs = require('fs');

// Constructor
function DebugOutput(config) {
    this.enabled = config['enabled'];
    this.dbg_output = config['output'];
    this.dbg_level = config['level'];
    this.dbg_file = config['file'];

    this.prev_level = 0;
}

// Debug level to text
var dbg_level_text = ["debug", "info", "warn", "error"];

// Log function
DebugOutput.prototype.log = function(message, level) {
    if(this.enabled) {
        var log_text = new Date().toISOString();
        log_text = log_text + " " + dbg_level_text[level] + ":\t" + message;

        fs.open(this.dbg_file, 'a', function(err, fd) {
            if(!err) {
                fs.write(fd, log_text + "\n", function() {});
            }
            else {
                console.log("Error writing to file " + this.dbg_file);
            }
        });

        if(this.dbg_output) {
            if(level >= this.dbg_level) {
                console.log(log_text);
            }
        }
    }

    this.prev_level = level;
};

// Add to previous log function
DebugOutput.prototype.prev = function(message) {
    if(this.enabled) {
        var log_text = new Date().toISOString();
        log_text = log_text + "\t" + message;

        fs.open(this.dbg_file, 'a', function(err, fd) {
            if(!err) {
                fs.write(fd, log_text + "\n", function() {});
            }
            else {
                console.log("Error writing to file " + this.dbg_file);
            }
        });

        if(this.dbg_output) {
            if(this.prev_level >= this.dbg_level) {
                console.log(log_text);
            }
        }
    }
};

// Finish it
module.exports = DebugOutput;<|endoftext|>


<commit_before>import React from 'react'
import { Row, Col, Input } from 'react-bootstrap'

import allPermissions from './permissions'

export default class UserSettings extends React.Component {

  getSettings () {
    var selectedFeeds = this.props.feeds.filter((feed) => {
      let checkbox = this.refs['feed-' + feed.id]
      return checkbox.getChecked()
    }).map((feed) => {
      return feed.id
    })

    var userPermissions = allPermissions.filter((permissionInfo) => {
      let checkbox = this.refs['permission-' + permissionInfo.type]
      return checkbox.getChecked()
    }).map((permissionInfo) => {
      var permission = {
        type : permissionInfo.type,
      }
      if(permissionInfo.feedSpecific) permission.feeds = selectedFeeds
      return permission
    })

    userPermissions.push({
      type: 'view-feed',
      feeds : selectedFeeds
    })

    return userPermissions
  }

  render () {
    var lookup = {}
    console.log('rendering userSettings', this.props.userPermissions)
    if (this.props.userPermissions) {
      for (var permission of this.props.userPermissions) {
        lookup[permission.type] = permission.feeds
      }
    }

    return (
      <Row>
        <Col xs={6}>

Agencies

          {this.props.feeds.map(function(feed, i){
            let name = (feed.name === '') ? '(unnamed feed)' : feed.name
            let ref = 'feed-' + feed.id
            let feedContained = lookup['view-feed'] && lookup['view-feed'].indexOf(feed.id) !== -1
            let checked = feedContained ? 'checked' : ''
            return <Input ref={ref} type="checkbox" defaultChecked={checked} label={name} />;
          })}
        </Col>
        <Col xs={6}>

Permissions

          {allPermissions.map(function(permission, i){
            let ref = 'permission-' + permission.type
            let checked = permission.type in lookup
            return <Input ref={ref} type="checkbox" defaultChecked={checked} label={permission.name} />;
          })}
        </Col>
      </Row>
    )
  }
}
<commit_msg>update copy<commit_after>import React from 'react'
import { Row, Col, Input } from 'react-bootstrap'

import allPermissions from './permissions'

export default class UserSettings extends React.Component {

  getSettings () {
    var selectedFeeds = this.props.feeds.filter((feed) => {
      let checkbox = this.refs['feed-' + feed.id]
      return checkbox.getChecked()
    }).map((feed) => {
      return feed.id
    })

    var userPermissions = allPermissions.filter((permissionInfo) => {
      let checkbox = this.refs['permission-' + permissionInfo.type]
      return checkbox.getChecked()
    }).map((permissionInfo) => {
      var permission = {
        type : permissionInfo.type,
      }
      if(permissionInfo.feedSpecific) permission.feeds = selectedFeeds
      return permission
    })

    userPermissions.push({
      type: 'view-feed',
      feeds : selectedFeeds
    })

    return userPermissions
  }

  render () {
    var lookup = {}
    console.log('rendering userSettings', this.props.userPermissions)
    if (this.props.userPermissions) {
      for (var permission of this.props.userPermissions) {
        lookup[permission.type] = permission.feeds
      }
    }

    return (
      <Row>
        <Col xs={6}>

Feed Sources

          {this.props.feeds.map(function(feed, i){
            let name = (feed.name === '') ? '(unnamed feed)' : feed.name
            let ref = 'feed-' + feed.id
            let feedContained = lookup['view-feed'] && lookup['view-feed'].indexOf(feed.id) !== -1
            let checked = feedContained ? 'checked' : ''
            return <Input ref={ref} type="checkbox" defaultChecked={checked} label={name} />;
          })}
        </Col>
        <Col xs={6}>

Permissions

          {allPermissions.map(function(permission, i){
            let ref = 'permission-' + permission.type
            let checked = permission.type in lookup
            return <Input ref={ref} type="checkbox" defaultChecked={checked} label={permission.name} />;
          })}
        </Col>
      </Row>
    )
  }
}
<|endoftext|>


<commit_before>/**
 * Created by william on 12/05/2017.
 */
export function runOnce(fn, context) { //控制让函数只触发一次
    return function () {
        try {
            if( fn ) {
                fn.apply(context || this, arguments);
            }

        }
        catch (e) {
            console.error(e);//一般可以注释掉这行
        }
        finally {
            fn = null;
        }
    }
}<commit_msg>Remove exception<commit_after>/**
 * Created by william on 12/05/2017.
 */
export function runOnce(fn, context) { //控制让函数只触发一次
    return function () {
        try {
            fn.apply(context || this, arguments);

        }
        catch (e) {
            //console.error(e);//一般可以注释掉这行
        }
        finally {
            fn = null;
        }
    }
}<|endoftext|>