<commit_before>const choo = require('choo');
const html = require('choo/html');
const http = require('xhr');
const find = require('lodash/find');
const queryString = require('query-string');
const store = require('./utils/localstorage.js');
const scrollIntoView = require('scroll-into-view');

const app = choo();
// const appURL = 'https://5calls.org';
const debug = false;
const appURL = 'http://localhost:8090';

// get the stored zip location
cachedAddress = '';
store.getAll('org.5calls.location', (location) => {
  if (location.length > 0) {
   cachedAddress = location[0]
  }
});

// get the stored geo location
cachedGeo = '';
store.getAll('org.5calls.geolocation', (geo) => {
  if (geo.length > 0) {
    console.log("geo get",geo[0]);
    cachedGeo = geo[0]
  }
});

// get the stored geo location
cachedAllowBrowserGeo = null;
store.getAll('org.5calls.allow_geolocation', (allowGeo) => {
  if (allowGeo.length > 0) {
    console.log("allowGeo get",allowGeo[0]);
    cachedAllowBrowserGeo = allowGeo[0]
  }
});

// get the time the geo was last fetched
cachedGeoTime = '';
store.getAll('org.5calls.geolocation_time', (geo) => {
  if (geo.length > 0) {
    console.log("geo time get",geo[0]);
    cachedGeoTime = geo[0]
  }
});

cachedCity = '';
store.getAll('org.5calls.geolocation_city', (city) => {
  if (city.length > 0) {
    console.log("city get",city[0]);
    cachedCity = city[0]
  }
});

// get the stored completed issues
completedIssues = [];
store.getAll('org.5calls.completed', (completed) => {
  completedIssues = completed == null ? [] : completed;
});

app.model({
  state: {
    // remote data
    issues: [],
    totalCalls: 0,
    splitDistrict: false,

    // manual input address
    address: cachedAddress,

    // automatically geolocating
    geolocation: cachedGeo,
    geoCacheTime: cachedGeoTime,
    allowBrowserGeo: cachedAllowBrowserGeo,
    cachedCity: cachedCity,

    // view state
    // getInfo: false,
    // activeIssue: false,
    // completeIssue: false,
    askingLocation: false,
    fetchingLocation: false,
    locationFetchType: null,
    contactIndex: 0,
    completedIssues: completedIssues,

    debug: debug,
  },

  reducers: {
    receiveIssues: (state, data) => {
      response = JSON.parse(data)
      issues = response.issues //.filter((v) => { return v.contacts.length > 0 });
      return { issues: issues, splitDistrict: response.splitDistrict }
    },
    receiveTotals: (state, data) => {
      totals = JSON.parse(data);
      return { totalCalls: totals.count }
    },
    receiveIPInfoLoc: (state, data) => {
      try {
        response = JSON.parse(data)
        if (response.city != "") {
          geo = response.loc
          city = response.city
          time = new Date().valueOf()
          store.replace("org.5calls.geolocation", 0, geo, () => {});
          store.replace("org.5calls.geolocation_city", 0, city, () => {});
          store.replace("org.5calls.geolocation_time", 0, time, () => {});
          return { geolocation: geo, cachedCity: city, geoCacheTime: time, askingLocation: false }
        } else {
          Raven.captureMessage("Location with no city: "+response.loc, { level: 'warning' });
        }
      } catch(e) {
        Raven.setExtraContext({ json: data })
        Raven.captureMessage("Couldn't parse ipinfo json", { level: 'error' });
      }
    },
    changeActiveIssue: (state, issueId) => {
      return { contactIndex: 0 }
    },
    setContactIndex: (state, data) => {
      if (data.newIndex != 0) {
        return { contactIndex: data.newIndex }
      } else {
        store.add("org.5calls.completed", data.issueid, () => {})
        return { contactIndex: 0, completedIssues: state.completedIssues.concat(data.issueid) }
      }
    },
    setAddress: (state, address) => {
      Raven.setExtraContext({ address: address })
      store.replace("org.5calls.location", 0, address, () => {});

      return { address: address, askingLocation: false }
    },
    setGeolocation: (state, data) => {
      store.replace("org.5calls.geolocation", 0, data, () => {});
      return { geolocation: data, askingLocation: false }
    },
    setCachedCity: (state, data) => {
      response = JSON.parse(data);
      console.log(state.cachedCity);
      if (response.normalizedLocation && state.cachedCity == '') {
        store.replace("org.5calls.geolocation_city", 0, response.normalizedLocation, () => {});
        return { cachedCity: response.normalizedLocation }
      } else {
        return null
      }
    },
    fetchingLocation: (state, data) => {
      return { fetchingLocation: data }
    },
    allowBrowserGeolocation: (state, data) => {
      store.replace("org.5calls.allow_geolocation", 0, data, () => {})
      return { allowBrowserGeo: data }
    },
    enterLocation: (state, data) => {
      return { askingLocation: true }
    },
    setLocationFetchType: (state, data) => {
      return { locationFetchType: data, askingLocation: true }
    },
    resetLocation: (state, data) => {
      store.remove("org.5calls.location", () => {});
      store.remove("org.5calls.geolocation", () => {});
      store.remove("org.5calls.geolocation_city", () => {});
      store.remove("org.5calls.geolocation_time", () => {});
      return { address: '', geolocation: '', cachedCity: '', geoCacheTime: '' }
    },
    resetCompletedIssues: (state, data) => {
      store.remove("org.5calls.completed", () => {});
      return { completedIssues: [] }
    },
    home: (state, data) => {
      return { activeIssue: false, getInfo: false }
    }
  },

  effects: {
    fetch: (state, data, send, done) => {
      address = "?address="
      if (state.address !== '') {
        address += state.address
      } else if (state.geolocation !== "") {
        address += state.geolocation
      }

      const issueURL = appURL+'/issues/'+address
      // console.log("fetching url",issueURL);
      http(issueURL, (err, res, body) => {
        send('setCachedCity', body, done)
        send('receiveIssues', body, done)
      })
    },
    getTotals: (state, data, send, done) => {
      http(appURL+'/report/', (err, res, body) => {
        send('receiveTotals', body, done)
      })
    },
    changeActiveIssueEffect: (state, issueId, send, done) => {
      send('location:set', "/#issue/"+issueId, done)
      send('changeActiveIssue', issueId, done)
    },
    setLocation: (state, data, send, done) => {
      send('setAddress', data, done);
      send('fetch', {}, done);
    },
    setBrowserGeolocation: (state, data, send, done) => {
      send('setGeolocation', data, done);
      send('fetch', {}, done);
    },
    unsetLocation: (state, data, send, done) => {
      send('resetLocation', data, done)
      send('startup', data, done)
    },
    fetchLocationBy: (state, data, send, done) => {
      send('setLocationFetchType', data, done)
      send('startup', data, done)
    },
    startup: (state, data, send, done) => {
      // sometimes we trigger this again when reloading mainView, check for issues
      if (state.issues.length == 0 || state.geolocation == '') {
        // Check for browser support of geolocation
        if ((state.allowBrowserGeo !== false && navigator.geolocation) &&
          state.locationFetchType === null && state.geolocation == '') {
          send('setLocationFetchType', 'browserGeolocation', done);
          send('fetch', {}, done)
        }
        else if (state.locationFetchType === null && state.geolocation == '') {
          send('setLocationFetchType', 'ipAddress', done);
          http('https://ipinfo.io/json', (err, res, body) => {
            if (res.statusCode == 200) {
              send('receiveIPInfoLoc', body, done)
            } else {
              Raven.captureMessage("Non-200 from ipinfo", { level: 'info' });
            }
            send('fetch', {}, done)
          })
        } else {
          send('fetch', {}, done)
        }
      }
    },
    incrementContact: (state, data, send, done) => {
      const issue = find(state.issues, ['id', data.issueid]);

      if (state.contactIndex < issue.contacts.length - 1) {
        scrollIntoView(document.querySelector('#contact'));
        send('setContactIndex', { newIndex: state.contactIndex + 1, issueid: issue.id }, done)
      } else {
        scrollIntoView(document.querySelector('#content'));
        store.add("org.5calls.completed", issue.id, () => {})
        send('location:set', "/#done", done)
        send('setContactIndex', { newIndex: 0, issueid: issue.id }, done)
      }
    },
    callComplete: (state, data, send, done) => {
      ga('send', 'called', data.result);

      const body = queryString.stringify({ location: state.zip, result: data.result, contactid: data.contactid, issueid: data.issueid })
      http.post(appURL+'/report', { body: body, headers: {"Content-Type": "application/x-www-form-urlencoded"} }, (err, res, body) => {
        // don't really care about the result
      })
      send('incrementContact', data, done);
    },
    skipCall: (state, data, send, done) => {
      ga('send', 'called', 'skip');

      send('incrementContact', data, done);
    },
    activateIssue: (state, data, send, done) => {
      scrollIntoView(document.querySelector('#content'));
      location.hash = "issue/" + data.id;
    }
  },
});

app.router({ default: '/404' }, [
  ['/', require('./pages/mainView.js')],
  ['/issue', require('./pages/mainView.js'),
    [':issueid', require('./pages/mainView.js')]
  ],
  ['/about', require('./pages/aboutView.js')],
  ['/done', require('./pages/doneView.js')],
]);

const tree = app.start();
const rootNode = document.getElementById('root');
document.body.replaceChild(tree, rootNode);
<commit_msg>Back to production.<commit_after>const choo = require('choo');
const html = require('choo/html');
const http = require('xhr');
const find = require('lodash/find');
const queryString = require('query-string');
const store = require('./utils/localstorage.js');
const scrollIntoView = require('scroll-into-view');

const app = choo();
const appURL = 'https://5calls.org';
const debug = false;
// const appURL = 'http://localhost:8090';

// get the stored zip location
cachedAddress = '';
store.getAll('org.5calls.location', (location) => {
  if (location.length > 0) {
   cachedAddress = location[0]
  }
});

// get the stored geo location
cachedGeo = '';
store.getAll('org.5calls.geolocation', (geo) => {
  if (geo.length > 0) {
    console.log("geo get",geo[0]);
    cachedGeo = geo[0]
  }
});

// get the stored geo location
cachedAllowBrowserGeo = null;
store.getAll('org.5calls.allow_geolocation', (allowGeo) => {
  if (allowGeo.length > 0) {
    console.log("allowGeo get",allowGeo[0]);
    cachedAllowBrowserGeo = allowGeo[0]
  }
});

// get the time the geo was last fetched
cachedGeoTime = '';
store.getAll('org.5calls.geolocation_time', (geo) => {
  if (geo.length > 0) {
    console.log("geo time get",geo[0]);
    cachedGeoTime = geo[0]
  }
});

cachedCity = '';
store.getAll('org.5calls.geolocation_city', (city) => {
  if (city.length > 0) {
    console.log("city get",city[0]);
    cachedCity = city[0]
  }
});

// get the stored completed issues
completedIssues = [];
store.getAll('org.5calls.completed', (completed) => {
  completedIssues = completed == null ? [] : completed;
});

app.model({
  state: {
    // remote data
    issues: [],
    totalCalls: 0,
    splitDistrict: false,

    // manual input address
    address: cachedAddress,

    // automatically geolocating
    geolocation: cachedGeo,
    geoCacheTime: cachedGeoTime,
    allowBrowserGeo: cachedAllowBrowserGeo,
    cachedCity: cachedCity,

    // view state
    // getInfo: false,
    // activeIssue: false,
    // completeIssue: false,
    askingLocation: false,
    fetchingLocation: false,
    locationFetchType: null,
    contactIndex: 0,
    completedIssues: completedIssues,

    debug: debug,
  },

  reducers: {
    receiveIssues: (state, data) => {
      response = JSON.parse(data)
      issues = response.issues //.filter((v) => { return v.contacts.length > 0 });
      return { issues: issues, splitDistrict: response.splitDistrict }
    },
    receiveTotals: (state, data) => {
      totals = JSON.parse(data);
      return { totalCalls: totals.count }
    },
    receiveIPInfoLoc: (state, data) => {
      try {
        response = JSON.parse(data)
        if (response.city != "") {
          geo = response.loc
          city = response.city
          time = new Date().valueOf()
          store.replace("org.5calls.geolocation", 0, geo, () => {});
          store.replace("org.5calls.geolocation_city", 0, city, () => {});
          store.replace("org.5calls.geolocation_time", 0, time, () => {});
          return { geolocation: geo, cachedCity: city, geoCacheTime: time, askingLocation: false }
        } else {
          Raven.captureMessage("Location with no city: "+response.loc, { level: 'warning' });
        }
      } catch(e) {
        Raven.setExtraContext({ json: data })
        Raven.captureMessage("Couldn't parse ipinfo json", { level: 'error' });
      }
    },
    changeActiveIssue: (state, issueId) => {
      return { contactIndex: 0 }
    },
    setContactIndex: (state, data) => {
      if (data.newIndex != 0) {
        return { contactIndex: data.newIndex }
      } else {
        store.add("org.5calls.completed", data.issueid, () => {})
        return { contactIndex: 0, completedIssues: state.completedIssues.concat(data.issueid) }
      }
    },
    setAddress: (state, address) => {
      Raven.setExtraContext({ address: address })
      store.replace("org.5calls.location", 0, address, () => {});

      return { address: address, askingLocation: false }
    },
    setGeolocation: (state, data) => {
      store.replace("org.5calls.geolocation", 0, data, () => {});
      return { geolocation: data, askingLocation: false }
    },
    setCachedCity: (state, data) => {
      response = JSON.parse(data);
      console.log(state.cachedCity);
      if (response.normalizedLocation && state.cachedCity == '') {
        store.replace("org.5calls.geolocation_city", 0, response.normalizedLocation, () => {});
        return { cachedCity: response.normalizedLocation }
      } else {
        return null
      }
    },
    fetchingLocation: (state, data) => {
      return { fetchingLocation: data }
    },
    allowBrowserGeolocation: (state, data) => {
      store.replace("org.5calls.allow_geolocation", 0, data, () => {})
      return { allowBrowserGeo: data }
    },
    enterLocation: (state, data) => {
      return { askingLocation: true }
    },
    setLocationFetchType: (state, data) => {
      return { locationFetchType: data, askingLocation: true }
    },
    resetLocation: (state, data) => {
      store.remove("org.5calls.location", () => {});
      store.remove("org.5calls.geolocation", () => {});
      store.remove("org.5calls.geolocation_city", () => {});
      store.remove("org.5calls.geolocation_time", () => {});
      return { address: '', geolocation: '', cachedCity: '', geoCacheTime: '' }
    },
    resetCompletedIssues: (state, data) => {
      store.remove("org.5calls.completed", () => {});
      return { completedIssues: [] }
    },
    home: (state, data) => {
      return { activeIssue: false, getInfo: false }
    }
  },

  effects: {
    fetch: (state, data, send, done) => {
      address = "?address="
      if (state.address !== '') {
        address += state.address
      } else if (state.geolocation !== "") {
        address += state.geolocation
      }

      const issueURL = appURL+'/issues/'+address
      // console.log("fetching url",issueURL);
      http(issueURL, (err, res, body) => {
        send('setCachedCity', body, done)
        send('receiveIssues', body, done)
      })
    },
    getTotals: (state, data, send, done) => {
      http(appURL+'/report/', (err, res, body) => {
        send('receiveTotals', body, done)
      })
    },
    changeActiveIssueEffect: (state, issueId, send, done) => {
      send('location:set', "/#issue/"+issueId, done)
      send('changeActiveIssue', issueId, done)
    },
    setLocation: (state, data, send, done) => {
      send('setAddress', data, done);
      send('fetch', {}, done);
    },
    setBrowserGeolocation: (state, data, send, done) => {
      send('setGeolocation', data, done);
      send('fetch', {}, done);
    },
    unsetLocation: (state, data, send, done) => {
      send('resetLocation', data, done)
      send('startup', data, done)
    },
    fetchLocationBy: (state, data, send, done) => {
      send('setLocationFetchType', data, done)
      send('startup', data, done)
    },
    startup: (state, data, send, done) => {
      // sometimes we trigger this again when reloading mainView, check for issues
      if (state.issues.length == 0 || state.geolocation == '') {
        // Check for browser support of geolocation
        if ((state.allowBrowserGeo !== false && navigator.geolocation) &&
          state.locationFetchType === null && state.geolocation == '') {
          send('setLocationFetchType', 'browserGeolocation', done);
          send('fetch', {}, done)
        }
        else if (state.locationFetchType === null && state.geolocation == '') {
          send('setLocationFetchType', 'ipAddress', done);
          http('https://ipinfo.io/json', (err, res, body) => {
            if (res.statusCode == 200) {
              send('receiveIPInfoLoc', body, done)
            } else {
              Raven.captureMessage("Non-200 from ipinfo", { level: 'info' });
            }
            send('fetch', {}, done)
          })
        } else {
          send('fetch', {}, done)
        }
      }
    },
    incrementContact: (state, data, send, done) => {
      const issue = find(state.issues, ['id', data.issueid]);

      if (state.contactIndex < issue.contacts.length - 1) {
        scrollIntoView(document.querySelector('#contact'));
        send('setContactIndex', { newIndex: state.contactIndex + 1, issueid: issue.id }, done)
      } else {
        scrollIntoView(document.querySelector('#content'));
        store.add("org.5calls.completed", issue.id, () => {})
        send('location:set', "/#done", done)
        send('setContactIndex', { newIndex: 0, issueid: issue.id }, done)
      }
    },
    callComplete: (state, data, send, done) => {
      ga('send', 'called', data.result);

      const body = queryString.stringify({ location: state.zip, result: data.result, contactid: data.contactid, issueid: data.issueid })
      http.post(appURL+'/report', { body: body, headers: {"Content-Type": "application/x-www-form-urlencoded"} }, (err, res, body) => {
        // don't really care about the result
      })
      send('incrementContact', data, done);
    },
    skipCall: (state, data, send, done) => {
      ga('send', 'called', 'skip');

      send('incrementContact', data, done);
    },
    activateIssue: (state, data, send, done) => {
      scrollIntoView(document.querySelector('#content'));
      location.hash = "issue/" + data.id;
    }
  },
});

app.router({ default: '/404' }, [
  ['/', require('./pages/mainView.js')],
  ['/issue', require('./pages/mainView.js'),
    [':issueid', require('./pages/mainView.js')]
  ],
  ['/about', require('./pages/aboutView.js')],
  ['/done', require('./pages/doneView.js')],
]);

const tree = app.start();
const rootNode = document.getElementById('root');
document.body.replaceChild(tree, rootNode);
<|endoftext|>


<commit_before>/**
 * @file Defines the provider settings.
 *
 * Will set the path to Mongo, and applications id
 * Most of the configuration can be done using system environment variables.
 */

// Load environment variables from .env file
var dotenv = require('dotenv');
dotenv.load();

// node_env can either be "development" or "production"
var nodeEnv = process.env.NODE_ENV || "development";

// Port to run the app on. 8000 for development
// (Vagrant syncs this port)
// 80 for production
var defaultPort = 8000;
if(nodeEnv === "production") {
  defaultPort = 80;
}

// Exports configuration for use by app.js
module.exports = {
  env: nodeEnv,
  port: process.env.PORT || defaultPort,

  trelloKey: process.env.TRELLO_API_KEY,
  trelloSecret: process.env.TRELLO_API_SECRET,
  trelloAppname: process.env.TRELLO_APPNAME,

  testAccessToken: process.env.TRELLO_ACCESS_TOKEN,

  usersConcurrency: process.env.USERS_CONCURRENCY || 1,
  concurrency: process.env.CONCURRENCY || 1,

  providerUrl: process.env.PROVIDER_URL,
  appId: process.env.ANYFETCH_API_ID,
  appSecret: process.env.ANYFETCH_API_SECRET,
};
<commit_msg>Add opbeat<commit_after>/**
 * @file Defines the provider settings.
 *
 * Will set the path to Mongo, and applications id
 * Most of the configuration can be done using system environment variables.
 */

// Load environment variables from .env file
var dotenv = require('dotenv');
dotenv.load();

// node_env can either be "development" or "production"
var nodeEnv = process.env.NODE_ENV || "development";

// Port to run the app on. 8000 for development
// (Vagrant syncs this port)
// 80 for production
var defaultPort = 8000;
if(nodeEnv === "production") {
  defaultPort = 80;
}

// Exports configuration for use by app.js
module.exports = {
  env: nodeEnv,
  port: process.env.PORT || defaultPort,

  trelloKey: process.env.TRELLO_API_KEY,
  trelloSecret: process.env.TRELLO_API_SECRET,
  trelloAppname: process.env.TRELLO_APPNAME,

  testAccessToken: process.env.TRELLO_ACCESS_TOKEN,

  usersConcurrency: process.env.USERS_CONCURRENCY || 1,
  concurrency: process.env.CONCURRENCY || 1,

  providerUrl: process.env.PROVIDER_URL,
  appId: process.env.ANYFETCH_API_ID,
  appSecret: process.env.ANYFETCH_API_SECRET,

  opbeat: {
    organizationId: process.env.OPBEAT_ORGANIZATION_ID,
    appId: process.env.OPBEAT_APP_ID,
    secretToken: process.env.OPBEAT_SECRET_TOKEN
  }
};
<|endoftext|>


<commit_before>'use strict';

var regexExec = RegExp.prototype.exec;

module.exports = function isRegex(value) {
	try {
		regexExec.call(value);
		return true;
	} catch (e) {
		return false;
	}
};

<commit_msg>Improve optimization by separating the try/catch, and bailing out early when not typeof "object".<commit_after>'use strict';

var regexExec = RegExp.prototype.exec;

var tryRegexExec = function tryRegexExec(value) {
	try {
		regexExec.call(value);
		return true;
	} catch (e) {
		return false;
	}
};

module.exports = function isRegex(value) {
	if (typeof value !== 'object') { return false; }
	return tryRegexExec(value);
};
<|endoftext|>


<commit_before>import fs from 'fs';
import path from 'path';
import electron from 'electron';
import windowStateKeeper from 'electron-window-state';
import helpers from './../../helpers/helpers';
import createMenu from './../menu/menu';
import initContextMenu from './../contextMenu/contextMenu';

const {BrowserWindow, shell, ipcMain, dialog} = electron;
const {isOSX, linkIsInternal, getCssToInject} = helpers;

const ZOOM_INTERVAL = 0.1;

/**
 *
 * @param {{}} options AppArgs from nativefier.json
 * @param {function} onAppQuit
 * @param {function} setDockBadge
 * @returns {electron.BrowserWindow}
 */
function createMainWindow(options, onAppQuit, setDockBadge) {
    const mainWindowState = windowStateKeeper({
        defaultWidth: options.width || 1280,
        defaultHeight: options.height || 800
    });

    const mainWindow = new BrowserWindow({
        width: mainWindowState.width,
        height: mainWindowState.height,
        x: mainWindowState.x,
        y: mainWindowState.y,
        'auto-hide-menu-bar': !options.showMenuBar,
        // Convert dashes to spaces because on linux the app name is joined with dashes
        title: options.name,
        'web-preferences': {
            javascript: true,
            plugins: true,
            // node globals causes problems with sites like messenger.com
            nodeIntegration: false,
            webSecurity: !options.insecure,
            preload: path.join(__dirname, 'static', 'preload.js')
        },
        // after webpack path here should reference `resources/app/`
        icon: path.join(__dirname, '../', '/icon.png'),
        // set to undefined and not false because explicitly setting to false will disable full screen
        fullscreen: options.fullScreen || undefined
    });

    // after first run, no longer force full screen to be true
    if (options.fullScreen) {
        options.fullScreen = undefined;
        fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
    }

    // after first run, no longer force maximize to be true
    if(options.maximize){
        mainWindow.maximize();
        options.maximize = undefined;
        fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
    }

    let currentZoom = 1;

    const onZoomIn = () => {
        currentZoom += ZOOM_INTERVAL;
        mainWindow.webContents.send('change-zoom', currentZoom);
    };

    const onZoomOut = () => {
        currentZoom -= ZOOM_INTERVAL;
        mainWindow.webContents.send('change-zoom', currentZoom);
    };

    const clearAppData = () => {
        dialog.showMessageBox(mainWindow, {
            type: 'warning',
            buttons: ['Yes', 'Cancel'],
            defaultId: 1,
            title: 'Clear cache confirmation',
            message: 'This will clear all data (cookies, local storage etc) from this app. Are you sure you wish to proceed?'
        }, response => {
            if (response === 0) {
                const session = mainWindow.webContents.session;
                session.clearStorageData(() => {
                    session.clearCache(() => {
                        mainWindow.loadURL(options.targetUrl);
                    });
                });
            }
        });
    };

    const onGoBack = () => {
        mainWindow.webContents.goBack();
    };

    const onGoForward = () => {
        mainWindow.webContents.goForward();
    };

    const getCurrentUrl = () => {
        return mainWindow.webContents.getURL();
    };

    const menuOptions = {
        nativefierVersion: options.nativefierVersion,
        appQuit: onAppQuit,
        zoomIn: onZoomIn,
        zoomOut: onZoomOut,
        goBack: onGoBack,
        goForward: onGoForward,
        getCurrentUrl: getCurrentUrl,
        clearAppData: clearAppData
    };

    createMenu(menuOptions);
    initContextMenu(mainWindow);

    if (options.userAgent) {
        mainWindow.webContents.setUserAgent(options.userAgent);
    }

    mainWindow.webContents.on('did-get-response-details', () => {
        mainWindow.webContents.send('params', JSON.stringify(options));
        mainWindow.webContents.insertCSS(getCssToInject());
    });

    if (options.counter) {
        mainWindow.on('page-title-updated', () => {
            if (mainWindow.isFocused()) {
                return;
            }

            if (options.counter) {
                const itemCountRegex = /[\(\[{](\d*?)[}\]\)]/;
                const match = itemCountRegex.exec(mainWindow.getTitle());
                if (match) {
                    setDockBadge(match[1]);
                }
                return;
            }
            setDockBadge('●');
        });
    }

    mainWindow.webContents.on('new-window', (event, urlToGo) => {
        if (mainWindow.useDefaultWindowBehaviour) {
            mainWindow.useDefaultWindowBehaviour = false;
            return;
        }

        if (linkIsInternal(options.targetUrl, urlToGo)) {
            return;
        }
        event.preventDefault();
        shell.openExternal(urlToGo);
    });

    mainWindow.loadURL(options.targetUrl);

    mainWindow.on('focus', () => {
        setDockBadge('');
    });

    mainWindow.on('close', event => {
        if (mainWindow.isFullScreen()) {
            mainWindow.setFullScreen(false);
            mainWindow.once('leave-full-screen', maybeHideWindow.bind(this, mainWindow, event));
        }
        maybeHideWindow(mainWindow, event);
    });

    mainWindowState.manage(mainWindow);
    return mainWindow;
}

ipcMain.on('cancelNewWindowOverride', () => {
    const allWindows = BrowserWindow.getAllWindows();
    allWindows.forEach(window => {
        window.useDefaultWindowBehaviour = false;
    });
});

function maybeHideWindow(window, event) {
    if (isOSX()) {
        // this is called when exiting from clicking the cross button on the window
        event.preventDefault();
        window.hide();
    }
    // will close the window on other platforms
}

export default createMainWindow;
<commit_msg>Added option for maximization of main window ( #133 ) - Fixed linting errors<commit_after>import fs from 'fs';
import path from 'path';
import electron from 'electron';
import windowStateKeeper from 'electron-window-state';
import helpers from './../../helpers/helpers';
import createMenu from './../menu/menu';
import initContextMenu from './../contextMenu/contextMenu';

const {BrowserWindow, shell, ipcMain, dialog} = electron;
const {isOSX, linkIsInternal, getCssToInject} = helpers;

const ZOOM_INTERVAL = 0.1;

/**
 *
 * @param {{}} options AppArgs from nativefier.json
 * @param {function} onAppQuit
 * @param {function} setDockBadge
 * @returns {electron.BrowserWindow}
 */
function createMainWindow(options, onAppQuit, setDockBadge) {
    const mainWindowState = windowStateKeeper({
        defaultWidth: options.width || 1280,
        defaultHeight: options.height || 800
    });

    const mainWindow = new BrowserWindow({
        width: mainWindowState.width,
        height: mainWindowState.height,
        x: mainWindowState.x,
        y: mainWindowState.y,
        'auto-hide-menu-bar': !options.showMenuBar,
        // Convert dashes to spaces because on linux the app name is joined with dashes
        title: options.name,
        'web-preferences': {
            javascript: true,
            plugins: true,
            // node globals causes problems with sites like messenger.com
            nodeIntegration: false,
            webSecurity: !options.insecure,
            preload: path.join(__dirname, 'static', 'preload.js')
        },
        // after webpack path here should reference `resources/app/`
        icon: path.join(__dirname, '../', '/icon.png'),
        // set to undefined and not false because explicitly setting to false will disable full screen
        fullscreen: options.fullScreen || undefined
    });

    // after first run, no longer force full screen to be true
    if (options.fullScreen) {
        options.fullScreen = undefined;
        fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
    }

    // after first run, no longer force maximize to be true
    if (options.maximize) {
        mainWindow.maximize();
        options.maximize = undefined;
        fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
    }

    let currentZoom = 1;

    const onZoomIn = () => {
        currentZoom += ZOOM_INTERVAL;
        mainWindow.webContents.send('change-zoom', currentZoom);
    };

    const onZoomOut = () => {
        currentZoom -= ZOOM_INTERVAL;
        mainWindow.webContents.send('change-zoom', currentZoom);
    };

    const clearAppData = () => {
        dialog.showMessageBox(mainWindow, {
            type: 'warning',
            buttons: ['Yes', 'Cancel'],
            defaultId: 1,
            title: 'Clear cache confirmation',
            message: 'This will clear all data (cookies, local storage etc) from this app. Are you sure you wish to proceed?'
        }, response => {
            if (response === 0) {
                const session = mainWindow.webContents.session;
                session.clearStorageData(() => {
                    session.clearCache(() => {
                        mainWindow.loadURL(options.targetUrl);
                    });
                });
            }
        });
    };

    const onGoBack = () => {
        mainWindow.webContents.goBack();
    };

    const onGoForward = () => {
        mainWindow.webContents.goForward();
    };

    const getCurrentUrl = () => {
        return mainWindow.webContents.getURL();
    };

    const menuOptions = {
        nativefierVersion: options.nativefierVersion,
        appQuit: onAppQuit,
        zoomIn: onZoomIn,
        zoomOut: onZoomOut,
        goBack: onGoBack,
        goForward: onGoForward,
        getCurrentUrl: getCurrentUrl,
        clearAppData: clearAppData
    };

    createMenu(menuOptions);
    initContextMenu(mainWindow);

    if (options.userAgent) {
        mainWindow.webContents.setUserAgent(options.userAgent);
    }

    mainWindow.webContents.on('did-get-response-details', () => {
        mainWindow.webContents.send('params', JSON.stringify(options));
        mainWindow.webContents.insertCSS(getCssToInject());
    });

    if (options.counter) {
        mainWindow.on('page-title-updated', () => {
            if (mainWindow.isFocused()) {
                return;
            }

            if (options.counter) {
                const itemCountRegex = /[\(\[{](\d*?)[}\]\)]/;
                const match = itemCountRegex.exec(mainWindow.getTitle());
                if (match) {
                    setDockBadge(match[1]);
                }
                return;
            }
            setDockBadge('●');
        });
    }

    mainWindow.webContents.on('new-window', (event, urlToGo) => {
        if (mainWindow.useDefaultWindowBehaviour) {
            mainWindow.useDefaultWindowBehaviour = false;
            return;
        }

        if (linkIsInternal(options.targetUrl, urlToGo)) {
            return;
        }
        event.preventDefault();
        shell.openExternal(urlToGo);
    });

    mainWindow.loadURL(options.targetUrl);

    mainWindow.on('focus', () => {
        setDockBadge('');
    });

    mainWindow.on('close', event => {
        if (mainWindow.isFullScreen()) {
            mainWindow.setFullScreen(false);
            mainWindow.once('leave-full-screen', maybeHideWindow.bind(this, mainWindow, event));
        }
        maybeHideWindow(mainWindow, event);
    });

    mainWindowState.manage(mainWindow);
    return mainWindow;
}

ipcMain.on('cancelNewWindowOverride', () => {
    const allWindows = BrowserWindow.getAllWindows();
    allWindows.forEach(window => {
        window.useDefaultWindowBehaviour = false;
    });
});

function maybeHideWindow(window, event) {
    if (isOSX()) {
        // this is called when exiting from clicking the cross button on the window
        event.preventDefault();
        window.hide();
    }
    // will close the window on other platforms
}

export default createMainWindow;
<|endoftext|>


<commit_before>function showQuestionsModal() {
	var $modalBackground = $('

').appendTo('body');
	var $modal = $('

').css('height', '400px').appendTo('body');
	$modalBackground.animate({ opacity: .7 });
	$modal.animate({ marginTop: '-200px', top: '50%' }, function() {
		$('#reply-composer').focus();
	});

	$('html, body').css({
	    'overflow': 'hidden',
	    'height': '100%'
	});

	$modalBackground.on('click', closeQuestionsModal);

	var groupId = $(this).attr('data-group-id');

	$.ajax({
        url: "/dashboard/" + groupId + "/details",
        type: "GET",
        success: function(data) {
        	var template = $('#modal-question').html();

        	data.groupId = groupId;

        	console.log(groupId);

			$modal.html(Mustache.to_html(template, data));
			$('.modal-close-button').on('click', closeQuestionsModal);

			$('#reply-button').on('click', function(){
				var groupId = $(this).attr('data-group-id');
				console.log('a');
				$.ajax({
			        url: "/dashboard/" + groupId + "/reply",
			        type: "POST",
			        data: {
			        	message: $('#reply-composer').val()
			        },
			        success: function(data){
						closeQuestionsModal();
			        },
				    dataType: "json",
			    	timeout: 2000
		   		});
			});
    	},
	    dataType: "json",
    	timeout: 2000
    });

	return false;
}

function closeQuestionsModal() {
	var $modalBackground = $('.modal-background');
	var $modal = $('.modal');
	$modal.animate({ marginTop: '0', top: '100%' }, function() { $modal.remove() });
	$modalBackground.animate({ opacity: 0 }, function() { $modalBackground.remove() });

	$('html, body').css({
	    'overflow': 'auto',
	    'height': 'auto'
	});

	return false;
}

function polling() {

	var eventId = parseInt($('body').attr('data-event-id'), 10);

    $.ajax({
        url: "/dashboard/event/" + eventId + "/polling",
        type: "GET",
        success: function(data) {

            // Update page
            $('.questions-overview .content .value').text(data.questionsNumber);
			$('.users-overview .content .value').text(data.usersOverview);
			$('.questions-list tbody').html('');

			data.unansweredQuestions.sort(function(a,b){return b.frequency-a.frequency});

			jQuery.each(data.unansweredQuestions, function(index, value) {

				if(value.sentiment === 'positive') {
					data.totalPositive++;
					sentiment = 'green';
				} else {
					if(value.sentiment === 'negative') {
						data.totalNegative++;
						sentiment = 'red';
					} else {
						data.totalNeutral++;
						sentiment = 'grey';
					}
				}

				$('.questions-list tbody').append('
\
						\
					' + value.question + '	\
		            ' + value.frequency + '	\

');
			});

			$('.positive').text('+' + data.totalPositive);
			$('.neutral').text(data.totalNeutral);
			$('.negative').text('-' + data.totalNegative);

			$('.questions-list tbody tr').on('click', showQuestionsModal);
        },
        dataType: "json",
        complete: setTimeout(function() { polling() }, 5000),
        timeout: 2000
    })
}

$(document).ready(function() {
	polling();
	$('.questions-list tbody tr').on('click', showQuestionsModal);
});<commit_msg>Update NLP thingie<commit_after>function showQuestionsModal() {
	var $modalBackground = $('

').appendTo('body');
	var $modal = $('

').css('height', '400px').appendTo('body');
	$modalBackground.animate({ opacity: .7 });
	$modal.animate({ marginTop: '-200px', top: '50%' }, function() {
		$('#reply-composer').focus();
	});

	$('html, body').css({
	    'overflow': 'hidden',
	    'height': '99%'
	});

	$modalBackground.on('click', closeQuestionsModal);

	var groupId = $(this).attr('data-group-id');

	$.ajax({
        url: "/dashboard/" + groupId + "/details",
        type: "GET",
        success: function(data) {
        	var template = $('#modal-question').html();

        	data.groupId = groupId;

        	console.log(groupId);

			$modal.html(Mustache.to_html(template, data));
			$('.modal-close-button').on('click', closeQuestionsModal);

			$('#reply-button').on('click', function(){
				var groupId = $(this).attr('data-group-id');
				console.log('a');
				$.ajax({
			        url: "/dashboard/" + groupId + "/reply",
			        type: "POST",
			        data: {
			        	message: $('#reply-composer').val()
			        },
			        success: function(data){
						closeQuestionsModal();
			        },
				    dataType: "json",
			    	timeout: 2000
		   		});
			});
    	},
	    dataType: "json",
    	timeout: 2000
    });

	return false;
}

function closeQuestionsModal() {
	var $modalBackground = $('.modal-background');
	var $modal = $('.modal');
	$modal.animate({ marginTop: '0', top: '100%' }, function() { $modal.remove() });
	$modalBackground.animate({ opacity: 0 }, function() { $modalBackground.remove() });

	$('html, body').css({
	    'overflow': 'auto',
	    'height': 'auto'
	});

	return false;
}

function polling() {

	var eventId = parseInt($('body').attr('data-event-id'), 10);

    $.ajax({
        url: "/dashboard/event/" + eventId + "/polling",
        type: "GET",
        success: function(data) {

            // Update page
            $('.questions-overview .content .value').text(data.questionsNumber);
			$('.users-overview .content .value').text(data.usersOverview);
			$('.questions-list tbody').html('');

			data.unansweredQuestions.sort(function(a,b){return b.frequency-a.frequency});

			jQuery.each(data.unansweredQuestions, function(index, value) {

				if(value.sentiment === 'positive') {
					data.totalPositive++;
					sentiment = 'green';
				} else {
					if(value.sentiment === 'negative') {
						data.totalNegative++;
						sentiment = 'red';
					} else {
						data.totalNeutral++;
						sentiment = 'grey';
					}
				}

				$('.questions-list tbody').append('
\
						\
					' + value.question + '	\
		            ' + value.frequency + '	\

');
			});

			$('.positive').text('+' + data.totalPositive);
			$('.neutral').text(data.totalNeutral);
			$('.negative').text('-' + data.totalNegative);

			$('.questions-list tbody tr').on('click', showQuestionsModal);
        },
        dataType: "json",
        complete: setTimeout(function() { polling() }, 5000),
        timeout: 2000
    })
}

$(document).ready(function() {
	polling();
	$('.questions-list tbody tr').on('click', showQuestionsModal);
});<|endoftext|>


<commit_before>/**
    Webhook listener.
*/
"use strict";
const GitHubApi = require("github");
const program = require("commander");
const http = require('http');
const main = require('./pull_request');
const config = require('./config');

const github = new GitHubApi({
	version: "3.0.0",
	headers: {
	    "user-agent": "80x40-client"
	}
});

program
    .version('0.0.0')
    .option('--token <token>', 'Github user token')
    .option('--secret <secret>', 'Github hook secret')
    .parse(process.argv);

github.authenticate({
    type: "oauth",
    token: program.token
});

const webhookHandler = require('github-webhook-handler')({
    path: '/',
    secret: program.secret
});

webhookHandler.on('pull_request', (event) => {
    const action = event.payload.action;
    if (action !== 'opened' || action !== 'synchronize') {
        console.log('ignoring action', action);
        return;
    }
	main.handlePullRequest(github, conf.user, conf.repo, event.payload.number,
	    (err) => {
            if (err) {
                console.error(err);
            } else {
                console.log("OK");
            }
        });
});

http.createServer(function (req, res) {
    webhookHandler(req, res, (err) => {
        res.statusCode = 404
        res.end('');
    });
}).listen(3000);
<commit_msg>Fixed bad conditional<commit_after>/**
    Webhook listener.
*/
"use strict";
const GitHubApi = require("github");
const program = require("commander");
const http = require('http');
const main = require('./pull_request');
const config = require('./config');

const github = new GitHubApi({
	version: "3.0.0",
	headers: {
	    "user-agent": "80x40-client"
	}
});

program
    .version('0.0.0')
    .option('--token <token>', 'Github user token')
    .option('--secret <secret>', 'Github hook secret')
    .parse(process.argv);

github.authenticate({
    type: "oauth",
    token: program.token
});

const webhookHandler = require('github-webhook-handler')({
    path: '/',
    secret: program.secret
});

webhookHandler.on('pull_request', (event) => {
    const action = event.payload.action;
    if (action !== 'opened' && action !== 'synchronize') {
        console.log('ignoring action', action);
        return;
    }
	main.handlePullRequest(github, conf.user, conf.repo, event.payload.number,
	    (err) => {
            if (err) {
                console.error(err);
            } else {
                console.log("OK");
            }
        });
});

http.createServer(function (req, res) {
    webhookHandler(req, res, (err) => {
        res.statusCode = 404
        res.end('');
    });
}).listen(3000);
<|endoftext|>


<commit_before>"use strict";

import Request       from "superagent";
import Constants     from "../constants";
import Dispatcher    from "../dispatcher";
import SettingsStore from '../stores/settings';
import _             from "lodash";

const TIMEOUT = 10000;
const GET = 'get';
const POST = 'post';
const PUT = 'put';
const DEL = 'del';

var _pendingRequests = {};
var _cache = {};

function abortPendingRequests(url) {
  if(_pendingRequests[url]) {
    _pendingRequests[url]._callback = function() {};
    _pendingRequests[url].abort();
    _pendingRequests[url] = null;
  }
}

// Get the JWT from settings
function jwt() {
  return SettingsStore.current().jwt;
}

function csrfToken() {
  return SettingsStore.current().csrfToken;
}

function makeUrl(part){
  if(part.indexOf("http") >= 0){
    return part;
  } else {
    var slash = _.last(SettingsStore.current().apiUrl.split("")) == "/" ? "" :"/";
    if(part[0] == "/"){
      part = part.slice(1);
    }
    return SettingsStore.current().apiUrl + slash + part;
  }
}

// GET request with a JWT token and CSRF token
function get(url) {
  return Request
    .get(url)
    .timeout(TIMEOUT)
    .set('Accept', 'application/json')
    .set('Authorization', 'Bearer ' + jwt())
    .set('X-CSRF-Token', csrfToken());
}

// POST request with a JWT token and CSRF token
function post(url, body) {
  return Request
    .post(url)
    .send(body)
    .set('Accept', 'application/json')
    .timeout(TIMEOUT)
    .set('Authorization', 'Bearer ' + jwt())
    .set('X-CSRF-Token', csrfToken());
}

// PUT request with a JWT token and CSRF token
function put(url, body) {
  return Request
    .put(url)
    .send(body)
    .set('Accept', 'application/json')
    .timeout(TIMEOUT)
    .set('Authorization', 'Bearer ' + jwt())
    .set('X-CSRF-Token', csrfToken());
}

// DELETER request with a JWT token and CSRF token
function del(url) {
  return Request
    .del(url)
    .set('Accept', 'application/json')
    .timeout(TIMEOUT)
    .set('Authorization', 'Bearer ' + jwt())
    .set('X-CSRF-Token', csrfToken());
}

function dispatch(key, response) {
  Dispatcher.dispatch({
    action: key,
    data: response
  });
  return true;
}

// Dispatch a response based on the server response
function dispatchResponse(key) {
  return (err, response) => {
    if(err && err.timeout === TIMEOUT) {
      return dispatch(Constants.TIMEOUT, response);
    } else if(err && err.length > 0) {
      return dispatch(Constants.ERROR, err);
    } else if(response.status === 401) {
      return dispatch(Constants.NOT_AUTHORIZED, response);
    } else if(response.status === 400) {
      return dispatch(Constants.ERROR, response);
    } else if(response.status === 302) {
      window.location = response.body.url;
    } else if(!response.ok) {
      return dispatch(Constants.ERROR, response);
    } else if(key) {
      return dispatch(key, response);
    }
    return false;
  };
}

function doRequest(key, url, requestMethod, requestType){

  var requestContainer = buildRequest(url, requestMethod, requestType);
  if(requestContainer.promise){
    return requestContainer.promise;
  }

  var promise = new Promise((resolve, reject) => {
    requestContainer.request.end((error, res) => {
      disposeRequest(url);
      var handled = dispatchResponse(key)(error, res);
      if(error && !handled){
        reject(error);
      } else {
        resolve(res);
      }
    });
  });

  requestContainer.promise = promise;
  return promise;
}

function buildRequest(url, requestMethod, requestType){
  //abortPendingRequests(url);
  if (requestType === POST) {
    return {
      request: requestMethod(makeUrl(url))
    };
  } else if (!_pendingRequests[url]) {
    _pendingRequests[url] = {
      request: requestMethod(makeUrl(url))
    };
  }
  return _pendingRequests[url];
}

function disposeRequest(url){
  delete _pendingRequests[url];
}

function promisify(request) {
  return new Promise((resolve, reject) => {
    request.end((error, res) => {
      if (error) {
        reject(error);
      } else {
        resolve(res);
      }
    });
  });
}

function *doCacheRequest(url, key, requestMethod, requestType){

  var promise;
  var fullUrl = makeUrl(url);

  if (_cache[fullUrl]) {
    setTimeout(() => {
      dispatchResponse(key)(null, _cache[fullUrl]);
    }, 1);

    promise = new Promise((resolve, reject) => {
      resolve(_cache[fullUrl]);
    });

    yield promise;
  };

  var requestContainer = buildRequest(url, requestMethod, requestType);
  if(requestContainer.promise){
    yield requestContainer.promise;
  } else {
    promise = promisify(requestContainer.request);
    requestContainer.promise = promise;
    promise.then((result) => {
      disposeRequest(url);
      dispatchResponse(key)(null, result);
      _cache[fullUrl] = result;
      _cache[fullUrl].isCached = true;
      return result;
    }, (err) => {
      dispatchResponse(key)(err, err.response);
    });
    yield promise;
  }

}

var API = {

  get(key, url){
    return doRequest(key, url, (fullUrl) => {
      return get(fullUrl);
    }, GET);
  },

  post(key, url, body){
    return doRequest(key, url, (fullUrl) => {
      return post(fullUrl, body);
    }, POST);
  },

  put(key, url, body){
    return doRequest(key, url, (fullUrl) => {
      return put(fullUrl, body);
    }, PUT);
  },

  del(key, url){
    return doRequest(key, url, (fullUrl) => {
      return del(fullUrl);
    }, DEL);
  },

  async cacheGet(url, params, key, refresh){
    url = `${url}${API.queryStringFrom(params)}`;
    var request = doCacheRequest(url, key, (fullUrl) => {
      return get(fullUrl);
    }, GET);
    if (key) {
      // We have a key. Invoke the generate to get data and dispatch.
      var response = request.next();
      while (refresh && !response.done){
        response = request.next();
      }
    } else {
      // Return the generator and let the calling code invoke it.
      return request;
    }
  },

  queryStringFrom(params){
    var query = _.chain(params)
      .map((val, key) => {
        if(val){
          return `${key}=${val}`;
        } else {
          return "";
        }
      })
      .compact()
      .value();

    if(query.length > 0){
      return `?${query.join("&")}`;
    } else {
      return "";
    }

  }

};

export default API;
<commit_msg>Changes error checking logic<commit_after>"use strict";

import Request       from "superagent";
import Constants     from "../constants";
import Dispatcher    from "../dispatcher";
import SettingsStore from '../stores/settings';
import _             from "lodash";

const TIMEOUT = 10000;
const GET = 'get';
const POST = 'post';
const PUT = 'put';
const DEL = 'del';

var _pendingRequests = {};
var _cache = {};

function abortPendingRequests(url) {
  if(_pendingRequests[url]) {
    _pendingRequests[url]._callback = function() {};
    _pendingRequests[url].abort();
    _pendingRequests[url] = null;
  }
}

// Get the JWT from settings
function jwt() {
  return SettingsStore.current().jwt;
}

function csrfToken() {
  return SettingsStore.current().csrfToken;
}

function makeUrl(part){
  if(part.indexOf("http") >= 0){
    return part;
  } else {
    var slash = _.last(SettingsStore.current().apiUrl.split("")) == "/" ? "" :"/";
    if(part[0] == "/"){
      part = part.slice(1);
    }
    return SettingsStore.current().apiUrl + slash + part;
  }
}

// GET request with a JWT token and CSRF token
function get(url) {
  return Request
    .get(url)
    .timeout(TIMEOUT)
    .set('Accept', 'application/json')
    .set('Authorization', 'Bearer ' + jwt())
    .set('X-CSRF-Token', csrfToken());
}

// POST request with a JWT token and CSRF token
function post(url, body) {
  return Request
    .post(url)
    .send(body)
    .set('Accept', 'application/json')
    .timeout(TIMEOUT)
    .set('Authorization', 'Bearer ' + jwt())
    .set('X-CSRF-Token', csrfToken());
}

// PUT request with a JWT token and CSRF token
function put(url, body) {
  return Request
    .put(url)
    .send(body)
    .set('Accept', 'application/json')
    .timeout(TIMEOUT)
    .set('Authorization', 'Bearer ' + jwt())
    .set('X-CSRF-Token', csrfToken());
}

// DELETER request with a JWT token and CSRF token
function del(url) {
  return Request
    .del(url)
    .set('Accept', 'application/json')
    .timeout(TIMEOUT)
    .set('Authorization', 'Bearer ' + jwt())
    .set('X-CSRF-Token', csrfToken());
}

function dispatch(key, response) {
  Dispatcher.dispatch({
    action: key,
    data: response
  });
  return true;
}

// Dispatch a response based on the server response
function dispatchResponse(key) {
  return (err, response) => {
    if(err && err.timeout === TIMEOUT) {
      return dispatch(Constants.TIMEOUT, response);
    } else if(err || !response) {
      return dispatch(Constants.ERROR, err);
    } else if(response.status === 401) {
      return dispatch(Constants.NOT_AUTHORIZED, response);
    } else if(response.status === 400) {
      return dispatch(Constants.ERROR, response);
    } else if(response.status === 302) {
      window.location = response.body.url;
    } else if(!response.ok) {
      return dispatch(Constants.ERROR, response);
    } else if(key) {
      return dispatch(key, response);
    }
    return false;
  };
}

function doRequest(key, url, requestMethod, requestType){

  var requestContainer = buildRequest(url, requestMethod, requestType);
  if(requestContainer.promise){
    return requestContainer.promise;
  }

  var promise = new Promise((resolve, reject) => {
    requestContainer.request.end((error, res) => {
      disposeRequest(url);
      var handled = dispatchResponse(key)(error, res);
      if(error && !handled){
        reject(error);
      } else {
        resolve(res);
      }
    });
  });

  requestContainer.promise = promise;
  return promise;
}

function buildRequest(url, requestMethod, requestType){
  //abortPendingRequests(url);
  if (requestType === POST) {
    return {
      request: requestMethod(makeUrl(url))
    };
  } else if (!_pendingRequests[url]) {
    _pendingRequests[url] = {
      request: requestMethod(makeUrl(url))
    };
  }
  return _pendingRequests[url];
}

function disposeRequest(url){
  delete _pendingRequests[url];
}

function promisify(request) {
  return new Promise((resolve, reject) => {
    request.end((error, res) => {
      if (error) {
        reject(error);
      } else {
        resolve(res);
      }
    });
  });
}

function *doCacheRequest(url, key, requestMethod, requestType){

  var promise;
  var fullUrl = makeUrl(url);

  if (_cache[fullUrl]) {
    setTimeout(() => {
      dispatchResponse(key)(null, _cache[fullUrl]);
    }, 1);

    promise = new Promise((resolve, reject) => {
      resolve(_cache[fullUrl]);
    });

    yield promise;
  };

  var requestContainer = buildRequest(url, requestMethod, requestType);
  if(requestContainer.promise){
    yield requestContainer.promise;
  } else {
    promise = promisify(requestContainer.request);
    requestContainer.promise = promise;
    promise.then((result) => {
      disposeRequest(url);
      dispatchResponse(key)(null, result);
      _cache[fullUrl] = result;
      _cache[fullUrl].isCached = true;
      return result;
    }, (err) => {
      dispatchResponse(key)(err, err.response);
    });
    yield promise;
  }

}

var API = {

  get(key, url){
    return doRequest(key, url, (fullUrl) => {
      return get(fullUrl);
    }, GET);
  },

  post(key, url, body){
    return doRequest(key, url, (fullUrl) => {
      return post(fullUrl, body);
    }, POST);
  },

  put(key, url, body){
    return doRequest(key, url, (fullUrl) => {
      return put(fullUrl, body);
    }, PUT);
  },

  del(key, url){
    return doRequest(key, url, (fullUrl) => {
      return del(fullUrl);
    }, DEL);
  },

  async cacheGet(url, params, key, refresh){
    url = `${url}${API.queryStringFrom(params)}`;
    var request = doCacheRequest(url, key, (fullUrl) => {
      return get(fullUrl);
    }, GET);
    if (key) {
      // We have a key. Invoke the generate to get data and dispatch.
      var response = request.next();
      while (refresh && !response.done){
        response = request.next();
      }
    } else {
      // Return the generator and let the calling code invoke it.
      return request;
    }
  },

  queryStringFrom(params){
    var query = _.chain(params)
      .map((val, key) => {
        if(val){
          return `${key}=${val}`;
        } else {
          return "";
        }
      })
      .compact()
      .value();

    if(query.length > 0){
      return `?${query.join("&")}`;
    } else {
      return "";
    }

  }

};

export default API;
<|endoftext|>


<commit_before>/*
 * Middleware that rewrites `req.url` to `/<fileName>.<localeId>.<extension>`
 * (as output by `buildProduction`) based on the value of a cookie
 * (optional, set by `options.cookieName`), the `Accept-Language`
 * request header, the `Accept` header, and the directory contents.
 *
 * rewriteStaticUrlToLocalized is intended to run right before `static`
 * pointed at the same directory as `options.root`.
 *
 * If `options.cookieName` is specified, the `cookieParser` middleware
 * must also be in the middleware chain.
 */

var fs = require('fs'),
    Path = require('path'),
    _ = require('underscore'),
    passError = require('passerror'),
    memoizeAsync = require('../memoizeAsync'),
    memoizer = require('memoizer'),
    mime = require('mime'),
    // List taken from an older version of CLDR, so it's probably incomplete:
    isLocaleIdRegExp = /^(?:aa|af|ak|am|ar|as|asa|az|be|bem|bez|bg|bm|bn|bo|br|brx|bs|byn|ca|cch|cgg|chr|cs|cy|da|dav|de|dv|dz|ebu|ee|el|en|eo|es|et|eu|fa|ff|fi|fil|fo|fr|fur|ga|gaa|gez|gl|gsw|gu|guz|gv|ha|haw|he|hi|hr|hu|hy|ia|id|ig|ii|in|is|it|iu|iw|ja|jmc|ka|kab|kaj|kam|kcg|kde|kea|kfo|khq|ki|kk|kl|kln|km|kn|ko|kok|kpe|ksb|ksh|ku|kw|ky|lag|lg|ln|lo|lt|luo|luy|lv|mas|mer|mfe|mg|mi|mk|ml|mn|mo|mr|ms|mt|my|naq|nb|nd|nds|ne|nl|nn|no|nr|nso|ny|nyn|oc|om|or|pa|pl|ps|pt|rm|ro|rof|root|ru|rw|rwk|sa|saq|se|seh|ses|sg|sh|shi|si|sid|sk|sl|sn|so|sq|sr|ss|ssy|st|sv|sw|syr|ta|te|teo|tg|th|ti|tig|tl|tn|to|tr|trv|ts|tt|tzm|ug|uk|ur|uz|ve|vi|vun|wal|wo|xh|xog|yo|zh|zu)(?:_|$)/;

// Note: These obscure extensions are listed in require('mime').types AND are considered valid locale ids: nb, so, ps, kfo, rm, st, sh, sid, in, tr, ms, gv, ts
// When resolving the variants we interpret them as locale ids take precendence.

// From Express 3.0:
function parseQualityToken(str) {
  var parts = str.split(/ *; */),
      val = parts[0],
      q = parts[1] ? parseFloat(parts[1].split(/ *= */)[1]) : 1;

  return { value: val, quality: q };
}

function parseQuality(str){
    return str
        .split(/ *, */)
        .map(parseQualityToken)
        .filter(function(obj) {
            return obj.quality;
        })
        .sort(function(a, b) {
            return b.quality - a.quality;
        });
}

/*
 * Replace - with _ and convert to lower case: en-GB => en_gb
 */
function normalizeLocaleId(localeId) {
    return localeId.replace(/-/g, '_').toLowerCase();
}

/*
 * Helper for getting a prioritized list of relevant locale ids from a
 * specific locale id. For instance, `en_US` produces `["en_US", "en"]`.
 */
function expandLocaleIdToPrioritizedList(localeId) {
    localeId = normalizeLocaleId(localeId);
    var localeIds = [localeId];
    while (/_[^_]+$/.test(localeId)) {
        localeId = localeId.replace(/_[^_]+$/, '');
        localeIds.push(localeId);
    }
    return localeIds;
}

// .html.en-US => {contentType: 'text/html', contentTypeFragments: ['text', 'html'], localeId: 'en_us'}
function getVariantInfoFromExtensionString(extensionString) {
    var variantInfo = {
        allExtensionsMatched: true
    };
    if (extensionString) {
        extensionString.split(/(?=\.)/).forEach(function (extension) {
            var localeId = normalizeLocaleId(extension.substr(1));
        if (isLocaleIdRegExp.test(localeId)) {
                variantInfo.localeId = localeId;
            } else {
                var contentType = mime.types[extension.substr(1)];
                if (contentType) {
                    variantInfo.contentType = contentType;
                    variantInfo.contentTypeFragments = contentType.split('/');
                } else {
                    variantInfo.allExtensionsMatched = false;
                }
            }
        });
    }
    return variantInfo;
}

module.exports = function (options) {
    options = options || {};
    if (!('root' in options)) {
        throw new Error('rewriteStaticUrlToLocalized: options.root is required');
    }
    var memoizerByDir = {};
    function getVariantInfosByBaseName(dir, cb) {
        if (!(dir in memoizerByDir)) {
            memoizerByDir[dir] = memoizeAsync(function (cb_) {
                fs.readdir(dir, function (err, fileNames) {
                    if (err) {
                        if (err.code === 'ENOENT') {
                            // Pretend that non-existent directories are empty
                            return cb(null, {});
                        } else {
                            return cb(err);
                        }
                    }
                    var variantInfosByBaseName = {};
                    if (!err) {
                        fileNames.forEach(function (fileName) {
                            var matchFileName = fileName.match(/^([\w\-~%!$&'\(\)*+,;=:@]*)((?:\.[\w\-~%!$&'\(\)*+,;=:@]*)*)$/);
                            if (matchFileName) {
                                var baseName = matchFileName[1] || 'index',
                                    extensionString = matchFileName[2],
                                    variantInfo = {
                                        fileName: fileName
                                    };

                                if (extensionString) {
                                    _.extend(variantInfo, getVariantInfoFromExtensionString(extensionString));
                                }
                                (variantInfosByBaseName[baseName] = variantInfosByBaseName[baseName] || []).push(variantInfo);
                            }
                        });
                    }
                    // Make sure that the variantInfo objects with the most specific locale ids come first:
                    Object.keys(variantInfosByBaseName).forEach(function (baseName) {
                        variantInfosByBaseName[baseName].sort(function (a, b) {
                            return (b.localeId ? b.localeId.length : 0) - (a.localeId ? a.localeId.length : 0);
                        });
                    });
                    cb_(null, variantInfosByBaseName);
                });
            });
        }
        memoizerByDir[dir](cb);
    }

    var getPrioritizedLocaleIds = memoizer(function (extensionLocaleId, localeParameterValue, localeCookieValue, acceptLanguageHeaderValue) {
        if (extensionLocaleId) {
            return [extensionLocaleId]; // /foo.en_US.html requires exact match
        }
        var prioritizedLocaleIds = [];
        if (localeParameterValue) {
            prioritizedLocaleIds.push(normalizeLocaleId(localeParameterValue));
        }
        if (localeCookieValue) {
            localeCookieValue.split(',').map(normalizeLocaleId).forEach(function (localeId) {
                Array.prototype.push.apply(prioritizedLocaleIds, expandLocaleIdToPrioritizedList(localeId));
            });
        }
        if (acceptLanguageHeaderValue) {
            acceptLanguageHeaderValue.replace(/;[^,]*/g, "").split(",").map(normalizeLocaleId).forEach(function (localeId) {
                Array.prototype.push.apply(prioritizedLocaleIds, expandLocaleIdToPrioritizedList(localeId));
            });
        }
        prioritizedLocaleIds.push('en_us', 'en');
        return prioritizedLocaleIds;
    });

    var cookieNameLowerCased = options.cookieName && options.cookieName.toLowerCase();

    return function rewriteStaticUrlToLocalized(req, res, next) {
        var matchDirAndFileName = req.url.match(/^(\/(?:[\w\.\-~%!$&'\(\)*+,;=:@]+\/)*)([\w\-~%!$&'\(\)*+,;=:@]*)((?:\.[\w\-~%!$&'\(\)*+,;=:@]*)*)((?:\?.*)?)$/);
        if (matchDirAndFileName) {
            var dir = matchDirAndFileName[1],
                baseName = matchDirAndFileName[2] ? decodeURIComponent(matchDirAndFileName[2]) : 'index',
                extensionString = matchDirAndFileName[3] ? decodeURIComponent(matchDirAndFileName[3]) : '',
                fileName = baseName + extensionString,
                queryString = matchDirAndFileName[4],
                localeCookieValue = cookieNameLowerCased && req.cookies && req.cookies[cookieNameLowerCased],
                matchLocaleParameterValue = queryString && queryString.match(/[&?]locale=([^&]+)(?:$|&)/),
                fileNameVariantInfo = getVariantInfoFromExtensionString(extensionString),
                prioritizedLocaleIds = getPrioritizedLocaleIds(fileNameVariantInfo.localeId,
                                                               matchLocaleParameterValue && matchLocaleParameterValue[1],
                                                               localeCookieValue,
                                                               req.headers['accept-language']),
                acceptHeader = req.headers.accept,
                acceptHeaderTokens = acceptHeader && parseQuality(acceptHeader);

            return getVariantInfosByBaseName(Path.resolve(options.root, dir.substr(1)), passError(next, function (variantInfosByBaseName) {
                var variantInfos = variantInfosByBaseName[baseName],
                    bestVariantInfo,
                    bestVariantQuality;
                if (variantInfos) {
                    for (var i = 0 ; i < prioritizedLocaleIds.length ; i += 1) {
                        var localeId = prioritizedLocaleIds[i];
                        for (var j = 0 ; j < variantInfos.length ; j += 1) {
                            var variantInfo = variantInfos[j];
                            if (variantInfo.fileName === fileName) {
                                // Exact file name match takes precedence over content negotiation
                                return next();
                            } else if (variantInfo.allExtensionsMatched) {
                                if (!variantInfo.localeId || variantInfo.localeId === localeId) {
                                    var contentTypeQuality = 0;
                                    if (variantInfo.contentType) {
                                        if (fileNameVariantInfo.contentType && fileNameVariantInfo.contentType === variantInfo.contentType) {
                                            contentTypeQuality = 2;
                                        }
                                        if (!contentTypeQuality) {
                                            if (acceptHeaderTokens) {
                                                acceptHeaderTokens.some(function (acceptHeaderToken) {
                                                    if (acceptHeaderToken.value === variantInfo.contentType ||
                                                        acceptHeaderToken.value === '*/*' ||
                                                        acceptHeaderToken.value === '*' ||
                                                        acceptHeaderToken.value === '*/' + variantInfo.contentTypeFragments[1] ||
                                                        acceptHeaderToken.value === variantInfo.contentTypeFragments[0] + '/*') {

                                                        contentTypeQuality = acceptHeaderToken.quality;
                                                        return true; // Short circuit out of the .some
                                                    }
                                                });
                                            } else {
                                                contentTypeQuality = 1;
                                            }
                                        }
                                    } else {
                                        contentTypeQuality = 1;
                                    }
                                    // In case two acceptable entries have the same quality, prefer the text/html one.
                                    if (contentTypeQuality > 0 && (!bestVariantInfo || contentTypeQuality > bestVariantQuality) || (contentTypeQuality === bestVariantQuality && variantInfo.contentType === 'text/html')) {
                                        bestVariantInfo = variantInfo;
                                        bestVariantQuality = contentTypeQuality;
                                    }
                                }
                            }
                        }
                    }
                    if (bestVariantInfo) {
                        var targetUrl = dir + bestVariantInfo.fileName + queryString;
                        req.url = targetUrl;
                        res.setHeader('Content-Location', targetUrl.substr(1));
                        res.setHeader('Vary', 'Cookie, Accept-Language, Accept');

                        if (bestVariantInfo.localeId) {
                            return fs.stat(Path.resolve(options.root, targetUrl.substr(1).replace(/\?.*$/, '')), passError(next, function (stat) {
                                res.setHeader('ETag', '"' + bestVariantInfo.localeId + '-' + stat.size + '-' + Number(stat.mtime) + '"');
                                // Disable conditional GET via If-Modified-Since for these requests, otherwise the static middleware
                                // might reply with a 304 after a locale switch.
                                delete req.headers['if-modified-since'];
                                next();
                            }));
                        } else {
                            next();
                        }
                    } else {
                        next();
                    }
                } else {
                    next();
                }
            }));
        } else {
            next();
        }
    };
};
<commit_msg>rewriteStaticUrlToLocalized: Also include the a negotiated Content-Type in the ETag.<commit_after>/*
 * Middleware that rewrites `req.url` to `/<fileName>.<localeId>.<extension>`
 * (as output by `buildProduction`) based on the value of a cookie
 * (optional, set by `options.cookieName`), the `Accept-Language`
 * request header, the `Accept` header, and the directory contents.
 *
 * rewriteStaticUrlToLocalized is intended to run right before `static`
 * pointed at the same directory as `options.root`.
 *
 * If `options.cookieName` is specified, the `cookieParser` middleware
 * must also be in the middleware chain.
 */

var fs = require('fs'),
    Path = require('path'),
    _ = require('underscore'),
    passError = require('passerror'),
    memoizeAsync = require('../memoizeAsync'),
    memoizer = require('memoizer'),
    mime = require('mime'),
    // List taken from an older version of CLDR, so it's probably incomplete:
    isLocaleIdRegExp = /^(?:aa|af|ak|am|ar|as|asa|az|be|bem|bez|bg|bm|bn|bo|br|brx|bs|byn|ca|cch|cgg|chr|cs|cy|da|dav|de|dv|dz|ebu|ee|el|en|eo|es|et|eu|fa|ff|fi|fil|fo|fr|fur|ga|gaa|gez|gl|gsw|gu|guz|gv|ha|haw|he|hi|hr|hu|hy|ia|id|ig|ii|in|is|it|iu|iw|ja|jmc|ka|kab|kaj|kam|kcg|kde|kea|kfo|khq|ki|kk|kl|kln|km|kn|ko|kok|kpe|ksb|ksh|ku|kw|ky|lag|lg|ln|lo|lt|luo|luy|lv|mas|mer|mfe|mg|mi|mk|ml|mn|mo|mr|ms|mt|my|naq|nb|nd|nds|ne|nl|nn|no|nr|nso|ny|nyn|oc|om|or|pa|pl|ps|pt|rm|ro|rof|root|ru|rw|rwk|sa|saq|se|seh|ses|sg|sh|shi|si|sid|sk|sl|sn|so|sq|sr|ss|ssy|st|sv|sw|syr|ta|te|teo|tg|th|ti|tig|tl|tn|to|tr|trv|ts|tt|tzm|ug|uk|ur|uz|ve|vi|vun|wal|wo|xh|xog|yo|zh|zu)(?:_|$)/;

// Note: These obscure extensions are listed in require('mime').types AND are considered valid locale ids: nb, so, ps, kfo, rm, st, sh, sid, in, tr, ms, gv, ts
// When resolving the variants we interpret them as locale ids take precendence.

// From Express 3.0:
function parseQualityToken(str) {
  var parts = str.split(/ *; */),
      val = parts[0],
      q = parts[1] ? parseFloat(parts[1].split(/ *= */)[1]) : 1;

  return { value: val, quality: q };
}

function parseQuality(str){
    return str
        .split(/ *, */)
        .map(parseQualityToken)
        .filter(function(obj) {
            return obj.quality;
        })
        .sort(function(a, b) {
            return b.quality - a.quality;
        });
}

/*
 * Replace - with _ and convert to lower case: en-GB => en_gb
 */
function normalizeLocaleId(localeId) {
    return localeId.replace(/-/g, '_').toLowerCase();
}

/*
 * Helper for getting a prioritized list of relevant locale ids from a
 * specific locale id. For instance, `en_US` produces `["en_US", "en"]`.
 */
function expandLocaleIdToPrioritizedList(localeId) {
    localeId = normalizeLocaleId(localeId);
    var localeIds = [localeId];
    while (/_[^_]+$/.test(localeId)) {
        localeId = localeId.replace(/_[^_]+$/, '');
        localeIds.push(localeId);
    }
    return localeIds;
}

// .html.en-US => {contentType: 'text/html', contentTypeFragments: ['text', 'html'], localeId: 'en_us'}
function getVariantInfoFromExtensionString(extensionString) {
    var variantInfo = {
        allExtensionsMatched: true
    };
    if (extensionString) {
        extensionString.split(/(?=\.)/).forEach(function (extension) {
            var localeId = normalizeLocaleId(extension.substr(1));
        if (isLocaleIdRegExp.test(localeId)) {
                variantInfo.localeId = localeId;
            } else {
                var contentType = mime.types[extension.substr(1)];
                if (contentType) {
                    variantInfo.contentType = contentType;
                    variantInfo.contentTypeFragments = contentType.split('/');
                } else {
                    variantInfo.allExtensionsMatched = false;
                }
            }
        });
    }
    return variantInfo;
}

module.exports = function (options) {
    options = options || {};
    if (!('root' in options)) {
        throw new Error('rewriteStaticUrlToLocalized: options.root is required');
    }
    var memoizerByDir = {};
    function getVariantInfosByBaseName(dir, cb) {
        if (!(dir in memoizerByDir)) {
            memoizerByDir[dir] = memoizeAsync(function (cb_) {
                fs.readdir(dir, function (err, fileNames) {
                    if (err) {
                        if (err.code === 'ENOENT') {
                            // Pretend that non-existent directories are empty
                            return cb(null, {});
                        } else {
                            return cb(err);
                        }
                    }
                    var variantInfosByBaseName = {};
                    if (!err) {
                        fileNames.forEach(function (fileName) {
                            var matchFileName = fileName.match(/^([\w\-~%!$&'\(\)*+,;=:@]*)((?:\.[\w\-~%!$&'\(\)*+,;=:@]*)*)$/);
                            if (matchFileName) {
                                var baseName = matchFileName[1] || 'index',
                                    extensionString = matchFileName[2],
                                    variantInfo = {
                                        fileName: fileName
                                    };

                                if (extensionString) {
                                    _.extend(variantInfo, getVariantInfoFromExtensionString(extensionString));
                                }
                                (variantInfosByBaseName[baseName] = variantInfosByBaseName[baseName] || []).push(variantInfo);
                            }
                        });
                    }
                    // Make sure that the variantInfo objects with the most specific locale ids come first:
                    Object.keys(variantInfosByBaseName).forEach(function (baseName) {
                        variantInfosByBaseName[baseName].sort(function (a, b) {
                            return (b.localeId ? b.localeId.length : 0) - (a.localeId ? a.localeId.length : 0);
                        });
                    });
                    cb_(null, variantInfosByBaseName);
                });
            });
        }
        memoizerByDir[dir](cb);
    }

    var getPrioritizedLocaleIds = memoizer(function (extensionLocaleId, localeParameterValue, localeCookieValue, acceptLanguageHeaderValue) {
        if (extensionLocaleId) {
            return [extensionLocaleId]; // /foo.en_US.html requires exact match
        }
        var prioritizedLocaleIds = [];
        if (localeParameterValue) {
            prioritizedLocaleIds.push(normalizeLocaleId(localeParameterValue));
        }
        if (localeCookieValue) {
            localeCookieValue.split(',').map(normalizeLocaleId).forEach(function (localeId) {
                Array.prototype.push.apply(prioritizedLocaleIds, expandLocaleIdToPrioritizedList(localeId));
            });
        }
        if (acceptLanguageHeaderValue) {
            acceptLanguageHeaderValue.replace(/;[^,]*/g, "").split(",").map(normalizeLocaleId).forEach(function (localeId) {
                Array.prototype.push.apply(prioritizedLocaleIds, expandLocaleIdToPrioritizedList(localeId));
            });
        }
        prioritizedLocaleIds.push('en_us', 'en');
        return prioritizedLocaleIds;
    });

    var cookieNameLowerCased = options.cookieName && options.cookieName.toLowerCase();

    return function rewriteStaticUrlToLocalized(req, res, next) {
        var matchDirAndFileName = req.url.match(/^(\/(?:[\w\.\-~%!$&'\(\)*+,;=:@]+\/)*)([\w\-~%!$&'\(\)*+,;=:@]*)((?:\.[\w\-~%!$&'\(\)*+,;=:@]*)*)((?:\?.*)?)$/);
        if (matchDirAndFileName) {
            var dir = matchDirAndFileName[1],
                baseName = matchDirAndFileName[2] ? decodeURIComponent(matchDirAndFileName[2]) : 'index',
                extensionString = matchDirAndFileName[3] ? decodeURIComponent(matchDirAndFileName[3]) : '',
                fileName = baseName + extensionString,
                queryString = matchDirAndFileName[4],
                localeCookieValue = cookieNameLowerCased && req.cookies && req.cookies[cookieNameLowerCased],
                matchLocaleParameterValue = queryString && queryString.match(/[&?]locale=([^&]+)(?:$|&)/),
                fileNameVariantInfo = getVariantInfoFromExtensionString(extensionString),
                prioritizedLocaleIds = getPrioritizedLocaleIds(fileNameVariantInfo.localeId,
                                                               matchLocaleParameterValue && matchLocaleParameterValue[1],
                                                               localeCookieValue,
                                                               req.headers['accept-language']),
                acceptHeader = req.headers.accept,
                acceptHeaderTokens = acceptHeader && parseQuality(acceptHeader);

            return getVariantInfosByBaseName(Path.resolve(options.root, dir.substr(1)), passError(next, function (variantInfosByBaseName) {
                var variantInfos = variantInfosByBaseName[baseName],
                    bestVariantInfo,
                    bestVariantQuality;
                if (variantInfos) {
                    for (var i = 0 ; i < prioritizedLocaleIds.length ; i += 1) {
                        var localeId = prioritizedLocaleIds[i];
                        for (var j = 0 ; j < variantInfos.length ; j += 1) {
                            var variantInfo = variantInfos[j];
                            if (variantInfo.fileName === fileName) {
                                // Exact file name match takes precedence over content negotiation
                                return next();
                            } else if (variantInfo.allExtensionsMatched) {
                                if (!variantInfo.localeId || variantInfo.localeId === localeId) {
                                    var contentTypeQuality = 0;
                                    if (variantInfo.contentType) {
                                        if (fileNameVariantInfo.contentType && fileNameVariantInfo.contentType === variantInfo.contentType) {
                                            contentTypeQuality = 2;
                                        }
                                        if (!contentTypeQuality) {
                                            if (acceptHeaderTokens) {
                                                acceptHeaderTokens.some(function (acceptHeaderToken) {
                                                    if (acceptHeaderToken.value === variantInfo.contentType ||
                                                        acceptHeaderToken.value === '*/*' ||
                                                        acceptHeaderToken.value === '*' ||
                                                        acceptHeaderToken.value === '*/' + variantInfo.contentTypeFragments[1] ||
                                                        acceptHeaderToken.value === variantInfo.contentTypeFragments[0] + '/*') {

                                                        contentTypeQuality = acceptHeaderToken.quality;
                                                        return true; // Short circuit out of the .some
                                                    }
                                                });
                                            } else {
                                                contentTypeQuality = 1;
                                            }
                                        }
                                    } else {
                                        contentTypeQuality = 1;
                                    }
                                    // In case two acceptable entries have the same quality, prefer the text/html one.
                                    if (contentTypeQuality > 0 && (!bestVariantInfo || contentTypeQuality > bestVariantQuality) || (contentTypeQuality === bestVariantQuality && variantInfo.contentType === 'text/html')) {
                                        bestVariantInfo = variantInfo;
                                        bestVariantQuality = contentTypeQuality;
                                    }
                                }
                            }
                        }
                    }
                    if (bestVariantInfo) {
                        var targetUrl = dir + bestVariantInfo.fileName + queryString;
                        req.url = targetUrl;
                        res.setHeader('Content-Location', targetUrl.substr(1));
                        res.setHeader('Vary', 'Cookie, Accept-Language, Accept');

                        var etag = '';
                        if (bestVariantInfo.contentType && !fileNameVariantInfo.contentType) {
                            etag += bestVariantInfo.contentType + '-';
                        }
                        if (bestVariantInfo.localeId && !fileNameVariantInfo.localeId) {
                            etag += bestVariantInfo.localeId + '-';
                        }

                        if (etag.length > 0) {
                            return fs.stat(Path.resolve(options.root, targetUrl.substr(1).replace(/\?.*$/, '')), passError(next, function (stat) {
                                res.setHeader('ETag', '"' + etag + stat.size + '-' + Number(stat.mtime) + '"');
                                // Disable conditional GET via If-Modified-Since for these requests, otherwise the static middleware
                                // might reply with a 304 after a locale switch.
                                delete req.headers['if-modified-since'];
                                next();
                            }));
                        } else {
                            next();
                        }
                    } else {
                        next();
                    }
                } else {
                    next();
                }
            }));
        } else {
            next();
        }
    };
};
<|endoftext|>