<commit_before>/*jslint indent: 2, unparam: true*/
/*global document: false, window: false, XMLHttpRequest: false, chrome: false, btoa: false, localStorage:false */
"use strict";

var TogglButton = chrome.extension.getBackgroundPage().TogglButton;
var Db = chrome.extension.getBackgroundPage().Db;

var Settings = {
  $startAutomatically: null,
  $stopAutomatically: null,
  $showRightClickButton: null,
  $postPopup: null,
  $socket: null,
  $nanny: null,
  $pomodoroMode: null,
  $pomodoroSound: null,
  $permissionsList: document.querySelector("#permissions-list"),
  $customPermissionsList: document.querySelector("#custom-permissions-list"),
  $newPermission: document.querySelector("#new-permission"),
  $originsSelect: document.querySelector("#origins"),
  origins: [],
  $pomodoroStopTimeTracking: null,
  $stopAtDayEnd: null,
  $tabs: null,
  $defaultProject: null,
  showPage: function () {
    var key, project, clientName, projects, clients, selected;
    document.querySelector("#version").innerHTML = "(" + chrome.runtime.getManifest().version + ")";
    Settings.setFromTo();
    document.querySelector("#nag-nanny-interval").value = Db.get("nannyInterval") / 60000;
    Settings.toggleState(Settings.$showRightClickButton, Db.get("showRightClickButton"));
    Settings.toggleState(Settings.$startAutomatically, Db.get("startAutomatically"));
    Settings.toggleState(Settings.$stopAutomatically, Db.get("stopAutomatically"));
    Settings.toggleState(Settings.$postPopup, Db.get("showPostPopup"));
    Settings.toggleState(Settings.$nanny, Db.get("nannyCheckEnabled"));
    Settings.toggleState(Settings.$socket, Db.get("socketEnabled"));
    Settings.toggleState(Settings.$idleDetection, Db.get("idleDetectionEnabled"));
    Settings.toggleState(Settings.$pomodoroMode, Db.get("pomodoroModeEnabled"));
    Settings.toggleState(Settings.$pomodoroSound, Db.get("pomodoroSoundEnabled"));
    Settings.toggleState(Settings.$pomodoroStopTimeTracking, Db.get("pomodoroStopTimeTrackingWhenTimerEnds"));

    document.querySelector("#pomodoro-interval").value = Db.get("pomodoroInterval");

    Settings.toggleState(Settings.$stopAtDayEnd, Db.get("stopAtDayEnd"));
    document.querySelector("#day-end-time").value = Db.get("dayEndTime");
    if (Db.get("projects") !== '') {
      projects = JSON.parse(Db.get("projects"));
      clients = JSON.parse(Db.get("clients"));
      Settings.$defaultProject.innerHTML = '<option value="0">- No project -</option>';
      for (key in projects) {
        if (projects.hasOwnProperty(key)) {
          selected = '';
          project = projects[key];
          clientName = (!!project.cid && !!clients[project.cid]) ? ' . ' + clients[project.cid].name  : '';
          if (parseInt(Db.get('defaultProject'), 10) === project.id) {
            selected = "selected ";
          }
          Settings.$defaultProject.innerHTML += "<option " + selected + "value='" + project.id + "'>" + project.name + clientName + "</option>";
        }
      }
    }
    TogglButton.analytics("settings", null);
    Settings.loadSitesIntoList();
  },
  setFromTo: function () {
    var fromTo = Db.get("nannyFromTo").split("-");
    document.querySelector("#nag-nanny-from").value = fromTo[0];
    document.querySelector("#nag-nanny-to").value = fromTo[1];
  },
  toggleState: function (elem, state) {
    elem.checked = state;
  },
  toggleSetting: function (elem, state, type) {
    var request = {
      type: type,
      state: state
    };
    if (elem !== null) {
      Settings.toggleState(elem, state);
    }
    chrome.extension.sendMessage(request);
  },
  saveSetting: function (value, type) {
    Settings.toggleSetting(null, value, type);
  },
  loadSitesIntoList: function () {
    var html = "",
      html_list = "",
      custom_html = "",
      url, name,
      i, key,
      origins,
      disabled,
      checked,
      customs;

    // Load permissions list
    chrome.permissions.getAll(function (results) {
      origins = results.origins;
      console.log("origins: ALL("+Object.keys(TogglOrigins).length+ ") | Enabled ("+origins.length+")");
      for (i = 0; i < origins.length; i++) {
        name = url = origins[i].replace("*://*.", "").replace("*://", "").replace("/*", "");
        if (url.split(".").length > 2) {
          name = url.substr(url.indexOf(".")+1)
        }
        Settings.origins[name] = {
          id: i,
          origin: origins[i],
          url: url,
          name: name
        };
      }

      for (key in TogglOrigins) {
        if (TogglOrigins.hasOwnProperty(key)) {
          disabled = '';
          checked = 'checked';

          if (!Settings.origins[key]) {
            disabled = 'class="disabled"';
            checked = '';
          }
          if (!TogglOrigins[key].clone) {
            html += "<option id='origin' data-id='" + i + "' value='" + key + "'>" + TogglOrigins[key].name + "</option>";
          }
          html_list += '
<input type="checkbox" data-host="' + TogglOrigins[key] + '" ' + checked + '>
' + key + '

';
        }
      }

      document.querySelector("#origins").innerHTML = html;
      Settings.$permissionsList.innerHTML = html_list;
    });

    // Load Custom Permissions list
    customs = Db.getAllOrigins();
    for (key in customs) {
      if (customs.hasOwnProperty(key)) {
        custom_html += "
×" + key + " - " + customs[key] + "
";
      }
    }
    Settings.$customPermissionsList.innerHTML = custom_html;
  }
};
document.addEventListener('DOMContentLoaded', function (e) {
  Settings.$startAutomatically = document.querySelector("#start_automatically");
  Settings.$stopAutomatically = document.querySelector("#stop_automatically");
  Settings.$showRightClickButton = document.querySelector("#show_right_click_button");
  Settings.$postPopup = document.querySelector("#show_post_start_popup");
  Settings.$socket = document.querySelector("#websocket");
  Settings.$nanny = document.querySelector("#nag-nanny");
  Settings.$idleDetection = document.querySelector("#idle-detection");
  Settings.$pomodoroMode = document.querySelector("#pomodoro-mode");
  Settings.$pomodoroSound = document.querySelector("#enable-sound-signal");
  Settings.$pomodoroStopTimeTracking = document.querySelector("#pomodoro-stop-time");
  Settings.$stopAtDayEnd = document.querySelector("#stop-at-day-end");
  Settings.$tabs = document.querySelector(".tabs");
  Settings.$defaultProject = document.querySelector("#default-project");
  Settings.showPage();
  Settings.$showRightClickButton.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("showRightClickButton") !== "true"), "toggle-right-click-button");
    TogglButton.toggleRightClickButton((localStorage.getItem("showRightClickButton") !== "true"));
  });
  Settings.$startAutomatically.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("startAutomatically") !== "true"), "toggle-start-automatically");
  });
  Settings.$stopAutomatically.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("stopAutomatically") !== "true"), "toggle-stop-automatically");
  });
  Settings.$postPopup.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("showPostPopup") !== "true"), "toggle-popup");
  });
  Settings.$socket.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("socketEnabled") !== "true"), "toggle-socket");
  });
  Settings.$nanny.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("nannyCheckEnabled") !== "true"), "toggle-nanny");
  });
  Settings.$idleDetection.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("idleDetectionEnabled") !== "true"), "toggle-idle");
  });
  Settings.$pomodoroMode.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("pomodoroModeEnabled") !== "true"), "toggle-pomodoro");
  });
  Settings.$pomodoroSound.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("pomodoroSoundEnabled") !== "true"), "toggle-pomodoro-sound");
  });
  Settings.$pomodoroStopTimeTracking.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("pomodoroStopTimeTrackingWhenTimerEnds") !== "true"), "toggle-pomodoro-stop-time");
  });
  Settings.$defaultProject.addEventListener('change', function (e) {
    var defaultProject = Settings.$defaultProject.options[Settings.$defaultProject.selectedIndex].value;
    Settings.saveSetting(defaultProject, "change-default-project");
  });

  Settings.$stopAtDayEnd.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("stopAtDayEnd") !== "true"), "toggle-stop-at-day-end");
  });

  document.querySelector(".tab-links").addEventListener('click', function (e) {
    document.querySelector("header .active").classList.remove("active");
    e.target.classList.add("active");
    Settings.$tabs.setAttribute("data-tab", e.target.getAttribute("data-tab"));
  });

  document.querySelector("#nag-nanny-from").addEventListener('blur', function (e) {
    if (e.target.value.length === 0) {
      Settings.setFromTo();
      return;
    }
    Settings.$fromTo = e.target.value + "-" + document.querySelector('#nag-nanny-to').value;
    Settings.saveSetting();
  });
  document.querySelector("#nag-nanny-to").addEventListener('blur', function (e) {
    if (e.target.value.length === 0) {
      Settings.setFromTo();
      return;
    }
    var fromTo = document.querySelector('#nag-nanny-from').value + "-" + e.target.value;
    Settings.saveSetting(fromTo, "toggle-nanny-from-to");
  });
  document.querySelector("#nag-nanny-interval").addEventListener('blur', function (e) {
    if (e.target.value < 1) {
      e.target.value = 1;
      return;
    }
    Settings.saveSetting((document.querySelector('#nag-nanny-interval').value * 60000), "toggle-nanny-interval");

  });

  document.querySelector("#pomodoro-interval").addEventListener('blur', function (e) {
    if (e.target.value < 1) {
      e.target.value = 1;
      return;
    }
    Settings.saveSetting(+(document.querySelector('#pomodoro-interval').value), "toggle-pomodoro-interval");

  });

// Add custom permission (custom domain)
  document.querySelector('#add-permission').addEventListener('click', function(e) {
    var domain = "*://" + Settings.$newPermission.value + "/",
      permission = {origins: [domain]},
      e = Settings.$originsSelect;

    chrome.permissions.request(permission, function(result) {
      if (result) {
        console.log(domain +" > [ " + document.querySelector("#origins").value + " ]");
        Db.setOrigin(Settings.$newPermission.value, Settings.$originsSelect.value);
        Settings.$newPermission.value = "";
      }
      Settings.loadSitesIntoList();
      if (result) {
        document.location.hash = domain;
      }
    });
  });

  document.querySelector('#permissions-list').addEventListener('click', function(e) {
    var permission = {origins: [e.target.getAttribute("data-host")]};

    if (e.target.checked) {
      chrome.permissions.request(permission, function(result) {
        if (result) {
          e.target.parentNode.classList.remove("disabled");
        }
      });
    } else {
      chrome.permissions.contains(permission, function(allowed) {
      if (allowed) {
        chrome.permissions.remove(permission, function(result) {
          if (result) {
            e.target.parentNode.classList.add("disabled");
          }
        });
      } else {
        alert('No "' + Settings.origins[e.target.getAttribute("data-id")] + '" host permission found.');
      }
    });
    }
  });

  document.querySelector('#custom-permissions-list').addEventListener('click', function(e) {
    var custom, domain, permission, parent;
    if (e.target.className === "remove-custom") {
      parent = e.target.parentNode;
      custom = parent.querySelector('strong').textContent;
      domain = "*://" + custom + "/";
      permission = {origins: [domain]};

      chrome.permissions.contains(permission, function(allowed) {
        if (allowed) {
          chrome.permissions.remove(permission, function(result) {
            if (result) {
              Db.removeOrigin(custom);
              parent.remove();
            } else {
              alert("Fail");
            }
          });
        } else {
          alert('No "' + custom + '" host permission found.');
        }
      });

    }
    return false;
  });

  document.querySelector("#day-end-time").addEventListener('blur', function (e) {
    if (e.target.value < 1) {
      e.target.value = 1;
      return;
    }
    Settings.saveSetting((document.querySelector('#day-end-time').value), "toggle-day-end-time");
  });

  document.querySelector(".container").addEventListener("transitionend", function (e) {
    if (e.propertyName === "height"
        && e.target.className === "subsettings-details"
        && e.target.clientHeight > 0) {
      e.target.scrollIntoView();
    }
  }, false);
});
<commit_msg>Small new permission value refactor<commit_after>/*jslint indent: 2, unparam: true*/
/*global document: false, window: false, XMLHttpRequest: false, chrome: false, btoa: false, localStorage:false */
"use strict";

var TogglButton = chrome.extension.getBackgroundPage().TogglButton;
var Db = chrome.extension.getBackgroundPage().Db;

var Settings = {
  $startAutomatically: null,
  $stopAutomatically: null,
  $showRightClickButton: null,
  $postPopup: null,
  $socket: null,
  $nanny: null,
  $pomodoroMode: null,
  $pomodoroSound: null,
  $permissionsList: document.querySelector("#permissions-list"),
  $customPermissionsList: document.querySelector("#custom-permissions-list"),
  $newPermission: document.querySelector("#new-permission"),
  $originsSelect: document.querySelector("#origins"),
  origins: [],
  $pomodoroStopTimeTracking: null,
  $stopAtDayEnd: null,
  $tabs: null,
  $defaultProject: null,
  showPage: function () {
    var key, project, clientName, projects, clients, selected;
    document.querySelector("#version").innerHTML = "(" + chrome.runtime.getManifest().version + ")";
    Settings.setFromTo();
    document.querySelector("#nag-nanny-interval").value = Db.get("nannyInterval") / 60000;
    Settings.toggleState(Settings.$showRightClickButton, Db.get("showRightClickButton"));
    Settings.toggleState(Settings.$startAutomatically, Db.get("startAutomatically"));
    Settings.toggleState(Settings.$stopAutomatically, Db.get("stopAutomatically"));
    Settings.toggleState(Settings.$postPopup, Db.get("showPostPopup"));
    Settings.toggleState(Settings.$nanny, Db.get("nannyCheckEnabled"));
    Settings.toggleState(Settings.$socket, Db.get("socketEnabled"));
    Settings.toggleState(Settings.$idleDetection, Db.get("idleDetectionEnabled"));
    Settings.toggleState(Settings.$pomodoroMode, Db.get("pomodoroModeEnabled"));
    Settings.toggleState(Settings.$pomodoroSound, Db.get("pomodoroSoundEnabled"));
    Settings.toggleState(Settings.$pomodoroStopTimeTracking, Db.get("pomodoroStopTimeTrackingWhenTimerEnds"));

    document.querySelector("#pomodoro-interval").value = Db.get("pomodoroInterval");

    Settings.toggleState(Settings.$stopAtDayEnd, Db.get("stopAtDayEnd"));
    document.querySelector("#day-end-time").value = Db.get("dayEndTime");
    if (Db.get("projects") !== '') {
      projects = JSON.parse(Db.get("projects"));
      clients = JSON.parse(Db.get("clients"));
      Settings.$defaultProject.innerHTML = '<option value="0">- No project -</option>';
      for (key in projects) {
        if (projects.hasOwnProperty(key)) {
          selected = '';
          project = projects[key];
          clientName = (!!project.cid && !!clients[project.cid]) ? ' . ' + clients[project.cid].name  : '';
          if (parseInt(Db.get('defaultProject'), 10) === project.id) {
            selected = "selected ";
          }
          Settings.$defaultProject.innerHTML += "<option " + selected + "value='" + project.id + "'>" + project.name + clientName + "</option>";
        }
      }
    }
    TogglButton.analytics("settings", null);
    Settings.loadSitesIntoList();
  },
  setFromTo: function () {
    var fromTo = Db.get("nannyFromTo").split("-");
    document.querySelector("#nag-nanny-from").value = fromTo[0];
    document.querySelector("#nag-nanny-to").value = fromTo[1];
  },
  toggleState: function (elem, state) {
    elem.checked = state;
  },
  toggleSetting: function (elem, state, type) {
    var request = {
      type: type,
      state: state
    };
    if (elem !== null) {
      Settings.toggleState(elem, state);
    }
    chrome.extension.sendMessage(request);
  },
  saveSetting: function (value, type) {
    Settings.toggleSetting(null, value, type);
  },
  loadSitesIntoList: function () {
    var html = "",
      html_list = "",
      custom_html = "",
      url, name,
      i, key,
      origins,
      disabled,
      checked,
      customs;

    // Load permissions list
    chrome.permissions.getAll(function (results) {
      origins = results.origins;
      console.log("origins: ALL("+Object.keys(TogglOrigins).length+ ") | Enabled ("+origins.length+")");
      for (i = 0; i < origins.length; i++) {
        name = url = origins[i].replace("*://*.", "").replace("*://", "").replace("/*", "");
        if (url.split(".").length > 2) {
          name = url.substr(url.indexOf(".")+1)
        }
        Settings.origins[name] = {
          id: i,
          origin: origins[i],
          url: url,
          name: name
        };
      }

      for (key in TogglOrigins) {
        if (TogglOrigins.hasOwnProperty(key)) {
          disabled = '';
          checked = 'checked';

          if (!Settings.origins[key]) {
            disabled = 'class="disabled"';
            checked = '';
          }
          if (!TogglOrigins[key].clone) {
            html += "<option id='origin' data-id='" + i + "' value='" + key + "'>" + TogglOrigins[key].name + "</option>";
          }
          html_list += '
<input type="checkbox" data-host="' + TogglOrigins[key] + '" ' + checked + '>
' + key + '

';
        }
      }

      document.querySelector("#origins").innerHTML = html;
      Settings.$permissionsList.innerHTML = html_list;
    });

    // Load Custom Permissions list
    customs = Db.getAllOrigins();
    for (key in customs) {
      if (customs.hasOwnProperty(key)) {
        custom_html += "
×" + key + " - " + customs[key] + "
";
      }
    }
    Settings.$customPermissionsList.innerHTML = custom_html;
  }
};
document.addEventListener('DOMContentLoaded', function (e) {
  Settings.$startAutomatically = document.querySelector("#start_automatically");
  Settings.$stopAutomatically = document.querySelector("#stop_automatically");
  Settings.$showRightClickButton = document.querySelector("#show_right_click_button");
  Settings.$postPopup = document.querySelector("#show_post_start_popup");
  Settings.$socket = document.querySelector("#websocket");
  Settings.$nanny = document.querySelector("#nag-nanny");
  Settings.$idleDetection = document.querySelector("#idle-detection");
  Settings.$pomodoroMode = document.querySelector("#pomodoro-mode");
  Settings.$pomodoroSound = document.querySelector("#enable-sound-signal");
  Settings.$pomodoroStopTimeTracking = document.querySelector("#pomodoro-stop-time");
  Settings.$stopAtDayEnd = document.querySelector("#stop-at-day-end");
  Settings.$tabs = document.querySelector(".tabs");
  Settings.$defaultProject = document.querySelector("#default-project");
  Settings.showPage();
  Settings.$showRightClickButton.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("showRightClickButton") !== "true"), "toggle-right-click-button");
    TogglButton.toggleRightClickButton((localStorage.getItem("showRightClickButton") !== "true"));
  });
  Settings.$startAutomatically.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("startAutomatically") !== "true"), "toggle-start-automatically");
  });
  Settings.$stopAutomatically.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("stopAutomatically") !== "true"), "toggle-stop-automatically");
  });
  Settings.$postPopup.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("showPostPopup") !== "true"), "toggle-popup");
  });
  Settings.$socket.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("socketEnabled") !== "true"), "toggle-socket");
  });
  Settings.$nanny.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("nannyCheckEnabled") !== "true"), "toggle-nanny");
  });
  Settings.$idleDetection.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("idleDetectionEnabled") !== "true"), "toggle-idle");
  });
  Settings.$pomodoroMode.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("pomodoroModeEnabled") !== "true"), "toggle-pomodoro");
  });
  Settings.$pomodoroSound.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("pomodoroSoundEnabled") !== "true"), "toggle-pomodoro-sound");
  });
  Settings.$pomodoroStopTimeTracking.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("pomodoroStopTimeTrackingWhenTimerEnds") !== "true"), "toggle-pomodoro-stop-time");
  });
  Settings.$defaultProject.addEventListener('change', function (e) {
    var defaultProject = Settings.$defaultProject.options[Settings.$defaultProject.selectedIndex].value;
    Settings.saveSetting(defaultProject, "change-default-project");
  });

  Settings.$stopAtDayEnd.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("stopAtDayEnd") !== "true"), "toggle-stop-at-day-end");
  });

  document.querySelector(".tab-links").addEventListener('click', function (e) {
    document.querySelector("header .active").classList.remove("active");
    e.target.classList.add("active");
    Settings.$tabs.setAttribute("data-tab", e.target.getAttribute("data-tab"));
  });

  document.querySelector("#nag-nanny-from").addEventListener('blur', function (e) {
    if (e.target.value.length === 0) {
      Settings.setFromTo();
      return;
    }
    Settings.$fromTo = e.target.value + "-" + document.querySelector('#nag-nanny-to').value;
    Settings.saveSetting();
  });
  document.querySelector("#nag-nanny-to").addEventListener('blur', function (e) {
    if (e.target.value.length === 0) {
      Settings.setFromTo();
      return;
    }
    var fromTo = document.querySelector('#nag-nanny-from').value + "-" + e.target.value;
    Settings.saveSetting(fromTo, "toggle-nanny-from-to");
  });
  document.querySelector("#nag-nanny-interval").addEventListener('blur', function (e) {
    if (e.target.value < 1) {
      e.target.value = 1;
      return;
    }
    Settings.saveSetting((document.querySelector('#nag-nanny-interval').value * 60000), "toggle-nanny-interval");

  });

  document.querySelector("#pomodoro-interval").addEventListener('blur', function (e) {
    if (e.target.value < 1) {
      e.target.value = 1;
      return;
    }
    Settings.saveSetting(+(document.querySelector('#pomodoro-interval').value), "toggle-pomodoro-interval");

  });

// Add custom permission (custom domain)
  document.querySelector('#add-permission').addEventListener('click', function(e) {
    var domain = "*://" + Settings.$newPermission.value + "/",
      permission = {origins: [domain]},
      o = Settings.$originsSelect;

    chrome.permissions.request(permission, function(result) {
      if (result) {
        console.log(domain +" > [ " + document.querySelector("#origins").value + " ]");
        Db.setOrigin(Settings.$newPermission.value, o.value);
        Settings.$newPermission.value = "";
      }
      Settings.loadSitesIntoList();
      if (result) {
        document.location.hash = domain;
      }
    });
  });

  document.querySelector('#permissions-list').addEventListener('click', function(e) {
    var permission = {origins: [e.target.getAttribute("data-host")]};

    if (e.target.checked) {
      chrome.permissions.request(permission, function(result) {
        if (result) {
          e.target.parentNode.classList.remove("disabled");
        }
      });
    } else {
      chrome.permissions.contains(permission, function(allowed) {
      if (allowed) {
        chrome.permissions.remove(permission, function(result) {
          if (result) {
            e.target.parentNode.classList.add("disabled");
          }
        });
      } else {
        alert('No "' + Settings.origins[e.target.getAttribute("data-id")] + '" host permission found.');
      }
    });
    }
  });

  document.querySelector('#custom-permissions-list').addEventListener('click', function(e) {
    var custom, domain, permission, parent;
    if (e.target.className === "remove-custom") {
      parent = e.target.parentNode;
      custom = parent.querySelector('strong').textContent;
      domain = "*://" + custom + "/";
      permission = {origins: [domain]};

      chrome.permissions.contains(permission, function(allowed) {
        if (allowed) {
          chrome.permissions.remove(permission, function(result) {
            if (result) {
              Db.removeOrigin(custom);
              parent.remove();
            } else {
              alert("Fail");
            }
          });
        } else {
          alert('No "' + custom + '" host permission found.');
        }
      });

    }
    return false;
  });

  document.querySelector("#day-end-time").addEventListener('blur', function (e) {
    if (e.target.value < 1) {
      e.target.value = 1;
      return;
    }
    Settings.saveSetting((document.querySelector('#day-end-time').value), "toggle-day-end-time");
  });

  document.querySelector(".container").addEventListener("transitionend", function (e) {
    if (e.propertyName === "height"
        && e.target.className === "subsettings-details"
        && e.target.clientHeight > 0) {
      e.target.scrollIntoView();
    }
  }, false);
});
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2020
 */
import moment from 'moment';
import { createSelector } from 'reselect';
import { UIDatabase } from '../database';
import { chunk } from '../utilities';
import { CHART_CONSTANTS } from '../utilities/modules/vaccines/constants';

export const selectSelectedFridgeID = ({ fridge }) => {
  const { selectedFridge = {} } = fridge;
  const { id } = selectedFridge;

  return id;
};

export const selectFridgeState = ({ fridge }) => fridge;

export const selectSelectedFridge = createSelector([selectSelectedFridgeID], selectedFridgeID => {
  const selectedFridge = UIDatabase.get('Location', selectedFridgeID);

  return selectedFridge;
});

export const selectFridgeTemperatureLogs = createSelector([selectSelectedFridge], fridge => {
  const { temperatureLogs = {} } = fridge ?? {};

  return temperatureLogs.sorted?.('timestamp') ?? [];
});

export const selectLeastRecentTemperatureLogDate = createSelector(
  [selectSelectedFridge],
  fridge => {
    const { leastRecentTemperatureLog } = fridge;

    const { timestamp: minimumDate } = leastRecentTemperatureLog ?? {};

    return minimumDate;
  }
);

export const selectMostRecentTemperatureLogDate = createSelector([selectSelectedFridge], fridge => {
  const { mostRecentTemperatureLog } = fridge;

  const { timestamp: minimumDate } = mostRecentTemperatureLog ?? {};

  return minimumDate;
});

export const selectTemperatureLogsFromDate = createSelector(
  [selectFridgeState, selectLeastRecentTemperatureLogDate],
  (fridgeState, minimumDate) => {
    const { fromDate } = fridgeState;
    return moment.max([moment(fromDate), moment(minimumDate)]).toDate();
  }
);

export const selectTemperatureLogsToDate = createSelector(
  [selectFridgeState, selectMostRecentTemperatureLogDate],
  (fridgeState, maximumDate) => {
    const { toDate } = fridgeState;
    return moment.min([moment(toDate), moment(maximumDate)]).toDate();
  }
);

export const selectFridgeTemperatureLogsFromDate = createSelector(
  [selectFridgeTemperatureLogs, selectTemperatureLogsFromDate, selectTemperatureLogsToDate],
  (logs, fromDate, toDate) => {
    const temperatureLogs =
      logs?.filtered?.('timestamp >= $0 && timestamp =< $1', fromDate, toDate) ?? [];

    return temperatureLogs;
  }
);

export const selectChunkedTemperatureLogs = createSelector(
  [selectFridgeTemperatureLogsFromDate],
  logs => {
    const { length: numberOfLogs } = logs;

    // If the number of temperature logs is less than the maximum number of data points,
    // then the array does not need to be chunked together - however still need to create
    // a 2D array, so chunk with a chunk size of 1.
    return chunk(logs, Math.ceil(numberOfLogs / CHART_CONSTANTS.MAX_DATA_POINTS));
  }
);

export const selectMinAndMaxLogs = createSelector([selectChunkedTemperatureLogs], logs =>
  logs.reduce(
    (acc, logGroup) => {
      const temperatures = logGroup.map(({ temperature }) => temperature);
      const timestamps = logGroup.map(({ timestamp }) => timestamp);

      const maxTemperature = Math.max(...temperatures);
      const minTemperature = Math.min(...temperatures);
      const timestamp = Math.min(...timestamps);

      const { minLine, maxLine } = acc;

      return {
        minLine: [...minLine, { temperature: minTemperature, timestamp }],
        maxLine: [...maxLine, { temperature: maxTemperature, timestamp }],
      };
    },
    { minLine: [], maxLine: [] }
  )
);

export const selectMinAndMaxDomains = createSelector([selectMinAndMaxLogs], minAndMaxLogs => {
  const { minLine, maxLine } = minAndMaxLogs;

  return {
    minDomain: Math.min(...minLine.map(({ temperature }) => temperature)),
    maxDomain: Math.max(...maxLine.map(({ temperature }) => temperature)),
  };
});

export const selectBreaches = createSelector(
  [selectTemperatureLogsFromDate, selectTemperatureLogsToDate, selectSelectedFridge],
  (fromDate, toDate, fridge) => {
    const { breaches } = fridge ?? {};
    const breachesInDateRange = breaches?.filtered(
      '(startTimestamp <= $0 && (endTimestamp >= $0 || endTimestamp == null)) || ' +
        '(startTimestamp >= $0 && startTimestamp <= $1)',
      fromDate,
      toDate
    );

    return breachesInDateRange;
  }
);

export const selectAdjustedBreaches = createSelector(
  [selectFridgeTemperatureLogsFromDate, selectBreaches],
  (fromDate, breaches) => {
    // If a breach started before the date range but is still on going, adjust the start date to
    // be the minimum date in the chart.
    const adjustedBreaches = breaches.map(({ timestamp, id, temperature }) => ({
      id,
      temperature,
      timestamp: moment(timestamp).isBefore(moment(fromDate)) ? fromDate : timestamp,
    }));

    return adjustedBreaches;
  }
);

export const selectTimestampFormatter = createSelector(
  [selectTemperatureLogsFromDate, selectTemperatureLogsToDate],
  (fromDate, toDate) => {
    const durationInDays = moment(toDate).diff(moment(fromDate), 'days', true);

    const time = 'HH:MM';
    const date = 'DD/MM';
    const dateAndTime = `${time} - ${date}`;

    const getTickFormat = format => tick => moment(tick).format(format);

    if (durationInDays <= 1) return getTickFormat(time);
    if (durationInDays <= 3) return getTickFormat(dateAndTime);

    return getTickFormat(date);
  }
);

export const selectNumberOfHotConsecutiveBreaches = createSelector([selectBreaches], breaches => {
  const hotBreaches = breaches?.filtered("type == 'HOT_CONSECUTIVE'");
  return hotBreaches.length;
});

export const selectNumberOfColdConsecutiveBreaches = createSelector([selectBreaches], breaches => {
  const coldBreaches = breaches?.filtered("type == 'COLD_CONSECUTIVE'");
  return coldBreaches.length;
});

const formatTime = sum => {
  const asMoment = moment.duration(sum);

  const asDays = Math.floor(asMoment.asDays());
  const asHours = Math.floor(asMoment.asHours());
  const asMinutes = Math.floor(asMoment.asMinutes());

  const lengthOfHours = String(asHours).length;
  const lengthOfMinutes = String(asMinutes).length;

  const addSuffix = (amount, suffix) => `${amount} ${suffix}`;

  if (lengthOfMinutes < 3) return addSuffix(asMinutes, 'm');
  if (lengthOfHours < 3) return addSuffix(asHours, 'h');

  return addSuffix(asDays, 'd');
};

export const selectHotCumulativeBreach = createSelector(
  [selectSelectedFridge, selectFridgeTemperatureLogsFromDate],
  (fridge, logs) => {
    console.log(fridge);
    const { hotCumulativeBreachConfig } = fridge;

    const { minimumTemperature, duration } = hotCumulativeBreachConfig;

    const logsOverThreshold = logs.filtered('temperature >= $0', minimumTemperature);
    const sum = logsOverThreshold.sum('logInterval') ?? 0;
    const hasCumulativeBreach = sum >= duration && duration;

    return hasCumulativeBreach ? formatTime(sum * 1000) : null;
  }
);

export const selectColdCumulativeBreach = createSelector(
  [selectSelectedFridge, selectFridgeTemperatureLogsFromDate],
  (fridge, logs) => {
    const { coldCumulativeBreachConfig } = fridge;

    const { maximumTemperature, duration } = coldCumulativeBreachConfig;

    const logsOverThreshold = logs.filtered('temperature <= $0', maximumTemperature);
    const sum = logsOverThreshold.sum('logInterval') ?? 0;
    const hasCumulativeBreach = sum >= duration && duration;

    return hasCumulativeBreach ? formatTime(sum * 1000) : null;
  }
);

export const selectAverageTemperature = createSelector(
  [selectFridgeTemperatureLogsFromDate],
  logs => {
    // Aggregate methods return undefined if the collection is empty.
    // Return early to avoid having to handle falsey values (the
    // average can be 0 which is a valid value but undefined is not)
    if (!logs.length) return null;

    const averageTemperature = logs.avg('temperature');
    return Number(averageTemperature).toFixed(1);
  }
);
<commit_msg>Add remove debug log<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2020
 */
import moment from 'moment';
import { createSelector } from 'reselect';
import { UIDatabase } from '../database';
import { chunk } from '../utilities';
import { CHART_CONSTANTS } from '../utilities/modules/vaccines/constants';

export const selectSelectedFridgeID = ({ fridge }) => {
  const { selectedFridge = {} } = fridge;
  const { id } = selectedFridge;

  return id;
};

export const selectFridgeState = ({ fridge }) => fridge;

export const selectSelectedFridge = createSelector([selectSelectedFridgeID], selectedFridgeID => {
  const selectedFridge = UIDatabase.get('Location', selectedFridgeID);

  return selectedFridge;
});

export const selectFridgeTemperatureLogs = createSelector([selectSelectedFridge], fridge => {
  const { temperatureLogs = {} } = fridge ?? {};

  return temperatureLogs.sorted?.('timestamp') ?? [];
});

export const selectLeastRecentTemperatureLogDate = createSelector(
  [selectSelectedFridge],
  fridge => {
    const { leastRecentTemperatureLog } = fridge;

    const { timestamp: minimumDate } = leastRecentTemperatureLog ?? {};

    return minimumDate;
  }
);

export const selectMostRecentTemperatureLogDate = createSelector([selectSelectedFridge], fridge => {
  const { mostRecentTemperatureLog } = fridge;

  const { timestamp: minimumDate } = mostRecentTemperatureLog ?? {};

  return minimumDate;
});

export const selectTemperatureLogsFromDate = createSelector(
  [selectFridgeState, selectLeastRecentTemperatureLogDate],
  (fridgeState, minimumDate) => {
    const { fromDate } = fridgeState;
    return moment.max([moment(fromDate), moment(minimumDate)]).toDate();
  }
);

export const selectTemperatureLogsToDate = createSelector(
  [selectFridgeState, selectMostRecentTemperatureLogDate],
  (fridgeState, maximumDate) => {
    const { toDate } = fridgeState;
    return moment.min([moment(toDate), moment(maximumDate)]).toDate();
  }
);

export const selectFridgeTemperatureLogsFromDate = createSelector(
  [selectFridgeTemperatureLogs, selectTemperatureLogsFromDate, selectTemperatureLogsToDate],
  (logs, fromDate, toDate) => {
    const temperatureLogs =
      logs?.filtered?.('timestamp >= $0 && timestamp =< $1', fromDate, toDate) ?? [];

    return temperatureLogs;
  }
);

export const selectChunkedTemperatureLogs = createSelector(
  [selectFridgeTemperatureLogsFromDate],
  logs => {
    const { length: numberOfLogs } = logs;

    // If the number of temperature logs is less than the maximum number of data points,
    // then the array does not need to be chunked together - however still need to create
    // a 2D array, so chunk with a chunk size of 1.
    return chunk(logs, Math.ceil(numberOfLogs / CHART_CONSTANTS.MAX_DATA_POINTS));
  }
);

export const selectMinAndMaxLogs = createSelector([selectChunkedTemperatureLogs], logs =>
  logs.reduce(
    (acc, logGroup) => {
      const temperatures = logGroup.map(({ temperature }) => temperature);
      const timestamps = logGroup.map(({ timestamp }) => timestamp);

      const maxTemperature = Math.max(...temperatures);
      const minTemperature = Math.min(...temperatures);
      const timestamp = Math.min(...timestamps);

      const { minLine, maxLine } = acc;

      return {
        minLine: [...minLine, { temperature: minTemperature, timestamp }],
        maxLine: [...maxLine, { temperature: maxTemperature, timestamp }],
      };
    },
    { minLine: [], maxLine: [] }
  )
);

export const selectMinAndMaxDomains = createSelector([selectMinAndMaxLogs], minAndMaxLogs => {
  const { minLine, maxLine } = minAndMaxLogs;

  return {
    minDomain: Math.min(...minLine.map(({ temperature }) => temperature)),
    maxDomain: Math.max(...maxLine.map(({ temperature }) => temperature)),
  };
});

export const selectBreaches = createSelector(
  [selectTemperatureLogsFromDate, selectTemperatureLogsToDate, selectSelectedFridge],
  (fromDate, toDate, fridge) => {
    const { breaches } = fridge ?? {};
    const breachesInDateRange = breaches?.filtered(
      '(startTimestamp <= $0 && (endTimestamp >= $0 || endTimestamp == null)) || ' +
        '(startTimestamp >= $0 && startTimestamp <= $1)',
      fromDate,
      toDate
    );

    return breachesInDateRange;
  }
);

export const selectAdjustedBreaches = createSelector(
  [selectFridgeTemperatureLogsFromDate, selectBreaches],
  (fromDate, breaches) => {
    // If a breach started before the date range but is still on going, adjust the start date to
    // be the minimum date in the chart.
    const adjustedBreaches = breaches.map(({ timestamp, id, temperature }) => ({
      id,
      temperature,
      timestamp: moment(timestamp).isBefore(moment(fromDate)) ? fromDate : timestamp,
    }));

    return adjustedBreaches;
  }
);

export const selectTimestampFormatter = createSelector(
  [selectTemperatureLogsFromDate, selectTemperatureLogsToDate],
  (fromDate, toDate) => {
    const durationInDays = moment(toDate).diff(moment(fromDate), 'days', true);

    const time = 'HH:MM';
    const date = 'DD/MM';
    const dateAndTime = `${time} - ${date}`;

    const getTickFormat = format => tick => moment(tick).format(format);

    if (durationInDays <= 1) return getTickFormat(time);
    if (durationInDays <= 3) return getTickFormat(dateAndTime);

    return getTickFormat(date);
  }
);

export const selectNumberOfHotConsecutiveBreaches = createSelector([selectBreaches], breaches => {
  const hotBreaches = breaches?.filtered("type == 'HOT_CONSECUTIVE'");
  return hotBreaches.length;
});

export const selectNumberOfColdConsecutiveBreaches = createSelector([selectBreaches], breaches => {
  const coldBreaches = breaches?.filtered("type == 'COLD_CONSECUTIVE'");
  return coldBreaches.length;
});

const formatTime = sum => {
  const asMoment = moment.duration(sum);

  const asDays = Math.floor(asMoment.asDays());
  const asHours = Math.floor(asMoment.asHours());
  const asMinutes = Math.floor(asMoment.asMinutes());

  const lengthOfHours = String(asHours).length;
  const lengthOfMinutes = String(asMinutes).length;

  const addSuffix = (amount, suffix) => `${amount} ${suffix}`;

  if (lengthOfMinutes < 3) return addSuffix(asMinutes, 'm');
  if (lengthOfHours < 3) return addSuffix(asHours, 'h');

  return addSuffix(asDays, 'd');
};

export const selectHotCumulativeBreach = createSelector(
  [selectSelectedFridge, selectFridgeTemperatureLogsFromDate],
  (fridge, logs) => {
    const { hotCumulativeBreachConfig } = fridge;

    const { minimumTemperature, duration } = hotCumulativeBreachConfig;

    const logsOverThreshold = logs.filtered('temperature >= $0', minimumTemperature);
    const sum = logsOverThreshold.sum('logInterval') ?? 0;
    const hasCumulativeBreach = sum >= duration && duration;

    return hasCumulativeBreach ? formatTime(sum * 1000) : null;
  }
);

export const selectColdCumulativeBreach = createSelector(
  [selectSelectedFridge, selectFridgeTemperatureLogsFromDate],
  (fridge, logs) => {
    const { coldCumulativeBreachConfig } = fridge;

    const { maximumTemperature, duration } = coldCumulativeBreachConfig;

    const logsOverThreshold = logs.filtered('temperature <= $0', maximumTemperature);
    const sum = logsOverThreshold.sum('logInterval') ?? 0;
    const hasCumulativeBreach = sum >= duration && duration;

    return hasCumulativeBreach ? formatTime(sum * 1000) : null;
  }
);

export const selectAverageTemperature = createSelector(
  [selectFridgeTemperatureLogsFromDate],
  logs => {
    // Aggregate methods return undefined if the collection is empty.
    // Return early to avoid having to handle falsey values (the
    // average can be 0 which is a valid value but undefined is not)
    if (!logs.length) return null;

    const averageTemperature = logs.avg('temperature');
    return Number(averageTemperature).toFixed(1);
  }
);
<|endoftext|>


<commit_before>/* collection of game related helper functions */
var $db = require('./mongo');
var debug = require('debug')('wol:leaderboard');
var gameres = require('./gameres');
var $q = require('q');

exports.process = function(game, dmp) {
    var match = gameres.parse(dmp);

    /* discontinue if no game id or match is less than 5 seconds */
    if (!match.idno || match.dura < 5) return;
    debug('game: %s, idno: %d', game, match.idno);

    // create raw dump entry
    // TODO: check against spid (sender id) to ensure only 1 packet from each player
    // TODO: idno is random but could collide. check if record exists and compare dates
    var $dumps = $db.get(game +'_dumps');
    $dumps.update(
        {idno: match.idno},
        {$push: {buffers: match.buffer.toString('utf8')}},
        {upsert: true},
        _stats
    );

    function _stats(err, data) {
        $dumps.find({idno: match.idno}, function(err, doc) {
            if (doc.length > 1) {
                // error scneario, we have two entries for the same game
                // todo: figure out what the hell to do?
                // todo: elimiate duplicates?
                console.log('found duplicates! game: %s, idno: %d', game, match.idno);
                return;
            }

            /* if we have ra stats normalize then carry on  */
            if (game == 'ra') {
                if (match.nump !== 2) return; /* only process stats for 1v1 */

                /* hack for now to process games only when 2 packets received */
                if (doc[0].buffers && doc[0].buffers.length > 1) {

                    /* interpret packet and update it to use wolv2 completion stats */
                    gameres.normalize(game, match);
                    gameres.process(game, match);
                }
                return;
            }

            // only continue if this is the first entry for a game
            if (doc[0].buffers && doc[0].buffers.length > 1) return;

            /* save game stats */
            gameres.process(game, match);
        });
    }

    return;
};

exports.information = function(game, gameId) {
    var defer = $q.defer();
    $db.get(game +'_games').find({idno: gameId}, function(err, data) {
        if (data.length < 1) return defer.reject();
        defer.resolve(data[0]);
    });
    return defer.promise;
};
<commit_msg>only process when 2 packets<commit_after>/* collection of game related helper functions */
var $db = require('./mongo');
var debug = require('debug')('wol:leaderboard');
var gameres = require('./gameres');
var $q = require('q');

exports.process = function(game, dmp) {
    var match = gameres.parse(dmp);

    /* discontinue if no game id or match is less than 5 seconds */
    if (!match.idno || match.dura < 5) return;
    debug('game: %s, idno: %d', game, match.idno);

    // create raw dump entry
    // TODO: check against spid (sender id) to ensure only 1 packet from each player
    // TODO: idno is random but could collide. check if record exists and compare dates
    var $dumps = $db.get(game +'_dumps');
    $dumps.update(
        {idno: match.idno},
        {$push: {buffers: match.buffer.toString('utf8')}},
        {upsert: true},
        _stats
    );

    function _stats(err, data) {
        $dumps.find({idno: match.idno}, function(err, doc) {
            if (doc.length > 1) {
                // error scneario, we have two entries for the same game
                // todo: figure out what the hell to do?
                // todo: elimiate duplicates?
                console.log('found duplicates! game: %s, idno: %d', game, match.idno);
                return;
            }

            /* if we have ra stats normalize then carry on  */
            if (game == 'ra') {
                if (match.nump !== 2) return; /* only process stats for 1v1 */

                /* hack for now to process games only when 2 packets received */
                if (doc[0].buffers && doc[0].buffers.length == 2) {

                    /* interpret packet and update it to use wolv2 completion stats */
                    gameres.normalize(game, match);
                    gameres.process(game, match);
                }
                return;
            }

            // only continue if this is the first entry for a game
            if (doc[0].buffers && doc[0].buffers.length > 1) return;

            /* save game stats */
            gameres.process(game, match);
        });
    }

    return;
};

exports.information = function(game, gameId) {
    var defer = $q.defer();
    $db.get(game +'_games').find({idno: gameId}, function(err, data) {
        if (data.length < 1) return defer.reject();
        defer.resolve(data[0]);
    });
    return defer.promise;
};
<|endoftext|>


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

	var scope;
	if ((typeof exports) !== "undefined"){
		scope = exports;
	}
	else {
		scope = {};
		window.scope.sanitationRules = scope;
	}

	const settings = require("./settings");

	scope.ruleset = [
		{
			name: "unnecessaryURLValue",
			check: function(errorCallback){
				const {url: urlContainer} = settings.getContainers();
				const containerValue = urlContainer.get();
				const errorMessage = browser.i18n.getMessage("sanitation_error.unnecessaryURLValue");
				function createErrorMessage(setting, urlValue){
					return errorMessage
						.replace(/{setting-technical}/g, setting.name)
						.replace(/{setting-title}/g, browser.i18n.getMessage(setting.name + "_title"))
						.replace(/{url}/g, urlValue.url);
				}
				containerValue.forEach(function(urlValues){
					Object.keys(urlValues).filter(function(key){
						return key !== "url";
					}).forEach(function(key){
						const setting = settings.getDefinition(key);
						if (setting && setting.urlSpecific){
							const globalValue = setting.get();
							if (urlValues[key] === globalValue){
								errorCallback({
									message: createErrorMessage(setting, urlValues),
									severity: "low",
									resolutions: [{
										label: browser.i18n.getMessage("sanitation_resolution.removeURLValue"),
										callback: function(){
											setting.reset(urlValues.url);
										}
									}]
								});
							}
						}
					});
				});
			}
		},
		{
			name: "disabledFeatures",
			check: function(errorCallback){
				const errorMessage = browser.i18n.getMessage("sanitation_error.disabledFeatures");
				function createErrorMessage(api){
					return errorMessage.replace(/{api}/g, browser.i18n.getMessage("section_" + api.section));
				}
				const protectedFeatures = settings.getDefinition("protectedAPIFeatures");
				const protectedFeaturesValue = protectedFeatures.get();
				function getSectionKeys(section){
					let inSection = false;
					return protectedFeatures.keys.filter(function(key){
						if (typeof key === "string"){
							return inSection;
						}
						else {
							if (key.level === 1){
								inSection = key.name === section;
							}
							return false;
						}
					});
				}
				[
					{mainFlag: "protectAudio", section: "Audio-API"},
					{mainFlag: "protectWindow", section: "Window-API"},
					{mainFlag: "protectDOMRect", section: "DOMRect-API"},
				].forEach(function(api){
					if (settings.get(api.mainFlag)){
						let inSection = false;
						let anyActive = false;
						if (getSectionKeys(api.section).every(function(key){
							return protectedFeaturesValue.hasOwnProperty(key) &&
								!protectedFeaturesValue[key];
						})){
							errorCallback({
								message: createErrorMessage(api),
								severity: "high",
								resolutions: [
									{
										label: browser.i18n.getMessage("sanitation_resolution.enableFeatures"),
										callback: function(){
											const protectedFeaturesValue = protectedFeatures.get();
											getSectionKeys(api.section).forEach(function(key){
												protectedFeaturesValue[key] = true;
											});
											protectedFeatures.set(protectedFeaturesValue);
										}
									},
									{
										label: browser.i18n.getMessage("sanitation_resolution.disableMainFlag"),
										callback: function(){
											settings.set(api.mainFlag, false);
										}
									},
								]
							});
						}
					}
				});
			}
		},
		{
			name: "blockMode",
			check: function(errorCallback){
				const switchMode = {
					label: browser.i18n.getMessage("sanitation_resolution.switchToFakeReadout"),
					callback: function(){
						settings.blockMode = "fakeReadout";
					}
				};
				const blockMode = settings.blockMode;
				const blockModeName = browser.i18n.getMessage("blockMode_options." + blockMode);
				if (!blockMode.match("^fake|^ask")){
					errorCallback({
						message: browser.i18n.getMessage("sanitation_error.badBlockMode"),
						severity: "medium",
						resolutions: [switchMode]
					});
				}
				["Audio", "Window", "DOMRect"].forEach(function(api){
					const mainFlag = "protect" + api;
					if (settings[mainFlag]){
						if (["fakeInput"].indexOf(blockMode) !== -1){
							const blockModeName = browser.i18n.getMessage("blockMode_options." + blockMode);
							errorCallback({
								message: browser.i18n.getMessage("sanitation_error.blockModeVsProtection")
									.replace(/{blockMode}/g, blockModeName)
									.replace(/{api}/g, browser.i18n.getMessage("section_" + api + "-api")),
								severity: "high",
								resolutions: [switchMode, {
									label: browser.i18n.getMessage("sanitation_resolution.disableFlag")
										.replace(/{flag}/g, browser.i18n.getMessage(mainFlag + "_title")),
									callback: function(){
										settings[mainFlag] = false;
									}
								}]
							});
						}
					}
				});
				if (blockMode === "fakeInput" && settings.rng === "white"){
					errorCallback({
						message: browser.i18n.getMessage("sanitation_error.fakeInputWithWhiteRng")
							.replace(/{blockMode}/g, blockModeName),
						severity: "low",
						resolutions: [switchMode, {
							label: browser.i18n.getMessage("sanitation_resolution.switchToNonPersistendRng"),
							callback: function(){
								settings.rng = "nonPersistent";
							}
						}]
					});
				}
			}
		},
		{
			name: "thresholds",
			check: function(errorCallback){
				const setToLabel = browser.i18n.getMessage("sanitation_resolution.setTo");
				const tooLowLabel = browser.i18n.getMessage("sanitation_error.valueTooLow");
				const tooHighLabel = browser.i18n.getMessage("sanitation_error.valueTooHigh");
				if (settings.minFakeSize > 1e2){
					errorCallback({
						message: tooHighLabel
							.replace(/{setting}/g, browser.i18n.getMessage("minFakeSize_title"))
							.replace(/{value}/g, "100"),
						severity: "high",
						resolutions: [{
							label: setToLabel.replace(/{value}/g, "100"),
							callback: function(){
								settings.minFakeSize = 1e2;
							}
						}]
					});
				}
				if (settings.maxFakeSize !== 0 && settings.maxFakeSize < 1e6){
					errorCallback({
						message: tooLowLabel
							.replace(/{setting}/g, browser.i18n.getMessage("maxFakeSize_title"))
							.replace(/{value}/g, "1 000 000"),
						severity: "high",
						resolutions: [{
							label: setToLabel.replace(/{value}/g, "1 000 000"),
							callback: function(){
								settings.maxFakeSize = 1e6;
							}
						}]
					});
				}
				if (settings.ignoreFrequentColors > 3){
					errorCallback({
						message: tooHighLabel
							.replace(/{setting}/g, browser.i18n.getMessage("ignoreFrequentColors_title"))
							.replace(/{value}/g, "3"),
						severity: "high",
						resolutions: [{
							label: setToLabel.replace(/{value}/g, "3"),
							callback: function(){
								settings.ignoreFrequentColors = 3;
							}
						}]
					});
				}
				if (settings.minColors > 10){
					errorCallback({
						message: tooHighLabel
							.replace(/{setting}/g, browser.i18n.getMessage("minColors_title"))
							.replace(/{value}/g, "10"),
						severity: "high",
						resolutions: [{
							label: setToLabel.replace(/{value}/g, "10"),
							callback: function(){
								settings.ignoreFrequentColors = 10;
							}
						}]
					});
				}
			}
		},
		{
			name: "performance",
			check: function(errorCallback){
				const disableLabel = browser.i18n.getMessage("sanitation_resolution.disableFlag");
				if (settings.storeNotificationData){
					errorCallback({
						message: browser.i18n.getMessage("sanitation_error.storeNotificationData"),
						severity: "low",
						resolutions: [{
							label: disableLabel
								.replace(/{flag}/g, browser.i18n.getMessage("storeNotificationData_title")),
							callback: function(){
								settings.storeNotificationData = false;
							}
						}]
					});
					if (settings.storeImageForInspection){
						errorCallback({
							message: browser.i18n.getMessage("sanitation_error.storeImage"),
							severity: "low",
							resolutions: [{
								label: disableLabel
									.replace(/{flag}/g, browser.i18n.getMessage("storeImageForInspection_title")),
								callback: function(){
									settings.storeImageForInspection = false;
								}
							}]
						});
					}
				}
			}
		},
	];
}());<commit_msg>Resolution for minColors was wrong.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function(){
	"use strict";

	var scope;
	if ((typeof exports) !== "undefined"){
		scope = exports;
	}
	else {
		scope = {};
		window.scope.sanitationRules = scope;
	}

	const settings = require("./settings");

	scope.ruleset = [
		{
			name: "unnecessaryURLValue",
			check: function(errorCallback){
				const {url: urlContainer} = settings.getContainers();
				const containerValue = urlContainer.get();
				const errorMessage = browser.i18n.getMessage("sanitation_error.unnecessaryURLValue");
				function createErrorMessage(setting, urlValue){
					return errorMessage
						.replace(/{setting-technical}/g, setting.name)
						.replace(/{setting-title}/g, browser.i18n.getMessage(setting.name + "_title"))
						.replace(/{url}/g, urlValue.url);
				}
				containerValue.forEach(function(urlValues){
					Object.keys(urlValues).filter(function(key){
						return key !== "url";
					}).forEach(function(key){
						const setting = settings.getDefinition(key);
						if (setting && setting.urlSpecific){
							const globalValue = setting.get();
							if (urlValues[key] === globalValue){
								errorCallback({
									message: createErrorMessage(setting, urlValues),
									severity: "low",
									resolutions: [{
										label: browser.i18n.getMessage("sanitation_resolution.removeURLValue"),
										callback: function(){
											setting.reset(urlValues.url);
										}
									}]
								});
							}
						}
					});
				});
			}
		},
		{
			name: "disabledFeatures",
			check: function(errorCallback){
				const errorMessage = browser.i18n.getMessage("sanitation_error.disabledFeatures");
				function createErrorMessage(api){
					return errorMessage.replace(/{api}/g, browser.i18n.getMessage("section_" + api.section));
				}
				const protectedFeatures = settings.getDefinition("protectedAPIFeatures");
				const protectedFeaturesValue = protectedFeatures.get();
				function getSectionKeys(section){
					let inSection = false;
					return protectedFeatures.keys.filter(function(key){
						if (typeof key === "string"){
							return inSection;
						}
						else {
							if (key.level === 1){
								inSection = key.name === section;
							}
							return false;
						}
					});
				}
				[
					{mainFlag: "protectAudio", section: "Audio-API"},
					{mainFlag: "protectWindow", section: "Window-API"},
					{mainFlag: "protectDOMRect", section: "DOMRect-API"},
				].forEach(function(api){
					if (settings.get(api.mainFlag)){
						let inSection = false;
						let anyActive = false;
						if (getSectionKeys(api.section).every(function(key){
							return protectedFeaturesValue.hasOwnProperty(key) &&
								!protectedFeaturesValue[key];
						})){
							errorCallback({
								message: createErrorMessage(api),
								severity: "high",
								resolutions: [
									{
										label: browser.i18n.getMessage("sanitation_resolution.enableFeatures"),
										callback: function(){
											const protectedFeaturesValue = protectedFeatures.get();
											getSectionKeys(api.section).forEach(function(key){
												protectedFeaturesValue[key] = true;
											});
											protectedFeatures.set(protectedFeaturesValue);
										}
									},
									{
										label: browser.i18n.getMessage("sanitation_resolution.disableMainFlag"),
										callback: function(){
											settings.set(api.mainFlag, false);
										}
									},
								]
							});
						}
					}
				});
			}
		},
		{
			name: "blockMode",
			check: function(errorCallback){
				const switchMode = {
					label: browser.i18n.getMessage("sanitation_resolution.switchToFakeReadout"),
					callback: function(){
						settings.blockMode = "fakeReadout";
					}
				};
				const blockMode = settings.blockMode;
				const blockModeName = browser.i18n.getMessage("blockMode_options." + blockMode);
				if (!blockMode.match("^fake|^ask")){
					errorCallback({
						message: browser.i18n.getMessage("sanitation_error.badBlockMode"),
						severity: "medium",
						resolutions: [switchMode]
					});
				}
				["Audio", "Window", "DOMRect"].forEach(function(api){
					const mainFlag = "protect" + api;
					if (settings[mainFlag]){
						if (["fakeInput"].indexOf(blockMode) !== -1){
							const blockModeName = browser.i18n.getMessage("blockMode_options." + blockMode);
							errorCallback({
								message: browser.i18n.getMessage("sanitation_error.blockModeVsProtection")
									.replace(/{blockMode}/g, blockModeName)
									.replace(/{api}/g, browser.i18n.getMessage("section_" + api + "-api")),
								severity: "high",
								resolutions: [switchMode, {
									label: browser.i18n.getMessage("sanitation_resolution.disableFlag")
										.replace(/{flag}/g, browser.i18n.getMessage(mainFlag + "_title")),
									callback: function(){
										settings[mainFlag] = false;
									}
								}]
							});
						}
					}
				});
				if (blockMode === "fakeInput" && settings.rng === "white"){
					errorCallback({
						message: browser.i18n.getMessage("sanitation_error.fakeInputWithWhiteRng")
							.replace(/{blockMode}/g, blockModeName),
						severity: "low",
						resolutions: [switchMode, {
							label: browser.i18n.getMessage("sanitation_resolution.switchToNonPersistendRng"),
							callback: function(){
								settings.rng = "nonPersistent";
							}
						}]
					});
				}
			}
		},
		{
			name: "thresholds",
			check: function(errorCallback){
				const setToLabel = browser.i18n.getMessage("sanitation_resolution.setTo");
				const tooLowLabel = browser.i18n.getMessage("sanitation_error.valueTooLow");
				const tooHighLabel = browser.i18n.getMessage("sanitation_error.valueTooHigh");
				if (settings.minFakeSize > 1e2){
					errorCallback({
						message: tooHighLabel
							.replace(/{setting}/g, browser.i18n.getMessage("minFakeSize_title"))
							.replace(/{value}/g, "100"),
						severity: "high",
						resolutions: [{
							label: setToLabel.replace(/{value}/g, "100"),
							callback: function(){
								settings.minFakeSize = 1e2;
							}
						}]
					});
				}
				if (settings.maxFakeSize !== 0 && settings.maxFakeSize < 1e6){
					errorCallback({
						message: tooLowLabel
							.replace(/{setting}/g, browser.i18n.getMessage("maxFakeSize_title"))
							.replace(/{value}/g, "1 000 000"),
						severity: "high",
						resolutions: [{
							label: setToLabel.replace(/{value}/g, "1 000 000"),
							callback: function(){
								settings.maxFakeSize = 1e6;
							}
						}]
					});
				}
				if (settings.ignoreFrequentColors > 3){
					errorCallback({
						message: tooHighLabel
							.replace(/{setting}/g, browser.i18n.getMessage("ignoreFrequentColors_title"))
							.replace(/{value}/g, "3"),
						severity: "high",
						resolutions: [{
							label: setToLabel.replace(/{value}/g, "3"),
							callback: function(){
								settings.ignoreFrequentColors = 3;
							}
						}]
					});
				}
				if (settings.minColors > 10){
					errorCallback({
						message: tooHighLabel
							.replace(/{setting}/g, browser.i18n.getMessage("minColors_title"))
							.replace(/{value}/g, "10"),
						severity: "high",
						resolutions: [{
							label: setToLabel.replace(/{value}/g, "10"),
							callback: function(){
								settings.minColors = 10;
							}
						}]
					});
				}
			}
		},
		{
			name: "performance",
			check: function(errorCallback){
				const disableLabel = browser.i18n.getMessage("sanitation_resolution.disableFlag");
				if (settings.storeNotificationData){
					errorCallback({
						message: browser.i18n.getMessage("sanitation_error.storeNotificationData"),
						severity: "low",
						resolutions: [{
							label: disableLabel
								.replace(/{flag}/g, browser.i18n.getMessage("storeNotificationData_title")),
							callback: function(){
								settings.storeNotificationData = false;
							}
						}]
					});
					if (settings.storeImageForInspection){
						errorCallback({
							message: browser.i18n.getMessage("sanitation_error.storeImage"),
							severity: "low",
							resolutions: [{
								label: disableLabel
									.replace(/{flag}/g, browser.i18n.getMessage("storeImageForInspection_title")),
								callback: function(){
									settings.storeImageForInspection = false;
								}
							}]
						});
					}
				}
			}
		},
	];
}());<|endoftext|>


<commit_before>//////////////////////////////
// Server-Side Sandbox     //
/////////////////////////////

// var firmata = require('firmata');

// var board = new firmata.Board('/dev/cu.usbmodem1421', function(err){
//   if (err) {
//     throw new Error(err);
//   }

// });

//////////////////////////////
// Client-Side Sandbox     //
/////////////////////////////

console.log('quicktest');
// p5.board('/dev/cu.usbmodem1421', 'arduino');

//////////////////////
// Example Scripts //
//////////////////////

// Board setup — obviously you may need to change the port
// p5.board('/dev/cu.usbmodem1421', 'arduino');

// // Test digital write
// var p = p5.pin(9, 'DIGITAL', 'OUTPUT');
// p.write('HIGH');

// // Test PWM write
// var p = p5.pin(9, 'PWM', 'OUTPUT');
// p.write(150);

// // Test digital read -- only uncomment one p.read() at a time
// var p = p5.pin(9, 'DIGITAL', 'INPUT');
// p.read(["val", "console.log('read val', val)"]);
// p.read();
// setInterval(function() { console.log(p.val) }, 500);

// // Test analog read -- only uncomment one p.read() at a time
// var p = p5.pin(0, 'ANALOG', 'INPUT');
// p.read(["val", "console.log('read val', val)"]);
// p.read();
// setInterval(function() { console.log(p.val) }, 500);

// // Draw ellipses with a button -- these work on the same
// // setup as the pin reads

// var p;

// function setup() {

//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   p = p5.pin(9, 'DIGITAL', 'INPUT');
//   p.read();

//   createCanvas(1200, 500);
//   noStroke();
// }

// function draw() {
//   if (p.val) {
//     fill(Math.random() * 255, Math.random() * 255, Math.random() * 255);
//     ellipse(Math.random() * width, Math.random() * height, 60, 60);
//   }
// }

// // Click the circle to light the LED -- this works on the same
// // setup as the pin writes
// var pin;

// function setup() {
//   createCanvas(400, 400);
//   noStroke();
//   fill(62, 0, 255);
//   ellipse(width/2, height/2, 100, 100);

//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   pin = p5.pin(9, 'DIGITAL', 'OUTPUT');
// }

// function mousePressed() {
//   var d = dist(width/2, height/2, mouseX, mouseY);
//   if (d < 100) {
//     pin.write('HIGH');
//   }
// }

// function mouseReleased() {
//   pin.write('LOW');
// }

// // Click a button to light the LED -- this works on the same
// // setup as the pin writes & uses the p5.dom lib

// function setup() {
//   createCanvas(400, 400);

//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   var pin = p5.pin(9, 'DIGITAL', 'OUTPUT');

//   var button = createButton('LIGHT THE LED!!');
//   button.position(width/2, height/2);
//   button.mousePressed(function(){
//     pin.write('HIGH');
//   });

//   button.mouseReleased(function(){
//     pin.write('LOW');
//   });
// }

// PWM Slider -- Same setup as above

// var slider, pin;

// function setup() {
//   slider = createSlider(0, 255, 150);
//   slider.position = (10, 10);

//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   pin = p5.pin(9, 'PWM', 'OUTPUT');

// }

// function draw() {
//   var val = slider.value();
//   pin.write(val);
// }<commit_msg>Tests added to quicktest for LED, RGBLED, Motor<commit_after>//////////////////////////////
// Server-Side Sandbox     //
/////////////////////////////

// var firmata = require('firmata');
// var gamma   = require('./gamma.js')

// var board = new firmata.Board('/dev/cu.usbmodem1421', function(err){
//   if (err) {
//     throw new Error(err);
//   }

//   console.log('going');

//   board.pinMode(9, board.MODES.PWM);
//   board.pinMode(10, board.MODES.PWM);
//   board.pinMode(11, board.MODES.PWM);

//   board.analogWrite(11, gamma[255]);

//   // setTimeout(function(){
//   //   board.analogWrite(10, 255);
//   // }, 1000);

//   // setTimeout(function(){
//   //   board.analogWrite(11, 255);
//   // }, 2000);

//   // board.pinMode(9, board.MODES.PWM);
//   // board.analogWrite(9, 0);

// });

//////////////////////////////
// Client-Side Sandbox     //
/////////////////////////////

// console.log('quicktest');
// p5.board('/dev/cu.usbmodem1421', 'arduino');

//////////////////////
// Example Scripts //
//////////////////////

// // Board setup — obviously you may need to change the port
// p5.board('/dev/cu.usbmodem1421', 'arduino');

// // Test digital write
// var p = p5.pin(9, 'DIGITAL', 'OUTPUT');
// p.write('HIGH');

// Test PWM write
// var p = p5.pin(9, 'PWM', 'OUTPUT');
// p.write(80);

// Test digital read -- only uncomment one p.read() at a time
// var p = p5.pin(9, 'DIGITAL', 'INPUT');
// p.read(function(val){console.log(val);});
// p.read();
// setInterval(function() { console.log(p.val) }, 500);

// // Test analog read -- only uncomment one p.read() at a time
// var p = p5.pin(0, 'ANALOG', 'INPUT');
// p.read(function(val){console.log(val);});
// p.read();
// setInterval(function() { console.log(p.val) }, 500);

// // Draw ellipses with a button -- these work on the same
// // setup as the pin reads

// var p;

// function setup() {

//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   p = p5.pin(9, 'DIGITAL', 'INPUT');
//   p.read();

//   createCanvas(1200, 500);
//   noStroke();
// }

// function draw() {
//   if (p.val) {
//     fill(Math.random() * 255, Math.random() * 255, Math.random() * 255);
//     ellipse(Math.random() * width, Math.random() * height, 60, 60);
//   }
// }

// Click the circle to light the LED -- this works on the same
// setup as the pin writes
// var pin;

// function setup() {
//   createCanvas(400, 400);
//   noStroke();
//   fill(62, 0, 255);
//   ellipse(width/2, height/2, 100, 100);

//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   pin = p5.pin(9, 'DIGITAL', 'OUTPUT');
// }

// function mousePressed() {
//   var d = dist(width/2, height/2, mouseX, mouseY);
//   if (d < 100) {
//     pin.write('HIGH');
//   }
// }

// function mouseReleased() {
//   pin.write('LOW');
// }

// // Click a button to light the LED -- this works on the same
// // setup as the pin writes & uses the p5.dom lib

// function setup() {
//   createCanvas(400, 400);

//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   var pin = p5.pin(9, 'DIGITAL', 'OUTPUT');

//   var button = createButton('LIGHT THE LED!!');
//   button.position(width/2, height/2);
//   button.mousePressed(function(){
//     pin.write('HIGH');
//   });

//   button.mouseReleased(function(){
//     pin.write('LOW');
//   });
// }

// PWM Slider -- Same setup as above

// var slider, pin;

// function setup() {
//   slider = createSlider(0, 255, 150);
//   slider.position = (10, 10);

//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   pin = p5.pin(9, 'PWM', 'OUTPUT');

// }

// function draw() {
//   var val = slider.value();
//   pin.write(val);
// }

// // LED On & Off -- Same setup as above

// var led;

// function setup() {
//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   led = p5.pin(2, 'LED');
//   console.log(led);
// }

// function keyPressed() {
//   if (keyCode === LEFT_ARROW){
//     led.on();
//   } else if (keyCode === RIGHT_ARROW) {
//     led.off();
//   } else if (keyCode === UP_ARROW){
//     led.blink();
//   } else if (keyCode === DOWN_ARROW) {
//     led.noBlink();
//   }
// }

// // LED Fade on Key -- Same setup as above

// var led;

// function setup() {
//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   led = p5.pin(9, 'LED');
// }

// function keyPressed(){
//  if (keyCode === DOWN_ARROW) {
//   led.write(200);
//   led.fade(200, 0);
//  }
// }

// // LED Fade in Setup -- Same setup as above [tests queueing]

// function setup() {
//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   var led = p5.pin(9, 'LED');
//   led.write(200);
//   led.fade(200, 0);
// }
//

// // RGB LED Write -- Breadboard with common-cathode RGB LED
// plugged into pins 9, 10, 11

// function setup() {
//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   var rgb = p5.pin({r: 9, g: 10, b: 11}, 'RGBLED');
//   var c = color(65);
//   rgb.write(c);
//   fill(c);
//   noStroke();
//   ellipse(80, 80, 40, 40);
// }

// RGB LED Read -- Same as above

// var rgb, c;

// function setup() {
//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   rgb = p5.pin({r: 9, g: 10, b: 11}, 'RGBLED');
//   c = color(Math.floor(Math.random() * 255), Math.floor(Math.random() * 255), Math.floor(Math.random() * 255));
//   rgb.write(c);
//   fill(c);
//   noStroke();
//   ellipse(80, 80, 40, 40);
// }

// function keyPressed(){
//   rgb.read(function(val){ console.log(val.toString()); });
// }

// // RGB LED On/Off/Blink -- Same as above
// var rgb, c;

// function setup() {
//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   rgb = p5.pin({r: 9, g: 10, b: 11}, 'RGBLED');
//   c = color(Math.floor(Math.random() * 255), Math.floor(Math.random() * 255), Math.floor(Math.random() * 255));
//   rgb.write(c);
//   fill(c);
//   noStroke();
//   ellipse(80, 80, 40, 40);
// }

// function keyPressed() {
//   if (keyCode === LEFT_ARROW){
//     rgb.on();
//   } else if (keyCode === RIGHT_ARROW) {
//     rgb.off();
//   } else if (keyCode === UP_ARROW){
//     rgb.blink();
//   } else if (keyCode === DOWN_ARROW) {
//     rgb.noBlink();
//   }
// }

// RGB LED Fade on Key -- Same setup as above

// var rgb;

// function setup() {
//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   rgb = p5.pin({r: 9, g: 10, b: 11}, 'RGBLED');
//   var c = color(Math.floor(Math.random() * 255), Math.floor(Math.random() * 255), Math.floor(Math.random() * 255));
//   rgb.write(c);
//   fill(c);
//   noStroke();
//   ellipse(80, 80, 40, 40);
// }

// function keyPressed(){
//  if (keyCode === DOWN_ARROW) {
//   rgb.write([200, 200, 200]);
//   rgb.fade([200, 0, 3000], [200, 0, 5000, 500], [200, 0, 1000, 50]);
//  }
// }

// // Motor tests -- Breadboard with motor power keyed to pin 9

// var motor;

// function setup() {
//   p5.board('/dev/cu.usbmodem1421', 'arduino');
//   motor = p5.pin(9, 'MOTOR');
// }

// function keyPressed() {
//   if (keyCode === LEFT_ARROW){
//     motor.on();
//   } else if (keyCode === RIGHT_ARROW) {
//     console.log('motor off called');
//     motor.off();
//   } else if (keyCode === UP_ARROW){
//     motor.write(100);
//   }
// }
<|endoftext|>


<commit_before>var React = require('react-native');
var {
    StyleSheet,
    Text,
    Image,
    View,
    TouchableHighlight,
    PropTypes,
} = React;

var screen = require('Dimensions').get('window');
var tweenState = require('react-tween-state');

var styles = StyleSheet.create({
    container: {
        flex: 1,
    },
    titleContainer: {
        flexDirection: 'row',
    },
    title: {
        //flex: 0,
        //backgroundColor: '#f3f3f3',
        alignItems: 'center',
        paddingHorizontal: 2,
        paddingVertical: 10,
    },
    spacer: {
        flex: 1,
    },
    barContainer: {
        height: 4,
        position: 'relative',
    },
    bar: {
        backgroundColor: 'blue',
        position: 'absolute',
        height: 4,
    }
});

var SegmentedView = React.createClass({

    mixins: [tweenState.Mixin],

    propTypes: {
        duration: PropTypes.number,
        onTransitionStart: PropTypes.func,
        onTransitionEnd: PropTypes.func,
        onPress: PropTypes.func,
        renderTitle: PropTypes.func,
        titles: PropTypes.array,
        index: PropTypes.number,
        barColor: PropTypes.string,
        underlayColor: PropTypes.string,
        stretch: PropTypes.bool,
        selectedTitleStyle: PropTypes.object,
        titleStyle: PropTypes.object,

    },

    getDefaultProps() {
        return {
            duration: 200,
            onTransitionStart: ()=>{},
            onTransitionEnd: ()=>{},
            onPress: ()=>{},
            renderTitle: null,
            index: 0,
            barColor: '#44B7E1',
            underlayColor: '#CCCCCC',
            stretch: false,
            selectedTextStyle: null,
            textStyle: null,
        };
    },

    getInitialState() {
        return {
            barLeft: 0,
            barRight: screen.width,
        };
    },

    componentDidMount() {
        setTimeout(() => this.moveTo(this.props.index), 0);
    },

    componentWillReceiveProps(nextProps) {
        this.moveTo(nextProps.index);
    },

    measureHandler(ox, oy, width) {

        this.tweenState('barLeft', {
            easing: tweenState.easingTypes.easeInOutQuad,
            duration: 200,
            endValue: ox
        });

        this.tweenState('barRight', {
            easing: tweenState.easingTypes.easeInOutQuad,
            duration: 200,
            endValue: screen.width - ox - width,
            onEnd: this.props.onTransitionEnd
        });

        setTimeout(this.props.onTransitionStart, 0);
    },

    moveTo(index) {
        this.refs[index].measure(this.measureHandler);
    },

    _renderTitle(title, i) {
        return (
            <View style={styles.title}>
                <Text style={[this.props.titleStyle, i === this.props.index && this.props.selectedTitleStyle]}>{title}</Text>
            </View>
        );
    },

    renderTitle(title, i) {
        return (
            <View key={i} ref={i} style={{ flex: this.props.stretch ? 1 : 0 }}>
                <TouchableHighlight underlayColor={this.props.underlayColor} onPress={() => this.props.onPress(i)}>
                    {this.props.renderTitle ? this.props.renderTitle(title, i) : this._renderTitle(title, i)}
                </TouchableHighlight>
            </View>
        );
    },

    render() {
        var items = [];
        var titles = this.props.titles;

        if (!this.props.stretch) {
            items.push(<View key={`s`} style={styles.spacer} />);
        }

        for (var i = 0; i < titles.length; i++) {
            items.push(this.renderTitle(titles[i], i));
            if (!this.props.stretch) {
                items.push(<View key={`s${i}`} style={styles.spacer} />);
            }
        }

        return (
            <View {...this.props} style={[styles.container, this.props.style]}>
                <View style={styles.barContainer}>
                    <View ref="bar" style={[styles.bar, {
                        left: this.getTweeningValue('barLeft'),
                        right: this.getTweeningValue('barRight'),
                        backgroundColor: this.props.barColor
                    }]} />
                </View>
                <View style={styles.titleContainer}>
                    {items}
                </View>
            </View>
        );
    }
});

module.exports = SegmentedView;<commit_msg>Allow bar position to be defined<commit_after>var React = require('react-native');
var {
    StyleSheet,
    Text,
    Image,
    View,
    TouchableHighlight,
    PropTypes,
} = React;

var screen = require('Dimensions').get('window');
var tweenState = require('react-tween-state');

var styles = StyleSheet.create({
    container: {
        flex: 1,
    },
    titleContainer: {
        flexDirection: 'row',
    },
    title: {
        //flex: 0,
        //backgroundColor: '#f3f3f3',
        alignItems: 'center',
        paddingHorizontal: 2,
        paddingVertical: 10,
    },
    spacer: {
        flex: 1,
    },
    barContainer: {
        height: 4,
        position: 'relative',
    },
    bar: {
        backgroundColor: 'blue',
        position: 'absolute',
        height: 4,
    }
});

var SegmentedView = React.createClass({

    mixins: [tweenState.Mixin],

    propTypes: {
        duration: PropTypes.number,
        onTransitionStart: PropTypes.func,
        onTransitionEnd: PropTypes.func,
        onPress: PropTypes.func,
        renderTitle: PropTypes.func,
        titles: PropTypes.array,
        index: PropTypes.number,
        barColor: PropTypes.string,
        underlayColor: PropTypes.string,
        stretch: PropTypes.bool,
        selectedTitleStyle: PropTypes.object,
        titleStyle: PropTypes.object,

    },

    getDefaultProps() {
        return {
            duration: 200,
            onTransitionStart: ()=>{},
            onTransitionEnd: ()=>{},
            onPress: ()=>{},
            renderTitle: null,
            index: 0,
            barColor: '#44B7E1',
            underlayColor: '#CCCCCC',
            stretch: false,
            selectedTextStyle: null,
            textStyle: null,
            barPosition:'top'
        };
    },

    getInitialState() {
        return {
            barLeft: 0,
            barRight: screen.width,
        };
    },

    componentDidMount() {
        setTimeout(() => this.moveTo(this.props.index), 0);
    },

    componentWillReceiveProps(nextProps) {
        this.moveTo(nextProps.index);
    },

    measureHandler(ox, oy, width) {

        this.tweenState('barLeft', {
            easing: tweenState.easingTypes.easeInOutQuad,
            duration: 200,
            endValue: ox
        });

        this.tweenState('barRight', {
            easing: tweenState.easingTypes.easeInOutQuad,
            duration: 200,
            endValue: screen.width - ox - width,
            onEnd: this.props.onTransitionEnd
        });

        setTimeout(this.props.onTransitionStart, 0);
    },

    moveTo(index) {
        this.refs[index].measure(this.measureHandler);
    },

    _renderTitle(title, i) {
        return (
            <View style={styles.title}>
                <Text style={[this.props.titleStyle, i === this.props.index && this.props.selectedTitleStyle]}>{title}</Text>
            </View>
        );
    },

    renderTitle(title, i) {
        return (
            <View key={i} ref={i} style={{ flex: this.props.stretch ? 1 : 0 }}>
                <TouchableHighlight underlayColor={this.props.underlayColor} onPress={() => this.props.onPress(i)}>
                    {this.props.renderTitle ? this.props.renderTitle(title, i) : this._renderTitle(title, i)}
                </TouchableHighlight>
            </View>
        );
    },

    render() {
        var items = [];
        var titles = this.props.titles;

        if (!this.props.stretch) {
            items.push(<View key={`s`} style={styles.spacer} />);
        }

        for (var i = 0; i < titles.length; i++) {
            items.push(this.renderTitle(titles[i], i));
            if (!this.props.stretch) {
                items.push(<View key={`s${i}`} style={styles.spacer} />);
            }
        }
        var barContainer = (
          <View style={styles.barContainer}>
              <View ref="bar" style={[styles.bar, {
                  left: this.getTweeningValue('barLeft'),
                  right: this.getTweeningValue('barRight'),
                  backgroundColor: this.props.barColor
              }]} />
          </View>
        );
        return (
            <View {...this.props} style={[styles.container, this.props.style]}>
                {this.props.barPosition == 'top' && barContainer}
                <View style={styles.titleContainer}>
                    {items}
                </View>
                {this.props.barPosition == 'bottom' && barContainer}
            </View>
        );
    }
});

module.exports = SegmentedView;
<|endoftext|>