<commit_before>// Import sw-precache
importScripts('service-worker.js');

// Set required functions
(() => {
  self.onmessage = e => {
  switch (e.data.cmd) {
    case 'setupPouch':
      importScripts('bower_components/pouchdb/dist/pouchdb.min.js');
      if (!e.data.uid) {
        e.ports[0].postMessage({ error: 'No uid provided!' });
        break;
      }
      db = new PouchDB(e.data.uid);
      db.put({ _id: 'data' }).then(() => e.ports[0].postMessage(true));
      break;

    case 'checkPouch':
      if (typeof db !== 'undefined') {
        db.get('data').then(_ => e.ports[0].postMessage(true), err => e.ports[0].postMessage({ error: err }));
      } else {
        e.ports[0].postMessage({ error: true });
      }
      break;

    case 'readData':
      if (db) {
        db.get('data').then(data => e.ports[0].postMessage(data));
      } else {
        e.ports[0].postMessage({ error: 'Pouch is unavailable' });
      }
      break;

    case 'pushToPouch':
      pushToPouch(e);
      break;

    case 'deletePouch':
      if (db) {
        e.waitUntil(new Promise(r => {
          db.destroy().then(() => {
            db = undefined;
            e.ports[0].postMessage(true);
            r();
          }, err => {
            e.ports[0].postMessage({ error: err });
            r();
          });
        }));
      } else {
        e.ports[0].postMessage(true);
      }
      break;

    default:
      console.warn(`Unknown message sent to Service Worker - command: ${e.data.cmd}`, e);
    }
  };
})();

function pushToPouch(e) {
  const data = e.data.data;
  db.get('data').catch(err => {
    if (err.name === 'not_found') {
      return data;
    } else {
      throw err;
    }
  }).then(resp => {
    const output = resp;
    output.config = data.config;
    output.settings = data.settings;
    output.timetable = data.timetable;
    output._id = 'data';
    output._rev = resp._rev || undefined;
    return db.put(output).catch(err => {
      if (err.status === 409) {
        pushToPouch(e);
        console.log('Conflict when pushing to Pouch, retrying');
      } else {
        console.error('Error when pushing to Pouch', err);
        e.ports[0].postMessage({ error: 'Unknown error occured, check console for details' });
      }
    });
  });
}

// Import and use Firebase
/*importScripts('https://www.gstatic.com/firebasejs/3.6.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.6.1/firebase-auth.js');
importScripts('https://www.gstatic.com/firebasejs/3.6.1/firebase-database.js');*/<commit_msg>Improve PouchDB handling<commit_after>// Import sw-precache
importScripts('service-worker.js');

// Set required functions
(() => {
  self.onmessage = e => {
  switch (e.data.cmd) {
    case 'setupPouch':
      importScripts('bower_components/pouchdb/dist/pouchdb.min.js');
      if (!e.data.uid) {
        e.ports[0].postMessage({ error: 'No uid provided!' });
        break;
      }
      db = new PouchDB(e.data.uid);
      db.get('data').then(() => e.ports[0].postMessage(true), err => {
        db.put({ _id: 'data' }).then(() => e.ports[0].postMessage(true));
      });
      break;

    case 'checkPouch':
      if (typeof db !== 'undefined') {
        db.get('data').then(() => e.ports[0].postMessage(true), err => e.ports[0].postMessage({ error: err }));
      } else {
        e.ports[0].postMessage({ error: true });
      }
      break;

    case 'readData':
      if (db) {
        db.get('data').then(data => e.ports[0].postMessage(data));
      } else {
        e.ports[0].postMessage({ error: 'Pouch is unavailable' });
      }
      break;

    case 'pushToPouch':
      pushToPouch(e);
      break;

    case 'deletePouch':
      if (db) {
        e.waitUntil(new Promise(r => {
          db.destroy().then(() => {
            db = undefined;
            e.ports[0].postMessage(true);
            r();
          }, err => {
            e.ports[0].postMessage({ error: err });
            r();
          });
        }));
      } else {
        e.ports[0].postMessage(true);
      }
      break;

    default:
      console.warn(`Unknown message sent to Service Worker - command: ${e.data.cmd}`, e);
    }
  };
})();

function pushToPouch(e) {
  const data = e.data.data;
  db.get('data').catch(err => {
    if (err.name === 'not_found') {
      return data;
    } else {
      throw err;
    }
  }).then(resp => {
    const output = resp;
    output.config = data.config;
    output.settings = data.settings;
    output.timetable = data.timetable;
    output._id = 'data';
    output._rev = resp._rev || undefined;
    return db.put(output).catch(err => {
      if (err.status === 409) {
        pushToPouch(e);
        console.log('Conflict when pushing to Pouch, retrying');
      } else {
        console.error('Error when pushing to Pouch', err);
        e.ports[0].postMessage({ error: 'Unknown error occured, check console for details' });
      }
    });
  });
}

// Import and use Firebase
/*importScripts('https://www.gstatic.com/firebasejs/3.6.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.6.1/firebase-auth.js');
importScripts('https://www.gstatic.com/firebasejs/3.6.1/firebase-database.js');*/<|endoftext|>


<commit_before>`use strict`

// lots of functions for figuring out if it's day or night
app.factory('timeFactory', function($http) {
  return {
    parseData() {
      return this.getSunData()
      .then(function(data) {
        return data
      })
    },
    getSunData() {
      const sunriseSunsetOrgUrl = 'http:' + '//' + 'api.sunrise-sunset.org/json?lat=36.1627&lng=-86.7816&date=today'
      return $http.get(sunriseSunsetOrgUrl)
      .then(function(value) {
        return value.data.results
      })
    },
    getSunrise() {
      getSunData()
      .then(function(value) {
        return value.sunrise
      })
    },
    getSunset() {
      getSunData()
      .then(function(value) {
        return value.sunset
      })
    },
    currentDate() {
      return new Date()
    },
    timeZoneOffsetInHours() {
      return this.currentDate().getTimezoneOffset()/60
    },
    currentHourInNashville() {
      return (this.currentDate().getHours() + this.timeZoneOffsetInHours() - 5)
    },
    isItAM() {
      if (currentHour()+1 < 12) {
        return true
      } else {
        return false
      }
    },

  }
})
<commit_msg>changed function call in if statement<commit_after>`use strict`

// lots of functions for figuring out if it's day or night
app.factory('timeFactory', function($http) {
  return {
    parseData() {
      return this.getSunData()
      .then(function(data) {
        return data
      })
    },
    getSunData() {
      const sunriseSunsetOrgUrl = 'http:' + '//' + 'api.sunrise-sunset.org/json?lat=36.1627&lng=-86.7816&date=today'
      return $http.get(sunriseSunsetOrgUrl)
      .then(function(value) {
        return value.data.results
      })
    },
    getSunrise() {
      getSunData()
      .then(function(value) {
        return value.sunrise
      })
    },
    getSunset() {
      getSunData()
      .then(function(value) {
        return value.sunset
      })
    },
    currentDate() {
      return new Date()
    },
    timeZoneOffsetInHours() {
      return this.currentDate().getTimezoneOffset()/60
    },
    currentHourInNashville() {
      return (this.currentDate().getHours() + this.timeZoneOffsetInHours() - 5)
    },
    isItAM() {
      if (currentHourInNashville() < 12) {
        return true
      } else {
        return false
      }
    },

  }
})
<|endoftext|>


<commit_before>var maxMatchLength = 50, generateMatch;

    generateMatch = function(uri, label, source) {
        var match = {
            'uri': uri,
            'source': source,
            'fullLabel': label
        };
        if (label.length > maxMatchLength) {
            match['label'] = label.substr(0, maxMatchLength) + '...';
        } else {
            match['label'] = label;
        }
        return match;
    };

module.exports = {
    agrovoc: function(res) {
        var i, answer, json;
        json = JSON.parse(res);
        answer = [];
        for (i = 0; i < json.results.length; i++) {
            answer.push(generateMatch(
                json.results[i].uri,
                json.results[i].prefLabel,
                'agrovoc'
            ));
        }
        return answer;
    },
    fast: function(res) {
        var i, answer, json, term, id;
        answer = [];
        json = JSON.parse(res);
        for (i = 0; i < json.response.docs.length; i++) {
            if (typeof json.response.docs[i].idroot !== 'undefined') {
                id = json.response.docs[i].idroot.substring(3).replace(/^0+/, '');
                answer.push(generateMatch(
                    'http://experimental.worldcat.org/fast/' + id + '/',
                    json.response.docs[i].suggestall,
                    'fast'
                ));
            }
        }
        return answer;
    },
    lc: function(res) {
        var i, answer, json;
        answer = [];
        json = JSON.parse(res);
        for (i = 0; i < json[1].length; i++) {
            answer.push(generateMatch(
                json[3][i],
                json[1][i],
                'lc'
            ));
        }
        return answer;
    },
    viaf: function(res) {
        var i, answer, json;
        answer = [];
        json = JSON.parse(res);
        for (i = 0; i < json.result.length; i++) {
            answer.push(generateMatch(
                'http://viaf.org/viaf/' + json.result[i].viafid,
                json.result[i].term,
                'viaf'
            ));
        }
        return answer;
    }
};
<commit_msg>reformat tabs<commit_after>var maxMatchLength = 50, generateMatch;

generateMatch = function(uri, label, source) {
    var match = {
        'uri': uri,
        'source': source,
        'fullLabel': label
    };
    if (label.length > maxMatchLength) {
        match['label'] = label.substr(0, maxMatchLength) + '...';
    } else {
        match['label'] = label;
    }
    return match;
};

module.exports = {
    agrovoc: function(res) {
        var i, answer, json;
        json = JSON.parse(res);
        answer = [];
        for (i = 0; i < json.results.length; i++) {
            answer.push(generateMatch(
                json.results[i].uri,
                json.results[i].prefLabel,
                'agrovoc'
            ));
        }
        return answer;
    },
    fast: function(res) {
        var i, answer, json, term, id;
        answer = [];
        json = JSON.parse(res);
        for (i = 0; i < json.response.docs.length; i++) {
            if (typeof json.response.docs[i].idroot !== 'undefined') {
                id = json.response.docs[i].idroot.substring(3).replace(/^0+/, '');
                answer.push(generateMatch(
                    'http://experimental.worldcat.org/fast/' + id + '/',
                    json.response.docs[i].suggestall,
                    'fast'
                ));
            }
        }
        return answer;
    },
    lc: function(res) {
        var i, answer, json;
        answer = [];
        json = JSON.parse(res);
        for (i = 0; i < json[1].length; i++) {
            answer.push(generateMatch(
                json[3][i],
                json[1][i],
                'lc'
            ));
        }
        return answer;
    },
    viaf: function(res) {
        var i, answer, json;
        answer = [];
        json = JSON.parse(res);
        for (i = 0; i < json.result.length; i++) {
            answer.push(generateMatch(
                'http://viaf.org/viaf/' + json.result[i].viafid,
                json.result[i].term,
                'viaf'
            ));
        }
        return answer;
    }
};
<|endoftext|>


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

const weatherApp = {
  selectedLocations: {},
  addDialogContainer: document.getElementById('addDialogContainer'),
};

/**
 * Toggles the visibility of the add location dialog box.
 */
function toggleAddDialog() {
  weatherApp.addDialogContainer.classList.toggle('visible');
}

/**
 * Event handler for butDialogAdd, adds the selected location to the list.
 */
function addLocation() {
  // Hide the dialog
  toggleAddDialog();
  // Get the selected city
  const select = document.getElementById('selectCityToAdd');
  const selected = select.options[select.selectedIndex];
  const geo = selected.value;
  const label = selected.textContent;
  const location = {label: label, geo: geo};
  // Create a new card & get the weather data from the server
  const card = getForecastCard(location);
  getForecastFromNetwork(geo).then((forecast) => {
    renderForecast(card, forecast);
  });
  // Save the updated list of selected cities.
  weatherApp.selectedLocations[geo] = location;
  saveLocationList(weatherApp.selectedLocations);
}

/**
 * Event handler for .remove-city, removes a location from the list.
 *
 * @param {Event} evt
 */
function removeLocation(evt) {
  const parent = evt.srcElement.parentElement;
  parent.setAttribute('hidden', true);
  if (weatherApp.selectedLocations[parent.id]) {
    delete weatherApp.selectedLocations[parent.id];
    saveLocationList(weatherApp.selectedLocations);
  }
}

/**
 * Renders the forecast data into the card element.
 *
 * @param {Element} card The card element to update.
 * @param {Object} data Weather forecast data to update the element with.
 */
function renderForecast(card, data) {
  if (!data) {
    // There's no data, skip the update.
    return;
  }

  // Find out when the element was last updated.
  const cardLastUpdatedElem = card.querySelector('.card-last-updated');
  const cardLastUpdated = cardLastUpdatedElem.textContent;
  const lastUpdated = parseInt(cardLastUpdated);

  // If the data on the element is newer, skip the update.
  if (lastUpdated >= data.currently.time) {
    return;
  }
  cardLastUpdatedElem.textContent = data.currently.time;

  // Render the forecast data into the card.
  card.querySelector('.description').textContent = data.currently.summary;
  const forecastFrom = luxon.DateTime
      .fromSeconds(data.currently.time)
      .setZone(data.timezone)
      .toFormat('DDDD t');
  card.querySelector('.date').textContent = forecastFrom;
  card.querySelector('.current .icon')
      .className = `icon ${data.currently.icon}`;
  card.querySelector('.current .temperature .value')
      .textContent = Math.round(data.currently.temperature);
  card.querySelector('.current .humidity .value')
      .textContent = Math.round(data.currently.humidity * 100);
  card.querySelector('.current .wind .value')
      .textContent = Math.round(data.currently.windSpeed);
  card.querySelector('.current .wind .direction')
      .textContent = Math.round(data.currently.windBearing);
  const sunrise = luxon.DateTime
      .fromSeconds(data.daily.data[0].sunriseTime)
      .setZone(data.timezone)
      .toFormat('t');
  card.querySelector('.current .sunrise .value').textContent = sunrise;
  const sunset = luxon.DateTime
      .fromSeconds(data.daily.data[0].sunsetTime)
      .setZone(data.timezone)
      .toFormat('t');
  card.querySelector('.current .sunset .value').textContent = sunset;

  // Render the next 7 days.
  const futureTiles = card.querySelectorAll('.future .oneday');
  futureTiles.forEach((tile, index) => {
    const forecast = data.daily.data[index + 1];
    const forecastFor = luxon.DateTime
        .fromSeconds(forecast.time)
        .setZone(data.timezone)
        .toFormat('ccc');
    tile.querySelector('.date').textContent = forecastFor;
    tile.querySelector('.icon').className = `icon ${forecast.icon}`;
    tile.querySelector('.temp-high .value')
        .textContent = Math.round(forecast.temperatureHigh);
    tile.querySelector('.temp-low .value')
        .textContent = Math.round(forecast.temperatureLow);
  });

  // If the loading spinner is still visible, remove it.
  const spinner = card.querySelector('.card-spinner');
  if (spinner) {
    card.removeChild(spinner);
  }
}

/**
 * Get's the latest forecast data from the network.
 *
 * @param {String} coords Location object to.
 * @return {Object} The weather forecast, if the request fails, return null.
 */
function getForecastFromNetwork(coords) {
  return fetch(`/forecast/${coords}`)
      .then((response) => {
        return response.json();
      })
      .catch(() => {
        return null;
      });
}

/**
 * Get's the cached forecast data from the caches object.
 *
 * @param {String} coords Location object to.
 * @return {Object} The weather forecast, if the request fails, return null.
 */
function getForecastFromCache(coords) {
  // CODELAB: Add code to get weather forecast from the caches object.
}

/**
 * Get's the HTML element for the weather forecast, or clones the template
 * and adds it to the DOM if we're adding a new item.
 *
 * @param {Object} location Location object
 * @return {Element} The element for the weather forecast.
 */
function getForecastCard(location) {
  const id = location.geo;
  const card = document.getElementById(id);
  if (card) {
    return card;
  }
  const newCard = document.getElementById('weather-template').cloneNode(true);
  newCard.querySelector('.location').textContent = location.label;
  newCard.setAttribute('id', id);
  newCard.querySelector('.remove-city')
      .addEventListener('click', removeLocation);
  document.querySelector('main').appendChild(newCard);
  newCard.removeAttribute('hidden');
  return newCard;
}

/**
 * Gets the latest weather forecast data and updates each card with the
 * new data.
 */
function updateData() {
  Object.keys(weatherApp.selectedLocations).forEach((key) => {
    const location = weatherApp.selectedLocations[key];
    const card = getForecastCard(location);
    // CODELAB: Add code to call getForecastFromCache
    getForecastFromNetwork(location.geo)
        .then((forecast) => {
          renderForecast(card, forecast);
        });
  });
}

/**
 * Saves the list of locations.
 *
 * @param {Object} locations The list of locations to save.
 */
function saveLocationList(locations) {
  const data = JSON.stringify(locations);
  localStorage.setItem('locationList', data);
}

/**
 * Loads the list of saved location.
 *
 * @return {Array}
 */
function loadLocationList() {
  let locations = localStorage.getItem('locationList');
  if (locations) {
    try {
      locations = JSON.parse(locations);
    } catch (ex) {
      locations = {};
    }
  }
  if (!locations || Object.keys(locations).length === 0) {
    const key = '40.7720232,-73.9732319';
    locations = {};
    locations[key] = {label: 'New York City', geo: '40.7720232,-73.9732319'};
  }
  return locations;
}

/**
 * Initialize the app, gets the list of locations from local storage, then
 * renders the initial data.
 */
function init() {
  // Get the location list, and update the UI.
  weatherApp.selectedLocations = loadLocationList();
  updateData();

  // Set up the event handlers for all of the buttons.
  document.getElementById('butRefresh').addEventListener('click', updateData);
  document.getElementById('butAdd').addEventListener('click', toggleAddDialog);
  document.getElementById('butDialogCancel')
      .addEventListener('click', toggleAddDialog);
  document.getElementById('butDialogAdd')
      .addEventListener('click', addLocation);
}

init();
<commit_msg>Update license & minor linting<commit_after>/*
 * @license
 * Your First PWA Codelab (https://g.co/codelabs/pwa)
 * Copyright 2019 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */
'use strict';

const weatherApp = {
  selectedLocations: {},
  addDialogContainer: document.getElementById('addDialogContainer'),
};

/**
 * Toggles the visibility of the add location dialog box.
 */
function toggleAddDialog() {
  weatherApp.addDialogContainer.classList.toggle('visible');
}

/**
 * Event handler for butDialogAdd, adds the selected location to the list.
 */
function addLocation() {
  // Hide the dialog
  toggleAddDialog();
  // Get the selected city
  const select = document.getElementById('selectCityToAdd');
  const selected = select.options[select.selectedIndex];
  const geo = selected.value;
  const label = selected.textContent;
  const location = {label: label, geo: geo};
  // Create a new card & get the weather data from the server
  const card = getForecastCard(location);
  getForecastFromNetwork(geo).then((forecast) => {
    renderForecast(card, forecast);
  });
  // Save the updated list of selected cities.
  weatherApp.selectedLocations[geo] = location;
  saveLocationList(weatherApp.selectedLocations);
}

/**
 * Event handler for .remove-city, removes a location from the list.
 *
 * @param {Event} evt
 */
function removeLocation(evt) {
  const parent = evt.srcElement.parentElement;
  parent.setAttribute('hidden', true);
  if (weatherApp.selectedLocations[parent.id]) {
    delete weatherApp.selectedLocations[parent.id];
    saveLocationList(weatherApp.selectedLocations);
  }
}

/**
 * Renders the forecast data into the card element.
 *
 * @param {Element} card The card element to update.
 * @param {Object} data Weather forecast data to update the element with.
 */
function renderForecast(card, data) {
  if (!data) {
    // There's no data, skip the update.
    return;
  }

  // Find out when the element was last updated.
  const cardLastUpdatedElem = card.querySelector('.card-last-updated');
  const cardLastUpdated = cardLastUpdatedElem.textContent;
  const lastUpdated = parseInt(cardLastUpdated);

  // If the data on the element is newer, skip the update.
  if (lastUpdated >= data.currently.time) {
    return;
  }
  cardLastUpdatedElem.textContent = data.currently.time;

  // Render the forecast data into the card.
  card.querySelector('.description').textContent = data.currently.summary;
  const forecastFrom = luxon.DateTime
      .fromSeconds(data.currently.time)
      .setZone(data.timezone)
      .toFormat('DDDD t');
  card.querySelector('.date').textContent = forecastFrom;
  card.querySelector('.current .icon')
      .className = `icon ${data.currently.icon}`;
  card.querySelector('.current .temperature .value')
      .textContent = Math.round(data.currently.temperature);
  card.querySelector('.current .humidity .value')
      .textContent = Math.round(data.currently.humidity * 100);
  card.querySelector('.current .wind .value')
      .textContent = Math.round(data.currently.windSpeed);
  card.querySelector('.current .wind .direction')
      .textContent = Math.round(data.currently.windBearing);
  const sunrise = luxon.DateTime
      .fromSeconds(data.daily.data[0].sunriseTime)
      .setZone(data.timezone)
      .toFormat('t');
  card.querySelector('.current .sunrise .value').textContent = sunrise;
  const sunset = luxon.DateTime
      .fromSeconds(data.daily.data[0].sunsetTime)
      .setZone(data.timezone)
      .toFormat('t');
  card.querySelector('.current .sunset .value').textContent = sunset;

  // Render the next 7 days.
  const futureTiles = card.querySelectorAll('.future .oneday');
  futureTiles.forEach((tile, index) => {
    const forecast = data.daily.data[index + 1];
    const forecastFor = luxon.DateTime
        .fromSeconds(forecast.time)
        .setZone(data.timezone)
        .toFormat('ccc');
    tile.querySelector('.date').textContent = forecastFor;
    tile.querySelector('.icon').className = `icon ${forecast.icon}`;
    tile.querySelector('.temp-high .value')
        .textContent = Math.round(forecast.temperatureHigh);
    tile.querySelector('.temp-low .value')
        .textContent = Math.round(forecast.temperatureLow);
  });

  // If the loading spinner is still visible, remove it.
  const spinner = card.querySelector('.card-spinner');
  if (spinner) {
    card.removeChild(spinner);
  }
}

/**
 * Get's the latest forecast data from the network.
 *
 * @param {string} coords Location object to.
 * @return {Object} The weather forecast, if the request fails, return null.
 */
function getForecastFromNetwork(coords) {
  return fetch(`/forecast/${coords}`)
      .then((response) => {
        return response.json();
      })
      .catch(() => {
        return null;
      });
}

/**
 * Get's the cached forecast data from the caches object.
 *
 * @param {string} coords Location object to.
 * @return {Object} The weather forecast, if the request fails, return null.
 */
function getForecastFromCache(coords) {
  // CODELAB: Add code to get weather forecast from the caches object.
}

/**
 * Get's the HTML element for the weather forecast, or clones the template
 * and adds it to the DOM if we're adding a new item.
 *
 * @param {Object} location Location object
 * @return {Element} The element for the weather forecast.
 */
function getForecastCard(location) {
  const id = location.geo;
  const card = document.getElementById(id);
  if (card) {
    return card;
  }
  const newCard = document.getElementById('weather-template').cloneNode(true);
  newCard.querySelector('.location').textContent = location.label;
  newCard.setAttribute('id', id);
  newCard.querySelector('.remove-city')
      .addEventListener('click', removeLocation);
  document.querySelector('main').appendChild(newCard);
  newCard.removeAttribute('hidden');
  return newCard;
}

/**
 * Gets the latest weather forecast data and updates each card with the
 * new data.
 */
function updateData() {
  Object.keys(weatherApp.selectedLocations).forEach((key) => {
    const location = weatherApp.selectedLocations[key];
    const card = getForecastCard(location);
    // CODELAB: Add code to call getForecastFromCache
    getForecastFromNetwork(location.geo)
        .then((forecast) => {
          renderForecast(card, forecast);
        });
  });
}

/**
 * Saves the list of locations.
 *
 * @param {Object} locations The list of locations to save.
 */
function saveLocationList(locations) {
  const data = JSON.stringify(locations);
  localStorage.setItem('locationList', data);
}

/**
 * Loads the list of saved location.
 *
 * @return {Array}
 */
function loadLocationList() {
  let locations = localStorage.getItem('locationList');
  if (locations) {
    try {
      locations = JSON.parse(locations);
    } catch (ex) {
      locations = {};
    }
  }
  if (!locations || Object.keys(locations).length === 0) {
    const key = '40.7720232,-73.9732319';
    locations = {};
    locations[key] = {label: 'New York City', geo: '40.7720232,-73.9732319'};
  }
  return locations;
}

/**
 * Initialize the app, gets the list of locations from local storage, then
 * renders the initial data.
 */
function init() {
  // Get the location list, and update the UI.
  weatherApp.selectedLocations = loadLocationList();
  updateData();

  // Set up the event handlers for all of the buttons.
  document.getElementById('butRefresh').addEventListener('click', updateData);
  document.getElementById('butAdd').addEventListener('click', toggleAddDialog);
  document.getElementById('butDialogCancel')
      .addEventListener('click', toggleAddDialog);
  document.getElementById('butDialogAdd')
      .addEventListener('click', addLocation);
}

init();
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Chris Wells (https://chriswells.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

/*jslint plusplus: true, white: true */
/*jshint laxbreak: true */
/*global window, document, confirm, $, CWA */

(function () {
	"use strict";

	window.CWA = window.CWA || {};

	CWA.DOM = {
		forms: {},
		includeScript: function (filePath, async) {
			var regexFileName = new RegExp(filePath + "$"),
				bIsIncluded = false,
				arrScripts = document.getElementsByTagName("script"),
				i,
				elNewScript;
			for (i = 0; i < arrScripts.length; i++) {
				if (regexFileName.test(arrScripts[i].src)) {
					bIsIncluded = true;
					break;
				}
			}
			if (!bIsIncluded) {
				elNewScript = document.createElement("script");
				elNewScript.async = async;
				elNewScript.src = filePath;
				document.getElementsByTagName("head")[0].appendChild(elNewScript);
			}
		}
	};

	CWA.DOM.Form = function (htmlForm, customOptions) {
		// Private variables and the main object, which is returned as a public instance:
		var jForm = $(htmlForm),
			identifier = jForm.attr("id") || jForm.attr("name"),
			initialState = jForm.serialize(),
			options = $.extend({
				autoValidate: "true",
				protectChanges: "true",
				patterns: {
					date: /^(\d{4}-[01]\d-[0-3]\d)?$/,
					email: /^(("[\w-\s]+")|([\w\-]+(?:\.[\w\-]+)*)|("[\w-\s]+")([\w\-]+(?:\.[\w\-]+)*))(@((?:[\w\-]+\.)*\w[\w\-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$)|(@\[?((25[0-5]\.|2[0-4][0-9]\.|1[0-9]{2}\.|[0-9]{1,2}\.))((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){2}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\]?$)/i,
					tel: /^((\+\d)*\s*(\(\d{3}\)\s*)*\d{3}(-{0,1}|\s{0,1})\d{2}(-{0,1}|\s{0,1})\d{2})?$/
				}
			}, htmlForm.dataset, customOptions),
			Form = {
				$: jForm,
				elements: htmlForm.elements,
				addPatterns: function (newPatterns) {
					$.extend(options.patterns, newPatterns);
				},
				clearError: function (field) {
					var fieldID = (field.attr("id") || field.attr("name"));
					field.removeClass("error");
					$("label[for='" + fieldID + "']").removeClass("error");
					$("#" + fieldID + "-error").remove();
				},
				clearErrors: function () {
					jForm.find(".dynamic-error").remove();
					jForm.find("label.error").removeClass("error");
					jForm.find(":input").removeClass("error");
				},
				getErrorCount: function () {
					return jForm.find(".dynamic-error:visible").length;
				},
				hasChanged: function () {
					return (initialState !== jForm.serialize());
				},
				protectChanges: function (fields, submit, warning) {
					fields = fields || "input:not(:button,:submit), select, textarea";
					submit = submit || "button[type='submit'], input[type='submit']";
					warning = warning || "You have unsaved changes on this page!";

					// ???: Should I only clear the warning if the form validates? -- cwells
					jForm.find(submit).click(function () {
						warning = null;
					});

					jForm.find(fields).bind("change input", function () {
						$(window).bind("beforeunload", function (e) {
							if (warning !== null) {
								(e || window.event).returnValue = warning;
								return warning;
							}
						});
					});

					jForm.on("reset", function () {
						warning = null;
					});
				},
				reset: function () {
					htmlForm.reset();
				},
				setError: function (field, message) {
					var fieldID = (field.attr("id") || field.attr("name")),
						errorID = fieldID + "-error",
						label = $("label[for='" + fieldID + "']");
					field.addClass("error");
					label.addClass("error");
					$("#" + errorID).remove();
					$("<div />", {
						id: errorID,
						class: "error dynamic-error",
						html: (message[0] === " " ? (label.text() + message) : message)
					}).insertAfter(field);
				},
				setPatterns: function (newPatterns) {
					options.patterns = newPatterns || {};
				},
				validate: function () {
					var fields = jForm.find(":input"),
						field,
						i;
					this.clearErrors();

					for (i = 0; i < fields.length; i++) {
						field = $(fields[i]);
						if (field.not(":hidden")) {
							if (field.attr("data-must-match")
									&& field.val() !== $("#" + field.attr("data-must-match")).val()) {
								this.setError(field, " must match " + $("label[for='" + field.attr("data-must-match") + "']").text() + ".");
							} else if (field.attr("required") && field.val().length === 0) {
								this.setError(field, " is required.");
							} else if (field.attr("minlength") && field.val().length < +field.attr("minlength")) {
								this.setError(field, " must be at least " + field.attr("minlength") + " characters long.");
							} else if (field.attr("maxlength") && field.val().length > +field.attr("maxlength")) {
								this.setError(field, " may not contain more than " + field.attr("maxlength") + " characters.");
							} else if (field.attr("pattern") && !(new RegExp("^" + field.attr("pattern") + "$")).test(field.val())) {
								this.setError(field, " is not valid.");
							} else if (!field.attr("pattern") && options.patterns[field.attr("type")]
									&& !options.patterns[field.attr("type")].test(field.val())) {
								this.setError(field, " is not valid.");
							}
						}
					}

					return (this.getErrorCount() === 0);
				}
			};

		if (options.autoValidate === "true") {
			jForm.submit(function (e) {
				if (!Form.validate()) {
					e.preventDefault();
				}
			});
		}

		if (options.protectChanges === "true") {
			Form.protectChanges();
		}

		if (identifier) { // Store for easy access. -- cwells
			CWA.DOM.forms[identifier] = Form;
		}
		return Form;
	};

	CWA.MVC = {
	};

	CWA.MVC.View = {
		cancelEdit: function (e) {
			e.preventDefault();
			if (document.referrer.indexOf(document.location.protocol + "//" + document.location.hostname) === 0) {
				history.back(); // The referring page was on this site, so just go back. -- cwells
			} else { // Go to the specified destination or the admin page for the current controller. -- cwells
				document.location = this.dataset.destination || (CWA.MVC.ControllerURL + "/admin");
			}
		},
		confirmDelete: function (e) {
			e.preventDefault();
			if (confirm("Are you sure you want to delete this item?")) {
				var href = $(this).attr("href"),
					itemID = href.substr(href.indexOf("/delete/") + 8),
					postData = { "itemID": itemID };
				postData[CWA.MVC.View.syncToken.name] = CWA.MVC.View.syncToken.value;
				$.ajax({
					type: "POST",
					url: href,
					data: postData,
					dataType: "json",
					complete: function (jqXHR) {
						var response = jqXHR.responseJSON,
							unknownError = "An error occurred while deleting the specified item.";
						if (!response || !response.status) {
							alert(unknownError);
						} else if (response.status.code !== 200) {
							alert(response.status.message || unknownError);
						} else { // The item was deleted. -- cwells
							if (response.data && response.data.NextURL) {
								window.location.href = response.data.NextURL;
							} else if (response.data && response.data.ControllerURL) {
								window.location.href = response.data.ControllerURL;
							} else {
								alert("Successfully deleted the specified item.");
							}
						}
					}
				});
			}
		},
		createSlug: function (str, allowUppercase) {
			if (typeof str !== "string") { return ""; }

			if (!allowUppercase) {
				str = str.toLowerCase();
			}
			str = str.replace(/'/g, ""); // Remove apostrophes.
			str = str.replace(/&/g, "and");
			// Allowed characters per RFC 1738:  alphanumeric and $-_.+!*'(),
			/*jslint regexp: true */
			str = str.replace(/[^a-z0-9$\-_.+!*()]/gi, "-"); // Replace all characters except our whitelist with hyphen.
			str = str.replace(/^[^a-z0-9]*|[^a-z0-9]*$/gi, ""); // Remove non-alphanumeric from the beginning and end.
			str = str.replace(/--+/g, "-"); // Replace multiple hyphens with one hyphen.
			/*jslint regexp: false */

			return str;
		},
		suggestSlug: function (e) {
			e.preventDefault();
			// Only suggest a new slug when the destination is empty. -- cwells
			if ($("#" + this.dataset.to).val() === "") {
				CWA.MVC.View.updateSlug.call(this, e);
			}
		},
		updateSlug: function (e) {
			e.preventDefault();
			var from = $("#" + this.dataset.from),
				to = $("#" + this.dataset.to);
			to.val(CWA.MVC.View.createSlug(from.val(), this.dataset.allowUppercase));
		}
	};

	$(document).ready(function () {
		$("#content-wrapper").on("click", "[data-cwa-click]", function (e) {
			if (typeof CWA.MVC.View[this.dataset.cwaClick] === "function") {
				CWA.MVC.View[this.dataset.cwaClick].call(this, e);
			}
		});
		$("#content-wrapper").on("focus", "[data-cwa-focus]", function (e) {
			if (typeof CWA.MVC.View[this.dataset.cwaFocus] === "function") {
				CWA.MVC.View[this.dataset.cwaFocus].call(this, e);
			}
		});
	});

}());
<commit_msg>Add Form.on() method.<commit_after>/*
 * Copyright (c) 2014 Chris Wells (https://chriswells.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

/*jslint plusplus: true, white: true */
/*jshint laxbreak: true */
/*global window, document, confirm, $, CWA */

(function () {
	"use strict";

	window.CWA = window.CWA || {};

	CWA.DOM = {
		forms: {},
		includeScript: function (filePath, async) {
			var regexFileName = new RegExp(filePath + "$"),
				bIsIncluded = false,
				arrScripts = document.getElementsByTagName("script"),
				i,
				elNewScript;
			for (i = 0; i < arrScripts.length; i++) {
				if (regexFileName.test(arrScripts[i].src)) {
					bIsIncluded = true;
					break;
				}
			}
			if (!bIsIncluded) {
				elNewScript = document.createElement("script");
				elNewScript.async = async;
				elNewScript.src = filePath;
				document.getElementsByTagName("head")[0].appendChild(elNewScript);
			}
		}
	};

	CWA.DOM.Form = function (htmlForm, customOptions) {
		// Private variables and the main object, which is returned as a public instance:
		var jForm = $(htmlForm),
			identifier = jForm.attr("id") || jForm.attr("name"),
			initialState = jForm.serialize(),
			options = $.extend({
				autoValidate: "true",
				protectChanges: "true",
				patterns: {
					date: /^(\d{4}-[01]\d-[0-3]\d)?$/,
					email: /^(("[\w-\s]+")|([\w\-]+(?:\.[\w\-]+)*)|("[\w-\s]+")([\w\-]+(?:\.[\w\-]+)*))(@((?:[\w\-]+\.)*\w[\w\-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$)|(@\[?((25[0-5]\.|2[0-4][0-9]\.|1[0-9]{2}\.|[0-9]{1,2}\.))((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){2}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\]?$)/i,
					tel: /^((\+\d)*\s*(\(\d{3}\)\s*)*\d{3}(-{0,1}|\s{0,1})\d{2}(-{0,1}|\s{0,1})\d{2})?$/
				}
			}, htmlForm.dataset, customOptions),
			Form = {
				$: jForm,
				elements: htmlForm.elements,
				addPatterns: function (newPatterns) {
					$.extend(options.patterns, newPatterns);
				},
				clearError: function (field) {
					var fieldID = (field.attr("id") || field.attr("name"));
					field.removeClass("error");
					$("label[for='" + fieldID + "']").removeClass("error");
					$("#" + fieldID + "-error").remove();
				},
				clearErrors: function () {
					jForm.find(".dynamic-error").remove();
					jForm.find("label.error").removeClass("error");
					jForm.find(":input").removeClass("error");
				},
				getErrorCount: function () {
					return jForm.find(".dynamic-error:visible").length;
				},
				hasChanged: function () {
					return (initialState !== jForm.serialize());
				},
				on: function () {
					jForm.on.apply(jForm, arguments);
				},
				protectChanges: function (fields, submit, warning) {
					fields = fields || "input:not(:button,:submit), select, textarea";
					submit = submit || "button[type='submit'], input[type='submit']";
					warning = warning || "You have unsaved changes on this page!";

					// ???: Should I only clear the warning if the form validates? -- cwells
					jForm.find(submit).click(function () {
						warning = null;
					});

					jForm.find(fields).bind("change input", function () {
						$(window).bind("beforeunload", function (e) {
							if (warning !== null) {
								(e || window.event).returnValue = warning;
								return warning;
							}
						});
					});

					jForm.on("reset", function () {
						warning = null;
					});
				},
				reset: function () {
					htmlForm.reset();
				},
				setError: function (field, message) {
					var fieldID = (field.attr("id") || field.attr("name")),
						errorID = fieldID + "-error",
						label = $("label[for='" + fieldID + "']");
					field.addClass("error");
					label.addClass("error");
					$("#" + errorID).remove();
					$("<div />", {
						id: errorID,
						class: "error dynamic-error",
						html: (message[0] === " " ? (label.text() + message) : message)
					}).insertAfter(field);
				},
				setPatterns: function (newPatterns) {
					options.patterns = newPatterns || {};
				},
				validate: function () {
					var fields = jForm.find(":input"),
						field,
						i;
					this.clearErrors();

					for (i = 0; i < fields.length; i++) {
						field = $(fields[i]);
						if (field.not(":hidden")) {
							if (field.attr("data-must-match")
									&& field.val() !== $("#" + field.attr("data-must-match")).val()) {
								this.setError(field, " must match " + $("label[for='" + field.attr("data-must-match") + "']").text() + ".");
							} else if (field.attr("required") && field.val().length === 0) {
								this.setError(field, " is required.");
							} else if (field.attr("minlength") && field.val().length < +field.attr("minlength")) {
								this.setError(field, " must be at least " + field.attr("minlength") + " characters long.");
							} else if (field.attr("maxlength") && field.val().length > +field.attr("maxlength")) {
								this.setError(field, " may not contain more than " + field.attr("maxlength") + " characters.");
							} else if (field.attr("pattern") && !(new RegExp("^" + field.attr("pattern") + "$")).test(field.val())) {
								this.setError(field, " is not valid.");
							} else if (!field.attr("pattern") && options.patterns[field.attr("type")]
									&& !options.patterns[field.attr("type")].test(field.val())) {
								this.setError(field, " is not valid.");
							}
						}
					}

					return (this.getErrorCount() === 0);
				}
			};

		if (options.autoValidate === "true") {
			jForm.submit(function (e) {
				if (!Form.validate()) {
					e.preventDefault();
				}
			});
		}

		if (options.protectChanges === "true") {
			Form.protectChanges();
		}

		if (identifier) { // Store for easy access. -- cwells
			CWA.DOM.forms[identifier] = Form;
		}
		return Form;
	};

	CWA.MVC = {
	};

	CWA.MVC.View = {
		cancelEdit: function (e) {
			e.preventDefault();
			if (document.referrer.indexOf(document.location.protocol + "//" + document.location.hostname) === 0) {
				history.back(); // The referring page was on this site, so just go back. -- cwells
			} else { // Go to the specified destination or the admin page for the current controller. -- cwells
				document.location = this.dataset.destination || (CWA.MVC.ControllerURL + "/admin");
			}
		},
		confirmDelete: function (e) {
			e.preventDefault();
			if (confirm("Are you sure you want to delete this item?")) {
				var href = $(this).attr("href"),
					itemID = href.substr(href.indexOf("/delete/") + 8),
					postData = { "itemID": itemID };
				postData[CWA.MVC.View.syncToken.name] = CWA.MVC.View.syncToken.value;
				$.ajax({
					type: "POST",
					url: href,
					data: postData,
					dataType: "json",
					complete: function (jqXHR) {
						var response = jqXHR.responseJSON,
							unknownError = "An error occurred while deleting the specified item.";
						if (!response || !response.status) {
							alert(unknownError);
						} else if (response.status.code !== 200) {
							alert(response.status.message || unknownError);
						} else { // The item was deleted. -- cwells
							if (response.data && response.data.NextURL) {
								window.location.href = response.data.NextURL;
							} else if (response.data && response.data.ControllerURL) {
								window.location.href = response.data.ControllerURL;
							} else {
								alert("Successfully deleted the specified item.");
							}
						}
					}
				});
			}
		},
		createSlug: function (str, allowUppercase) {
			if (typeof str !== "string") { return ""; }

			if (!allowUppercase) {
				str = str.toLowerCase();
			}
			str = str.replace(/'/g, ""); // Remove apostrophes.
			str = str.replace(/&/g, "and");
			// Allowed characters per RFC 1738:  alphanumeric and $-_.+!*'(),
			/*jslint regexp: true */
			str = str.replace(/[^a-z0-9$\-_.+!*()]/gi, "-"); // Replace all characters except our whitelist with hyphen.
			str = str.replace(/^[^a-z0-9]*|[^a-z0-9]*$/gi, ""); // Remove non-alphanumeric from the beginning and end.
			str = str.replace(/--+/g, "-"); // Replace multiple hyphens with one hyphen.
			/*jslint regexp: false */

			return str;
		},
		suggestSlug: function (e) {
			e.preventDefault();
			// Only suggest a new slug when the destination is empty. -- cwells
			if ($("#" + this.dataset.to).val() === "") {
				CWA.MVC.View.updateSlug.call(this, e);
			}
		},
		updateSlug: function (e) {
			e.preventDefault();
			var from = $("#" + this.dataset.from),
				to = $("#" + this.dataset.to);
			to.val(CWA.MVC.View.createSlug(from.val(), this.dataset.allowUppercase));
		}
	};

	$(document).ready(function () {
		$("#content-wrapper").on("click", "[data-cwa-click]", function (e) {
			if (typeof CWA.MVC.View[this.dataset.cwaClick] === "function") {
				CWA.MVC.View[this.dataset.cwaClick].call(this, e);
			}
		});
		$("#content-wrapper").on("focus", "[data-cwa-focus]", function (e) {
			if (typeof CWA.MVC.View[this.dataset.cwaFocus] === "function") {
				CWA.MVC.View[this.dataset.cwaFocus].call(this, e);
			}
		});
	});

}());
<|endoftext|>


<commit_before>/**
 * Javascript required to power the user defined forms.
 *
 * Rewritten and refactored from the prototype version FieldEditor.
 *
 * @todo Upgrade to jQuery 1.3 so we can use live rather
 * 			then livequery
 */
(function($) {
	$(document).ready(function() {

		/*--------------------- SUBMISSIONS ------------------------ */

		/**
		 * Delete a given Submission from the form, or all submissions
		 * we let the href of the delete link to do all the work for us
		 */

		$("#FormSubmissions .deleteSubmission").click(function() {
			var deletedSubmission = $(this);
			$.post($(this).attr('href'), function(data) {
				deletedSubmission.parents('div.report').fadeOut();
			});
			return false;
		});

		/*-------------------- FIELD EDITOR ----------------------- */

		/**
		 * Create a new instance of a field in the current form
		 * area. the type information should all be on this object
		 */

		$("div.FieldEditor .MenuHolder .action").livequery('click',function() {

			// if this form is readonly...
			if($("#Fields").hasClass('readonly')) return false;

			// Give the user some feedback
			statusMessage(ss.i18n._t('UserForms.ADDINGNEWFIELD', 'Adding New Field'));

			// variables
			var action = $("#Form_EditForm").attr("action") + '/field/Fields/addfield';
			var length = $(".FieldInfo").length + 1;
			var securityID = ($("#SecurityID").length > 0) ? '&SecurityID='+$("#SecurityID").attr("value") : '';
			var type = $(this).siblings("select").val();

			//send ajax request to the page
			$.ajax({
				type: "GET",
				url: action,
				data: 'NewID='+ length +"&Type="+ type + securityID,

				// create a new field
				success: function(msg){
					$('#Fields_fields').append(msg);
					statusMessage(ss.i18n._t('UserForms.ADDEDNEWFIELD', 'Added New Field'));

					//update the internal lists
					var name = $("#Fields_fields li.EditableFormField:last").attr("id").split(' ');

					$("#Fields_fields select.fieldOption").append("<option value='"+ name[2] +"'>New "+ name[2] + "</option>");
				},

				// error creating new field
				error: function(request, text, error) {
					statusMessage(ss.i18n._t('UserForms.ERRORCREATINGFIELD', 'Error Creating Field'));
				}
			});
			return false;
		});
		/**
		 * Upon renaming a field we should go through and rename all the
		 * fields in the select fields to use this new field title. We can
		 * just worry about the title text - don't mess around with the keys
		 */
		$('.EditableFormField .fieldInfo .text').livequery('change', function() {
			var value = $(this).val();
			var name = $(this).parents("li").attr("id").split(' ');
			$("#Fields_fields select.fieldOption option").each(function(i, domElement) {
				if($(domElement).val() == name[2]) {
					$(domElement).text(value);
				}
			});
		})
		/**
		 * Show the more options popdown. Or hide it if we
		 * currently have it open
		 */
		$(".EditableFormField .moreOptions").livequery('click',function() {

			var parentID = $(this).parents(".EditableFormField");
			if(parentID) {
				var extraOptions = parentID.children(".extraOptions");
				if(extraOptions) {
					if(extraOptions.hasClass('hidden')) {
						$(this).html(ss.i18n._t('UserForms.HIDEOPTIONS', 'Hide Options'));
						$(this).addClass("showing");
						extraOptions.removeClass('hidden').show();
					}
					else {
						$(this).html(ss.i18n._t('UserForms.SHOWOPTIONS', 'Show Options'));
						$(this).removeClass("showing");
						extraOptions.addClass('hidden').hide();
					}
				}
			}
			return false;
		});

		/**
		 * Delete a field from the user defined form
		 */
		$(".EditableFormField .delete").livequery('click', function() {
			// remove all the rules with relate to this field
			var text = $(this).parents("li").find(".fieldInfo .text").val();
			$("#Fields_fields .customRules select.fieldOption option").each(function(i, domElement) {
				if($(domElement).text() == text) {

					// check to see if this is selected. If it is then just remove the whole rule
					if($(domElement).parent('select.customRuleField').val() == $(domElement).val()) {
						$(domElement).parents('li.customRule').remove();
					}
					// otherwise remove the option
					else {
						$(domElement).remove();
					}
				}
			});
			$(this).parents(".EditableFormField").remove();
			return false;
		});

		/**
		 * Add a suboption to a radio field or to a dropdown box
		 * for example
		 */
		$(".EditableFormField .addableOption").livequery('click', function() {

			// Give the user some feedback
			statusMessage(ss.i18n._t('UserForms.ADDINGNEWFIELD', 'Adding New Option'));

			// variables
			var options = $(this).parent("li");
			var action = $("#Form_EditForm").attr("action") + '/field/Fields/addoptionfield';
			var parent = $(this).attr("rel");

			//send ajax request to the page
			$.ajax({
				type: "GET",
				url: action,
				data: 'Parent='+ parent,

				// create a new field
				success: function(msg){
					options.before(msg);
					statusMessage(ss.i18n._t('UserForms.ADDEDNEWFIELD', 'Added New Field'));
				},

				// error creating new field
				error: function(request, text, error) {
					statusMessage(ss.i18n._t('UserForms.ERRORCREATINGFIELD', 'Error Creating Field'));
				}
			});
			return false;
		});

		/**
		 * Delete a suboption such as an dropdown option or a
		 * checkbox field
		 */
		$(".EditableFormField .deleteOption").livequery('click', function() {
			// pass the deleted status onto the element
			$(this).parent("li").children("[type=text]").attr("value", "field-node-deleted");
			$(this).parent("li").hide();

			// Give the user some feedback
			statusMessage(ss.i18n._t('UserForms.REMOVINGOPTION', 'Removed Option'));
			return false;
		});

		/**
		 * Sort Fields in the Field List
		 */
		$("#Fields_fields").livequery(function() {
			$(this).sortable({
	  	 		handle : '.fieldHandler',
				cursor: 'pointer',
				items: 'li.EditableFormField',
				placeholder: 'removed-form-field',
				opacity: 0.6,
				revert: true,
				change : function (event, ui) {
					$("#Fields_fields").sortable('refreshPositions');
				},
		    	update : function (event, ui) {
		      		// get all the fields
					var sort = 1;
					$("li.EditableFormField").each(function() {
						$(this).find(".sortHidden").val(sort++);
					});
		    	}
			});
		});

		/**
		 * Sort Options in a Field List - Such as options in a
		 * dropdown field.
		 */
		$(".editableOptions").livequery(function() {
			$(this).sortable({
				handle : '.handle',
				cursor: 'pointer',
				items: 'li',
				placeholder: 'removed-form-field',
				opacity: 0.6,
				revert: true,
				change : function (event, ui) {
					$(this).sortable('refreshPositions');
				},
		    	update : function (event, ui) {
		      		// get all the fields
					var sort = 1;
					$(".editableOptions li").each(function() {
						$(this).find(".sortOptionHidden").val(sort++);
					});
		    	}
			});
		});

		/**
		 * Custom Rules Interface
		 */
		$(".customRules .conditionOption").livequery('change', function(){
			var valueInput = $(this).siblings(".ruleValue");
			if($(this).val() == "ValueNot" || $(this).val() == "HasValue") {
				valueInput.show();
			}
			else {
				valueInput.hide();
			}
		});
		/**
		 * Delete a custom rule
		 */
		$(".customRules .deleteCondition").livequery('click', function() {
			$(this).parent("li").fadeOut().remove();
		});
		/**
		 * Adding a custom rule to a given form
		 */
		$(".customRules .addCondition").livequery('click', function() {

			// Give the user some feedback
			statusMessage(ss.i18n._t('UserForms.ADDINGNEWRULE', 'Adding New Rule'));

			// get the fields li which to duplicate
			var currentRules = $(this).parent("li").parent("ul");
			var defaultRule = currentRules.children("li.hidden:first");
			var newRule = defaultRule.clone();

			newRule.children(".customRuleField").each(function(i, domElement) {
				var currentName = domElement.name.split("][");
				currentName[3] = currentName[2];
				currentName[2] = currentRules.children().size() + 1;
				domElement.name = currentName.join("][");
			});

			// remove hidden tag
			newRule.removeClass("hidden");

			// update the fields dropdown
			newRule.children("select.fieldOption").empty();

			$("#Fields_fields li.EditableFormField").each(function (i, domElement) {
				var name = $(domElement).attr("id").split(' ');
				newRule.children("select.fieldOption").append("<option value='"+ name[2] + "'>"+ $(domElement).find(".text").val() + "</option>");
			});

			// append to the list
			currentRules.append(newRule);

			return false;
		});
	});
})
(jQuery);<commit_msg>BUGFIX added return false to delete custom rule function<commit_after>/**
 * Javascript required to power the user defined forms.
 *
 * Rewritten and refactored from the prototype version FieldEditor.
 *
 * @todo Upgrade to jQuery 1.3 so we can use live rather
 * 			then livequery
 */
(function($) {
	$(document).ready(function() {

		/*--------------------- SUBMISSIONS ------------------------ */

		/**
		 * Delete a given Submission from the form, or all submissions
		 * we let the href of the delete link to do all the work for us
		 */

		$("#FormSubmissions .deleteSubmission").click(function() {
			var deletedSubmission = $(this);
			$.post($(this).attr('href'), function(data) {
				deletedSubmission.parents('div.report').fadeOut();
			});
			return false;
		});

		/*-------------------- FIELD EDITOR ----------------------- */

		/**
		 * Create a new instance of a field in the current form
		 * area. the type information should all be on this object
		 */

		$("div.FieldEditor .MenuHolder .action").livequery('click',function() {

			// if this form is readonly...
			if($("#Fields").hasClass('readonly')) return false;

			// Give the user some feedback
			statusMessage(ss.i18n._t('UserForms.ADDINGNEWFIELD', 'Adding New Field'));

			// variables
			var action = $("#Form_EditForm").attr("action") + '/field/Fields/addfield';
			var length = $(".FieldInfo").length + 1;
			var securityID = ($("#SecurityID").length > 0) ? '&SecurityID='+$("#SecurityID").attr("value") : '';
			var type = $(this).siblings("select").val();

			//send ajax request to the page
			$.ajax({
				type: "GET",
				url: action,
				data: 'NewID='+ length +"&Type="+ type + securityID,

				// create a new field
				success: function(msg){
					$('#Fields_fields').append(msg);
					statusMessage(ss.i18n._t('UserForms.ADDEDNEWFIELD', 'Added New Field'));

					//update the internal lists
					var name = $("#Fields_fields li.EditableFormField:last").attr("id").split(' ');

					$("#Fields_fields select.fieldOption").append("<option value='"+ name[2] +"'>New "+ name[2] + "</option>");
				},

				// error creating new field
				error: function(request, text, error) {
					statusMessage(ss.i18n._t('UserForms.ERRORCREATINGFIELD', 'Error Creating Field'));
				}
			});
			return false;
		});
		/**
		 * Upon renaming a field we should go through and rename all the
		 * fields in the select fields to use this new field title. We can
		 * just worry about the title text - don't mess around with the keys
		 */
		$('.EditableFormField .fieldInfo .text').livequery('change', function() {
			var value = $(this).val();
			var name = $(this).parents("li").attr("id").split(' ');
			$("#Fields_fields select.fieldOption option").each(function(i, domElement) {
				if($(domElement).val() == name[2]) {
					$(domElement).text(value);
				}
			});
		})
		/**
		 * Show the more options popdown. Or hide it if we
		 * currently have it open
		 */
		$(".EditableFormField .moreOptions").livequery('click',function() {

			var parentID = $(this).parents(".EditableFormField");
			if(parentID) {
				var extraOptions = parentID.children(".extraOptions");
				if(extraOptions) {
					if(extraOptions.hasClass('hidden')) {
						$(this).html(ss.i18n._t('UserForms.HIDEOPTIONS', 'Hide Options'));
						$(this).addClass("showing");
						extraOptions.removeClass('hidden').show();
					}
					else {
						$(this).html(ss.i18n._t('UserForms.SHOWOPTIONS', 'Show Options'));
						$(this).removeClass("showing");
						extraOptions.addClass('hidden').hide();
					}
				}
			}
			return false;
		});

		/**
		 * Delete a field from the user defined form
		 */
		$(".EditableFormField .delete").livequery('click', function() {
			// remove all the rules with relate to this field
			var text = $(this).parents("li").find(".fieldInfo .text").val();
			$("#Fields_fields .customRules select.fieldOption option").each(function(i, domElement) {
				if($(domElement).text() == text) {

					// check to see if this is selected. If it is then just remove the whole rule
					if($(domElement).parent('select.customRuleField').val() == $(domElement).val()) {
						$(domElement).parents('li.customRule').remove();
					}
					// otherwise remove the option
					else {
						$(domElement).remove();
					}
				}
			});
			$(this).parents(".EditableFormField").remove();
			return false;
		});

		/**
		 * Add a suboption to a radio field or to a dropdown box
		 * for example
		 */
		$(".EditableFormField .addableOption").livequery('click', function() {

			// Give the user some feedback
			statusMessage(ss.i18n._t('UserForms.ADDINGNEWFIELD', 'Adding New Option'));

			// variables
			var options = $(this).parent("li");
			var action = $("#Form_EditForm").attr("action") + '/field/Fields/addoptionfield';
			var parent = $(this).attr("rel");

			//send ajax request to the page
			$.ajax({
				type: "GET",
				url: action,
				data: 'Parent='+ parent,

				// create a new field
				success: function(msg){
					options.before(msg);
					statusMessage(ss.i18n._t('UserForms.ADDEDNEWFIELD', 'Added New Field'));
				},

				// error creating new field
				error: function(request, text, error) {
					statusMessage(ss.i18n._t('UserForms.ERRORCREATINGFIELD', 'Error Creating Field'));
				}
			});
			return false;
		});

		/**
		 * Delete a suboption such as an dropdown option or a
		 * checkbox field
		 */
		$(".EditableFormField .deleteOption").livequery('click', function() {
			// pass the deleted status onto the element
			$(this).parent("li").children("[type=text]").attr("value", "field-node-deleted");
			$(this).parent("li").hide();

			// Give the user some feedback
			statusMessage(ss.i18n._t('UserForms.REMOVINGOPTION', 'Removed Option'));
			return false;
		});

		/**
		 * Sort Fields in the Field List
		 */
		$("#Fields_fields").livequery(function() {
			$(this).sortable({
	  	 		handle : '.fieldHandler',
				cursor: 'pointer',
				items: 'li.EditableFormField',
				placeholder: 'removed-form-field',
				opacity: 0.6,
				revert: true,
				change : function (event, ui) {
					$("#Fields_fields").sortable('refreshPositions');
				},
		    	update : function (event, ui) {
		      		// get all the fields
					var sort = 1;
					$("li.EditableFormField").each(function() {
						$(this).find(".sortHidden").val(sort++);
					});
		    	}
			});
		});

		/**
		 * Sort Options in a Field List - Such as options in a
		 * dropdown field.
		 */
		$(".editableOptions").livequery(function() {
			$(this).sortable({
				handle : '.handle',
				cursor: 'pointer',
				items: 'li',
				placeholder: 'removed-form-field',
				opacity: 0.6,
				revert: true,
				change : function (event, ui) {
					$(this).sortable('refreshPositions');
				},
		    	update : function (event, ui) {
		      		// get all the fields
					var sort = 1;
					$(".editableOptions li").each(function() {
						$(this).find(".sortOptionHidden").val(sort++);
					});
		    	}
			});
		});

		/**
		 * Custom Rules Interface
		 */
		$(".customRules .conditionOption").livequery('change', function(){
			var valueInput = $(this).siblings(".ruleValue");
			if($(this).val() == "ValueNot" || $(this).val() == "HasValue") {
				valueInput.show();
			}
			else {
				valueInput.hide();
			}
		});
		/**
		 * Delete a custom rule
		 */
		$(".customRules .deleteCondition").livequery('click', function() {
			$(this).parent("li").fadeOut().remove();

			return false;
		});
		/**
		 * Adding a custom rule to a given form
		 */
		$(".customRules .addCondition").livequery('click', function() {

			// Give the user some feedback
			statusMessage(ss.i18n._t('UserForms.ADDINGNEWRULE', 'Adding New Rule'));

			// get the fields li which to duplicate
			var currentRules = $(this).parent("li").parent("ul");
			var defaultRule = currentRules.children("li.hidden:first");
			var newRule = defaultRule.clone();

			newRule.children(".customRuleField").each(function(i, domElement) {
				var currentName = domElement.name.split("][");
				currentName[3] = currentName[2];
				currentName[2] = currentRules.children().size() + 1;
				domElement.name = currentName.join("][");
			});

			// remove hidden tag
			newRule.removeClass("hidden");

			// update the fields dropdown
			newRule.children("select.fieldOption").empty();

			$("#Fields_fields li.EditableFormField").each(function (i, domElement) {
				var name = $(domElement).attr("id").split(' ');
				newRule.children("select.fieldOption").append("<option value='"+ name[2] + "'>"+ $(domElement).find(".text").val() + "</option>");
			});

			// append to the list
			currentRules.append(newRule);

			return false;
		});
	});
})
(jQuery);<|endoftext|>


<commit_before>Giblets = new Mongo.Collection('giblets');
Notifications = new Mongo.Collection('notifications');

if (Meteor.isServer) {
  Meteor.methods({
      startGibletsOnBoot: function() {
        var giblets = Giblets.find().fetch();
        for( var giblet in giblets ) {
          var thisGiblet = giblets[giblet];
          console.log(thisGiblet.active);
          if (thisGiblet.active) {
            // start cron job!
            Meteor.call('scheduleGiblet', thisGiblet._id, thisGiblet.frequency)
          }
        }
      }
  });
  Meteor.call('startGibletsOnBoot');
}

Meteor.methods({
  updateGiblet: function ( gibletID, data ) {
    Giblets.update({_id: gibletID}, {$set: data});
  },
  addUserSettings: function ( userSettings ) {
    var currentUser = Meteor.users.find().fetch();
    var userId = currentUser[0]._id;
    Meteor.users.update({_id: userId},
      {$set: {
        chosenName: userSettings.chosenName,
        chosenPhoneNumber: userSettings.chosenPhoneNumber,
        chosenEmail: userSettings.chosenEmail
      }
    });
  },
  clearGibletsDB: function () {
    Giblets.remove({});
    SyncedCron.stop();
  }
});<commit_msg>repair user settings issue<commit_after>Giblets = new Mongo.Collection('giblets');
Notifications = new Mongo.Collection('notifications');

if (Meteor.isServer) {
  Meteor.methods({
      startGibletsOnBoot: function() {
        var giblets = Giblets.find().fetch();
        for( var giblet in giblets ) {
          var thisGiblet = giblets[giblet];
          console.log(thisGiblet.active);
          if (thisGiblet.active) {
            // start cron job!
            Meteor.call('scheduleGiblet', thisGiblet._id, thisGiblet.frequency)
          }
        }
      }
  });
  Meteor.call('startGibletsOnBoot');
}

Meteor.methods({
  updateGiblet: function ( gibletID, data ) {
    Giblets.update({_id: gibletID}, {$set: data});
  },
  addUserSettings: function ( userSettings ) {
    var userId = Meteor.userId();
    Meteor.users.update({_id: userId},
      {$set: {
        chosenName: userSettings.chosenName,
        chosenPhoneNumber: userSettings.chosenPhoneNumber,
        chosenEmail: userSettings.chosenEmail
      }
    });
  },
  clearGibletsDB: function () {
    Giblets.remove({});
    SyncedCron.stop();
  }
});<|endoftext|>


<commit_before>import React from 'react';
import {connect} from 'react-redux';
import {Button, Modal} from 'antd';
import {action, selector} from './redux';
import {action as authAction} from '../../util/auth';
import style from './UserOp.module.scss';

class UserOp extends React.Component {
  constructor(props) {
    super(props);
  }

  onDetail = () => {
    this.props.showUserDetailModal({});
  };

  onCreate = () => {
    this.props.showUserCreateModal({});
  };

  onEdit = () => {
    this.props.showUserEditModal({});
  };

  onDelete = () => {
    Modal.confirm({
      title: '确定要删除该用户吗?',
      okText: '确定',
      okType: 'danger',
      cancelText: '取消',
      onOk: () => {
        if (this.props.selectedUserIds.length !== 1)
            return;

        const id = this.props.selectedUserIds[0];
        this.props.deleteUser({
          params: {
            id
          },
          onSuccess: () => {
            console.log('on Delete')
            this.props.fetchUserList({});
          }
        });
      },
      onCancel: () => {
      },
    });
  };

  onRefresh = () => {
    this.props.fetchUserList({});
  };

  render() {
    return (

        <Button.Group>
          <Button icon="info-circle-o"
                  disabled={this.props.selectedUserIds.length !== 1}
                  onClick={this.onDetail}>
            查看
          </Button>
          <Button icon="plus-circle-o"
                  onClick={this.onCreate}>
            新增
          </Button>
          <Button icon="edit"
                  disabled={this.props.selectedUserIds.length !== 1}
                  onClick={this.onEdit}>
            编辑
          </Button>
          <Button type="danger"
                  icon="minus-circle-o"
                  disabled={this.props.selectedUserIds.length < 1}
                  onClick={this.onDelete}>
            删除
          </Button>
          <Button icon="reload"
                  onClick={this.onRefresh}>
            刷新
          </Button>
        </Button.Group>

    );
  }
}

const mapStateToProps = (appState, ownProps) => {
  const selectedUserIds = selector.selectSelectedUserIds(appState);
  const checkedUserRoles = selector.selectCheckedUserRoles(appState);

  return {
    selectedUserIds,
    checkedUserRoles,
  };
};

const mapDispatchToProps = {
  ...action,
  ...authAction,
};

export default connect(mapStateToProps, mapDispatchToProps)(UserOp);
<commit_msg>UserOp: cleanup<commit_after>import React from 'react';
import {connect} from 'react-redux';
import {Button, Modal} from 'antd';
import {action, selector} from './redux';
import {action as authAction} from '../../util/auth';
import style from './UserOp.module.scss';

class UserOp extends React.Component {
  constructor(props) {
    super(props);
  }

  onDetail = () => {
    this.props.showUserDetailModal({});
  };

  onCreate = () => {
    this.props.showUserCreateModal({});
  };

  onEdit = () => {
    this.props.showUserEditModal({});
  };

  onDelete = () => {
    Modal.confirm({
      title: '确定要删除该用户吗?',
      okText: '确定',
      okType: 'danger',
      cancelText: '取消',
      onOk: () => {
        if (this.props.selectedUserIds.length !== 1)
            return;

        const id = this.props.selectedUserIds[0];
        this.props.deleteUser({
          params: {
            id
          },
          onSuccess: () => {
            this.props.fetchUserList({});
          }
        });
      },
      onCancel: () => {
      },
    });
  };

  onRefresh = () => {
    this.props.fetchUserList({});
  };

  render() {
    return (

        <Button.Group>
          <Button icon="info-circle-o"
                  disabled={this.props.selectedUserIds.length !== 1}
                  onClick={this.onDetail}>
            查看
          </Button>
          <Button icon="plus-circle-o"
                  onClick={this.onCreate}>
            新增
          </Button>
          <Button icon="edit"
                  disabled={this.props.selectedUserIds.length !== 1}
                  onClick={this.onEdit}>
            编辑
          </Button>
          <Button type="danger"
                  icon="minus-circle-o"
                  disabled={this.props.selectedUserIds.length < 1}
                  onClick={this.onDelete}>
            删除
          </Button>
          <Button icon="reload"
                  onClick={this.onRefresh}>
            刷新
          </Button>
        </Button.Group>

    );
  }
}

const mapStateToProps = (appState, ownProps) => {
  const selectedUserIds = selector.selectSelectedUserIds(appState);
  const checkedUserRoles = selector.selectCheckedUserRoles(appState);

  return {
    selectedUserIds,
    checkedUserRoles,
  };
};

const mapDispatchToProps = {
  ...action,
  ...authAction,
};

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


<commit_before>'use strict';

angular.module('myApp.user', ['ngRoute'])

.config(['$routeProvider', function($routeProvider, $routeParams) {
  $routeProvider.when('/user/', {
    templateUrl: 'user/user.html',
    controller: 'UserCtrl'
  });
}])

.controller('UserCtrl', ['$scope', '$routeParams', '$rootScope', '$resource', function($scope, $routeParams, $rootScope, $resource) {
  $scope.model = $resource('https://api.stackexchange.com/2.2/me?access_token=' + $rootScope.access_token);
}]);<commit_msg>added ngResource dependancy to user module<commit_after>'use strict';

angular.module('myApp.user', ['ngRoute', 'ngResource'])

.config(['$routeProvider', function($routeProvider, $routeParams) {
  $routeProvider.when('/user/', {
    templateUrl: 'user/user.html',
    controller: 'UserCtrl'
  });
}])

.controller('UserCtrl', ['$scope', '$routeParams', '$rootScope', '$resource', function($scope, $routeParams, $rootScope, $resource) {
  $scope.model = $resource('https://api.stackexchange.com/2.2/me?access_token=' + $rootScope.access_token);
}]);<|endoftext|>


<commit_before>/**
* Copyright 2012-2018, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var colorscaleAttrs = require('../../components/colorscale/attributes');
var colorbarAttrs = require('../../components/colorbar/attributes');
var surfaceAtts = require('../surface/attributes');
var baseAttrs = require('../../plots/attributes');

var extendFlat = require('../../lib/extend').extendFlat;

module.exports = extendFlat({
    x: {
        valType: 'data_array',
        role: 'info',
        editType: 'calc+clearAxisTypes',
        description: [
            'Sets the X coordinates of the vertices on X axis.'
        ].join(' ')
    },
    y: {
        valType: 'data_array',
        role: 'info',
        editType: 'calc+clearAxisTypes',
        description: [
            'Sets the Y coordinates of the vertices on Y axis.'
        ].join(' ')
    },
    z: {
        valType: 'data_array',
        role: 'info',
        editType: 'calc+clearAxisTypes',
        description: [
            'Sets the Z coordinates of the vertices on Z axis.'
        ].join(' ')
    },
    value: {
        valType: 'data_array',
        role: 'info',
        editType: 'calc',
        description: [
            'Sets the 4th dimension of the vertices. It should be',
            'one dimensional array containing n=width*height*depth numbers.'
        ].join(' ')
    },
    isocap: {
        valType: 'boolean',
        role: 'info',
        editType: 'calc',
        dflt: false,
        description: [
            'Enable caps between minimum and maximum iso values.'
        ].join(' ')
    },
    isovalue: {
        valType: 'data_array',
        role: 'info',
        editType: 'calc',
        dflt: [0],
        description: [
            'Sets iso surface boundaries.'
        ].join(' ')
    },
    meshalgo: {
        valType: 'string',
        role: 'info',
        dflt: 'MarchingCubes',
        editType: 'calc',
        description: [
            'Sets the isosurface polygonizer algorithm:',
            'including: `MarchingCubes` (i.e. default),',
            '`MarchingTetrahedra` or `SurfaceNets`.'
        ].join(' ')
    },

    text: {
        valType: 'string',
        role: 'info',
        dflt: '',
        arrayOk: true,
        editType: 'calc',
        description: [
            'Sets the text elements associated with the vertices.',
            'If trace `hoverinfo` contains a *text* flag and *hovertext* is not set,',
            'these elements will be seen in the hover labels.'
        ].join(' ')
    },

    transforms: undefined
},

colorscaleAttrs('', {
    colorAttr: '`intensity`',
    showScaleDflt: true,
    editTypeOverride: 'calc'
}), {

    colorbar: colorbarAttrs,

    opacity: surfaceAtts.opacity,

    // Flat shaded mode
    flatshading: {
        valType: 'boolean',
        role: 'style',
        dflt: false,
        editType: 'calc',
        description: [
            'Determines whether or not normal smoothing is applied to the isosurfaces,',
            'creating isosurfaces with an angular, low-poly look via flat reflections.'
        ].join(' ')
    },

    contour: {
        show: extendFlat({}, surfaceAtts.contours.x.show, {
            description: [
                'Sets whether or not dynamic contours are shown on hover'
            ].join(' ')
        }),
        color: surfaceAtts.contours.x.color,
        width: surfaceAtts.contours.x.width,
        editType: 'calc'
    },

    lightposition: {
        x: extendFlat({}, surfaceAtts.lightposition.x, {dflt: 1e5}),
        y: extendFlat({}, surfaceAtts.lightposition.y, {dflt: 1e5}),
        z: extendFlat({}, surfaceAtts.lightposition.z, {dflt: 0}),
        editType: 'calc'
    },
    lighting: extendFlat({
        vertexnormalsepsilon: {
            valType: 'number',
            role: 'style',
            min: 0.00,
            max: 1,
            dflt: 1e-12, // otherwise finely tessellated things eg. the brain will have no specular light reflection
            editType: 'calc',
            description: 'Epsilon for vertex normals calculation avoids math issues arising from degenerate geometry.'
        },
        facenormalsepsilon: {
            valType: 'number',
            role: 'style',
            min: 0.00,
            max: 1,
            dflt: 1e-6, // even the brain model doesn't appear to need finer than this
            editType: 'calc',
            description: 'Epsilon for face normals calculation avoids math issues arising from degenerate geometry.'
        },
        editType: 'calc'
    }, surfaceAtts.lighting),

    hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {editType: 'calc'})
});
<commit_msg>intensity > value<commit_after>/**
* Copyright 2012-2018, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var colorscaleAttrs = require('../../components/colorscale/attributes');
var colorbarAttrs = require('../../components/colorbar/attributes');
var surfaceAtts = require('../surface/attributes');
var baseAttrs = require('../../plots/attributes');

var extendFlat = require('../../lib/extend').extendFlat;

module.exports = extendFlat({
    x: {
        valType: 'data_array',
        role: 'info',
        editType: 'calc+clearAxisTypes',
        description: [
            'Sets the X coordinates of the vertices on X axis.'
        ].join(' ')
    },
    y: {
        valType: 'data_array',
        role: 'info',
        editType: 'calc+clearAxisTypes',
        description: [
            'Sets the Y coordinates of the vertices on Y axis.'
        ].join(' ')
    },
    z: {
        valType: 'data_array',
        role: 'info',
        editType: 'calc+clearAxisTypes',
        description: [
            'Sets the Z coordinates of the vertices on Z axis.'
        ].join(' ')
    },
    value: {
        valType: 'data_array',
        role: 'info',
        editType: 'calc',
        description: [
            'Sets the 4th dimension of the vertices. It should be',
            'one dimensional array containing n=width*height*depth numbers.'
        ].join(' ')
    },
    isocap: {
        valType: 'boolean',
        role: 'info',
        editType: 'calc',
        dflt: false,
        description: [
            'Enable caps between minimum and maximum iso values.'
        ].join(' ')
    },
    isovalue: {
        valType: 'data_array',
        role: 'info',
        editType: 'calc',
        dflt: [0],
        description: [
            'Sets iso surface boundaries.'
        ].join(' ')
    },
    meshalgo: {
        valType: 'string',
        role: 'info',
        dflt: 'MarchingCubes',
        editType: 'calc',
        description: [
            'Sets the isosurface polygonizer algorithm:',
            'including: `MarchingCubes` (i.e. default),',
            '`MarchingTetrahedra` or `SurfaceNets`.'
        ].join(' ')
    },

    text: {
        valType: 'string',
        role: 'info',
        dflt: '',
        arrayOk: true,
        editType: 'calc',
        description: [
            'Sets the text elements associated with the vertices.',
            'If trace `hoverinfo` contains a *text* flag and *hovertext* is not set,',
            'these elements will be seen in the hover labels.'
        ].join(' ')
    },

    transforms: undefined
},

colorscaleAttrs('', {
    colorAttr: '`value`',
    showScaleDflt: true,
    editTypeOverride: 'calc'
}), {

    colorbar: colorbarAttrs,

    opacity: surfaceAtts.opacity,

    // Flat shaded mode
    flatshading: {
        valType: 'boolean',
        role: 'style',
        dflt: false,
        editType: 'calc',
        description: [
            'Determines whether or not normal smoothing is applied to the isosurfaces,',
            'creating isosurfaces with an angular, low-poly look via flat reflections.'
        ].join(' ')
    },

    contour: {
        show: extendFlat({}, surfaceAtts.contours.x.show, {
            description: [
                'Sets whether or not dynamic contours are shown on hover'
            ].join(' ')
        }),
        color: surfaceAtts.contours.x.color,
        width: surfaceAtts.contours.x.width,
        editType: 'calc'
    },

    lightposition: {
        x: extendFlat({}, surfaceAtts.lightposition.x, {dflt: 1e5}),
        y: extendFlat({}, surfaceAtts.lightposition.y, {dflt: 1e5}),
        z: extendFlat({}, surfaceAtts.lightposition.z, {dflt: 0}),
        editType: 'calc'
    },
    lighting: extendFlat({
        vertexnormalsepsilon: {
            valType: 'number',
            role: 'style',
            min: 0.00,
            max: 1,
            dflt: 1e-12, // otherwise finely tessellated things eg. the brain will have no specular light reflection
            editType: 'calc',
            description: 'Epsilon for vertex normals calculation avoids math issues arising from degenerate geometry.'
        },
        facenormalsepsilon: {
            valType: 'number',
            role: 'style',
            min: 0.00,
            max: 1,
            dflt: 1e-6, // even the brain model doesn't appear to need finer than this
            editType: 'calc',
            description: 'Epsilon for face normals calculation avoids math issues arising from degenerate geometry.'
        },
        editType: 'calc'
    }, surfaceAtts.lighting),

    hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {editType: 'calc'})
});
<|endoftext|>