<commit_before>function getMonzo() {
  return {
    // general
    name: "Monzo Account",
    provider: "monzo",

    // auth
    authType: "oauth2",
    accessToken: null,
    accessExpires: -1,
    // variables below used while auth is being negotiated
    clientId: null,
    clientSecret: null,
    state: null,
    redirectUri: null,

    // other variables
    accountId: null,
    cardId: null
  };
}

// auth functions
function monzoAuth(monzo) {
  if (monzo.clientId == null) {
    throw new Error("clientId isn't set and yet auth was called!");
  }
  if (monzo.clientSecret == null) {
    throw new Error("clientSecret isn't set and yet auth was called!");
  }
  if (monzo.state == null) {
    throw new Error("state isn't set and yet auth was called!");
  }
  if (monzo.redirectUri == null) {
    throw new Error("redirectUri isn't set and yet auth was called!");
  }
  window.location = "https://auth.getmondo.co.uk/?client_id=" + monzo.clientId + "&redirect_uri=" + monzo.redirectUri + "&response_type=code&state=" + monzo.state;
}

function monzoCallback(monzo, authCode) {
  if (monzo.clientId == null) {
    throw new Error("clientId isn't set and yet callback was called!");
  }
  if (monzo.clientSecret == null) {
    throw new Error("clientSecret isn't set and yet callback was called!");
  }
  if (monzo.state == null) {
    throw new Error("state isn't set and yet callback was called!");
  }
  if (monzo.redirectUri == null) {
    throw new Error("redirectUri isn't set and yet callback was called!");
  }

  var request = new XMLHttpRequest();
  request.open("POST", "https://api.monzo.com/oauth2/token", false);
  request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  request.send("grant_type=authorization_code&client_id=" + monzo.clientId + "&client_secret=" + monzo.clientSecret + "&redirect_uri=" + monzo.redirectUri + "&code=" + authCode);
  if (request.status == 200) {
    monzo.accessToken = JSON.parse(request.responseText).access_token;
    monzo.accessExpires = Date.now() + JSON.parse(request.responseText).expires_in * 1000;
    // clear the variables we were using while getting an access token
    delete monzo["clientId"];
    delete monzo["clientSecret"];
    delete monzo["redirectUri"];
    delete monzo["state"];
    // get the account id
    var request = new XMLHttpRequest();
    request.open("GET", "https://api.monzo.com/accounts", false);
    request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
    request.send();
    monzo.accountId = JSON.parse(request.responseText).accounts[0].id;
    monzo.cardId = monzoGetCards(monzo).id;
  }
}

// HTML functions
function monzoTransactionHTML(monzo, id) {
  // alasql
  try {
    alasql("drop table transactions")
  } catch(err) {
    console.log(err.message);
  }
  try {
    alasql("drop table merchants")
  } catch(err) {
    console.log(err.message);
  }
  alasql('CREATE TABLE transactions (' +
    'id STRING PRIMARY KEY,' +
    'created Date,' +
    'settled Date,' +
    'is_load BOOLEAN,' +
    'merchant_id STRING,' +
    'description STRING,' +
    'currency STRING,' +
    'amount INT,' +
    'account_balance INT,' +
    'category STRING,' +
    'notes STRING);');
  alasql('CREATE TABLE merchants (' +
    'id STRING PRIMARY KEY,' +
    'name STRING,' +
    'address STRING,' +
    'city STRING,' +
    'region STRING,' +
    'country STRING,' +
    'postcode STRING,' +
    'latitude DOUBLE PRECISION,' +
    'longitude DOUBLE PRECISION,' +
    'zoom_level INT,' +
    'group_id STRING,' +
    'logo STRING,' +
    'emoji STRING,' +
    'category STRING);');
  // create a fake merchant for top-ups
  alasql('INSERT INTO merchants VALUES("merch_monzo", "Monzo", null, null, null, null, null, null, null, null, null, null, null, null);');

  transactions = monzoGetTransactions(monzo, true);
  for (var i = 0; i < transactions.length; i++) {
    var merchantId;
    if (transactions[i].merchant == null) {
      merchantId = null;
      if (transactions[i].is_load) {
        merchantId = "merch_monzo";
      }
    } else {
      merchantId = transactions[i].merchant.id;
      if (alasql('SELECT id from merchants where id="' + merchantId + '"').length == 0) {
        // add merchant to alasql
        alasql('INSERT INTO merchants VALUES(' +
          '"' + transactions[i].merchant.id + '"' + ',' +
          '"' + transactions[i].merchant.name + '"' + ',' +
          '"' + transactions[i].merchant.address.address + '"' + ',' +
          '"' + transactions[i].merchant.address.city + '"' + ',' +
          '"' + transactions[i].merchant.address.region + '"' + ',' +
          '"' + transactions[i].merchant.address.country + '"' + ',' +
          '"' + transactions[i].merchant.address.postcode + '"' + ',' +
          transactions[i].merchant.address.latitude + ',' +
          transactions[i].merchant.address.longitude + ',' +
          transactions[i].merchant.address.zoom_level + ',' +
          '"' + transactions[i].merchant.group_id + '"' + ',' +
          '"' + transactions[i].merchant.logo + '"' + ',' +
          '"' + transactions[i].merchant.emoji + '"' + ',' +
          '"' + transactions[i].merchant.category + '");');
      }
    }
    alasql('INSERT INTO transactions VALUES(' +
      '"' + transactions[i].id + '"' + ',' +
      '"' + transactions[i].created + '"' + ',' +
      '"' + transactions[i].settled + '"' + ',' +
      transactions[i].is_load + ',' +
      '"' + merchantId + '"' + ',' +
      '"' + transactions[i].description + '"' + ',' +
      '"' + transactions[i].currency + '"' + ',' +
      transactions[i].amount + ',' +
      transactions[i].account_balance + ',' +
      '"' + transactions[i].category + '"' + ',' +
      '"' + transactions[i].notes + '"' + ');');
  }

  var HTML = "";
  HTML += "
" + monzo.name + "
";
  HTML += '<textarea id="sql"></textarea>';
  HTML += '<button type="button" onclick="javascript:monzoSql(document.getElementById(\'sql\').value)" id="searchButton">Search!</button>';
  HTML += '<button type="button" onclick="javascript:monzoSaveQuery(document.getElementById(\'sql\').value)" id="searchButton">Save Query</button>';
  HTML += '

';
  return HTML;
}

function monzoDetailsHTML(monzo, id) {
  var HTML = "";
  var balance = monzoGetBalance(monzo);
  var card = monzoGetCards(monzo);
  HTML += "
Welcome, " + card.name + "!
";
  HTML += "
Balance: £" + (balance.balance*0.01).toFixed(2) + "
";
  HTML += "
You spent £" + (balance.spend_today*0.01).toFixed(2) + " today.
";
  if (card.status == "ACTIVE") {
    HTML += "
Your card (last 4 digits " + card.last_digits + ") is currently active.
";
    HTML += 'Freeze your card
';
  } else if (card.status == "INACTIVE") {
    HTML += "
Your card (last 4 digits " + card.last_digits + ") is currently frozen.
";
    HTML += 'Defrost your card
';
  } else {
    HTML += "
Your card (last 4 digits " + card.last_digits + ") has unknown status!
";
  }
  HTML += "
Saved Queries
";
  HTML += '

';
  return HTML;
}

// other functions
function monzoGetBalance(monzo) {
  var request = new XMLHttpRequest();
  request.open("GET", "https://api.monzo.com/balance?account_id=" + monzo.accountId, false);
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  return JSON.parse(request.responseText);
}

function monzoGetCards(monzo) {
  var request = new XMLHttpRequest();
  request.open("GET", "https://api.monzo.com/card/list?account_id=" + monzo.accountId, false);
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  return JSON.parse(request.responseText).cards[0];
}

function monzoGetTransactions(monzo, expand) {
  if (typeof expand === "undefined") { expand = false; }
  var request = new XMLHttpRequest();
  if (expand) {
    request.open("GET", "https://api.monzo.com/transactions?account_id=" + monzo.accountId + "&expand[]=merchant", false);
  } else {
    request.open("GET", "https://api.monzo.com/transactions?account_id=" + monzo.accountId, false);
  }
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  return JSON.parse(request.responseText).transactions;
}

function monzoFreezeCard(monzo, targetStatus, id) {
  var request = new XMLHttpRequest();
  request.open("PUT", "https://api.monzo.com/card/toggle?card_id=" + monzo.cardId + "&status=" + targetStatus, false);
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  document.getElementById("accountDetails").innerHTML = monzoDetailsHTML(monzo, id)
}

function monzoSaveQuery(query, name) {
  if (typeof name === "undefined") {
    var name = prompt("What would you like to call this query?");
    if (name == null || name == "") {
      return;
    }
  }
  if (query == "") {
    for (var i = 0; i < monzoSavedQueries.length; i++) {
      if (monzoSavedQueries[i].name == name) {
        monzoSavedQueries.splice(i, 1);
      }
    }
  } else {
    document.getElementById("monzoSavedQueries").innerHTML += '
' + name + '
';
    monzoSavedQueries.push({query: query, name: name});
  }
  var cookiePath = new RegExp("https://.+?(/.+?)(?:index.html)?").exec(document.location.href)[1];
  setCookie("monzoSavedQueries", JSON.stringify(monzoSavedQueries), cookiePath, 28*24); // store queries for 28 days
}

function monzoSql(query) {
  alasql(query.replace(' from ', ' INTO HTML("#transactions", {headers:true}) from '));
}<commit_msg>Added code to remove a saved query from the DOM<commit_after>function getMonzo() {
  return {
    // general
    name: "Monzo Account",
    provider: "monzo",

    // auth
    authType: "oauth2",
    accessToken: null,
    accessExpires: -1,
    // variables below used while auth is being negotiated
    clientId: null,
    clientSecret: null,
    state: null,
    redirectUri: null,

    // other variables
    accountId: null,
    cardId: null
  };
}

// auth functions
function monzoAuth(monzo) {
  if (monzo.clientId == null) {
    throw new Error("clientId isn't set and yet auth was called!");
  }
  if (monzo.clientSecret == null) {
    throw new Error("clientSecret isn't set and yet auth was called!");
  }
  if (monzo.state == null) {
    throw new Error("state isn't set and yet auth was called!");
  }
  if (monzo.redirectUri == null) {
    throw new Error("redirectUri isn't set and yet auth was called!");
  }
  window.location = "https://auth.getmondo.co.uk/?client_id=" + monzo.clientId + "&redirect_uri=" + monzo.redirectUri + "&response_type=code&state=" + monzo.state;
}

function monzoCallback(monzo, authCode) {
  if (monzo.clientId == null) {
    throw new Error("clientId isn't set and yet callback was called!");
  }
  if (monzo.clientSecret == null) {
    throw new Error("clientSecret isn't set and yet callback was called!");
  }
  if (monzo.state == null) {
    throw new Error("state isn't set and yet callback was called!");
  }
  if (monzo.redirectUri == null) {
    throw new Error("redirectUri isn't set and yet callback was called!");
  }

  var request = new XMLHttpRequest();
  request.open("POST", "https://api.monzo.com/oauth2/token", false);
  request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  request.send("grant_type=authorization_code&client_id=" + monzo.clientId + "&client_secret=" + monzo.clientSecret + "&redirect_uri=" + monzo.redirectUri + "&code=" + authCode);
  if (request.status == 200) {
    monzo.accessToken = JSON.parse(request.responseText).access_token;
    monzo.accessExpires = Date.now() + JSON.parse(request.responseText).expires_in * 1000;
    // clear the variables we were using while getting an access token
    delete monzo["clientId"];
    delete monzo["clientSecret"];
    delete monzo["redirectUri"];
    delete monzo["state"];
    // get the account id
    var request = new XMLHttpRequest();
    request.open("GET", "https://api.monzo.com/accounts", false);
    request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
    request.send();
    monzo.accountId = JSON.parse(request.responseText).accounts[0].id;
    monzo.cardId = monzoGetCards(monzo).id;
  }
}

// HTML functions
function monzoTransactionHTML(monzo, id) {
  // alasql
  try {
    alasql("drop table transactions")
  } catch(err) {
    console.log(err.message);
  }
  try {
    alasql("drop table merchants")
  } catch(err) {
    console.log(err.message);
  }
  alasql('CREATE TABLE transactions (' +
    'id STRING PRIMARY KEY,' +
    'created Date,' +
    'settled Date,' +
    'is_load BOOLEAN,' +
    'merchant_id STRING,' +
    'description STRING,' +
    'currency STRING,' +
    'amount INT,' +
    'account_balance INT,' +
    'category STRING,' +
    'notes STRING);');
  alasql('CREATE TABLE merchants (' +
    'id STRING PRIMARY KEY,' +
    'name STRING,' +
    'address STRING,' +
    'city STRING,' +
    'region STRING,' +
    'country STRING,' +
    'postcode STRING,' +
    'latitude DOUBLE PRECISION,' +
    'longitude DOUBLE PRECISION,' +
    'zoom_level INT,' +
    'group_id STRING,' +
    'logo STRING,' +
    'emoji STRING,' +
    'category STRING);');
  // create a fake merchant for top-ups
  alasql('INSERT INTO merchants VALUES("merch_monzo", "Monzo", null, null, null, null, null, null, null, null, null, null, null, null);');

  transactions = monzoGetTransactions(monzo, true);
  for (var i = 0; i < transactions.length; i++) {
    var merchantId;
    if (transactions[i].merchant == null) {
      merchantId = null;
      if (transactions[i].is_load) {
        merchantId = "merch_monzo";
      }
    } else {
      merchantId = transactions[i].merchant.id;
      if (alasql('SELECT id from merchants where id="' + merchantId + '"').length == 0) {
        // add merchant to alasql
        alasql('INSERT INTO merchants VALUES(' +
          '"' + transactions[i].merchant.id + '"' + ',' +
          '"' + transactions[i].merchant.name + '"' + ',' +
          '"' + transactions[i].merchant.address.address + '"' + ',' +
          '"' + transactions[i].merchant.address.city + '"' + ',' +
          '"' + transactions[i].merchant.address.region + '"' + ',' +
          '"' + transactions[i].merchant.address.country + '"' + ',' +
          '"' + transactions[i].merchant.address.postcode + '"' + ',' +
          transactions[i].merchant.address.latitude + ',' +
          transactions[i].merchant.address.longitude + ',' +
          transactions[i].merchant.address.zoom_level + ',' +
          '"' + transactions[i].merchant.group_id + '"' + ',' +
          '"' + transactions[i].merchant.logo + '"' + ',' +
          '"' + transactions[i].merchant.emoji + '"' + ',' +
          '"' + transactions[i].merchant.category + '");');
      }
    }
    alasql('INSERT INTO transactions VALUES(' +
      '"' + transactions[i].id + '"' + ',' +
      '"' + transactions[i].created + '"' + ',' +
      '"' + transactions[i].settled + '"' + ',' +
      transactions[i].is_load + ',' +
      '"' + merchantId + '"' + ',' +
      '"' + transactions[i].description + '"' + ',' +
      '"' + transactions[i].currency + '"' + ',' +
      transactions[i].amount + ',' +
      transactions[i].account_balance + ',' +
      '"' + transactions[i].category + '"' + ',' +
      '"' + transactions[i].notes + '"' + ');');
  }

  var HTML = "";
  HTML += "
" + monzo.name + "
";
  HTML += '<textarea id="sql"></textarea>';
  HTML += '<button type="button" onclick="javascript:monzoSql(document.getElementById(\'sql\').value)" id="searchButton">Search!</button>';
  HTML += '<button type="button" onclick="javascript:monzoSaveQuery(document.getElementById(\'sql\').value)" id="searchButton">Save Query</button>';
  HTML += '

';
  return HTML;
}

function monzoDetailsHTML(monzo, id) {
  var HTML = "";
  var balance = monzoGetBalance(monzo);
  var card = monzoGetCards(monzo);
  HTML += "
Welcome, " + card.name + "!
";
  HTML += "
Balance: £" + (balance.balance*0.01).toFixed(2) + "
";
  HTML += "
You spent £" + (balance.spend_today*0.01).toFixed(2) + " today.
";
  if (card.status == "ACTIVE") {
    HTML += "
Your card (last 4 digits " + card.last_digits + ") is currently active.
";
    HTML += 'Freeze your card
';
  } else if (card.status == "INACTIVE") {
    HTML += "
Your card (last 4 digits " + card.last_digits + ") is currently frozen.
";
    HTML += 'Defrost your card
';
  } else {
    HTML += "
Your card (last 4 digits " + card.last_digits + ") has unknown status!
";
  }
  HTML += "
Saved Queries
";
  HTML += '

';
  return HTML;
}

// other functions
function monzoGetBalance(monzo) {
  var request = new XMLHttpRequest();
  request.open("GET", "https://api.monzo.com/balance?account_id=" + monzo.accountId, false);
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  return JSON.parse(request.responseText);
}

function monzoGetCards(monzo) {
  var request = new XMLHttpRequest();
  request.open("GET", "https://api.monzo.com/card/list?account_id=" + monzo.accountId, false);
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  return JSON.parse(request.responseText).cards[0];
}

function monzoGetTransactions(monzo, expand) {
  if (typeof expand === "undefined") { expand = false; }
  var request = new XMLHttpRequest();
  if (expand) {
    request.open("GET", "https://api.monzo.com/transactions?account_id=" + monzo.accountId + "&expand[]=merchant", false);
  } else {
    request.open("GET", "https://api.monzo.com/transactions?account_id=" + monzo.accountId, false);
  }
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  return JSON.parse(request.responseText).transactions;
}

function monzoFreezeCard(monzo, targetStatus, id) {
  var request = new XMLHttpRequest();
  request.open("PUT", "https://api.monzo.com/card/toggle?card_id=" + monzo.cardId + "&status=" + targetStatus, false);
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  document.getElementById("accountDetails").innerHTML = monzoDetailsHTML(monzo, id)
}

function monzoSaveQuery(query, name) {
  if (typeof name === "undefined") {
    var name = prompt("What would you like to call this query?");
    if (name == null || name == "") {
      return;
    }
  }
  if (query == "") {
    for (var i = 0; i < monzoSavedQueries.length; i++) {
      if (monzoSavedQueries[i].name == name) {
        document.getElementById("monzoSavedQueries").innerHTML.replace('
' + monzoSavedQueries[i].name + '
', '');
        monzoSavedQueries.splice(i, 1);
      }
    }
  } else {
    document.getElementById("monzoSavedQueries").innerHTML += '
' + name + '
';
    monzoSavedQueries.push({query: query, name: name});
  }
  var cookiePath = new RegExp("https://.+?(/.+?)(?:index.html)?").exec(document.location.href)[1];
  setCookie("monzoSavedQueries", JSON.stringify(monzoSavedQueries), cookiePath, 28*24); // store queries for 28 days
}

function monzoSql(query) {
  alasql(query.replace(' from ', ' INTO HTML("#transactions", {headers:true}) from '));
}<|endoftext|>


<commit_before>// Mozilla Firefox User Preferences

// -----------------------------------------------------------------------------
// Startup
// -----------------------------------------------------------------------------

// Show a blank page when Firefox starts.
user_pref("browser.startup.page", 0);
user_pref("browser.startup.homepage", "about:blank");
user_pref("browser.startup.homepage_override.mstone", "ignore");
user_pref("startup.homepage_welcome_url", "");
user_pref("startup.homepage_welcome_url.additional", "");
user_pref("startup.homepage_override_url", "");

// Show a blank page when opening new tab.
user_pref("browser.newtabpage.enabled", false);
user_pref("browser.newtab.preload", false);

// -----------------------------------------------------------------------------
// Localization
// -----------------------------------------------------------------------------

// Choose preferred languages for displaying pages.
user_pref("intl.accept_languages", "ja, zh-tw, zh, en-us, en");

// Disable GeoIP-based search results.
user_pref("browser.search.countryCode", "JP");
user_pref("browser.search.region", "JP");
user_pref("browser.search.geoip.url", "");

// Disable geographically specific results/search engines.
user_pref("browser.search.geoSpecificDefaults", false);
user_pref("browser.search.geoSpecificDefaults.url", "");

// -----------------------------------------------------------------------------
// Content
// -----------------------------------------------------------------------------

// Set minimum font size.
user_pref("font.minimum-size.ja", 10);
user_pref("font.minimum-size.ko", 10);
user_pref("font.minimum-size.zh-CN", 10);
user_pref("font.minimum-size.zh-HK", 10);
user_pref("font.minimum-size.zh-TW", 10);

// Disable Geolocation.
user_pref("geo.enabled", false);
user_pref("geo.wifi.uri", "");
user_pref("geo.wifi.logging.enabled", false);
user_pref("permissions.default.geo", 2);

// Disable WebRTC.
user_pref("media.peerconnection.enabled", false);
user_pref("media.peerconnection.ice.tcp", false);
user_pref("media.peerconnection.identity.enabled", false);
user_pref("media.peerconnection.identity.timeout", 1);
user_pref("media.peerconnection.turn.disable", true);
user_pref("media.peerconnection.use_document_iceservers", false);
user_pref("media.peerconnection.video.enabled", false);
user_pref("media.navigator.enabled", false);
user_pref("media.navigator.video.enabled", false);

// Disable Notifications API.
user_pref("dom.webnotifications.enabled", false);
user_pref("dom.webnotifications.serviceworker.enabled", false);
user_pref("permissions.default.desktop-notification", 2);

// Disable Push API.
user_pref("dom.push.enabled", false);
user_pref("dom.push.connection.enabled", false);
user_pref("dom.push.serverURL", "");
user_pref("dom.push.userAgentID", "");

// Block camera and microphone permissions.
user_pref("permissions.default.camera", 2);
user_pref("permissions.default.microphone", 2);

// Disable links opening in a new window.
user_pref("browser.link.open_newwindow", 3);
user_pref("browser.link.open_newwindow.restriction", 0);

// Limit pop-up window events.
user_pref("dom.disable_open_during_load", true);
user_pref("dom.popup_maximum", 2);
user_pref("dom.popup_allowed_events", "click dblclick mouseup");

// Disallow JavaScript to move or resize window.
user_pref("dom.disable_window_move_resize", true);

// Disable audio auto-play in non-active tabs.
user_pref("media.block-autoplay-until-in-foreground", true);

// -----------------------------------------------------------------------------
// Privacy
// -----------------------------------------------------------------------------

// Accept third-party cookies from visited.
user_pref("network.cookie.cookieBehavior", 3);

// Set third-party cookies to session-only.
user_pref("network.cookie.thirdparty.sessionOnly", true);
user_pref("network.cookie.thirdparty.nonsecureSessionOnly", true);

// Enable DNT HTTP header.
user_pref("privacy.donottrackheader.enabled", true);

// Enable Tracking Protection.
user_pref("privacy.trackingprotection.enabled", true);
user_pref("privacy.trackingprotection.pbmode.enabled", true);
user_pref("privacy.trackingprotection.ui.enabled", true);
user_pref("privacy.trackingprotection.introCount", 20);

// Enable First Party Isolation.
user_pref("privacy.firstparty.isolate", true);
user_pref("privacy.firstparty.isolate.restrict_opener_access", true);

// Disable Health Report.
user_pref("datareporting.healthreport.uploadEnabled", false);
user_pref("datareporting.policy.dataSubmissionEnabled", false);

// Disable Crash Reporter.
user_pref("breakpad.reportURL", "");
user_pref("browser.tabs.crashReporting.sendReport", false);
user_pref("browser.crashReports.unsubmittedCheck.enabled", false);
user_pref("browser.crashReports.unsubmittedCheck.autoSubmit", false);
user_pref("browser.crashReports.unsubmittedCheck.autoSubmit2", false);

// Disable Browser Error Reporter.
user_pref("browser.chrome.errorReporter.enabled", false);
user_pref("browser.chrome.errorReporter.submitUrl", "");

// Disable Telemetry.
user_pref("toolkit.telemetry.enabled", false);
user_pref("toolkit.telemetry.unified", false);
user_pref("toolkit.telemetry.archive.enabled", false);
user_pref("toolkit.telemetry.cachedClientID", "");
user_pref("toolkit.telemetry.server", "data:,");
user_pref("toolkit.telemetry.newProfilePing.enabled", false);
user_pref("toolkit.telemetry.shutdownPingSender.enabled", false);
user_pref("toolkit.telemetry.updatePing.enabled", false);
user_pref("toolkit.telemetry.bhrPing.enabled", false);
user_pref("toolkit.telemetry.firstShutdownPing.enabled", false);
user_pref("toolkit.telemetry.hybridContent.enabled", false);

// Disable prefetching.
user_pref("network.prefetch-next", false);
user_pref("network.dns.disablePrefetch", true);
user_pref("network.dns.disablePrefetchFromHTTPS", true);
user_pref("network.predictor.enable-prefetch", false);

// Disable Seer/Necko.
user_pref("network.predictor.enabled", false);
user_pref("network.captive-portal-service.enabled", false);
user_pref("captivedetect.canonicalURL", "");

// Disable opening connection to links when mouseover.
user_pref("network.http.speculative-parallel-limit", 0);

// Disable pings but enforce same host in case.
user_pref("browser.send_pings", false);
user_pref("browser.send_pings.require_same_host", true);

// Disable Web Beacon.
user_pref("beacon.enabled", false);

// -----------------------------------------------------------------------------
// Security
// -----------------------------------------------------------------------------

// Enable Safe Browsing.
user_pref("browser.safebrowsing.phishing.enabled", true);
user_pref("browser.safebrowsing.malware.enabled", true);
user_pref("browser.safebrowsing.downloads.enabled", true);

// Disable saving passwords.
user_pref("signon.rememberSignons", false);

// Enable OCSP stapling.
user_pref("security.ssl.enable_ocsp_stapling", true);
user_pref("security.OCSP.enabled", 1);
user_pref("security.OCSP.require", true);

// Skip the security delay on "Install" and "Open/Save" dialogs.
user_pref("security.dialog_enable_delay", 0);

// Disable Windows Family Safety Mode.
user_pref("security.family_safety.mode", 0);

// -----------------------------------------------------------------------------
// Features
// -----------------------------------------------------------------------------

// Disable Normandy and Shield (telemetry system).
user_pref("app.normandy.enabled", false);
user_pref("app.normandy.api_url", "");
user_pref("app.shield.optoutstudies.enabled", false);
user_pref("shield.savant.enabled", false);

// Disable Ping-centre telemetry.
user_pref("browser.ping-centre.telemetry", false);

// Disable UITour backend.
user_pref("browser.uitour.enabled", false);
user_pref("browser.uitour.url", "");

// Disable Reader View.
user_pref("reader.parse-on-load.enabled", false);

// Disable various developer tools.
user_pref("devtools.chrome.enabled", false);
user_pref("devtools.webide.enabled", false);
user_pref("devtools.webide.autoinstallADBHelper", false);
user_pref("devtools.debugger.remote-enabled", false);

// -----------------------------------------------------------------------------
// Add-ons
// -----------------------------------------------------------------------------

// Disable "Get Add-ons" pane.
user_pref("extensions.getAddons.showPane", false);
user_pref("extensions.webservice.discoverURL", "");

// Lock down allowed extension directories.
user_pref("extensions.enabledScopes", 1);

// Clear localStorage and UUID when an extension is uninstalled.
user_pref("extensions.webextensions.keepStorageOnUninstall", false);
user_pref("extensions.webextensions.keepUuidOnUninstall", false);

// Disable Mozilla permission to silently opt you into tests.
user_pref("network.allow-experiments", false);

// Disable Activity Stream.
user_pref("browser.library.activity-stream.enabled", false);
user_pref("browser.newtabpage.activity-stream.feeds.section.highlights", false);
user_pref("browser.newtabpage.activity-stream.feeds.snippets", false);
user_pref("browser.newtabpage.activity-stream.feeds.topsites", false);
user_pref("browser.newtabpage.activity-stream.migrationExpired", true);
user_pref("browser.newtabpage.activity-stream.section.highlights.includeBookmarks", false);
user_pref("browser.newtabpage.activity-stream.section.highlights.includeDownloads", false);
user_pref("browser.newtabpage.activity-stream.section.highlights.includePocket", false);
user_pref("browser.newtabpage.activity-stream.section.highlights.includeVisited", false);
user_pref("browser.newtabpage.activity-stream.showSearch", false);

// Disable Onboarding.
user_pref("browser.onboarding.enabled", false);

// Disable Form Autofill.
user_pref("extensions.formautofill.available", "off");
user_pref("extensions.formautofill.addresses.enabled", false);
user_pref("extensions.formautofill.creditCards.enabled", false);
user_pref("extensions.formautofill.heuristics.enabled", false);

// Disable Pocket.
user_pref("extensions.pocket.enabled", false);

// Disable uploading screenshots to the Screenshots server.
user_pref("extensions.screenshots.upload-disabled", true);

// Disable Web Compatibility Reporter.
user_pref("extensions.webcompat-reporter.enabled", false);

// Disable all plugins by default.
user_pref("plugin.default.state", 0);
user_pref("plugin.defaultXpi.state", 0);

// Enable click-to-play and set the session permission.
user_pref("plugins.click_to_play", true);
user_pref("plugin.sessionPermissionNow.intervalInMinutes", 0);

// Disable scanning plugins on Windows.
user_pref("plugin.scan.plid.all", false);

// Disable all GMP (Gecko Media Plugins).
user_pref("media.gmp-provider.enabled", false);
user_pref("media.gmp-manager.updateEnabled", false);
user_pref("media.gmp-manager.url", "data:text/plain,");
user_pref("media.gmp-manager.url.override", "data:text/plain,");
user_pref("media.gmp.trial-create.enabled", false);

// Disable widevine CDM.
user_pref("media.gmp-widevinecdm.enabled", false);
user_pref("media.gmp-widevinecdm.visible", false);
user_pref("media.gmp-widevinecdm.autoupdate", false);

// Disable all DRM content.
user_pref("media.eme.enabled", false);
user_pref("browser.eme.ui.enabled", false);

// Disable OpenH264 Video Codec by Cisco.
user_pref("media.gmp-gmpopenh264.enabled", false);
user_pref("media.gmp-gmpopenh264.autoupdate", false);

// Disable signature enforcement for Nightly and ESR.
user_pref("xpinstall.signatures.required", false);

// -----------------------------------------------------------------------------
// Others
// -----------------------------------------------------------------------------

// Disable `about:config` warning.
user_pref("general.warnOnAboutConfig", false);

// Check for Firefox updates but let me choose to install them.
user_pref("app.update.enabled", true);
user_pref("app.update.auto", false);

// Prevent accessibility services from accessing my browser.
user_pref("accessibility.force_disabled", 1);

// Always ask me where to save download files.
user_pref("browser.download.useDownloadDir", false);

// Disable downloading on desktop.
user_pref("browser.download.folderList", 2);

// Always display download button on toolbar.
user_pref("browser.download.autohideButton", false);

// Disable "Open with" in download dialog.
user_pref("browser.download.forbid_open_with", true);

// Disable disk cache.
user_pref("browser.cache.disk.enable", false);
user_pref("browser.cache.disk.smart_size.enabled", false);
user_pref("browser.cache.disk.smart_size.first_run", false);
user_pref("browser.cache.disk.capacity", 0);
user_pref("browser.cache.disk_cache_ssl", false);

// Set the minimum interval between session save operations.
user_pref("browser.sessionstore.interval", 600000);

// Disable location bar domain guessing.
user_pref("browser.fixup.alternate.enabled", false);

// Display all parts of the URL in the location bar.
user_pref("browser.urlbar.trimURLs", false);

// Disable search suggestions in the location bar.
user_pref("browser.urlbar.suggest.searches", false);
user_pref("browser.urlbar.userMadeSearchSuggestionsChoice", true);
user_pref("browser.urlbar.oneOffSearches", false);

// Disable location bar making speculative connections.
user_pref("browser.urlbar.speculativeConnect.enabled", false);
user_pref("browser.urlbar.usepreloadedtopurls.enabled", false);

// Display the raw Punycode in the location bar.
user_pref("network.IDN_show_punycode", true);

// Disable closing browser with last tab.
user_pref("browser.tabs.closeWindowWithLastTab", false);

// Disable privacy items in Windows Taskbar Jump List.
user_pref("browser.taskbar.lists.frequent.enabled", false);
user_pref("browser.taskbar.lists.recent.enabled", false);

// Disable capturing page thumbnails.
user_pref("browser.pagethumbnails.capturing_disabled", true)

// Disable site specific zoom.
user_pref("browser.zoom.siteSpecific", false);

// Disable UI animation.
user_pref("toolkit.cosmeticAnimations.enabled", false);

// Disable Alt key toggling the menu bar.
user_pref("ui.key.menuAccessKey", 0);
<commit_msg>Lock `extensions.autoDisableScopes` value<commit_after>// Mozilla Firefox User Preferences

// -----------------------------------------------------------------------------
// Startup
// -----------------------------------------------------------------------------

// Show a blank page when Firefox starts.
user_pref("browser.startup.page", 0);
user_pref("browser.startup.homepage", "about:blank");
user_pref("browser.startup.homepage_override.mstone", "ignore");
user_pref("startup.homepage_welcome_url", "");
user_pref("startup.homepage_welcome_url.additional", "");
user_pref("startup.homepage_override_url", "");

// Show a blank page when opening new tab.
user_pref("browser.newtabpage.enabled", false);
user_pref("browser.newtab.preload", false);

// -----------------------------------------------------------------------------
// Localization
// -----------------------------------------------------------------------------

// Choose preferred languages for displaying pages.
user_pref("intl.accept_languages", "ja, zh-tw, zh, en-us, en");

// Disable GeoIP-based search results.
user_pref("browser.search.countryCode", "JP");
user_pref("browser.search.region", "JP");
user_pref("browser.search.geoip.url", "");

// Disable geographically specific results/search engines.
user_pref("browser.search.geoSpecificDefaults", false);
user_pref("browser.search.geoSpecificDefaults.url", "");

// -----------------------------------------------------------------------------
// Content
// -----------------------------------------------------------------------------

// Set minimum font size.
user_pref("font.minimum-size.ja", 10);
user_pref("font.minimum-size.ko", 10);
user_pref("font.minimum-size.zh-CN", 10);
user_pref("font.minimum-size.zh-HK", 10);
user_pref("font.minimum-size.zh-TW", 10);

// Disable Geolocation.
user_pref("geo.enabled", false);
user_pref("geo.wifi.uri", "");
user_pref("geo.wifi.logging.enabled", false);
user_pref("permissions.default.geo", 2);

// Disable WebRTC.
user_pref("media.peerconnection.enabled", false);
user_pref("media.peerconnection.ice.tcp", false);
user_pref("media.peerconnection.identity.enabled", false);
user_pref("media.peerconnection.identity.timeout", 1);
user_pref("media.peerconnection.turn.disable", true);
user_pref("media.peerconnection.use_document_iceservers", false);
user_pref("media.peerconnection.video.enabled", false);
user_pref("media.navigator.enabled", false);
user_pref("media.navigator.video.enabled", false);

// Disable Notifications API.
user_pref("dom.webnotifications.enabled", false);
user_pref("dom.webnotifications.serviceworker.enabled", false);
user_pref("permissions.default.desktop-notification", 2);

// Disable Push API.
user_pref("dom.push.enabled", false);
user_pref("dom.push.connection.enabled", false);
user_pref("dom.push.serverURL", "");
user_pref("dom.push.userAgentID", "");

// Block camera and microphone permissions.
user_pref("permissions.default.camera", 2);
user_pref("permissions.default.microphone", 2);

// Disable links opening in a new window.
user_pref("browser.link.open_newwindow", 3);
user_pref("browser.link.open_newwindow.restriction", 0);

// Limit pop-up window events.
user_pref("dom.disable_open_during_load", true);
user_pref("dom.popup_maximum", 2);
user_pref("dom.popup_allowed_events", "click dblclick mouseup");

// Disallow JavaScript to move or resize window.
user_pref("dom.disable_window_move_resize", true);

// Disable audio auto-play in non-active tabs.
user_pref("media.block-autoplay-until-in-foreground", true);

// -----------------------------------------------------------------------------
// Privacy
// -----------------------------------------------------------------------------

// Accept third-party cookies from visited.
user_pref("network.cookie.cookieBehavior", 3);

// Set third-party cookies to session-only.
user_pref("network.cookie.thirdparty.sessionOnly", true);
user_pref("network.cookie.thirdparty.nonsecureSessionOnly", true);

// Enable DNT HTTP header.
user_pref("privacy.donottrackheader.enabled", true);

// Enable Tracking Protection.
user_pref("privacy.trackingprotection.enabled", true);
user_pref("privacy.trackingprotection.pbmode.enabled", true);
user_pref("privacy.trackingprotection.ui.enabled", true);
user_pref("privacy.trackingprotection.introCount", 20);

// Enable First Party Isolation.
user_pref("privacy.firstparty.isolate", true);
user_pref("privacy.firstparty.isolate.restrict_opener_access", true);

// Disable Health Report.
user_pref("datareporting.healthreport.uploadEnabled", false);
user_pref("datareporting.policy.dataSubmissionEnabled", false);

// Disable Crash Reporter.
user_pref("breakpad.reportURL", "");
user_pref("browser.tabs.crashReporting.sendReport", false);
user_pref("browser.crashReports.unsubmittedCheck.enabled", false);
user_pref("browser.crashReports.unsubmittedCheck.autoSubmit", false);
user_pref("browser.crashReports.unsubmittedCheck.autoSubmit2", false);

// Disable Browser Error Reporter.
user_pref("browser.chrome.errorReporter.enabled", false);
user_pref("browser.chrome.errorReporter.submitUrl", "");

// Disable Telemetry.
user_pref("toolkit.telemetry.enabled", false);
user_pref("toolkit.telemetry.unified", false);
user_pref("toolkit.telemetry.archive.enabled", false);
user_pref("toolkit.telemetry.cachedClientID", "");
user_pref("toolkit.telemetry.server", "data:,");
user_pref("toolkit.telemetry.newProfilePing.enabled", false);
user_pref("toolkit.telemetry.shutdownPingSender.enabled", false);
user_pref("toolkit.telemetry.updatePing.enabled", false);
user_pref("toolkit.telemetry.bhrPing.enabled", false);
user_pref("toolkit.telemetry.firstShutdownPing.enabled", false);
user_pref("toolkit.telemetry.hybridContent.enabled", false);

// Disable prefetching.
user_pref("network.prefetch-next", false);
user_pref("network.dns.disablePrefetch", true);
user_pref("network.dns.disablePrefetchFromHTTPS", true);
user_pref("network.predictor.enable-prefetch", false);

// Disable Seer/Necko.
user_pref("network.predictor.enabled", false);
user_pref("network.captive-portal-service.enabled", false);
user_pref("captivedetect.canonicalURL", "");

// Disable opening connection to links when mouseover.
user_pref("network.http.speculative-parallel-limit", 0);

// Disable pings but enforce same host in case.
user_pref("browser.send_pings", false);
user_pref("browser.send_pings.require_same_host", true);

// Disable Web Beacon.
user_pref("beacon.enabled", false);

// -----------------------------------------------------------------------------
// Security
// -----------------------------------------------------------------------------

// Enable Safe Browsing.
user_pref("browser.safebrowsing.phishing.enabled", true);
user_pref("browser.safebrowsing.malware.enabled", true);
user_pref("browser.safebrowsing.downloads.enabled", true);

// Disable saving passwords.
user_pref("signon.rememberSignons", false);

// Enable OCSP stapling.
user_pref("security.ssl.enable_ocsp_stapling", true);
user_pref("security.OCSP.enabled", 1);
user_pref("security.OCSP.require", true);

// Skip the security delay on "Install" and "Open/Save" dialogs.
user_pref("security.dialog_enable_delay", 0);

// Disable Windows Family Safety Mode.
user_pref("security.family_safety.mode", 0);

// -----------------------------------------------------------------------------
// Features
// -----------------------------------------------------------------------------

// Disable Normandy and Shield (telemetry system).
user_pref("app.normandy.enabled", false);
user_pref("app.normandy.api_url", "");
user_pref("app.shield.optoutstudies.enabled", false);
user_pref("shield.savant.enabled", false);

// Disable Ping-centre telemetry.
user_pref("browser.ping-centre.telemetry", false);

// Disable UITour backend.
user_pref("browser.uitour.enabled", false);
user_pref("browser.uitour.url", "");

// Disable Reader View.
user_pref("reader.parse-on-load.enabled", false);

// Disable various developer tools.
user_pref("devtools.chrome.enabled", false);
user_pref("devtools.webide.enabled", false);
user_pref("devtools.webide.autoinstallADBHelper", false);
user_pref("devtools.debugger.remote-enabled", false);

// -----------------------------------------------------------------------------
// Add-ons
// -----------------------------------------------------------------------------

// Disable "Get Add-ons" pane.
user_pref("extensions.getAddons.showPane", false);
user_pref("extensions.webservice.discoverURL", "");

// Lock down allowed extension directories.
user_pref("extensions.enabledScopes", 1);
user_pref("extensions.autoDisableScopes", 15);

// Clear localStorage and UUID when an extension is uninstalled.
user_pref("extensions.webextensions.keepStorageOnUninstall", false);
user_pref("extensions.webextensions.keepUuidOnUninstall", false);

// Disable Mozilla permission to silently opt you into tests.
user_pref("network.allow-experiments", false);

// Disable Activity Stream.
user_pref("browser.library.activity-stream.enabled", false);
user_pref("browser.newtabpage.activity-stream.feeds.section.highlights", false);
user_pref("browser.newtabpage.activity-stream.feeds.snippets", false);
user_pref("browser.newtabpage.activity-stream.feeds.topsites", false);
user_pref("browser.newtabpage.activity-stream.migrationExpired", true);
user_pref("browser.newtabpage.activity-stream.section.highlights.includeBookmarks", false);
user_pref("browser.newtabpage.activity-stream.section.highlights.includeDownloads", false);
user_pref("browser.newtabpage.activity-stream.section.highlights.includePocket", false);
user_pref("browser.newtabpage.activity-stream.section.highlights.includeVisited", false);
user_pref("browser.newtabpage.activity-stream.showSearch", false);

// Disable Onboarding.
user_pref("browser.onboarding.enabled", false);

// Disable Form Autofill.
user_pref("extensions.formautofill.available", "off");
user_pref("extensions.formautofill.addresses.enabled", false);
user_pref("extensions.formautofill.creditCards.enabled", false);
user_pref("extensions.formautofill.heuristics.enabled", false);

// Disable Pocket.
user_pref("extensions.pocket.enabled", false);

// Disable uploading screenshots to the Screenshots server.
user_pref("extensions.screenshots.upload-disabled", true);

// Disable Web Compatibility Reporter.
user_pref("extensions.webcompat-reporter.enabled", false);

// Disable all plugins by default.
user_pref("plugin.default.state", 0);
user_pref("plugin.defaultXpi.state", 0);

// Enable click-to-play and set the session permission.
user_pref("plugins.click_to_play", true);
user_pref("plugin.sessionPermissionNow.intervalInMinutes", 0);

// Disable scanning plugins on Windows.
user_pref("plugin.scan.plid.all", false);

// Disable all GMP (Gecko Media Plugins).
user_pref("media.gmp-provider.enabled", false);
user_pref("media.gmp-manager.updateEnabled", false);
user_pref("media.gmp-manager.url", "data:text/plain,");
user_pref("media.gmp-manager.url.override", "data:text/plain,");
user_pref("media.gmp.trial-create.enabled", false);

// Disable widevine CDM.
user_pref("media.gmp-widevinecdm.enabled", false);
user_pref("media.gmp-widevinecdm.visible", false);
user_pref("media.gmp-widevinecdm.autoupdate", false);

// Disable all DRM content.
user_pref("media.eme.enabled", false);
user_pref("browser.eme.ui.enabled", false);

// Disable OpenH264 Video Codec by Cisco.
user_pref("media.gmp-gmpopenh264.enabled", false);
user_pref("media.gmp-gmpopenh264.autoupdate", false);

// Disable signature enforcement for Nightly and ESR.
user_pref("xpinstall.signatures.required", false);

// -----------------------------------------------------------------------------
// Others
// -----------------------------------------------------------------------------

// Disable `about:config` warning.
user_pref("general.warnOnAboutConfig", false);

// Check for Firefox updates but let me choose to install them.
user_pref("app.update.enabled", true);
user_pref("app.update.auto", false);

// Prevent accessibility services from accessing my browser.
user_pref("accessibility.force_disabled", 1);

// Always ask me where to save download files.
user_pref("browser.download.useDownloadDir", false);

// Disable downloading on desktop.
user_pref("browser.download.folderList", 2);

// Always display download button on toolbar.
user_pref("browser.download.autohideButton", false);

// Disable "Open with" in download dialog.
user_pref("browser.download.forbid_open_with", true);

// Disable disk cache.
user_pref("browser.cache.disk.enable", false);
user_pref("browser.cache.disk.smart_size.enabled", false);
user_pref("browser.cache.disk.smart_size.first_run", false);
user_pref("browser.cache.disk.capacity", 0);
user_pref("browser.cache.disk_cache_ssl", false);

// Set the minimum interval between session save operations.
user_pref("browser.sessionstore.interval", 600000);

// Disable location bar domain guessing.
user_pref("browser.fixup.alternate.enabled", false);

// Display all parts of the URL in the location bar.
user_pref("browser.urlbar.trimURLs", false);

// Disable search suggestions in the location bar.
user_pref("browser.urlbar.suggest.searches", false);
user_pref("browser.urlbar.userMadeSearchSuggestionsChoice", true);
user_pref("browser.urlbar.oneOffSearches", false);

// Disable location bar making speculative connections.
user_pref("browser.urlbar.speculativeConnect.enabled", false);
user_pref("browser.urlbar.usepreloadedtopurls.enabled", false);

// Display the raw Punycode in the location bar.
user_pref("network.IDN_show_punycode", true);

// Disable closing browser with last tab.
user_pref("browser.tabs.closeWindowWithLastTab", false);

// Disable privacy items in Windows Taskbar Jump List.
user_pref("browser.taskbar.lists.frequent.enabled", false);
user_pref("browser.taskbar.lists.recent.enabled", false);

// Disable capturing page thumbnails.
user_pref("browser.pagethumbnails.capturing_disabled", true)

// Disable site specific zoom.
user_pref("browser.zoom.siteSpecific", false);

// Disable UI animation.
user_pref("toolkit.cosmeticAnimations.enabled", false);

// Disable Alt key toggling the menu bar.
user_pref("ui.key.menuAccessKey", 0);
<|endoftext|>


<commit_before>const Signal = require('signals')
const Vec3 = require('pex-math/Vec3')
const Mat4 = require('pex-math/Mat4')

function Morph (opts) {
  this.type = 'Morph'
  this.entity = null
  this.targets = opts.targets || []
  this.weights = opts.weights || []
  this.changed = new Signal()
  this.set(opts)
}

Morph.prototype.init = function (entity) {
  this.entity = entity
  let geom = this.entity.getComponent('Geometry')
  this.originalPositions = geom.positions.slice(0)
}

Morph.prototype.set = function (opts) {
  Object.assign(this, opts)
  Object.keys(opts).forEach((prop) => this.changed.dispatch(prop))
}

Morph.prototype.update = function () {
  let geom = this.entity.getComponent('Geometry')
  // this.targets.forEach((target, i) => {
  //   let weight = this.weights[i]
  //   let newGeom = this.originalPositions.map((pos, k) => {
  //     let targetVertex = target[k]
  //     let morphedVertex = (targetVertex - pos) * weight
  //     morphedVertex += pos
  //     return morphedVertex
  //   })
  //   geom.set({
  //     positions: newGeom
  //   })
  // })
  let newGeom = this.originalPositions.map((pos, i) => {
    let newPos = 0
    let originalPos = this.originalPositions[i]
    this.targets.forEach((target, k) => {
      let weight = this.weights[k]
      let targetVertex = target[i]
      let morphedVertex = (targetVertex - originalPos) * weight
      newPos += morphedVertex
    })
    newPos += originalPos
    return newPos
  })
  geom.set({
    positions: newGeom
  })
}

module.exports = function createMorph (opts) {
  return new Morph(opts)
}
<commit_msg>Update morph on demand and add support for Vec3 vertices<commit_after>const Signal = require('signals')

function Morph (opts) {
  this.type = 'Morph'
  this.entity = null
  this.dirty = false
  this.targets = opts.targets || []
  this.weights = opts.weights || []
  this.changed = new Signal()
  this.set(opts)
}

Morph.prototype.init = function (entity) {
  this.entity = entity
  let geom = this.entity.getComponent('Geometry')
  this.originalPositions = geom.positions.slice(0)
}

Morph.prototype.set = function (opts) {
  Object.assign(this, opts)
  Object.keys(opts).forEach((prop) => this.changed.dispatch(prop))
  this.dirty = true
}

Morph.prototype.update = function () {
  if (!this.dirty) return
  this.dirty = false

  let geom = this.entity.getComponent('Geometry')
  let newGeom = this.originalPositions.map((pos, i) => {
    let newPos = 0
    if (pos.length) {
      newPos = [0, 0, 0]
    }
    let originalPos = this.originalPositions[i]
    this.targets.forEach((target, k) => {
      let weight = this.weights[k]
      let targetVertex = target[i]
      if (pos.length) {
        newPos[0] += targetVertex[0] * weight
        newPos[1] += targetVertex[1] * weight
        newPos[2] += targetVertex[2] * weight
      } else {
        newPos += targetVertex * weight
      }
    })
    if (pos.length) {
      newPos[0] += originalPos[0]
      newPos[1] += originalPos[1]
      newPos[2] += originalPos[2]
    } else {
      newPos += originalPos
    }
    return newPos
  })
  geom.set({
    positions: newGeom
  })
}

module.exports = function createMorph (opts) {
  return new Morph(opts)
}
<|endoftext|>


<commit_before>// Call Congress
(function() {
  function validatePhoneNumber(phone) {
    // Remove spaces, parentheses, dashes
    phone = phone.replace(/\s/g, '')
      .replace(/\(/g, '')
      .replace(/\)/g, '')
      .replace(/\-/g, '');

    // Remove country code
    // TODO: Add support for non-US country codes on backend?
    phone = phone.replace('+', '')
    if (phone.charAt(0) == '1') phone = phone.substr(1);

    // Return formatted phone number if valid
    return phone.length == 10 ? phone : false;
  }

  var callCongress = document.getElementById('fftf-call-congress');

  callCongress.addEventListener('submit', function(e) {
    e.preventDefault();

    var phone = validatePhoneNumber(e.currentTarget.querySelector('input[type=text]').value);

    if (!phone) return alert('Please enter a valid US phone number!');

    var data = new FormData();
    data.append('campaignId', 'battleforthenet-2017');
    data.append('userPhone', phone);

    var xhr = new XMLHttpRequest();

    function logStatus(e) {
      if (e.type == 'error') {
        alert('If you are using Privacy Badger or another ad blocker, please allow requests to https://call-congress.fightforthefuture.org and try again.')
      }

      if (xhr.status) console.log(xhr.status, xhr.statusText);
    }

    xhr.addEventListener('load', logStatus);
    xhr.addEventListener('error', logStatus);

    xhr.open('post', 'https://call-congress.fightforthefuture.org/create', true);
    xhr.send(data);

    document.getElementById("call-congress-form").style.display = "none";
    document.getElementById("phone-script").style.display = "block";

    // new CallActionModal();
  });
})();
<commit_msg>Don’t use FormData because its no supported everywhere<commit_after>// Call Congress
(function() {
  function validatePhoneNumber(phone) {
    // Remove spaces, parentheses, dashes
    phone = phone.replace(/\s/g, '')
      .replace(/\(/g, '')
      .replace(/\)/g, '')
      .replace(/\-/g, '');

    // Remove country code
    // TODO: Add support for non-US country codes on backend?
    phone = phone.replace('+', '')
    if (phone.charAt(0) == '1') phone = phone.substr(1);

    // Return formatted phone number if valid
    return phone.length == 10 ? phone : false;
  }

  var callCongress = document.getElementById('fftf-call-congress');

  callCongress.addEventListener('submit', function(e) {
    e.preventDefault();

    var phone = validatePhoneNumber(e.currentTarget.querySelector('input[type=text]').value);

    if (!phone) return alert('Please enter a valid US phone number!');

    var xhr = new XMLHttpRequest();

    function logStatus(e) {
      if (e.type == 'error') {
        alert('If you are using Privacy Badger or another ad blocker, please allow requests to https://call-congress.fightforthefuture.org and try again.')
      }

      if (xhr.status) console.log(xhr.status, xhr.statusText);
    }

    xhr.addEventListener('load', logStatus);
    xhr.addEventListener('error', logStatus);

    xhr.open('post', 'https://call-congress.fightforthefuture.org/create', true);
    xhr.send("campaignId=battleforthenet-2017&userPhone=" + phone);

    document.getElementById("call-congress-form").style.display = "none";
    document.getElementById("phone-script").style.display = "block";

    // new CallActionModal();
  });
})();
<|endoftext|>


<commit_before>CT.align = {
	"absers": [],
	"fullscreeners": [],
	"centerscreeners": [],

	"_setLeft": function(n, x) {
	    // 8px padding
	    return n.style.left = (8 + x) + "px";
	},
	"absed": function(node, leftpx, toppx) {
	    node.style.position = "absolute";
	    CT.align._setLeft(node, leftpx);
	    if (toppx != null)
	        node.style.top = toppx + "px";
	    CT.align.absers.push([node, leftpx]);
	    return node;
	},
	"centernode": function(n) {
	    if (n.scrollHeight) {
	        n.origScrollHeight = n.origScrollHeight || n.scrollHeight;
	        var parentHeight = CT.align.height();
	        var paddedHeight = parentHeight - 100;
	        var nodeHeight = parseInt(n.style.height) || n.origScrollHeight;
	        if (nodeHeight > paddedHeight || nodeHeight < n.origScrollHeight) {
	            nodeHeight = Math.min(n.origScrollHeight, paddedHeight);
	            n.style.height = nodeHeight + "px";
	        }
	        n.style.top = (parentHeight - nodeHeight) / 2 + "px";
	        n.style.left = (CT.align.width() / 2) - (n.clientWidth / 2) + "px";
	    }
	},
	"centerall": function() {
	    for (var i = 0; i < CT.align.centerscreeners.length; i++)
	        CT.align.centernode(CT.align.centerscreeners[i]);
	},
	"centered": function(n) {
	    n.style.position = "fixed";
	    CT.align.centernode(n);
	    if (CT.align.centerscreeners.indexOf(n) == -1)
	        CT.align.centerscreeners.push(n);
	    return n;
	},
	"resize": function() {
		var abs = CT.align.absers;
	    for (var i = 0; i < abs.length; i++)
	    	CT.align._setLeft(abs[i][0], abs[i][1]);
	    CT.align.centerall();
	},

	// position finders -- merge?
	"position": function(obj) {
	    var curleft = curtop = 0;
	    if (obj.offsetParent) do {
	        curleft += obj.offsetLeft;
	        curtop += obj.offsetTop;
	    } while (obj = obj.offsetParent);
	    return [curleft, curtop];
	},
	"offset": function(n) {
	    var o = {
	        "top": 0,
	        "left": 0
	    };
	    while (n) {
	        if (n == (CT.dom.ALLNODE || document.body))
	            break;
	        o["top"] += n.offsetTop;
	        o["left"] += n.offsetLeft;
	        n = n.offsetParent;
	    }
	    return o;
	},

	"height": function() {
	    return window.innerHeight || document.body.clientHeight;
	},
	"width": function() {
	    return window.innerWidth || document.body.clientWidth;
	}
};<commit_msg>CT.align.absed(): support all 4 coordinate origins<commit_after>CT.align = {
	"absers": [],
	"fullscreeners": [],
	"centerscreeners": [],

	"_setLeft": function(n, x) {
	    // 8px padding
	    return n.style.left = (8 + x) + "px";
	},
	"absed": function(node, leftpx, toppx, rightpx, bottompx) {
	    node.style.position = "absolute";
	    CT.align._setLeft(node, leftpx);
	    if (toppx != null)
	        node.style.top = toppx + "px";
	    if (rightpx != null)
	        node.style.right = rightpx + "px";
	    if (bottompx != null)
	        node.style.bottom = bottompx + "px";
	    CT.align.absers.push([node, leftpx, toppx, rightpx, bottompx]);
	    return node;
	},
	"centernode": function(n) {
	    if (n.scrollHeight) {
	        n.origScrollHeight = n.origScrollHeight || n.scrollHeight;
	        var parentHeight = CT.align.height();
	        var paddedHeight = parentHeight - 100;
	        var nodeHeight = parseInt(n.style.height) || n.origScrollHeight;
	        if (nodeHeight > paddedHeight || nodeHeight < n.origScrollHeight) {
	            nodeHeight = Math.min(n.origScrollHeight, paddedHeight);
	            n.style.height = nodeHeight + "px";
	        }
	        n.style.top = (parentHeight - nodeHeight) / 2 + "px";
	        n.style.left = (CT.align.width() / 2) - (n.clientWidth / 2) + "px";
	    }
	},
	"centerall": function() {
	    for (var i = 0; i < CT.align.centerscreeners.length; i++)
	        CT.align.centernode(CT.align.centerscreeners[i]);
	},
	"centered": function(n) {
	    n.style.position = "fixed";
	    CT.align.centernode(n);
	    if (CT.align.centerscreeners.indexOf(n) == -1)
	        CT.align.centerscreeners.push(n);
	    return n;
	},
	"resize": function() {
		var abs = CT.align.absers;
	    for (var i = 0; i < abs.length; i++)
	    	CT.align._setLeft(abs[i][0], abs[i][1]);
	    CT.align.centerall();
	},

	// position finders -- merge?
	"position": function(obj) {
	    var curleft = curtop = 0;
	    if (obj.offsetParent) do {
	        curleft += obj.offsetLeft;
	        curtop += obj.offsetTop;
	    } while (obj = obj.offsetParent);
	    return [curleft, curtop];
	},
	"offset": function(n) {
	    var o = {
	        "top": 0,
	        "left": 0
	    };
	    while (n) {
	        if (n == (CT.dom.ALLNODE || document.body))
	            break;
	        o["top"] += n.offsetTop;
	        o["left"] += n.offsetLeft;
	        n = n.offsetParent;
	    }
	    return o;
	},

	"height": function() {
	    return window.innerHeight || document.body.clientHeight;
	},
	"width": function() {
	    return window.innerWidth || document.body.clientWidth;
	}
};<|endoftext|>


<commit_before>/*
This module contains functions for generating lists of items that,
when clicked, show corresponding content or trigger corresponding
logic. Here are three examples.

### CT.panel.simple(pnames, keystring, itemnode, panelnode, cbs)
This function wraps CT.panel.load(), supporting a subset of load()'s
options (the simple ones). It supports the following args, of which
only the first is required:

    - pnames (string array): short for 'panel names'
    - keystring (string, default: 'sb'): identifier for collections of content/lister nodes
    - itemnode (node, default: CT.dom.id(keystring + "items")): parent node for lister items
    - panelnode (node, default: CT.dom.id(keystring + "panels")): parent node for content panels
    - cbs (function array, optional): callbacks to invoke on lister item click post- panel swap

### CT.panel.pager(getContent, request, limit, colClass, dataClass, ks)
This function generates and returns a node containing a
(CT.Pager-backed) paging lister node and a corresponding content panel.

    - getContent (function): combined with CT.panel.simple() in pager's renderCb
    - request (function): pager's requestCb
    - limit (number, default: 20): pager's limit (chunk size)
    - colClass (string, optional): class of generated pager (list selector) node
    - dataClass (string, optional): class of generated data (content panel) node
    - ks (string, default: "p" + CT.Pager._id): keystring of data and list nodes

### CT.panel.triggerList(data, cb, node)
This function fills the 'node' node with a list of clickable items, each of
which triggers cb(d), where d is the corresponding object in the 'data' array.

    - data (object array): data set used to generate list
      - for d in data: link content equals d.label || d.title
    - cb (function): the callback to invoke when an item is clicked
    - node (node): the list parent node
*/

CT.panel = {
	"lastClicked": {},
	"accordion": function(data, triggerCb, activeClass, renderCb, condition, notick) {
		var t, n = CT.dom.node(null, null, "tabbed pointer");
		data.forEach(function(d) {
			if (d.children) {
				var tick = CT.dom.node(">", "div", "inline-block ph10" + (notick && " hidden" || ""));
				t = CT.dom.node([tick, CT.dom.node(d.name, "div", "inline-block")]);
				n.appendChild(t);
				new CT.Drop({
					anchor: t,
					parent: n,
					relative: true,
					constrained: false,
					content: CT.panel.accordion(d.children, triggerCb, activeClass, renderCb, condition),
					onShow: function() {
						CT.trans.rotate(tick, { duration: 100, degrees: 90 });
					},
					onHide: function() {
						CT.trans.rotate(tick, { duration: 100, degrees: 0 });
					}
				});
			} else {
				t = CT.panel.trigger(d, triggerCb, activeClass, renderCb, condition);
				n.appendChild(t);
			}
		});
		return n;
	},
	"trigger": function(data, cb, activeClass, content, condition) {
		var label = data.label || data.title || data.name || data,
			n = CT.dom.node(null, "div", null, label && ("tl" + label));
		activeClass = activeClass || "activetab";
		n.data = data;
		n.select = function() {
			CT.dom.each(n.parentNode, function(c) {
				if (c == n)
					c.classList.add(activeClass);
				else
					c.classList.remove(activeClass);
				c.className = (c == n) ? activeClass : "";
			});
		};
		n.trigger = function() {
			if (condition && !condition()) return;
			n.select();
			cb(data);
		};
		n.appendChild(CT.dom.link(content ? content(data) : label, n.trigger));
		return n;
	},
	"triggerList": function(data, cb, node, activeClass, content, condition) {
		node = node || CT.dom.node();
		data.forEach(function(d) {
			node.appendChild(CT.panel.trigger(d, cb, activeClass, content, condition));
		});
		return node;
	},
	"swap": function(key, trysidepanel, keystring, noitem) {
	    keystring = keystring || "sb";
	    key = key.replace(/ /g, "");
	    var items = CT.dom.className(keystring + "panel");
	    for (var i = 0; i < items.length; i++)
	        items[i].className = keystring + "panel hidden";
	    CT.dom.id(keystring +  "panel" + key, true).className = keystring + "panel";
	    if (trysidepanel) {
	        var n = CT.dom.id(keystring + "panel" + key + "Side");
	        if (n) n.className = keystring+"panel";
	        var n2 = CT.dom.id(keystring + "panel" + key + "Side2");
	        if (n2) n2.className = keystring + "panel";
	    }
	    if (!noitem)
	        CT.panel.select(key, keystring);
	},
	"drill": function(keymap) { // keystring: key
		for (var k in keymap)
			CT.panel.swap(keymap[k], false, k);
	},
	"select": function(key, keystring) {
	    keystring = keystring || "sb";
	    var items = CT.dom.className(keystring + "item");
	    for (var i = 0; i < items.length; i++)
	        items[i].className = items[i].className.replace(" activetab", "");
	    if (key) {
	        CT.dom.id(keystring + "item" + key, true).className += " activetab";
	        CT.panel.lastClicked[keystring] = key;
	    }
	    CT.mobile && CT.mobile.mobileSnap();
	},
	"selectLister": function(newkey, oldkey, newhtml) {
	    if (oldkey)
	        CT.dom.id("ll" + oldkey).className = "pointer";
	    var newnode = CT.dom.id("ll" + newkey);
	    newnode.className = "pointer activetab";
	    if (newhtml)
	        newnode.firstChild.innerHTML = newhtml;
	    CT.mobile && CT.mobile.mobileSnap();
	},
	"add": function(key, trysidepanel, keystring, itemnode, panelnode, nospace, icon, cb, condition) {
	    nospace = nospace || key.replace(/ /g, "");
	    keystring = keystring || "sb";
	    if (!CT.dom.id(keystring + "panel" + nospace)) {
	        var n = CT.dom.node("", "div", keystring + "panel hidden", keystring + "panel" + nospace);
	        n.appendChild(CT.dom.node(key, "div", "bigger blue bold bottompadded"));
	        n.appendChild(CT.dom.node("", "div", "", keystring+"content"+nospace));
	        (panelnode || CT.dom.id(keystring+"panels")).appendChild(n);
	    }
	    var i = CT.dom.id(keystring+"item"+nospace);
	    if (i)
	        i.style.display = "block";
	    else {
	        itemnode = itemnode || CT.dom.id(keystring+"items");
	        var clickfunc = function() {
	        	if (condition && !condition()) return;
	            CT.panel.swap(nospace, trysidepanel, keystring);
	            cb && cb();
	        };
	        if (icon)
	            itemnode.appendChild(CT.dom.node(CT.dom.img(icon,
	                null, clickfunc), "div", "lfloat shiftleft"));
	        itemnode.appendChild(CT.dom.node(CT.dom.link(key, clickfunc),
	            "div", keystring + "item pointer", keystring + "item" + nospace));
	        if (icon)
	            itemnode.appendChild(CT.dom.node("", "div", "clearnode"));
	    }
	},
	"rename": function(oldkey, newkey, keystring, item2keystring) {
		keystring = keystring || "sb";
		var nsold = oldkey.replace(/ /g, ""),
			nsnew = newkey.replace(/ /g, ""),
			item = CT.dom.id(keystring + "item" + nsold),
			panel = CT.dom.id(keystring + "panel" + nsold),
			content = CT.dom.id(keystring + "content" + nsold);
		item.id = keystring + "item" + nsnew;
		panel.id = keystring + "panel" + nsnew;
		if (content) content.id = keystring + "content" + nsnew;
		item.firstChild.innerHTML = panel.firstChild.innerHTML = newkey;
		if (item2keystring) {
			var item2 = CT.dom.id(item2keystring + "item" + nsold);
			item2.id = nsnew;
			item2.firstChild.innerHTML = newkey;
		}
	},
	"remove": function(key, keystring) {
	    var nospace = key.replace(/ /g, "");
	    var p = CT.dom.id(keystring+"panel"+nospace);
	    if (p) p.style.display = "none";
	    var l = CT.dom.id(keystring+"item"+nospace);
	    if (l) l.style.display = "none";
	},
	"load": function(pnames, trysidepanel, keystring, itemnode, panelnode, nospaces, icons, noclear, stillswap, cbs, condition) {
	    keystring = keystring || "sb";
	    if (!noclear)
	        (itemnode || CT.dom.id(keystring+"items")).innerHTML = "";
	    for (var i = 0; i < pnames.length; i++)
	        CT.panel.add(pnames[i], trysidepanel, keystring, itemnode,
	            panelnode, nospaces && nospaces[i] || null,
	            icons && icons[i] || null, cbs && cbs[i] || null, condition);
	    if (pnames.length && (stillswap || (!itemnode && !noclear)))
	        CT.panel.swap(pnames[0], trysidepanel, keystring);
	},
	"simple": function(pnames, keystring, itemnode, panelnode, cbs, condition) {
		CT.panel.load(pnames, null, keystring, itemnode, panelnode, null, null, null, true, cbs, condition);
	},
	"pager": function(getContent, request, limit, colClass, dataClass, ks) {
		var keystring = ks || ("p" + CT.Pager._id),
			content = CT.dom.node(null, null, dataClass, keystring + "panels"),
			sideBar = CT.dom.node(),
			pager = new CT.Pager(function(data) {
				var dnames = data.map(function(d) { return d.label; });
				CT.panel.simple(dnames, keystring, sideBar, content);
				data.forEach(function(d) {
					CT.dom.setContent(CT.dom.id(keystring + "content"
						+ d.label.replace(/ /g, ""),
						true), getContent(d));
				});
				return sideBar;
			}, request, limit, colClass, keystring + "items"),
			n = CT.dom.node([pager.node, content]);
		n.pager = pager;
		return n;
	},
	"alternatebg": function(n, watchforicons, resetonbreak) {
	    n = n || document.getElementById("sbitems");
	    if (watchforicons) {
	        var switcher = 1;
	        for (var i = 0; i < n.childNodes.length; i++) {
	            var c = n.childNodes[i];
	            if (c.className.indexOf("lfloat") == -1 && c.className.indexOf("clearnode") == -1) {
	                if (resetonbreak && c.nodeName == "BR") {
	                    switcher = 1;
	                    continue;
	                }
	                if (switcher == 1)
	                    c.style.background = "#899fb0";
	                switcher *= -1;
	            }
	        }
	    }
	    else {
	        for (var i = 0; i < n.childNodes.length; i+=2)
	            n.childNodes[i].style.background = "#899fb0";
	    }
	}
};<commit_msg>CT.panel.accordion(): support content() cb; minor tweaks<commit_after>/*
This module contains functions for generating lists of items that,
when clicked, show corresponding content or trigger corresponding
logic. Here are three examples.

### CT.panel.simple(pnames, keystring, itemnode, panelnode, cbs)
This function wraps CT.panel.load(), supporting a subset of load()'s
options (the simple ones). It supports the following args, of which
only the first is required:

    - pnames (string array): short for 'panel names'
    - keystring (string, default: 'sb'): identifier for collections of content/lister nodes
    - itemnode (node, default: CT.dom.id(keystring + "items")): parent node for lister items
    - panelnode (node, default: CT.dom.id(keystring + "panels")): parent node for content panels
    - cbs (function array, optional): callbacks to invoke on lister item click post- panel swap

### CT.panel.pager(getContent, request, limit, colClass, dataClass, ks)
This function generates and returns a node containing a
(CT.Pager-backed) paging lister node and a corresponding content panel.

    - getContent (function): combined with CT.panel.simple() in pager's renderCb
    - request (function): pager's requestCb
    - limit (number, default: 20): pager's limit (chunk size)
    - colClass (string, optional): class of generated pager (list selector) node
    - dataClass (string, optional): class of generated data (content panel) node
    - ks (string, default: "p" + CT.Pager._id): keystring of data and list nodes

### CT.panel.triggerList(data, cb, node)
This function fills the 'node' node with a list of clickable items, each of
which triggers cb(d), where d is the corresponding object in the 'data' array.

    - data (object array): data set used to generate list
      - for d in data: link content equals d.label || d.title
    - cb (function): the callback to invoke when an item is clicked
    - node (node): the list parent node
*/

CT.panel = {
	"lastClicked": {},
	"accordion": function(data, triggerCb, activeClass, content, condition, notick) {
		var t, n = CT.dom.node(null, null, "tabbed pointer");
		data.forEach(function(d) {
			if (d.children) {
				var tick = CT.dom.node(">", "div", "inline-block" + (notick && " hidden" || ""));
				t = CT.dom.node([tick, CT.dom.node(content ? content(d) : d.name, "div", "inline-block")]);
				n.appendChild(t);
				new CT.Drop({
					anchor: t,
					parent: n,
					relative: true,
					constrained: false,
					setNodeClick: false,
					content: CT.panel.accordion(d.children, triggerCb, activeClass, content, condition, notick),
					onShow: function() {
						CT.trans.rotate(tick, { duration: 100, degrees: 90 });
					},
					onHide: function() {
						CT.trans.rotate(tick, { duration: 100, degrees: 0 });
					}
				});
			} else {
				t = CT.panel.trigger(d, triggerCb, activeClass, content, condition);
				n.appendChild(t);
			}
		});
		return n;
	},
	"trigger": function(data, cb, activeClass, content, condition) {
		var label = data.label || data.title || data.name || data,
			n = CT.dom.node(null, "div", null, label && ("tl" + label));
		activeClass = activeClass || "activetab";
		n.data = data;
		n.select = function() {
			CT.dom.each(n.parentNode, function(c) {
				if (c == n)
					c.classList.add(activeClass);
				else
					c.classList.remove(activeClass);
				c.className = (c == n) ? activeClass : "";
			});
		};
		n.trigger = function() {
			if (condition && !condition()) return;
			n.select();
			cb(data);
		};
		n.appendChild(CT.dom.link(content ? content(data) : label, n.trigger));
		return n;
	},
	"triggerList": function(data, cb, node, activeClass, content, condition) {
		node = node || CT.dom.node();
		data.forEach(function(d) {
			node.appendChild(CT.panel.trigger(d, cb, activeClass, content, condition));
		});
		return node;
	},
	"swap": function(key, trysidepanel, keystring, noitem) {
	    keystring = keystring || "sb";
	    key = key.replace(/ /g, "");
	    var items = CT.dom.className(keystring + "panel");
	    for (var i = 0; i < items.length; i++)
	        items[i].className = keystring + "panel hidden";
	    CT.dom.id(keystring +  "panel" + key, true).className = keystring + "panel";
	    if (trysidepanel) {
	        var n = CT.dom.id(keystring + "panel" + key + "Side");
	        if (n) n.className = keystring+"panel";
	        var n2 = CT.dom.id(keystring + "panel" + key + "Side2");
	        if (n2) n2.className = keystring + "panel";
	    }
	    if (!noitem)
	        CT.panel.select(key, keystring);
	},
	"drill": function(keymap) { // keystring: key
		for (var k in keymap)
			CT.panel.swap(keymap[k], false, k);
	},
	"select": function(key, keystring) {
	    keystring = keystring || "sb";
	    var items = CT.dom.className(keystring + "item");
	    for (var i = 0; i < items.length; i++)
	        items[i].className = items[i].className.replace(" activetab", "");
	    if (key) {
	        CT.dom.id(keystring + "item" + key, true).className += " activetab";
	        CT.panel.lastClicked[keystring] = key;
	    }
	    CT.mobile && CT.mobile.mobileSnap();
	},
	"selectLister": function(newkey, oldkey, newhtml) {
	    if (oldkey)
	        CT.dom.id("ll" + oldkey).className = "pointer";
	    var newnode = CT.dom.id("ll" + newkey);
	    newnode.className = "pointer activetab";
	    if (newhtml)
	        newnode.firstChild.innerHTML = newhtml;
	    CT.mobile && CT.mobile.mobileSnap();
	},
	"add": function(key, trysidepanel, keystring, itemnode, panelnode, nospace, icon, cb, condition) {
	    nospace = nospace || key.replace(/ /g, "");
	    keystring = keystring || "sb";
	    if (!CT.dom.id(keystring + "panel" + nospace)) {
	        var n = CT.dom.node("", "div", keystring + "panel hidden", keystring + "panel" + nospace);
	        n.appendChild(CT.dom.node(key, "div", "bigger blue bold bottompadded"));
	        n.appendChild(CT.dom.node("", "div", "", keystring+"content"+nospace));
	        (panelnode || CT.dom.id(keystring+"panels")).appendChild(n);
	    }
	    var i = CT.dom.id(keystring+"item"+nospace);
	    if (i)
	        i.style.display = "block";
	    else {
	        itemnode = itemnode || CT.dom.id(keystring+"items");
	        var clickfunc = function() {
	        	if (condition && !condition()) return;
	            CT.panel.swap(nospace, trysidepanel, keystring);
	            cb && cb();
	        };
	        if (icon)
	            itemnode.appendChild(CT.dom.node(CT.dom.img(icon,
	                null, clickfunc), "div", "lfloat shiftleft"));
	        itemnode.appendChild(CT.dom.node(CT.dom.link(key, clickfunc),
	            "div", keystring + "item pointer", keystring + "item" + nospace));
	        if (icon)
	            itemnode.appendChild(CT.dom.node("", "div", "clearnode"));
	    }
	},
	"rename": function(oldkey, newkey, keystring, item2keystring) {
		keystring = keystring || "sb";
		var nsold = oldkey.replace(/ /g, ""),
			nsnew = newkey.replace(/ /g, ""),
			item = CT.dom.id(keystring + "item" + nsold),
			panel = CT.dom.id(keystring + "panel" + nsold),
			content = CT.dom.id(keystring + "content" + nsold);
		item.id = keystring + "item" + nsnew;
		panel.id = keystring + "panel" + nsnew;
		if (content) content.id = keystring + "content" + nsnew;
		item.firstChild.innerHTML = panel.firstChild.innerHTML = newkey;
		if (item2keystring) {
			var item2 = CT.dom.id(item2keystring + "item" + nsold);
			item2.id = nsnew;
			item2.firstChild.innerHTML = newkey;
		}
	},
	"remove": function(key, keystring) {
	    var nospace = key.replace(/ /g, "");
	    var p = CT.dom.id(keystring+"panel"+nospace);
	    if (p) p.style.display = "none";
	    var l = CT.dom.id(keystring+"item"+nospace);
	    if (l) l.style.display = "none";
	},
	"load": function(pnames, trysidepanel, keystring, itemnode, panelnode, nospaces, icons, noclear, stillswap, cbs, condition) {
	    keystring = keystring || "sb";
	    if (!noclear)
	        (itemnode || CT.dom.id(keystring+"items")).innerHTML = "";
	    for (var i = 0; i < pnames.length; i++)
	        CT.panel.add(pnames[i], trysidepanel, keystring, itemnode,
	            panelnode, nospaces && nospaces[i] || null,
	            icons && icons[i] || null, cbs && cbs[i] || null, condition);
	    if (pnames.length && (stillswap || (!itemnode && !noclear)))
	        CT.panel.swap(pnames[0], trysidepanel, keystring);
	},
	"simple": function(pnames, keystring, itemnode, panelnode, cbs, condition) {
		CT.panel.load(pnames, null, keystring, itemnode, panelnode, null, null, null, true, cbs, condition);
	},
	"pager": function(getContent, request, limit, colClass, dataClass, ks) {
		var keystring = ks || ("p" + CT.Pager._id),
			content = CT.dom.node(null, null, dataClass, keystring + "panels"),
			sideBar = CT.dom.node(),
			pager = new CT.Pager(function(data) {
				var dnames = data.map(function(d) { return d.label; });
				CT.panel.simple(dnames, keystring, sideBar, content);
				data.forEach(function(d) {
					CT.dom.setContent(CT.dom.id(keystring + "content"
						+ d.label.replace(/ /g, ""),
						true), getContent(d));
				});
				return sideBar;
			}, request, limit, colClass, keystring + "items"),
			n = CT.dom.node([pager.node, content]);
		n.pager = pager;
		return n;
	},
	"alternatebg": function(n, watchforicons, resetonbreak) {
	    n = n || document.getElementById("sbitems");
	    if (watchforicons) {
	        var switcher = 1;
	        for (var i = 0; i < n.childNodes.length; i++) {
	            var c = n.childNodes[i];
	            if (c.className.indexOf("lfloat") == -1 && c.className.indexOf("clearnode") == -1) {
	                if (resetonbreak && c.nodeName == "BR") {
	                    switcher = 1;
	                    continue;
	                }
	                if (switcher == 1)
	                    c.style.background = "#899fb0";
	                switcher *= -1;
	            }
	        }
	    }
	    else {
	        for (var i = 0; i < n.childNodes.length; i+=2)
	            n.childNodes[i].style.background = "#899fb0";
	    }
	}
};<|endoftext|>