<commit_before>// ////////////////////////////////////////////////////////////////////////////
/*
// Define Constants
*/
// ////////////////////////////////////////////////////////////////////////////

const osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: `©
  OpenStreetMap`,
  minZoom: 2,
  maxZoom: 19
})

const stamenToner = L.tileLayer(`http://stamen-tiles-{s}.a.ssl.fastly.net/
toner/{z}/{x}/{y}.{ext}`, {
  attribution: `Map tiles by Stamen Design,
  CC BY 3.0
  — Map data ©
  OpenStreetMap`,
  subdomains: 'abcd',
  minZoom: 2,
  maxZoom: 19,
  ext: 'png'
})

const esriWorldImagery = L.tileLayer(`http://server.arcgisonline.com/ArcGIS/
rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}`, {
  attribution: `Tiles © Esri — Source: Esri, i-cubed, USDA,
  USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP,
  and the GIS User Community`
})

// set up map, view and base layer
const myMap = new L.Map('mapid', {
  center: {lat: 0, lng: 8.8460},
  zoom: 2,
  layers: osm,
  scrollWheelZoom: false
})

const baseLayers = {
  'Open Street Maps': osm,
  'Black and White': stamenToner,
  'ESRI World Map': esriWorldImagery
}

const circleOptions = {
  radius: 8,
  fillColor: '#ee74f7',
  color: 'green',
  weight: 1,
  opacity: 1,
  fillOpacity: 0.7
}

// Constant values
const value = document.getElementById('dataset_pk').getAttribute('value')
const datasetUrl = `/load_dataset/${value}`
const ext = document.getElementById('dataset_ext').getAttribute('value')

// Empty container to be filled with data from XMLHttpRequest
const dataset = []

// Text input elements
const minLngInput = document.getElementById('min_lng_input')
const maxLngInput = document.getElementById('max_lng_input')
const minLatInput = document.getElementById('min_lat_input')
const maxLatInput = document.getElementById('max_lat_input')

// Display elements
const featureCountElement = document.getElementById('feature_count')

// Button elements
const submitValuesButton = document.getElementById('submit_values_button')

// ////////////////////////////////////////////////////////////////////////////
/*
// Define Functions
*/
// ////////////////////////////////////////////////////////////////////////////

// Do function stuff
// Control button creation -- eventually this will need to be written better
L.Control.Watermark = L.Control.extend({
  onAdd: (map) => {
    const img = L.DomUtil.create('img')
    // this will have to be changed relative to the site for production
    img.src = 'http://localhost:8000/static/images/zmt_logo_blue_black' +
      '_100px.png'
    img.style.width = '100px'
    return img
  },
  onRemove: (map) => {
    // Nothing to do here
  }
})

L.Control.HomeButton = L.Control.extend({
  onAdd: (map) => {
    const container = L.DomUtil.create('div',
      'leaflet-bar leaflet-control leaflet-control-custom')
    //  container.innerHTML = ''
    container.style.backgroundImage = 'url("http://localhost:8000/static/images/home_icon.png")'
    container.style.backgroundRepeat = 'no-repeat'
    container.style.backgroundColor = 'white'
    container.style.width = '34px'
    container.style.height = '34px'
    container.addEventListener('click', () => map.setView({lat: 0, lng: 0}, 2))
    return container
  },
  onRemove: (map) => {
    // Nothing to do here
  }
})

L.Control.ToggleScrollButton = L.Control.extend({
  onAdd: (map) => {
    const container = L.DomUtil.create('div',
      'leaflet-bar leaflet-control leaflet-control-custom')
    container.style.backgroundImage = 'url("http://localhost:8000/static/images/mouse.png")'
    container.style.backgroundRepeat = 'no-repeat'
    container.style.backgroundColor = 'white'
    container.style.width = '34px'
    container.style.height = '34px'
    container.addEventListener('click', () => {
      map.scrollWheelZoom.enabled()
        ? map.scrollWheelZoom.disable()
        : map.scrollWheelZoom.enable()
    })
    return container
  },
  onRemove: (map) => {
    // Nothing to do here
  }
})

// Can I separate XMLHttpRequest calls, Promises, and if/else statements?
// Start by separating the kml / csv / json XMLHttpRequests.
// function to get data from geojson dataset with a promise
// Can I do this without using all the promise logic in here?

// I hate repeating code, i've already had issues with errors because of it

// test whether I need promise to be wrapped in a function
// I do need the url... so

// JSON
const getJSONDataset = (url) => {
  const promise = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.onload = () => {
      xhr.readyState === 4 && xhr.status === 200
        ? resolve(JSON.parse(xhr.responseText)) : reject(Error(xhr.statusText))
    }
    xhr.onerror = () => reject(Error('Network Error - JSON'))
    xhr.send()
  })
  return promise
}

// KML
const getKMLDataset = (url) => {
  const promise = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    // The next few lines are different than the getJSONDataset and getCSVDataset
    // calls from here
    xhr.responseType = 'document'
    xhr.overrideMimeType('text/xml')
    xhr.onload = () => {
      xhr.readyState === 4 && xhr.status === 200
        ? resolve(toGeoJSON.kml(xhr.response)) : reject(Error(xhr.statusText))
    }
    // to here
    xhr.onerror = () => reject(Error('Network Error - KML'))
    xhr.send()
  })
  return promise
}

// CSV
const getCSVDataset = (url) => {
  const promise = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.onload = () => {
      xhr.readyState === 4 && xhr.status === 200
        ? csv2geojson.csv2geojson(
            xhr.responseText, (err, data) => {
              err ? reject(Error(err)) : resolve(data)
            }
          )
        : reject(Error(xhr.statusText))
    }
    xhr.onerror = () => reject(Error('Network Error - CSV'))
    xhr.send()
  })
  return promise
}

// I don't really like the little html droplets, so I'm going to make all
// points into little circle markers. This is the function for that.
const makeCircles = (feature, latlng) => L.circleMarker(latlng, circleOptions)

// function to run through feature and layer once and count

// function to count dataset features

// Since I was having trouble with the .map feature, I decided to use the
// .forEach feature to add data to the popupContent array
const onReadyPopups = (feature, layer) => {
  // empty array to contain popup content
  const popupContent = []
  // run through feature properties, if there are any and add them to array
  feature.properties.length !== undefined || feature.properties.length !== 0
    ? Object.keys(feature.properties).forEach(key => {
      popupContent.push(`<b>${key}</b>: ${feature.properties[key]}`)
    }) : console.log('No feature properties')
  // push coordinates to array, if the feature is of the type point
  feature.geometry.type === 'Point'
    ? popupContent.push(
        `<b>Latitude:</b> ${feature.geometry.coordinates[1]}`,
        `<b>Longitude:</b> ${feature.geometry.coordinates[0]}`
    ) : console.log(feature.geometry.type)
  layer.bindPopup(popupContent.join('
'))
}

// I need to make a function or something that takes the ext and calls the
// correct getData function

// build filtering select function

const getInputValue = (element) => element.value

// check longitude and latitude values
const checkVal = (val, defaultVal, minVal, maxVal) => {
  val = (typeof val === 'undefined') ? defaultVal
    : val === '' ? defaultVal
    : val < minVal ? minVal
    : val > maxVal ? maxVal
    : val
  return val
}

const filterValues = (feature, layer, minLng, maxlng, minLat, maxLat) => {
  // set default values for minimum and maximum lat and lng, incase no
  // values are passed through
  minLng = checkVal(getInputValue(minLngInput), -180, -180, 180)
  maxLng = checkVal(getInputValue(maxLngInput), 180, -180, 180)
  minLat = checkVal(getInputValue(minLatInput), -90, -90, 90)
  maxLat = checkVal(getInputValue(maxLatInput), 90, -90, 90)

  const coords = feature.geometry.coordinates
  const filteredData = coords[0] > minLng &&
                       coords[0] < maxLng &&
                       coords[1] > minLat &&
                       coords[1] < maxLat
  return filteredData
}

const buildFeaturePropertiesSelector = data => {
  // get keys for feature properties for first element dataset and add to array
  const featureKeys = Object.keys(dataset[0].features[0].properties)
    .map(key => key)

  // create elements
  const ifFeaturesElement = document.getElementById('if_features')
  const p = document.createElement('p')
  const span = document.createElement('span')
  const b = document.createElement('b')
  const text = document.createTextNode('Select property to filter by: ')
  const selector = document.createElement('select')
  const input = document.createElement('input')

  // set id of p and select elements
  p.setAttribute('id', 'selector_container')
  selector.setAttribute('id', 'property_selector')
  input.setAttribute('id', 'property_selector_input')
  input.setAttribute('type', 'text')

  // put them all together
  b.appendChild(text)
  span.appendChild(b)
  span.appendChild(selector)
  span.appendChild(input)
  p.appendChild(span)
  ifFeaturesElement.appendChild(p)

  // create and add options to the feature selector element
  featureKeys.forEach(i => {
    const opt = document.createElement('option')
    opt.value = i
    opt.innerHTML = i
    selector.appendChild(opt)
  })
}

const onDatasetImport = data => {
  // make sure that original data are saved somewhere
  dataset.push(data)

  // get keys for feature properties for first element dataset and add to array
  Object.keys(dataset[0].features[0].properties).length > 0 ?
    buildFeaturePropertiesSelector(dataset)
    : console.log('No feature properties to filter')

  // add dataset to the filteredLayer and filteredLayer to allMarkers
  filteredLayer.addData(dataset)
  allMarkers.addLayer(filteredLayer)

  // get count of features in filteredLayer
  const featureCount = Object.keys(filteredLayer._layers).length
  featureCountElement.innerHTML = ` ${featureCount}`
}

// ////////////////////////////////////////////////////////////////////////////
/*
// Implement Functions
*/
// ////////////////////////////////////////////////////////////////////////////

// ////////////////////////////////////////////////////////////////////////////
// Special Interlude - Make constants that require functions to be created

// It looks like 'onEachFeature' can be called more than once
// It also looks like I need to check if the dataset is a points type or not
// how do I pass values to the filter function?
const filteredLayer = L.geoJson(null, {
  filter: filterValues,
  onEachFeature: onReadyPopups,
  pointToLayer: makeCircles
})

const allMarkers = L.markerClusterGroup({
  showCoverageOnHover: false,
  maxClusterRadius: 50
})
// ////////////////////////////////////////////////////////////////////////////

// Implement functions
// this one is sorta not in the right style
L.control.layers(baseLayers).addTo(myMap)

L.control.watermark = (options) => new L.Control.Watermark(options)
L.control.watermark({position: 'bottomleft'}).addTo(myMap)

L.control.homebutton = (options) => new L.Control.HomeButton(options)
L.control.homebutton({position: 'topleft'}).addTo(myMap)

L.control.togglescrollbutton = (options) => new L.Control.ToggleScrollButton(options)
L.control.togglescrollbutton({position: 'topleft'}).addTo(myMap)

// Add filteredLayer to the marker cluster stuff, then add the markercluster
// group to the map
// allMarkers.addLayer(filteredLayer)
// This should only work for points datasets
myMap.addLayer(allMarkers)

// make sure filteredLayer is added to the map
// I might have to create the filteredLayer with options here
// now every time a layer is added to the filteredLayer it automatically adds
// the popupContent and stuff
// Function that calls correct get___Dataset function based on ext
// this should be written differently

// I need to set this up as a function that returns a function, then the
// rest of the . notation stuff can be used, I think
ext === 'kml'
  ? getKMLDataset(datasetUrl)
    .then(onDatasetImport, error => console.log(error))
: ext === 'csv'
  ? getCSVDataset(datasetUrl)
    .then(onDatasetImport, error => console.log(error))
  : getJSONDataset(datasetUrl)
    .then(onDatasetImport, error => console.log(error))

// ////////////////////////////////////////////////////////////////////////////
// Add Event Listeners for function calls

// submit button:
// clearLayers in filteredLayer
// remove filteredLayer from allMarkers
// add data to filteredLayer -- and hopefully the data are filtered
// add filteredLayer to allMarkers

submitValuesButton.addEventListener("click", () => {
  filteredLayer.clearLayers()
  allMarkers.clearLayers()
  filteredLayer.addData(dataset)
  allMarkers.addLayer(filteredLayer)
  const featureCount = Object.keys(filteredLayer._layers).length
  featureCountElement.innerHTML = ` ${featureCount}`
})

// ////////////////////////////////////////////////////////////////////////////
<commit_msg>linting detailView.js<commit_after>// ////////////////////////////////////////////////////////////////////////////
/*
// Define Constants
*/
// ////////////////////////////////////////////////////////////////////////////

const osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: `©
  OpenStreetMap`,
  minZoom: 2,
  maxZoom: 19
})

const stamenToner = L.tileLayer(`http://stamen-tiles-{s}.a.ssl.fastly.net/
toner/{z}/{x}/{y}.{ext}`, {
  attribution: `Map tiles by Stamen Design,
  CC BY 3.0
  — Map data ©
  OpenStreetMap`,
  subdomains: 'abcd',
  minZoom: 2,
  maxZoom: 19,
  ext: 'png'
})

const esriWorldImagery = L.tileLayer(`http://server.arcgisonline.com/ArcGIS/
rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}`, {
  attribution: `Tiles © Esri — Source: Esri, i-cubed, USDA,
  USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP,
  and the GIS User Community`
})

// set up map, view and base layer
const myMap = new L.Map('mapid', {
  center: {lat: 0, lng: 8.8460},
  zoom: 2,
  layers: osm,
  scrollWheelZoom: false
})

const baseLayers = {
  'Open Street Maps': osm,
  'Black and White': stamenToner,
  'ESRI World Map': esriWorldImagery
}

const circleOptions = {
  radius: 8,
  fillColor: '#ee74f7',
  color: 'green',
  weight: 1,
  opacity: 1,
  fillOpacity: 0.7
}

// Constant values
const value = document.getElementById('dataset_pk').getAttribute('value')
const datasetUrl = `/load_dataset/${value}`
const ext = document.getElementById('dataset_ext').getAttribute('value')

// Empty container to be filled with data from XMLHttpRequest
const dataset = []

// Text input elements
const minLngInput = document.getElementById('min_lng_input')
const maxLngInput = document.getElementById('max_lng_input')
const minLatInput = document.getElementById('min_lat_input')
const maxLatInput = document.getElementById('max_lat_input')

// Display elements
const featureCountElement = document.getElementById('feature_count')

// Button elements
const submitValuesButton = document.getElementById('submit_values_button')

// ////////////////////////////////////////////////////////////////////////////
/*
// Define Functions
*/
// ////////////////////////////////////////////////////////////////////////////

// Do function stuff
// Control button creation -- eventually this will need to be written better
L.Control.Watermark = L.Control.extend({
  onAdd: (map) => {
    const img = L.DomUtil.create('img')
    // this will have to be changed relative to the site for production
    img.src = 'http://localhost:8000/static/images/zmt_logo_blue_black' +
      '_100px.png'
    img.style.width = '100px'
    return img
  },
  onRemove: (map) => {
    // Nothing to do here
  }
})

L.Control.HomeButton = L.Control.extend({
  onAdd: (map) => {
    const container = L.DomUtil.create('div',
      'leaflet-bar leaflet-control leaflet-control-custom')
    //  container.innerHTML = ''
    container.style.backgroundImage = 'url("http://localhost:8000/static/images/home_icon.png")'
    container.style.backgroundRepeat = 'no-repeat'
    container.style.backgroundColor = 'white'
    container.style.width = '34px'
    container.style.height = '34px'
    container.addEventListener('click', () => map.setView({lat: 0, lng: 0}, 2))
    return container
  },
  onRemove: (map) => {
    // Nothing to do here
  }
})

L.Control.ToggleScrollButton = L.Control.extend({
  onAdd: (map) => {
    const container = L.DomUtil.create('div',
      'leaflet-bar leaflet-control leaflet-control-custom')
    container.style.backgroundImage = 'url("http://localhost:8000/static/images/mouse.png")'
    container.style.backgroundRepeat = 'no-repeat'
    container.style.backgroundColor = 'white'
    container.style.width = '34px'
    container.style.height = '34px'
    container.addEventListener('click', () => {
      map.scrollWheelZoom.enabled()
        ? map.scrollWheelZoom.disable()
        : map.scrollWheelZoom.enable()
    })
    return container
  },
  onRemove: (map) => {
    // Nothing to do here
  }
})

// Can I separate XMLHttpRequest calls, Promises, and if/else statements?
// Start by separating the kml / csv / json XMLHttpRequests.
// function to get data from geojson dataset with a promise
// Can I do this without using all the promise logic in here?

// I hate repeating code, i've already had issues with errors because of it

// test whether I need promise to be wrapped in a function
// I do need the url... so

// JSON
const getJSONDataset = (url) => {
  const promise = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.onload = () => {
      xhr.readyState === 4 && xhr.status === 200
        ? resolve(JSON.parse(xhr.responseText)) : reject(Error(xhr.statusText))
    }
    xhr.onerror = () => reject(Error('Network Error - JSON'))
    xhr.send()
  })
  return promise
}

// KML
const getKMLDataset = (url) => {
  const promise = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    // The next few lines are different than the getJSONDataset and getCSVDataset
    // calls from here
    xhr.responseType = 'document'
    xhr.overrideMimeType('text/xml')
    xhr.onload = () => {
      xhr.readyState === 4 && xhr.status === 200
        ? resolve(toGeoJSON.kml(xhr.response)) : reject(Error(xhr.statusText))
    }
    // to here
    xhr.onerror = () => reject(Error('Network Error - KML'))
    xhr.send()
  })
  return promise
}

// CSV
const getCSVDataset = (url) => {
  const promise = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.onload = () => {
      xhr.readyState === 4 && xhr.status === 200
        ? csv2geojson.csv2geojson(
            xhr.responseText, (err, data) => {
              err ? reject(Error(err)) : resolve(data)
            }
          )
        : reject(Error(xhr.statusText))
    }
    xhr.onerror = () => reject(Error('Network Error - CSV'))
    xhr.send()
  })
  return promise
}

// I don't really like the little html droplets, so I'm going to make all
// points into little circle markers. This is the function for that.
const makeCircles = (feature, latlng) => L.circleMarker(latlng, circleOptions)

// function to run through feature and layer once and count

// function to count dataset features

// Since I was having trouble with the .map feature, I decided to use the
// .forEach feature to add data to the popupContent array
const onReadyPopups = (feature, layer) => {
  // empty array to contain popup content
  const popupContent = []
  // run through feature properties, if there are any and add them to array
  feature.properties.length !== undefined || feature.properties.length !== 0
    ? Object.keys(feature.properties).forEach(key => {
      popupContent.push(`<b>${key}</b>: ${feature.properties[key]}`)
    }) : console.log('No feature properties')
  // push coordinates to array, if the feature is of the type point
  feature.geometry.type === 'Point'
    ? popupContent.push(
        `<b>Latitude:</b> ${feature.geometry.coordinates[1]}`,
        `<b>Longitude:</b> ${feature.geometry.coordinates[0]}`
    ) : console.log(feature.geometry.type)
  layer.bindPopup(popupContent.join('
'))
}

// I need to make a function or something that takes the ext and calls the
// correct getData function

// build filtering select function

const getInputValue = (element) => element.value

// check longitude and latitude values
const checkVal = (val, defaultVal, minVal, maxVal) => {
  val = (typeof val === 'undefined') ? defaultVal
    : val === '' ? defaultVal
    : val < minVal ? minVal
    : val > maxVal ? maxVal
    : val
  return val
}

const filterValues = (feature, layer, minLng, maxLng, minLat, maxLat) => {
  // set default values for minimum and maximum lat and lng, incase no
  // values are passed through
  minLng = checkVal(getInputValue(minLngInput), -180, -180, 180)
  maxLng = checkVal(getInputValue(maxLngInput), 180, -180, 180)
  minLat = checkVal(getInputValue(minLatInput), -90, -90, 90)
  maxLat = checkVal(getInputValue(maxLatInput), 90, -90, 90)

  const coords = feature.geometry.coordinates
  const filteredData = coords[0] > minLng &&
                       coords[0] < maxLng &&
                       coords[1] > minLat &&
                       coords[1] < maxLat
  return filteredData
}

const buildFeaturePropertiesSelector = data => {
  // get keys for feature properties for first element dataset and add to array
  const featureKeys = Object.keys(dataset[0].features[0].properties)
    .map(key => key)

  // create elements
  const ifFeaturesElement = document.getElementById('if_features')
  const p = document.createElement('p')
  const span = document.createElement('span')
  const b = document.createElement('b')
  const text = document.createTextNode('Select property to filter by: ')
  const selector = document.createElement('select')
  const input = document.createElement('input')

  // set id of p and select elements
  p.setAttribute('id', 'selector_container')
  selector.setAttribute('id', 'property_selector')
  input.setAttribute('id', 'property_selector_input')
  input.setAttribute('type', 'text')

  // put them all together
  b.appendChild(text)
  span.appendChild(b)
  span.appendChild(selector)
  span.appendChild(input)
  p.appendChild(span)
  ifFeaturesElement.appendChild(p)

  // create and add options to the feature selector element
  featureKeys.forEach(i => {
    const opt = document.createElement('option')
    opt.value = i
    opt.innerHTML = i
    selector.appendChild(opt)
  })
}

const onDatasetImport = data => {
  // make sure that original data are saved somewhere
  dataset.push(data)

  // get keys for feature properties for first element dataset and add to array
  Object.keys(dataset[0].features[0].properties).length > 0
    ? buildFeaturePropertiesSelector(dataset)
    : console.log('No feature properties to filter')

  // add dataset to the filteredLayer and filteredLayer to allMarkers
  filteredLayer.addData(dataset)
  allMarkers.addLayer(filteredLayer)

  // get count of features in filteredLayer
  const featureCount = Object.keys(filteredLayer._layers).length
  featureCountElement.innerHTML = ` ${featureCount}`
}

// ////////////////////////////////////////////////////////////////////////////
/*
// Implement Functions
*/
// ////////////////////////////////////////////////////////////////////////////

// ////////////////////////////////////////////////////////////////////////////
// Special Interlude - Make constants that require functions to be created

// It looks like 'onEachFeature' can be called more than once
// It also looks like I need to check if the dataset is a points type or not
// how do I pass values to the filter function?
const filteredLayer = L.geoJson(null, {
  filter: filterValues,
  onEachFeature: onReadyPopups,
  pointToLayer: makeCircles
})

const allMarkers = L.markerClusterGroup({
  showCoverageOnHover: false,
  maxClusterRadius: 50
})
// ////////////////////////////////////////////////////////////////////////////

// Implement functions
// this one is sorta not in the right style
L.control.layers(baseLayers).addTo(myMap)

L.control.watermark = (options) => new L.Control.Watermark(options)
L.control.watermark({position: 'bottomleft'}).addTo(myMap)

L.control.homebutton = (options) => new L.Control.HomeButton(options)
L.control.homebutton({position: 'topleft'}).addTo(myMap)

L.control.togglescrollbutton = (options) => new L.Control.ToggleScrollButton(options)
L.control.togglescrollbutton({position: 'topleft'}).addTo(myMap)

// Add filteredLayer to the marker cluster stuff, then add the markercluster
// group to the map
// allMarkers.addLayer(filteredLayer)
// This should only work for points datasets
myMap.addLayer(allMarkers)

// make sure filteredLayer is added to the map
// I might have to create the filteredLayer with options here
// now every time a layer is added to the filteredLayer it automatically adds
// the popupContent and stuff
// Function that calls correct get___Dataset function based on ext
// this should be written differently

// I need to set this up as a function that returns a function, then the
// rest of the . notation stuff can be used, I think
ext === 'kml'
  ? getKMLDataset(datasetUrl)
    .then(onDatasetImport, error => console.log(error))
: ext === 'csv'
  ? getCSVDataset(datasetUrl)
    .then(onDatasetImport, error => console.log(error))
  : getJSONDataset(datasetUrl)
    .then(onDatasetImport, error => console.log(error))

// ////////////////////////////////////////////////////////////////////////////
// Add Event Listeners for function calls

// submit button:
// clearLayers in filteredLayer
// remove filteredLayer from allMarkers
// add data to filteredLayer -- and hopefully the data are filtered
// add filteredLayer to allMarkers

submitValuesButton.addEventListener('click', () => {
  filteredLayer.clearLayers()
  allMarkers.clearLayers()
  filteredLayer.addData(dataset)
  allMarkers.addLayer(filteredLayer)
  const featureCount = Object.keys(filteredLayer._layers).length
  featureCountElement.innerHTML = ` ${featureCount}`
})

// ////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>// ERPNext - web based ERP (http://erpnext.com)
// Copyright (C) 2012 Web Notes Technologies Pvt Ltd
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

cur_frm.fields_dict['delivery_note'].get_query = function(doc, cdt, cdn) {
	return 'SELECT name FROM `tabDelivery Note` WHERE docstatus=0 AND %(key)s LIKE "%s"';
}

cur_frm.fields_dict['item_details'].grid.get_field('item_code').get_query =
		function(doc, cdt, cdn) {
	var query = 'SELECT name, item_name, description FROM `tabItem` WHERE name IN ( \
		SELECT item_code FROM `tabDelivery Note Item` dnd \
		WHERE parent="'	+ doc.delivery_note + '" AND IFNULL(qty, 0) > IFNULL(packed_qty, 0)) AND %(key)s LIKE "%s" LIMIT 50';
	return query;
}

// Fetch item details
cur_frm.add_fetch("item_code", "item_name", "item_name");
cur_frm.add_fetch("item_code", "stock_uom", "stock_uom");
cur_frm.add_fetch("item_code", "net_weight", "net_weight");
cur_frm.add_fetch("item_code", "weight_uom", "weight_uom");

cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
	console.log(make_doclist(cdt, cdn));
	if(doc.delivery_note && doc.__islocal) {
		var ps_detail = getchildren('Packing Slip Item', doc.name, 'item_details');
		if(!(flt(ps_detail.net_weight) && cstr(ps_detail.weight_uom))) {
			cur_frm.cscript.update_item_details(doc);
		}
	}
}

cur_frm.cscript.refresh = function(doc, dt, dn) {
	if(!doc.amended_from) {
		hide_field('misc_details');
	} else {
		unhide_field('misc_details');
	}
}

cur_frm.cscript.update_item_details = function(doc) {
	$c_obj(make_doclist(doc.doctype, doc.name), 'update_item_details', '', function(r, rt) {
		if(r.exc) {
			msgprint(r.exc);
		} else {
			refresh_many(['item_details', 'naming_series', 'from_case_no', 'to_case_no'])
		}
	});
}

cur_frm.cscript.validate = function(doc, cdt, cdn) {
	cur_frm.cscript.validate_case_nos(doc);
	cur_frm.cscript.validate_calculate_item_details(doc);
}

// To Case No. cannot be less than From Case No.
cur_frm.cscript.validate_case_nos = function(doc) {
	doc = locals[doc.doctype][doc.name];
	if(cint(doc.from_case_no)==0) {
		msgprint("Case No. cannot be 0")
		validated = false;
	} else if(!cint(doc.to_case_no)) {
		doc.to_case_no = doc.from_case_no;
		refresh_field('to_case_no');
	} else if(cint(doc.to_case_no) < cint(doc.from_case_no)) {
		msgprint("'To Case No.' cannot be less than 'From Case No.'");
		validated = false;
	}
}

cur_frm.cscript.validate_calculate_item_details = function(doc) {
	doc = locals[doc.doctype][doc.name];
	var ps_detail = getchildren('Packing Slip Item', doc.name, 'item_details');

	cur_frm.cscript.validate_duplicate_items(doc, ps_detail);
	cur_frm.cscript.calc_net_total_pkg(doc, ps_detail);
}

// Do not allow duplicate items i.e. items with same item_code
// Also check for 0 qty
cur_frm.cscript.validate_duplicate_items = function(doc, ps_detail) {
	for(var i=0; i<ps_detail.length; i++) {
		for(var j=0; j<ps_detail.length; j++) {
			if(i!=j && ps_detail[i].item_code==ps_detail[j].item_code) {
				msgprint("You have entered duplicate items. Please rectify and try again.");
				validated = false;
				return;
			}
		}
		if(flt(ps_detail[i].qty)<=0) {
			msgprint("Invalid quantity specified for item " + ps_detail[i].item_code +
				". Quantity should be greater than 0.");
			validated = false;
		}
	}
}

// Calculate Net Weight of Package
cur_frm.cscript.calc_net_total_pkg = function(doc, ps_detail) {
	var net_weight_pkg = 0;
	doc.net_weight_uom = ps_detail?ps_detail[0].weight_uom:'';
	doc.gross_weight_uom = doc.net_weight_uom;

	for(var i=0; i<ps_detail.length; i++) {
		var item = ps_detail[i];
		if(item.weight_uom != doc.net_weight_uom) {
			msgprint("Different UOM for items will lead to incorrect \
			(Total) Net Weight value. Make sure that Net Weight of each item is \
			in the same UOM.")
			validated = false;
		}
		net_weight_pkg += flt(item.net_weight) * flt(item.qty);
	}

	doc.net_weight_pkg = roundNumber(net_weight_pkg, 2);
	if(!flt(doc.gross_weight_pkg)) {
		doc.gross_weight_pkg = doc.net_weight_pkg
	}
	refresh_many(['net_weight_pkg', 'net_weight_uom', 'gross_weight_uom', 'gross_weight_pkg']);
}

<commit_msg>[packing slip] [fix] removed console log<commit_after>// ERPNext - web based ERP (http://erpnext.com)
// Copyright (C) 2012 Web Notes Technologies Pvt Ltd
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

cur_frm.fields_dict['delivery_note'].get_query = function(doc, cdt, cdn) {
	return 'SELECT name FROM `tabDelivery Note` WHERE docstatus=0 AND %(key)s LIKE "%s"';
}

cur_frm.fields_dict['item_details'].grid.get_field('item_code').get_query =
		function(doc, cdt, cdn) {
	var query = 'SELECT name, item_name, description FROM `tabItem` WHERE name IN ( \
		SELECT item_code FROM `tabDelivery Note Item` dnd \
		WHERE parent="'	+ doc.delivery_note + '" AND IFNULL(qty, 0) > IFNULL(packed_qty, 0)) AND %(key)s LIKE "%s" LIMIT 50';
	return query;
}

// Fetch item details
cur_frm.add_fetch("item_code", "item_name", "item_name");
cur_frm.add_fetch("item_code", "stock_uom", "stock_uom");
cur_frm.add_fetch("item_code", "net_weight", "net_weight");
cur_frm.add_fetch("item_code", "weight_uom", "weight_uom");

cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
	if(doc.delivery_note && doc.__islocal) {
		var ps_detail = getchildren('Packing Slip Item', doc.name, 'item_details');
		if(!(flt(ps_detail.net_weight) && cstr(ps_detail.weight_uom))) {
			cur_frm.cscript.update_item_details(doc);
		}
	}
}

cur_frm.cscript.refresh = function(doc, dt, dn) {
	if(!doc.amended_from) {
		hide_field('misc_details');
	} else {
		unhide_field('misc_details');
	}
}

cur_frm.cscript.update_item_details = function(doc) {
	$c_obj(make_doclist(doc.doctype, doc.name), 'update_item_details', '', function(r, rt) {
		if(r.exc) {
			msgprint(r.exc);
		} else {
			refresh_many(['item_details', 'naming_series', 'from_case_no', 'to_case_no'])
		}
	});
}

cur_frm.cscript.validate = function(doc, cdt, cdn) {
	cur_frm.cscript.validate_case_nos(doc);
	cur_frm.cscript.validate_calculate_item_details(doc);
}

// To Case No. cannot be less than From Case No.
cur_frm.cscript.validate_case_nos = function(doc) {
	doc = locals[doc.doctype][doc.name];
	if(cint(doc.from_case_no)==0) {
		msgprint("Case No. cannot be 0")
		validated = false;
	} else if(!cint(doc.to_case_no)) {
		doc.to_case_no = doc.from_case_no;
		refresh_field('to_case_no');
	} else if(cint(doc.to_case_no) < cint(doc.from_case_no)) {
		msgprint("'To Case No.' cannot be less than 'From Case No.'");
		validated = false;
	}
}

cur_frm.cscript.validate_calculate_item_details = function(doc) {
	doc = locals[doc.doctype][doc.name];
	var ps_detail = getchildren('Packing Slip Item', doc.name, 'item_details');

	cur_frm.cscript.validate_duplicate_items(doc, ps_detail);
	cur_frm.cscript.calc_net_total_pkg(doc, ps_detail);
}

// Do not allow duplicate items i.e. items with same item_code
// Also check for 0 qty
cur_frm.cscript.validate_duplicate_items = function(doc, ps_detail) {
	for(var i=0; i<ps_detail.length; i++) {
		for(var j=0; j<ps_detail.length; j++) {
			if(i!=j && ps_detail[i].item_code==ps_detail[j].item_code) {
				msgprint("You have entered duplicate items. Please rectify and try again.");
				validated = false;
				return;
			}
		}
		if(flt(ps_detail[i].qty)<=0) {
			msgprint("Invalid quantity specified for item " + ps_detail[i].item_code +
				". Quantity should be greater than 0.");
			validated = false;
		}
	}
}

// Calculate Net Weight of Package
cur_frm.cscript.calc_net_total_pkg = function(doc, ps_detail) {
	var net_weight_pkg = 0;
	doc.net_weight_uom = ps_detail?ps_detail[0].weight_uom:'';
	doc.gross_weight_uom = doc.net_weight_uom;

	for(var i=0; i<ps_detail.length; i++) {
		var item = ps_detail[i];
		if(item.weight_uom != doc.net_weight_uom) {
			msgprint("Different UOM for items will lead to incorrect \
			(Total) Net Weight value. Make sure that Net Weight of each item is \
			in the same UOM.")
			validated = false;
		}
		net_weight_pkg += flt(item.net_weight) * flt(item.qty);
	}

	doc.net_weight_pkg = roundNumber(net_weight_pkg, 2);
	if(!flt(doc.gross_weight_pkg)) {
		doc.gross_weight_pkg = doc.net_weight_pkg
	}
	refresh_many(['net_weight_pkg', 'net_weight_uom', 'gross_weight_uom', 'gross_weight_pkg']);
}

<|endoftext|>


<commit_before>var currentTestName = "";
var currentSteps = [];
var currentStepIndex = 0;

var testCases = [];

var testMode = "manual";      // can be "auto"
var isRecursive = false;
var runningRecursive = false;

var snapshotPrefix = "";
var snapshotIndex = 0;

var advanceKey = "n";
var pathSeparator = ".";

var previousCameraMode;
var secondaryCameraHasBeenEnabled = false;

var previousThrottleFPS;

var downloadInProgress = false;
var loadingContentIsStillDisplayed = false;

// This will be set when each test case begins
var originFrame;

// origin is located 1m below avatar centre
// avatar eyes are set to 1.76 cm above the origin
const ORIGIN_FRAME_OFFSET = { x: 0.0, y: -1.0, z: 0.0 };
const VALIDATION_CAMERA_OFFSET = { x: 0.0, y: 1.76, z: 0.0 };

var spectatorCameraConfig;

TestCase = function (name, path, func, usePrimaryCamera) {
    this.name = name;
    this.path = path;
    this.func = func;
    this.usePrimaryCamera = usePrimaryCamera;
}

var currentTestCase = null;
var currentRecursiveTestCompleted = false;

// TAA will be disabled for each test.  A test may enable as desired
var fxaaWasOn;

//returns n as a string, padded to length characters with the character ch
function pad(n, length, ch) {
    ch = ch || '0';  // default is '0'
    n += '';         // convert n to string

    // returns n as is, if it is too long
    return (n.length >= length) ? n : new Array(length - n.length + 1).join(ch) + n;
}

function onDownloadInfoChanged(info) {
    // After download is complete, the "LOADING CONTENT..." message is still displayed for a short time
    if (info.downloading.length == 0 && info.pending == 0) {
        downloadInProgress = false;
        loadingContentIsStillDisplayed = true;
    }
}

var runOneStep = function (stepFunctor, stepIndex) {
    console.warn("Running step " + (stepIndex + 1) + "/" + (currentSteps.length) +": " + stepFunctor.name);

    if (isManualMode()) {
        Window.displayAnnouncement("Running step " + (stepIndex + 1) + "/" + (currentSteps.length) +": " + stepFunctor.name);
    }

    if (stepFunctor.func !== undefined) {
        stepFunctor.func();
    }

    // Not quite sure this is the definitive solution here because of the snapshot bug latency issue.
    // but this seems to work ok if the snapshot is a separate step
    if ((stepFunctor.snap !== undefined) && stepFunctor.snap) {
        console.warn("Taking snapshot for step " + (stepIndex + 1));

        // Image numbers are padded to 5 digits
        // Changing this number requires changing the nitpick C++ code!
        var NUM_DIGITS = 5;
        var currentSnapshotName = snapshotPrefix + pad(snapshotIndex, NUM_DIGITS, '0') + ".png";

        currentTestCase.usePrimaryCamera
            ? Window.takeSnapshot(isManualMode(), false, 0.0, currentSnapshotName)
            : Window.takeSecondaryCameraSnapshot(isManualMode(), currentSnapshotName);

        ++snapshotIndex;
    }
}

var runNextStep = function () {
    // Run next step and increment only if there is one more
    if (currentStepIndex < currentSteps.length) {
        runOneStep(currentSteps[currentStepIndex], currentStepIndex);
        currentStepIndex++;
    }

    // Return true to go on or false if done
    return (currentStepIndex < currentSteps.length);
}

var autoTimeStep = 2000;

var onRunAutoNext = function() {
    var timeStep = autoTimeStep;

    // If not downloading then run the next step...
    if (!downloadInProgress  && !loadingContentIsStillDisplayed) {
        // Only run next step if current step is complete
        if (!runNextStep()) {
            tearDownTest();
            return;
        }
    } else if (!downloadInProgress) {
        // This assumes the message is displayed for not more than 4 seconds
        console.warn("Waiting for 'LOADING CONTENT...' message to be removed");
        timeStep = 4000;
        loadingContentIsStillDisplayed = false;
    } else {
        console.warn("Waiting for download to complete");
    }

    // and call itself after next timer
    Script.setTimeout(
        onRunAutoNext,
        timeStep
    );
}

var onKeyPressEventNextStep = function (event) {
    if (String.fromCharCode(event.key) == advanceKey.toUpperCase()) {
        if (!runNextStep()) {
            tearDownTest();
        }
    }
}

var onRunManual = function() {
    Window.displayAnnouncement(
        "Ready to run test " + currentTestName + "\n" +
        currentSteps.length + " steps\nPress " + "'" + advanceKey + "'" + " for next steps");

    Controller.keyPressEvent.connect(onKeyPressEventNextStep);
}

function isManualMode() {
    return (testMode === "manual");
}

var onRunAuto = function() {
    // run the next step after next timer
    Script.setTimeout(
        onRunAutoNext,
        autoTimeStep
    );
}

// Add Steps to the test case
var doAddStep = function (name, stepFunction, snapshot) {
    currentSteps.push({"index": currentSteps.length, "name": name, "func": stepFunction, "snap": snapshot })
    console.warn("PUSHING STEP" + currentSteps.length);
}

runOneTestCase = function(testCase, testType) {
    setUpTest(testCase);
    testCase.func(testType);
}

setUpTest = function(testCase) {
    // Setup origin
    originFrame = Vec3.sum(MyAvatar.position, ORIGIN_FRAME_OFFSET);

    currentStepIndex = 0;
    currentTestName = testCase.name;

    // resolvePath(".") returns a string that looks like "file:/" + <current folder>
    // We need the current folder
    var path = testCase.path.substring(testCase.path.indexOf(":") + 4);
    var pathParts = path.split("/");

    // Snapshots are saved in the user-selected folder
    // For a test running from D:/GitHub/hifi-tests/tests/content/entity/zone/create/tests.js
    // the tests are named tests.content.entity.zone.create.00000.jpg and so on
    // (assuming pathSeparator is ".")
    // Date and time are not used as part of the name, to keep the path lengths to a minimum
    // (the Windows API limit is 260 characters).
    //
    // Find location of "tests"
    var testsIndex;
    for (testsIndex = pathParts.length - 1; testsIndex > 0; --testsIndex) {
        if (pathParts[testsIndex] === "tests") {
            break;
        }
    }

    snapshotPrefix = pathParts[testsIndex];
    for (var i = testsIndex + 1; i < pathParts.length; ++i) {
        snapshotPrefix += pathSeparator + pathParts[i];
    }

    snapshotIndex = 0;

    // Setup validation camera
    var p0 = Vec3.sum(VALIDATION_CAMERA_OFFSET, Vec3.sum(MyAvatar.position, ORIGIN_FRAME_OFFSET));
    var q0 = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0);

    if (testCase.usePrimaryCamera) {
        // Set the camera mode to independent
        previousCameraMode = Camera.mode;
        Camera.mode = "independent";

        Camera.setOrientation(q0);
        Camera.setPosition(p0);

        // The avatar is also pointed down the Z axis, so the test can be seen on-screen
        MyAvatar.orientation = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0);
        MyAvatar.headYaw =   0.0;
        MyAvatar.headPitch = 0.0;
        MyAvatar.headRoll =  0.0;
    } else {
        spectatorCameraConfig = Render.getConfig("SecondaryCamera");

        // Turn on secondary camera if not already on
        if (!spectatorCameraConfig.enabled) {
            secondaryCameraHasBeenEnabled = true;
            spectatorCameraConfig.enableSecondaryCameraRenderConfigs(true);
        }

        spectatorCameraConfig.resetSizeSpectatorCamera(1920, 1080);
        spectatorCameraConfig.vFoV = 45;
        Render.getConfig("SecondaryCameraJob.ToneMapping").curve = 0;
        Render.getConfig("SecondaryCameraJob.DrawHighlight").enabled = false;

        spectatorCameraConfig.orientation = q0;
        spectatorCameraConfig.position = p0;
    }

    // Hide the avatar
    MyAvatar.setEnableMeshVisible(false);

    if (typeof Test !== 'undefined') {
        // In command line mode, maximize window size
        // so that primary camera snapshots will have the correct size
        // This is not used on Mac (an AppleScript is used for that)
        if (Test.getOperatingSystemType() === 'WINDOWS') {
            Test.showMaximized();
        }
    }

    if (!isManualMode()) {
        // Also, remove 2D overlays and mouse from the window, so that they won't appear in snapshots
        Menu.setIsOptionChecked("Show Overlays", false);
        Reticle.visible = false;
        Reticle.allowMouseCapture = false;
    }

    // Set callback for changes in download status.  This is used so we don't advance steps when data is downloading
    AccountServices.downloadInfoChanged.connect(onDownloadInfoChanged);
    AccountServices.updateDownloadInfo();

    // Enforce Desktop display (unless manual mode)
    if (!isManualMode()) {
        Menu.setIsOptionChecked("Desktop", true);
    }

    // Disable TAA
    fxaaWasOn = Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff;
    Render.getConfig("RenderMainView.JitterCam").none();
    Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = true;

    // This is needed to enable valid tests when Interface does not have focus
    // The problem is that models aren't rendered when there is no focus
    previousThrottleFPS = Menu.isOptionChecked("Throttle FPS If Not Focus");
    Menu.setIsOptionChecked("Throttle FPS If Not Focus", false)
}

tearDownTest = function() {
    // Clear the test case steps
    currentSteps = [];

   // Reset camera mode if needed
    if (previousCameraMode) {
        Camera.mode = previousCameraMode;
    }

    if (isManualMode()) {
        // Disconnect key event
        Controller.keyPressEvent.disconnect(onKeyPressEventNextStep);
        Window.displayAnnouncement("Test " + currentTestName + " have been completed");
    }

    if (!isManualMode()) {
        //Restore 2D overlays and mouse from the window
        Menu.setIsOptionChecked("Show Overlays", true);
        Reticle.visible = true;
        Reticle.allowMouseCapture = true;
    }

    // Show the avatar
    MyAvatar.setEnableMeshVisible(true);

    if (isRecursive) {
        currentRecursiveTestCompleted = true;
    } else {
        // Just stop the script
        Script.stop();
    }

    // Turn secondary camera off if it was off before test
    if (secondaryCameraHasBeenEnabled) {
        spectatorCameraConfig.enableSecondaryCameraRenderConfigs(false);
    }

    // Disconnect callback
    AccountServices.downloadInfoChanged.disconnect(onDownloadInfoChanged);

    // Enable TAA as required
    if (!fxaaWasOn) {
        Render.getConfig("RenderMainView.JitterCam").play();
        Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = false;
    }

    // Restore as required
    Menu.setIsOptionChecked("Throttle FPS If Not Focus", previousThrottleFPS)
}

validationCamera_setTranslation = function(position) {
    // The camera position is the sum of the origin frame, position (relative to that frame) and the eye (i.e. camera) offset
    var cameraPosition = Vec3.sum(originFrame, Vec3.sum(position, VALIDATION_CAMERA_OFFSET));

    if (currentTestCase.usePrimaryCamera) {
        Camera.setPosition(cameraPosition);
    } else {
        spectatorCameraConfig.position = cameraPosition;
    }
}

validationCamera_translate = function (offset) {
    if (currentTestCase.usePrimaryCamera) {
        Camera.setPosition(Vec3.sum(Camera.getPosition(), offset));
    } else {
        spectatorCameraConfig.position = Vec3.sum(spectatorCameraConfig.position, offset);
    }
}

validationCamera_setRotation = function (rotation) {
    var orientation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z);
    if (currentTestCase.usePrimaryCamera) {
        Camera.setOrientation(orientation);
    } else {
        spectatorCameraConfig.orientation = orientation;
    }
}

// The following are exported methods, accessible to test scripts

// Perform is the main method of a test
//      testName - name of the test
//      testPath - path the test is executing in
//      testMain - a function that creates the test
//
// The method creates a test case in currentTestCase.
// If the test mode is manual or auto then its execution is started
module.exports.perform = function (testName, testPath, validationCamera, testMain) {
    var usePrimaryCamera = (validationCamera === "primary");
    currentTestCase = new TestCase(testName, testPath, testMain, usePrimaryCamera);

    // Manual and auto tests are run immediately, recursive tests are stored in a queue
    if (isRecursive) {
        console.warn("Not running yet - in recursive mode");
        testCases.push(currentTestCase);
    } else if (isManualMode()) {
        console.warn("Begin manual test:" + testName);
        runOneTestCase(currentTestCase, "manual");
    } else { // testMode === "auto"
        console.warn("Begin auto test:" + testName);
        runOneTestCase(currentTestCase, "auto");
    }
}

// Add steps to the test case, take snapshot if 3rd parameter is true
module.exports.addStep = function (name, stepFunction) {
    doAddStep(name, stepFunction, false);
}

module.exports.addDelay = function (delaySeconds) {
    // Ignore this function in manual mode
    if (!isManualMode()) {
        var timeStepSeconds = autoTimeStep / 1000;
        var numDelays = 1 + delaySeconds / timeStepSeconds;
        for (var i = 0; i < numDelays; ++i) {
            doAddStep(String(timeStepSeconds) + " seconds delay");
        }
    }
}

// Add steps to the test case, take snapshot
module.exports.addStepSnapshot = function (name, stepFunction) {
    doAddStep(name, stepFunction, true);
}

// The default mode is manual
// The default time between test steps may be modified through these methods
module.exports.enableAuto = function () {
    testMode = "auto";

    console.warn("TEST MODE AUTO SELECTED");
}

module.exports.enableRecursive = function () {
    isRecursive = true;
    console.warn("TEST MODE RECURSIVE SELECTED");
}

// Steps is an array of functions; each function being a test step
module.exports.runTest = function (testType) {
    // In recursive mode, this call is ignored during script load
    if (isRecursive && !runningRecursive) {
        return;
    }

    if (testType  === "auto") {
        onRunAuto();
    } else { // testType === "manual"
        onRunManual();
    }
}

module.exports.runRecursive = function () {
    console.warn("Starting recursive tests");
    runningRecursive = true;

    currentRecursiveTestCompleted = true;
    Script.setInterval(
        function () {
            if (currentRecursiveTestCompleted) {
                currentRecursiveTestCompleted = false;
                if (testCases.length > 0) {
                    currentTestCase = testCases.pop();
                    runOneTestCase(currentTestCase, testMode);
                } else {
                    console.warn("Recursive tests complete");

                    // Create "finished" file so nitpick knows tests ran to completion
                    //    note that the contents are not important
                    if (typeof Test !== 'undefined') {
                        Test.saveObject({ complete: true }, "tests_completed.txt");
                    };

                    Script.stop();
                }
            }
        },
        1000
    );
}

// This is a position; the orientation is assumed to be looking down the Z axis, Y is up.
module.exports.getOriginFrame = function () {
    return originFrame;
}

// Utilities to provide GitHub repository path
var _repositoryPath;
module.exports.setRepositoryInfo = function (repositoryPath) {
    _repositoryPath = repositoryPath;
}

module.exports.getRepositoryPath = function () {
    return _repositoryPath;
}

module.exports.getTestsRootPath = function () {
    return _repositoryPath + "tests/";
}

module.exports.getUtilsRootPath = function () {
    return _repositoryPath + "tests/utils/";
}

module.exports.getAssetsRootPath = function () {
    return _repositoryPath + "assets/";
}
<commit_msg>Mitigate Mac LOD issues by setting LOD level to specific value on Mac.<commit_after>var currentTestName = "";
var currentSteps = [];
var currentStepIndex = 0;

var testCases = [];

var testMode = "manual";      // can be "auto"
var isRecursive = false;
var runningRecursive = false;

var snapshotPrefix = "";
var snapshotIndex = 0;

var advanceKey = "n";
var pathSeparator = ".";

var previousCameraMode;
var secondaryCameraHasBeenEnabled = false;

var previousThrottleFPS;

var downloadInProgress = false;
var loadingContentIsStillDisplayed = false;

// This will be set when each test case begins
var originFrame;

// origin is located 1m below avatar centre
// avatar eyes are set to 1.76 cm above the origin
const ORIGIN_FRAME_OFFSET = { x: 0.0, y: -1.0, z: 0.0 };
const VALIDATION_CAMERA_OFFSET = { x: 0.0, y: 1.76, z: 0.0 };

var spectatorCameraConfig;

TestCase = function (name, path, func, usePrimaryCamera) {
    this.name = name;
    this.path = path;
    this.func = func;
    this.usePrimaryCamera = usePrimaryCamera;
}

var currentTestCase = null;
var currentRecursiveTestCompleted = false;

// TAA will be disabled for each test.  A test may enable as desired
var fxaaWasOn;

//returns n as a string, padded to length characters with the character ch
function pad(n, length, ch) {
    ch = ch || '0';  // default is '0'
    n += '';         // convert n to string

    // returns n as is, if it is too long
    return (n.length >= length) ? n : new Array(length - n.length + 1).join(ch) + n;
}

function onDownloadInfoChanged(info) {
    // After download is complete, the "LOADING CONTENT..." message is still displayed for a short time
    if (info.downloading.length == 0 && info.pending == 0) {
        downloadInProgress = false;
        loadingContentIsStillDisplayed = true;
    }
}

var runOneStep = function (stepFunctor, stepIndex) {
    console.warn("Running step " + (stepIndex + 1) + "/" + (currentSteps.length) +": " + stepFunctor.name);

    if (isManualMode()) {
        Window.displayAnnouncement("Running step " + (stepIndex + 1) + "/" + (currentSteps.length) +": " + stepFunctor.name);
    }

    if (stepFunctor.func !== undefined) {
        stepFunctor.func();
    }

    // Not quite sure this is the definitive solution here because of the snapshot bug latency issue.
    // but this seems to work ok if the snapshot is a separate step
    if ((stepFunctor.snap !== undefined) && stepFunctor.snap) {
        console.warn("Taking snapshot for step " + (stepIndex + 1));

        // Image numbers are padded to 5 digits
        // Changing this number requires changing the nitpick C++ code!
        var NUM_DIGITS = 5;
        var currentSnapshotName = snapshotPrefix + pad(snapshotIndex, NUM_DIGITS, '0') + ".png";

        currentTestCase.usePrimaryCamera
            ? Window.takeSnapshot(isManualMode(), false, 0.0, currentSnapshotName)
            : Window.takeSecondaryCameraSnapshot(isManualMode(), currentSnapshotName);

        ++snapshotIndex;
    }
}

var runNextStep = function () {
    // Run next step and increment only if there is one more
    if (currentStepIndex < currentSteps.length) {
        runOneStep(currentSteps[currentStepIndex], currentStepIndex);
        currentStepIndex++;
    }

    // Return true to go on or false if done
    return (currentStepIndex < currentSteps.length);
}

var autoTimeStep = 2000;

var onRunAutoNext = function() {
    var timeStep = autoTimeStep;

    // If not downloading then run the next step...
    if (!downloadInProgress  && !loadingContentIsStillDisplayed) {
        // Only run next step if current step is complete
        if (!runNextStep()) {
            tearDownTest();
            return;
        }
    } else if (!downloadInProgress) {
        // This assumes the message is displayed for not more than 4 seconds
        console.warn("Waiting for 'LOADING CONTENT...' message to be removed");
        timeStep = 4000;
        loadingContentIsStillDisplayed = false;
    } else {
        console.warn("Waiting for download to complete");
    }

    // and call itself after next timer
    Script.setTimeout(
        onRunAutoNext,
        timeStep
    );
}

var onKeyPressEventNextStep = function (event) {
    if (String.fromCharCode(event.key) == advanceKey.toUpperCase()) {
        if (!runNextStep()) {
            tearDownTest();
        }
    }
}

var onRunManual = function() {
    Window.displayAnnouncement(
        "Ready to run test " + currentTestName + "\n" +
        currentSteps.length + " steps\nPress " + "'" + advanceKey + "'" + " for next steps");

    Controller.keyPressEvent.connect(onKeyPressEventNextStep);
}

function isManualMode() {
    return (testMode === "manual");
}

var onRunAuto = function() {
    // run the next step after next timer
    Script.setTimeout(
        onRunAutoNext,
        autoTimeStep
    );
}

// Add Steps to the test case
var doAddStep = function (name, stepFunction, snapshot) {
    currentSteps.push({"index": currentSteps.length, "name": name, "func": stepFunction, "snap": snapshot })
    console.warn("PUSHING STEP" + currentSteps.length);
}

runOneTestCase = function(testCase, testType) {
    setUpTest(testCase);
    testCase.func(testType);
}

setUpTest = function(testCase) {
    // Setup origin
    originFrame = Vec3.sum(MyAvatar.position, ORIGIN_FRAME_OFFSET);

    currentStepIndex = 0;
    currentTestName = testCase.name;

    // resolvePath(".") returns a string that looks like "file:/" + <current folder>
    // We need the current folder
    var path = testCase.path.substring(testCase.path.indexOf(":") + 4);
    var pathParts = path.split("/");

    // Snapshots are saved in the user-selected folder
    // For a test running from D:/GitHub/hifi-tests/tests/content/entity/zone/create/tests.js
    // the tests are named tests.content.entity.zone.create.00000.jpg and so on
    // (assuming pathSeparator is ".")
    // Date and time are not used as part of the name, to keep the path lengths to a minimum
    // (the Windows API limit is 260 characters).
    //
    // Find location of "tests"
    var testsIndex;
    for (testsIndex = pathParts.length - 1; testsIndex > 0; --testsIndex) {
        if (pathParts[testsIndex] === "tests") {
            break;
        }
    }

    snapshotPrefix = pathParts[testsIndex];
    for (var i = testsIndex + 1; i < pathParts.length; ++i) {
        snapshotPrefix += pathSeparator + pathParts[i];
    }

    snapshotIndex = 0;

    // Setup validation camera
    var p0 = Vec3.sum(VALIDATION_CAMERA_OFFSET, Vec3.sum(MyAvatar.position, ORIGIN_FRAME_OFFSET));
    var q0 = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0);

    if (testCase.usePrimaryCamera) {
        // Set the camera mode to independent
        previousCameraMode = Camera.mode;
        Camera.mode = "independent";

        Camera.setOrientation(q0);
        Camera.setPosition(p0);

        // The avatar is also pointed down the Z axis, so the test can be seen on-screen
        MyAvatar.orientation = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0);
        MyAvatar.headYaw =   0.0;
        MyAvatar.headPitch = 0.0;
        MyAvatar.headRoll =  0.0;
    } else {
        spectatorCameraConfig = Render.getConfig("SecondaryCamera");

        // Turn on secondary camera if not already on
        if (!spectatorCameraConfig.enabled) {
            secondaryCameraHasBeenEnabled = true;
            spectatorCameraConfig.enableSecondaryCameraRenderConfigs(true);
        }

        spectatorCameraConfig.resetSizeSpectatorCamera(1920, 1080);
        spectatorCameraConfig.vFoV = 45;
        Render.getConfig("SecondaryCameraJob.ToneMapping").curve = 0;
        Render.getConfig("SecondaryCameraJob.DrawHighlight").enabled = false;

        spectatorCameraConfig.orientation = q0;
        spectatorCameraConfig.position = p0;
    }

    // Hide the avatar
    MyAvatar.setEnableMeshVisible(false);

    if (typeof Test !== 'undefined') {
        // In command line mode, maximize window size
        // so that primary camera snapshots will have the correct size
        // This is not used on Mac (an AppleScript is used for that)
        if (Test.getOperatingSystemType() === 'WINDOWS') {
            Test.showMaximized();
        } else if (Test.getOperatingSystemType() === 'MACOS') {
            // Mitigate Mac LOD issues
            LODManager.setAutomaticLODAdjust(false);
            LODManager.setOctreeSizeScale(8000000);
        }
        }
    }

    if (!isManualMode()) {
        // Also, remove 2D overlays and mouse from the window, so that they won't appear in snapshots
        Menu.setIsOptionChecked("Show Overlays", false);
        Reticle.visible = false;
        Reticle.allowMouseCapture = false;
    }

    // Set callback for changes in download status.  This is used so we don't advance steps when data is downloading
    AccountServices.downloadInfoChanged.connect(onDownloadInfoChanged);
    AccountServices.updateDownloadInfo();

    // Enforce Desktop display (unless manual mode)
    if (!isManualMode()) {
        Menu.setIsOptionChecked("Desktop", true);
    }

    // Disable TAA
    fxaaWasOn = Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff;
    Render.getConfig("RenderMainView.JitterCam").none();
    Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = true;

    // This is needed to enable valid tests when Interface does not have focus
    // The problem is that models aren't rendered when there is no focus
    previousThrottleFPS = Menu.isOptionChecked("Throttle FPS If Not Focus");
    Menu.setIsOptionChecked("Throttle FPS If Not Focus", false)
}

tearDownTest = function() {
    // Clear the test case steps
    currentSteps = [];

   // Reset camera mode if needed
    if (previousCameraMode) {
        Camera.mode = previousCameraMode;
    }

    if (isManualMode()) {
        // Disconnect key event
        Controller.keyPressEvent.disconnect(onKeyPressEventNextStep);
        Window.displayAnnouncement("Test " + currentTestName + " have been completed");
    }

    if (!isManualMode()) {
        //Restore 2D overlays and mouse from the window
        Menu.setIsOptionChecked("Show Overlays", true);
        Reticle.visible = true;
        Reticle.allowMouseCapture = true;
    }

    // Show the avatar
    MyAvatar.setEnableMeshVisible(true);

    if (isRecursive) {
        currentRecursiveTestCompleted = true;
    } else {
        // Just stop the script
        Script.stop();
    }

    // Turn secondary camera off if it was off before test
    if (secondaryCameraHasBeenEnabled) {
        spectatorCameraConfig.enableSecondaryCameraRenderConfigs(false);
    }

    // Disconnect callback
    AccountServices.downloadInfoChanged.disconnect(onDownloadInfoChanged);

    // Enable TAA as required
    if (!fxaaWasOn) {
        Render.getConfig("RenderMainView.JitterCam").play();
        Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = false;
    }

    // Restore as required
    Menu.setIsOptionChecked("Throttle FPS If Not Focus", previousThrottleFPS)
}

validationCamera_setTranslation = function(position) {
    // The camera position is the sum of the origin frame, position (relative to that frame) and the eye (i.e. camera) offset
    var cameraPosition = Vec3.sum(originFrame, Vec3.sum(position, VALIDATION_CAMERA_OFFSET));

    if (currentTestCase.usePrimaryCamera) {
        Camera.setPosition(cameraPosition);
    } else {
        spectatorCameraConfig.position = cameraPosition;
    }
}

validationCamera_translate = function (offset) {
    if (currentTestCase.usePrimaryCamera) {
        Camera.setPosition(Vec3.sum(Camera.getPosition(), offset));
    } else {
        spectatorCameraConfig.position = Vec3.sum(spectatorCameraConfig.position, offset);
    }
}

validationCamera_setRotation = function (rotation) {
    var orientation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z);
    if (currentTestCase.usePrimaryCamera) {
        Camera.setOrientation(orientation);
    } else {
        spectatorCameraConfig.orientation = orientation;
    }
}

// The following are exported methods, accessible to test scripts

// Perform is the main method of a test
//      testName - name of the test
//      testPath - path the test is executing in
//      testMain - a function that creates the test
//
// The method creates a test case in currentTestCase.
// If the test mode is manual or auto then its execution is started
module.exports.perform = function (testName, testPath, validationCamera, testMain) {
    var usePrimaryCamera = (validationCamera === "primary");
    currentTestCase = new TestCase(testName, testPath, testMain, usePrimaryCamera);

    // Manual and auto tests are run immediately, recursive tests are stored in a queue
    if (isRecursive) {
        console.warn("Not running yet - in recursive mode");
        testCases.push(currentTestCase);
    } else if (isManualMode()) {
        console.warn("Begin manual test:" + testName);
        runOneTestCase(currentTestCase, "manual");
    } else { // testMode === "auto"
        console.warn("Begin auto test:" + testName);
        runOneTestCase(currentTestCase, "auto");
    }
}

// Add steps to the test case, take snapshot if 3rd parameter is true
module.exports.addStep = function (name, stepFunction) {
    doAddStep(name, stepFunction, false);
}

module.exports.addDelay = function (delaySeconds) {
    // Ignore this function in manual mode
    if (!isManualMode()) {
        var timeStepSeconds = autoTimeStep / 1000;
        var numDelays = 1 + delaySeconds / timeStepSeconds;
        for (var i = 0; i < numDelays; ++i) {
            doAddStep(String(timeStepSeconds) + " seconds delay");
        }
    }
}

// Add steps to the test case, take snapshot
module.exports.addStepSnapshot = function (name, stepFunction) {
    doAddStep(name, stepFunction, true);
}

// The default mode is manual
// The default time between test steps may be modified through these methods
module.exports.enableAuto = function () {
    testMode = "auto";

    console.warn("TEST MODE AUTO SELECTED");
}

module.exports.enableRecursive = function () {
    isRecursive = true;
    console.warn("TEST MODE RECURSIVE SELECTED");
}

// Steps is an array of functions; each function being a test step
module.exports.runTest = function (testType) {
    // In recursive mode, this call is ignored during script load
    if (isRecursive && !runningRecursive) {
        return;
    }

    if (testType  === "auto") {
        onRunAuto();
    } else { // testType === "manual"
        onRunManual();
    }
}

module.exports.runRecursive = function () {
    console.warn("Starting recursive tests");
    runningRecursive = true;

    currentRecursiveTestCompleted = true;
    Script.setInterval(
        function () {
            if (currentRecursiveTestCompleted) {
                currentRecursiveTestCompleted = false;
                if (testCases.length > 0) {
                    currentTestCase = testCases.pop();
                    runOneTestCase(currentTestCase, testMode);
                } else {
                    console.warn("Recursive tests complete");

                    // Create "finished" file so nitpick knows tests ran to completion
                    //    note that the contents are not important
                    if (typeof Test !== 'undefined') {
                        Test.saveObject({ complete: true }, "tests_completed.txt");
                    };

                    Script.stop();
                }
            }
        },
        1000
    );
}

// This is a position; the orientation is assumed to be looking down the Z axis, Y is up.
module.exports.getOriginFrame = function () {
    return originFrame;
}

// Utilities to provide GitHub repository path
var _repositoryPath;
module.exports.setRepositoryInfo = function (repositoryPath) {
    _repositoryPath = repositoryPath;
}

module.exports.getRepositoryPath = function () {
    return _repositoryPath;
}

module.exports.getTestsRootPath = function () {
    return _repositoryPath + "tests/";
}

module.exports.getUtilsRootPath = function () {
    return _repositoryPath + "tests/utils/";
}

module.exports.getAssetsRootPath = function () {
    return _repositoryPath + "assets/";
}
<|endoftext|>


<commit_before>var chai = require('chai');
var expect = chai.expect;
var wildcardRegex = require('../wildcard-regex.js')

describe('wildcardRegex', function() {

  describe('when called with a String', function() {

    it('returns a RegExp object', function() {
      var wildcardString = 'We have all of your *';
      var regex = wildcardRegex(wildcardString);
      expect(regex.constructor).to.equal(RegExp);
    });

    it('correctly tests for patterns with a ending wildcard', function() {
      var wildcardEnd = 'The answer is *';
      var regexEnd = wildcardRegex(wildcardEnd);
      var correctTestString = 'The answer is here!';
      var incorrectTestString = 'Wrong!';

      expect(regexEnd.test(correctTestString)).to.equal(true);
      expect(regexEnd.test(incorrectTestString)).to.equal(false);
    });

    it('correctly tests for patterns with a starting wildcard', function() {
      var wildcardStart = '* error was found';
      var regexStart = wildcardRegex(wildcardStart);
      var correctTestString = '404 error was found';
      var incorrectTestString = 'Wrong!';

      expect(regexStart.test(correctTestString)).to.equal(true);
      expect(regexStart.test(incorrectTestString)).to.equal(false);
    });

    it('correctly tests for patterns with a middle wildcard', function() {
      var wildcardMiddle = 'The * was not found';
      var regexMiddle = wildcardRegex(wildcardMiddle);
      var correctTestString = 'The resource was not found';
      var incorrectTestString = 'Wrong!';

      expect(regexMiddle.test(correctTestString)).to.equal(true);
      expect(regexMiddle.test(incorrectTestString)).to.equal(false);
    });

  });

  describe('when called with an Array of strings', function() {

    it('returns a RegExp object', function() {
      var wildcardArray = ['t*', 'b*b', '*em'];
      var regex = wildcardRegex(wildcardArray);
      expect(regex.constructor).to.equal(RegExp);
    });

    it('correctly tests for multiple patterns from an array of wildcard strings', function() {
      var wildcardArray = ['t*', 'b*b', '*em'];
      var regex = wildcardRegex(wildcardArray);
      expect(regex.test('test')).to.equal(true);
      expect(regex.test('bob')).to.equal(true);
      expect(regex.test('team')).to.equal(true);
      expect(regex.test('wrong')).to.equal(false);
    });

  });

});
<commit_msg>Add test for empty string pattern<commit_after>var chai = require('chai');
var expect = chai.expect;
var wildcardRegex = require('../wildcard-regex.js')

describe('wildcardRegex', function() {

  describe('when called with a String', function() {

    it('returns a RegExp object', function() {
      var wildcardString = 'We have all of your *';
      var regex = wildcardRegex(wildcardString);
      expect(regex.constructor).to.equal(RegExp);
    });

    it('correctly tests for patterns with a ending wildcard', function() {
      var wildcardEnd = 'The answer is *';
      var regexEnd = wildcardRegex(wildcardEnd);
      var correctTestString = 'The answer is here!';
      var incorrectTestString = 'Wrong!';

      expect(regexEnd.test(correctTestString)).to.equal(true);
      expect(regexEnd.test(incorrectTestString)).to.equal(false);
    });

    it('correctly tests for patterns with a starting wildcard', function() {
      var wildcardStart = '* error was found';
      var regexStart = wildcardRegex(wildcardStart);
      var correctTestString = '404 error was found';
      var incorrectTestString = 'Wrong!';

      expect(regexStart.test(correctTestString)).to.equal(true);
      expect(regexStart.test(incorrectTestString)).to.equal(false);
    });

    it('correctly tests for patterns with a middle wildcard', function() {
      var wildcardMiddle = 'The * was not found';
      var regexMiddle = wildcardRegex(wildcardMiddle);
      var correctTestString = 'The resource was not found';
      var incorrectTestString = 'Wrong!';

      expect(regexMiddle.test(correctTestString)).to.equal(true);
      expect(regexMiddle.test(incorrectTestString)).to.equal(false);
    });

    it('correctly tests for an empty string pattern', function() {
      var wildcardEmptyString = '';
      var regexEmptyString = wildcardRegex(wildcardEmptyString);
      var correctTestString = '';
      var incorrectTestString = 'Wrong!';

      expect(regexEmptyString.test(correctTestString)).to.equal(true);
      expect(regexEmptyString.test(incorrectTestString)).to.equal(false);
    });

  });

  describe('when called with an Array of strings', function() {

    it('returns a RegExp object', function() {
      var wildcardArray = ['t*', 'b*b', '*em'];
      var regex = wildcardRegex(wildcardArray);
      expect(regex.constructor).to.equal(RegExp);
    });

    it('correctly tests for multiple patterns from an array of wildcard strings', function() {
      var wildcardArray = ['t*', 'b*b', '*em'];
      var regex = wildcardRegex(wildcardArray);
      expect(regex.test('test')).to.equal(true);
      expect(regex.test('bob')).to.equal(true);
      expect(regex.test('team')).to.equal(true);
      expect(regex.test('wrong')).to.equal(false);
    });

  });

});
<|endoftext|>


<commit_before>//************//
// Couche DAO //
//************//
// -> CLIENT : receive JSON frame from "metier.js"
// -> CLIENT : send JSON frame to Arduino
// -> CLIENT : get Arduino JSON answer
// -> SERVER : keep a list of connected Arduinos
// -> Transmit JSON answer to 'metier.js'

// Imports modules:
var util = require('util');

//////// CLIENT : send request to Arduino
exports.send = function(ipArduino, jsonObject) {
	return jsonObject;
}

//////// SERVER : receive arduino authntication

<commit_msg>Registration server added<commit_after>//************//
// Couche DAO //
//************//
// -> CLIENT : receive JSON frame from "metier.js"
// -> CLIENT : send JSON frame to Arduino
// -> CLIENT : get Arduino JSON answer
// -> SERVER : keep a list of connected Arduinos
// -> Transmit JSON answer to 'metier.js'

// Imports modules:
var util = require('util');
var net = require('net');

//////// CLIENT : send request to Arduino
exports.send = function(ipArduino, jsonObject) {
	return jsonObject;
}

//////// SERVER : receive arduino authntication
var HOST = '192.168.2.1';
var PORT = 100;
var arduinos = [];

//registration server
net.createServer(function(sock) {
	var data = '';

	// We have a connection - a socket object is assigned to the connection automatically
	console.log('CONNECTED: ' + sock.remoteAddress +':'+ sock.remotePort);

	sock.on('data', function(chunk) { //called every time data is received
        	data += chunk;  //add data chunk to data
	});

	sock.on('end', function() { //called when end of line character is received
		//console.log('DATA ' + sock.remoteAddress + ': ' + data);
		arduinos.push(data);
	});

	// Add a 'close' event handler to this instance of socket
	sock.on('close', function(data) {
		console.log('CLOSED: ' + sock.remoteAddress +' '+ sock.remotePort);
	});

}).listen(PORT, HOST);

console.log('Server listening on ' + HOST +':'+ PORT);

function getArduinos() { //return arduinos array, containing string from arduinos' registrations
	return arduinos;
}

exports.getArduinos = getArduinos;
<|endoftext|>


<commit_before>(function (mw, $) {
	"use strict";

	mw.PluginManager.add( 'tvpapiGetLicensedLinks', mw.KBasePlugin.extend( {

		defaultConfig: {
			"restMethod": "GetLicensedLinks",
			"restApiBaseUrl": "",
			"baseConfig": {
				"initObj": {
					"Locale": {
						LocaleLanguage: "",
						LocaleCountry: "",
						LocaleDevice: "",
						LocaleUserState: "Unknown"
					},
					Platform: "Web",
					SiteGuid: "-1",
					DomainID: "",
					UDID: "",
					"ApiUser": "",
					"ApiPass": ""
				},
				"mediaFileID": "",
				"baseLink": ""
			},
		},

		isDisabled: false,
		lastSelectedSource: "",

		setup: function ( embedPlayer ) {
			this.addBindings();
		},

		getProxyConfig: function( attr, raw ) {
			if( raw ){
				return this.embedPlayer.getRawKalturaConfig( "proxyData", attr );
			}
			return this.embedPlayer.getKalturaConfig( "proxyData", attr );
		},

		addBindings: function () {
			var _this = this;
			this.bind("SourceSelected", function(event, source){
				//Prevent dispatching request if source haven't change or it's not valid source (assetid is not valid)
				if ((source.assetid != 0) && (_this.lastSelectedSource != source.assetid)) {
					_this.lastSelectedSource = source.assetid;
					_this.getMediaLicenseLink( event, source );
				}
			});
		},

		getMediaLicenseLink: function(event, source){
			var baseUrl = this.getPlayer().getKalturaConfig( null, 'TVPAPIBaseUrl' ) || this.getConfig( "restApiBaseUrl" );
			var restMethod = this.getConfig("restMethod");
			if (baseUrl != "" && restMethod != "") {
				var url = baseUrl + restMethod;
				var proxyConfig = this.getProxyConfig();
				var baseConfig = this.getConfig( "baseConfig" );
				var _this = this;

				var merge = function( base, data ) {
					var combinedData = {};
					var confItem;
					for ( confItem in base ) {
						if ( typeof(base[confItem]) == "object" ) {
							combinedData[confItem] = merge( base[confItem], data[confItem] );
						} else {
							if ( data[confItem] !== undefined ) {
								combinedData[confItem] = data[confItem];
							}
						}
					}
					return combinedData;
				};

				var combinedData = merge( baseConfig, proxyConfig );

				combinedData["mediaFileID"] = source.assetid;
				combinedData["baseLink"] = source.src;

				var successHandler = function ( res ) {
					if( res.Status && res.Status.Code !== 0 ) {
						_this.getPlayer().triggerHelper('tvpapiNoSubscription', [res]);
					} else {
						_this.getPlayer().triggerHelper('tvpapiSubscription', [res]);
						source.src = res.mainUrl;
					}
				};
				var errorHandler = function ( xmlHttpRequest, status ) {
					//TODO:Handle error - dispatch event
				};

				$.ajax( {
					url: url,
					type: "POST",
					dataType: "json",
					contentType: "application/json",
					data: JSON.stringify( combinedData ),
					//complete: completeHandler,
					success: successHandler,
					error: errorHandler,
					async: false
				} );
			}

			return false;
		}
	}));
})(window.mw, window.jQuery);
<commit_msg>#FEC-3938 #comment supporting npvr & epg #time 1d<commit_after>(function (mw, $) {
	"use strict";

	mw.PluginManager.add( 'tvpapiGetLicensedLinks', mw.KBasePlugin.extend( {

		defaultConfig: {
			"restMethod": "GetLicensedLinks",
			"restApiBaseUrl": "",
			"baseConfig": {
				"initObj": {
					"Locale": {
						LocaleLanguage: "",
						LocaleCountry: "",
						LocaleDevice: "",
						LocaleUserState: "Unknown"
					},
					Platform: "Web",
					SiteGuid: "-1",
					DomainID: "",
					UDID: "",
					"ApiUser": "",
					"ApiPass": ""
				},
				"mediaFileID": "",
				"baseLink": ""
			}
		},

		isDisabled: false,
		lastSelectedSource: "",

		setup: function ( embedPlayer ) {
			this.addBindings();
		},

		getProxyConfig: function( attr, raw ) {
			if( raw ){
				return this.embedPlayer.getRawKalturaConfig( "proxyData", attr );
			}
			return this.embedPlayer.getKalturaConfig( "proxyData", attr );
		},

		addBindings: function () {
			var _this = this;
			this.bind("SourceSelected", function(event, source){
				//Prevent dispatching request if source haven't change or it's not valid source (assetid is not valid)
				if ((source.assetid != 0) && (_this.lastSelectedSource != source.assetid)) {
					_this.lastSelectedSource = source.assetid;
					_this.getMediaLicenseLink( event, source );
				}
			});
		},

		getMediaLicenseLink: function(event, source){
			var baseUrl = "http://stg.eu.tvinci.com/tvpapi_v3_3/gateways/jsonpostgw.aspx?m=";
			var restMethod = this.getConfig("restMethod");
			if (baseUrl != "" && restMethod != "") {
				var url = baseUrl + restMethod;
				var proxyConfig = this.getProxyConfig();
				var baseConfig = this.getConfig( "baseConfig" );
				var _this = this;

				var merge = function( base, data ) {
					var combinedData = {};
					var confItem;
					for ( confItem in base ) {
						if ( typeof(base[confItem]) == "object" ) {
							combinedData[confItem] = merge( base[confItem], data[confItem] );
						} else {
							if ( data[confItem] !== undefined ) {
								combinedData[confItem] = data[confItem];
							}
						}
					}
					return combinedData;
				};

				var combinedData = $.extend(true, baseConfig, proxyConfig );// merge( baseConfig, proxyConfig );

				combinedData["mediaFileID"] = source.assetid;
				combinedData["baseLink"] = combinedData["basicLink"] =  source.src;

				var successHandler = function ( res ) {
					var url = getResponseLink(res);
					if( url ) {
						_this.getPlayer().triggerHelper('tvpapiSubscription', [res]);
						source.src = getResponseLink(res);
					} else {
						_this.getPlayer().triggerHelper('tvpapiNoSubscription', [res]);
					}
				};
				var errorHandler = function ( xmlHttpRequest, status ) {
					//TODO:Handle error - dispatch event
				};

				var getResponseLink = function(res) {
					return res.mainUrl ||
						( res.licensed_link && res.licensed_link.main_url );
				}

				$.ajax( {
					url: url,
					type: "POST",
					dataType: "json",
					contentType: "application/json",
					data: JSON.stringify( combinedData ),
					//complete: completeHandler,
					success: successHandler,
					error: errorHandler,
					async: false
				} );
			}

			return false;
		}
	}));
})(window.mw, window.jQuery);
<|endoftext|>


<commit_before>/**
 * Conversions commands plugin
 *
 * This is where the conversion command is housed, as well
 * as various different conversions that it might use.
 *
 * @license MIT license
 */
'use strict';

exports.commands = {
    temp: 'conversion',
    c: 'conversion',
    convert: 'conversion',
    conversion: function (target, room, user, cmd, trigger) {
        if (!target) return Chat.send(room, `Usage: ${trigger}${cmd} [temp][C/F] - Attempts to convert a given temperature.`);
        let tarId = Chat.toId(target);
        if (tarId.includes('c')) {
            let num = tarId.substr(0, tarId.length - 1);
            if (!Number(num)) return Chat.send(room, 'Invalid number to convert.');
            return Chat.send(room, Math.round((num * 9/5) + 32) + '°F');
        } else if (tarId.includes('f')) {
            let num = tarId.substr(0, tarId.length - 1);
            if (!Number(num)) return Chat.send(room, 'Invalid number to convert.');
            return Chat.send(room, Math.round((num - 32) * 5/9) + '°C');
        } else {
            Chat.send(room, 'Unknown conversion.');
        }
    },
};
<commit_msg>Fix spacing in new conversions plugin<commit_after>/**
 * Conversions commands plugin
 *
 * This is where the conversion command is housed, as well
 * as various different conversions that it might use.
 *
 * @license MIT license
 */
'use strict';

exports.commands = {
	temp: 'conversion',
	c: 'conversion',
	convert: 'conversion',
	conversion: function (target, room, user, cmd, trigger) {
		if (!target) return Chat.send(room, `Usage: ${trigger}${cmd} [temp][C/F] - Attempts to convert a given temperature.`);
		let tarId = Chat.toId(target);
		if (tarId.includes('c')) {
			let num = tarId.substr(0, tarId.length - 1);
			if (!Number(num)) return Chat.send(room, 'Invalid number to convert.');
			return Chat.send(room, Math.round((num * 9/5) + 32) + '°F');
		} else if (tarId.includes('f')) {
			let num = tarId.substr(0, tarId.length - 1);
			if (!Number(num)) return Chat.send(room, 'Invalid number to convert.');
			return Chat.send(room, Math.round((num - 32) * 5/9) + '°C');
		} else {
			Chat.send(room, 'Unknown conversion.');
		}
	},
};
<|endoftext|>


<commit_before>/**
* TCG & Tabletop: Yugioh wiki plugin
* This is a command that allows users to search the yugioh wiki for cards. It will display the closest match with a given query, or a separate message if there isn't anything found.
* By bumbadadabum with help from ascriptmaster, codelegend and the PS development team.
*/

'use strict';

const http = require('http');

function noop() {}

function wikiaSearch(subdomain, query) {
	console.log(`http://${subdomain}.wikia.com/api/v1/Search/List/?query=${encodeURIComponent(query)}&limit=1`);
	return new Promise(function (resolve, reject) {
		http.get(`http://${subdomain}.wikia.com/api/v1/Search/List/?query=${encodeURIComponent(query)}&limit=1`, res => {
			let buffer = '';
			res.setEncoding('utf8');
			res.on('data', data => {
				buffer += data;
			});
			res.on('end', () => {
				console.log(buffer);
				let result;
				try {
					result = JSON.parse(buffer);
				} catch (e) {
					return reject(e);
				}
				if (!result) return reject(new Error("Malformed data"));
				if (result.exception) return reject(new Error(Tools.getString(result.exception.message) || "Not found"));
				if (!Array.isArray(result.items) || !result.items[0] || typeof result.items[0] !== 'object') return reject(new Error("Malformed data"));

				return resolve(result.items[0]);
			});
		}).once('error', function (err) {
			this.on('error', noop);
			reject(err);
		});
	});
}
function getCardDetails(subdomain, id) {
	return new Promise(function (resolve, reject) {
		http.get(`http://${subdomain}.wikia.com/api/v1/Articles/Details?ids=${id}&abstract=0&width=80&height=115`, res => {
			let buffer = '';
			res.setEncoding('utf8');
			res.on('data', data => {
				buffer += data;
			});
			res.on('end', () => {
				let result;
				try {
					result = JSON.parse(buffer);
				} catch (e) {
					return reject(e);
				}
				if (!result) return reject(new Error("Malformed data"));
				if (result.exception) return reject(new Error(Tools.getString(result.exception.message) || "Not found"));
				if (typeof result.items !== 'object' || !result.items[id] || typeof result.items[id] !== 'object') return reject(new Error("Malformed data"));

				return resolve(result.items[id]);
			});
		}).once('error', function (err) {
			this.on('error', noop);
			reject(err);
		});
	});
}

exports.commands = {
	ygo: 'yugioh',
	yugioh: function (target, room, user) {
		if (!this.canBroadcast()) return;
		if (room.id !== 'tcgtabletop') return this.errorReply("This command can only be used in the TCG & Tabletop room.");
		let subdomain = 'yugioh';
		let query = target.trim();

		wikiaSearch(subdomain, query).then(data => {
			if (!this.runBroadcast()) return;
			let entryUrl = Tools.getString(data.url);
			let entryTitle = Tools.getString(data.title);
			let htmlReply = `<strong>Best result for ${Tools.escapeHTML(query)}:</strong><br/><a href="${Tools.escapeHTML(entryUrl)}">${Tools.escapeHTML(entryTitle)}</a>`;
			if (data.id) {
				getCardDetails(subdomain, data.id).then(card => {
					if (card.thumbnail) {
						htmlReply = `<table><tr><td style="padding-right:5px;"><img src="${card.thumbnail}" width=80 height=115></td><td>${htmlReply}</td></tr></table>`;
					}
					if (!this.broadcasting) return this.sendReply(`|raw|<div class="infobox">${htmlReply}</div>`);
					room.addRaw(`<div class="infobox">${htmlReply}</div>`).update();
				}, () => {
					if (!this.broadcasting) return this.sendReply(`|raw|<div class="infobox">${htmlReply}</div>`);
					room.addRaw(`<div class="infobox">${htmlReply}</div>`).update();
				});
			}
		}, err => {
			if (!this.runBroadcast()) return;

			if (err instanceof SyntaxError || err.message === 'Malformed data') {
				if (!this.broadcasting) return this.sendReply(`Error: Something went wrong in the request: ${err.message}`);
				return room.add(`Error: Something went wrong in the request: ${err.message}`).update();
			} else if (err.message === 'Not found') {
				if (!this.broadcasting) return this.sendReply('|raw|
No results found.
');
				return room.addRaw('
No results found.
').update();
			}
			if (!this.broadcasting) return this.sendReply(`Error: ${err.message}`);
			return room.add(`Error: ${err.message}`).update();
		});
	},
};
<commit_msg>TCG: fix mistakes in 3123e35<commit_after>/**
* TCG & Tabletop: Yugioh wiki plugin
* This is a command that allows users to search the yugioh wiki for cards. It will display the closest match with a given query, or a separate message if there isn't anything found.
* By bumbadadabum with help from ascriptmaster, codelegend and the PS development team.
*/

'use strict';

const http = require('http');

function wikiaSearch(subdomain, query) {
	return new Promise(function (resolve, reject) {
		http.get(`http://${subdomain}.wikia.com/api/v1/Search/List/?query=${encodeURIComponent(query)}&limit=1`, res => {
			let buffer = '';
			res.setEncoding('utf8');
			res.on('data', data => {
				buffer += data;
			});
			res.on('error', err => {
				reject(err);
			});
			res.on('end', () => {
				let result;
				try {
					result = JSON.parse(buffer);
				} catch (e) {
					return reject(e);
				}
				if (!result) return reject(new Error("Malformed data"));
				if (result.exception) return reject(new Error(Tools.getString(result.exception.message) || "Not found"));
				if (!Array.isArray(result.items) || !result.items[0] || typeof result.items[0] !== 'object') return reject(new Error("Malformed data"));

				return resolve(result.items[0]);
			});
		});
	});
}
function getCardDetails(subdomain, id) {
	return new Promise(function (resolve, reject) {
		http.get(`http://${subdomain}.wikia.com/api/v1/Articles/Details?ids=${encodeURIComponent(id)}&abstract=0&width=80&height=115`, res => {
			let buffer = '';
			res.setEncoding('utf8');
			res.on('data', data => {
				buffer += data;
			});
			res.on('error', err => {
				reject(err);
			});
			res.on('end', () => {
				let result;
				try {
					result = JSON.parse(buffer);
				} catch (e) {
					return reject(e);
				}
				if (!result) return reject(new Error("Malformed data"));
				if (result.exception) return reject(new Error(Tools.getString(result.exception.message) || "Not found"));
				if (typeof result.items !== 'object' || !result.items[id] || typeof result.items[id] !== 'object') return reject(new Error("Malformed data"));

				return resolve(result.items[id]);
			});
		});
	});
}

exports.commands = {
	ygo: 'yugioh',
	yugioh: function (target, room, user) {
		if (!this.canBroadcast()) return;
		if (room.id !== 'tcgtabletop') return this.errorReply("This command can only be used in the TCG & Tabletop room.");
		let subdomain = 'yugioh';
		let query = target.trim();

		wikiaSearch(subdomain, query).then(data => {
			if (!this.runBroadcast()) return;
			let entryUrl = Tools.getString(data.url);
			let entryTitle = Tools.getString(data.title);
			let id = Tools.getString(data.id);
			let htmlReply = `<strong>Best result for ${Tools.escapeHTML(query)}:</strong><br/><a href="${Tools.escapeHTML(entryUrl)}">${Tools.escapeHTML(entryTitle)}</a>`;
			if (id) {
				getCardDetails(subdomain, id).then(card => {
					let thumb = Tools.getString(card.thumbnail);
					if (thumb) {
						htmlReply = `<table><tr><td style="padding-right:5px;"><img src="${Tools.escapeHTML(thumb)}" width=80 height=115></td><td>${htmlReply}</td></tr></table>`;
					}
					if (!this.broadcasting) return this.sendReply(`|raw|<div class="infobox">${htmlReply}</div>`);
					room.addRaw(`<div class="infobox">${htmlReply}</div>`).update();
				}, () => {
					if (!this.broadcasting) return this.sendReply(`|raw|<div class="infobox">${htmlReply}</div>`);
					room.addRaw(`<div class="infobox">${htmlReply}</div>`).update();
				});
			} else {
				if (!this.broadcasting) return this.sendReply(`|raw|<div class="infobox">${htmlReply}</div>`);
				room.addRaw(`<div class="infobox">${htmlReply}</div>`).update();
			}
		}, err => {
			if (!this.runBroadcast()) return;

			if (err instanceof SyntaxError || err.message === 'Malformed data') {
				if (!this.broadcasting) return this.sendReply(`Error: Something went wrong in the request: ${err.message}`);
				return room.add(`Error: Something went wrong in the request: ${err.message}`).update();
			} else if (err.message === 'Not found') {
				if (!this.broadcasting) return this.sendReply('|raw|
No results found.
');
				return room.addRaw('
No results found.
').update();
			}
			if (!this.broadcasting) return this.sendReply(`Error: ${err.message}`);
			return room.add(`Error: ${err.message}`).update();
		});
	},
};
<|endoftext|>