<commit_before>const fs = require('fs-promise')
const tls = require('tls')
const http = require('http')
const spdy = require('spdy')
const express = require('express')
const path = require('path')
const crypto = require('crypto')
const shell = require('shelljs')
const pm2 = require('pm2')
const proxy = require('http-proxy').createProxyServer({})
const username = require('username')
const resolveHome = require('./resolveHome')

const homeDir = resolveHome('~/.easy-pm')
const configsFile = path.resolve(homeDir, './configs')
const startFlagFile = path.resolve(homeDir, './start_flag')

const LE = require('letsencrypt')
const leStore = require('le-store-certbot').create({
	configDir: path.resolve(homeDir, './letsencrypt/etc'),
	debug: false
})
const leChallenge = require('le-challenge-fs').create({
	webrootPath: path.resolve(homeDir, './letsencrypt/var/'),
	debug: false
})
function leAgree(opts, agreeCb) {
	agreeCb(null, opts.tosUrl)
}
const le = LE.create({
	server: LE.productionServerUrl,
	store: leStore,
	challenges: { 'http-01': leChallenge },
	challengeType: 'http-01',
	agreeToTerms: leAgree,
	debug: false
})

const setupScriptPath = path.resolve(__dirname, './setup.js')
const isRoot = process.getuid() === 0
let rootPrefix = ''
let startFlag = false
let configPaths = []

fs.readFile(startFlagFile, 'utf8')
	.then(flag => {
		startFlag = flag === '1'
		return username()
	})
	.then(name => {
		rootPrefix = isRoot ? `sudo -u ${name}` : ''
		if (startFlag) return
		shell.exec(`${rootPrefix} node ${setupScriptPath}`)
		return new Promise((resolve, reject) => {
			pm2.connect(err => {
				if (err) return reject(err)

				pm2.list((err, apps) => {
					if (err) return reject(err)
					resolve(apps)
				})
			})
		})
			.then(apps => Promise.all(
				apps.filter(app => app.pm2_env.epm_config_path)
					.map(app => new Promise((resolve, reject) => {
						pm2.delete(app.pm_id, err => {
							if (err) return reject(err)
							resolve()
						})
					}))
			))
			.then(() => pm2.disconnect())
	})
	.then(() => fs.readFile(configsFile, 'utf8'))
	.then(configsString => {
		configPaths = configsString.split('\n').filter(s => !/^\s*$/.test(s))
		return Promise.all(configPaths.map(configPath => fs.readFile(configPath, 'utf8')))
	})
	.then(configStrs => {
		return new Promise((resolve, reject) => {
			pm2.connect(err => {
				if (err) return reject(err)
				resolve(configStrs)
			})
		})
	})
	.then(configStrs => Promise.all(configStrs.map((configStr, index) => {
		const configPath = configPaths[index]
		const config = JSON.parse(configStr)
		if (startFlag) return config

		const root = path.resolve(configPath, '..', resolveHome(config.root))
		const apps = config.apps.map(app => {
			const branch = app.branch || 'master'
			const configPathHash = crypto.createHash('sha1').update(configPath).digest('hex')
			app.env = Object.assign({
				epm_config_path: configPath,
				epm_server_port: config.port || 80,
				PORT: app.port
			}, app.env)
			return Object.assign({
				cwd: path.resolve(root, `${app.name}@${branch}`),
				script: 'npm',
				args: 'start',
				watch: true
			}, app, {
				name: `${app.name}-${branch}-${configPathHash}`
			})
		})

		return new Promise((resolve, reject) => {
			pm2.start({ apps }, err => {
				if (err) reject(err)
				resolve(config)
			})
		})
	})))
	.then(configs => {
		pm2.disconnect()
		return configs
	})
	.then(configs => Promise.all(configs.map((config, index) => createServer(configPaths[index], config))))
	.then(() => fs.writeFile(startFlagFile, '0', 'utf8'))

function createServer(configPath, config) {
	const root = path.resolve(configPath, '..', resolveHome(config.root))
	const port = config.port || 80

	const routes = {}
	config.apps.forEach(app => {
		const port = app.env && app.env.PORT || app.port
		if (app.domains && port) {
			app.domains.forEach(domain => {
				routes[domain] = port
			})
		}
	})
	function serverHandler(req, res) {
		const host = req.headers.host.split(':')[0]
		if (routes[host]) {
			proxy.web(req, res, {
				target: `http://127.0.0.1:${routes[host]}`,
				ws: true
			}, err => {
				console.log(err)
				res.end()
			})
		} else {
			const chunks = []
			const hookedApp = config.apps.find(app => {
				const re = new RegExp(`^/hooks/${app.name}/?([\?|#].*)?$`)
				return re.test(req.url)
			})
			if (hookedApp) {
				const branch = hookedApp.branch || 'master'
				const appPath = path.resolve(root, `${hookedApp.name}@${branch}`)
				req.on('data', chunk => chunks.push(chunk))
					.on('end', () => {
						const body = Buffer.concat(chunks).toString()
						const ghSignature = req.headers['x-hub-signature'].replace(/^sha1=/, '')
						const signature = crypto.createHmac('sha1', config.webhook.token).update(body).digest('hex')
						if (signature === ghSignature) {
							shell.exec(`cd ${appPath} && ${rootPrefix} git pull && ${rootPrefix} git checkout ${branch} && ${rootPrefix} npm install`)
						}
					})
			}
			res.end()
		}
	}
	const server = express()
	server.use('/', le.middleware())
	server.get('*', serverHandler)
	http.createServer(server).listen(port)

	if (config.ssl) {
		const ssl = config.ssl
		const port = ssl.port || 443
		const secureContext = {}
		const acmeDomains = []
		Object.keys(ssl.sites).forEach(domain => {
			const site = ssl.sites[domain]
			if (site === 'auto') {
				acmeDomains.push(domain)
			} else {
				const context = {}
				;['key', 'cert', 'ca'].forEach(key => {
					if (site[key]) {
						context[key] = fs.readFileSync(path.resolve(configPath, '..', resolveHome(site[key])), 'utf8')
					}
				})
				secureContext[domain] = tls.createSecureContext(context)
			}
		})
		const options = {
			SNICallback(domain, callback) {
				if (secureContext[domain]) {
					if (callback) {
						callback(null, secureContext[domain])
					} else {
						return secureContext[domain]
					}
				} else {
					throw new Error(`https not supported for ${domain}`)
				}
			}
		}
		if (acmeDomains.length <= 0) {
			spdy.createServer(options, server).listen(port)
			return
		}

		return Promise.all(acmeDomains.map(domain => le.check({ domains: [domain] })))
			.then(results => {
				if (results.reduce((prev, current) => prev && current, true)) {
					return results
				}
				return Promise.all(acmeDomains.map(domain => le.register({
					domains: [domain],
					email: ssl.email || 'me@imsun.net',
					agreeTos: true,
					rsaKeySize: ssl.rsaKeySize || 2048,
					challengeType: 'http-01'
				})))
			})
			.then(results => {
				acmeDomains.forEach((domain, index) => {
					secureContext[domain] = tls.createSecureContext({
						key: results[index].privkey,
						cert: results[index].cert
					})
				})
				spdy.createServer(options, server).listen(port)
			})
	}
}<commit_msg>support force redirecting to https<commit_after>const fs = require('fs-promise')
const tls = require('tls')
const http = require('http')
const spdy = require('spdy')
const express = require('express')
const path = require('path')
const crypto = require('crypto')
const shell = require('shelljs')
const pm2 = require('pm2')
const proxy = require('http-proxy').createProxyServer({})
const username = require('username')
const resolveHome = require('./resolveHome')

const homeDir = resolveHome('~/.easy-pm')
const configsFile = path.resolve(homeDir, './configs')
const startFlagFile = path.resolve(homeDir, './start_flag')

const LE = require('letsencrypt')
const leStore = require('le-store-certbot').create({
	configDir: path.resolve(homeDir, './letsencrypt/etc'),
	debug: false
})
const leChallenge = require('le-challenge-fs').create({
	webrootPath: path.resolve(homeDir, './letsencrypt/var/'),
	debug: false
})
function leAgree(opts, agreeCb) {
	agreeCb(null, opts.tosUrl)
}
const le = LE.create({
	server: LE.productionServerUrl,
	store: leStore,
	challenges: { 'http-01': leChallenge },
	challengeType: 'http-01',
	agreeToTerms: leAgree,
	debug: false
})

const setupScriptPath = path.resolve(__dirname, './setup.js')
const isRoot = process.getuid() === 0
let rootPrefix = ''
let startFlag = false
let configPaths = []

fs.readFile(startFlagFile, 'utf8')
	.then(flag => {
		startFlag = flag === '1'
		return username()
	})
	.then(name => {
		rootPrefix = isRoot ? `sudo -u ${name}` : ''
		if (startFlag) return
		shell.exec(`${rootPrefix} node ${setupScriptPath}`)
		return new Promise((resolve, reject) => {
			pm2.connect(err => {
				if (err) return reject(err)

				pm2.list((err, apps) => {
					if (err) return reject(err)
					resolve(apps)
				})
			})
		})
			.then(apps => Promise.all(
				apps.filter(app => app.pm2_env.epm_config_path)
					.map(app => new Promise((resolve, reject) => {
						pm2.delete(app.pm_id, err => {
							if (err) return reject(err)
							resolve()
						})
					}))
			))
			.then(() => pm2.disconnect())
	})
	.then(() => fs.readFile(configsFile, 'utf8'))
	.then(configsString => {
		configPaths = configsString.split('\n').filter(s => !/^\s*$/.test(s))
		return Promise.all(configPaths.map(configPath => fs.readFile(configPath, 'utf8')))
	})
	.then(configStrs => {
		return new Promise((resolve, reject) => {
			pm2.connect(err => {
				if (err) return reject(err)
				resolve(configStrs)
			})
		})
	})
	.then(configStrs => Promise.all(configStrs.map((configStr, index) => {
		const configPath = configPaths[index]
		const config = JSON.parse(configStr)
		if (startFlag) return config

		const root = path.resolve(configPath, '..', resolveHome(config.root))
		const apps = config.apps.map(app => {
			const branch = app.branch || 'master'
			const configPathHash = crypto.createHash('sha1').update(configPath).digest('hex')
			app.env = Object.assign({
				epm_config_path: configPath,
				epm_server_port: config.port || 80,
				PORT: app.port
			}, app.env)
			return Object.assign({
				cwd: path.resolve(root, `${app.name}@${branch}`),
				script: 'npm',
				args: 'start',
				watch: true
			}, app, {
				name: `${app.name}-${branch}-${configPathHash}`
			})
		})

		return new Promise((resolve, reject) => {
			pm2.start({ apps }, err => {
				if (err) reject(err)
				resolve(config)
			})
		})
	})))
	.then(configs => {
		pm2.disconnect()
		return configs
	})
	.then(configs => Promise.all(configs.map((config, index) => createServer(configPaths[index], config))))
	.then(() => fs.writeFile(startFlagFile, '0', 'utf8'))

function createServer(configPath, config) {
	const root = path.resolve(configPath, '..', resolveHome(config.root))
	const port = config.port || 80
	const ssl = config.ssl

	const routes = {}
	config.apps.forEach(app => {
		const port = app.env && app.env.PORT || app.port
		if (app.domains && port) {
			app.domains.forEach(domain => {
				routes[domain] = port
			})
		}
	})
	function serverHandler(req, res) {
		const host = req.headers.host.split(':')[0]
		if (ssl
			&& ssl.sites
			&& !ssl.disable_redirect
			&& ssl.sites[host]
			&& !ssl.sites[host].disable_redirect
			&& req.protocol !== 'https') {
			const port = ssl.port || 443
			res.redirect(`https://${host}:${port}${req.url}`)
			return
		}
		if (routes[host]) {
			proxy.web(req, res, {
				target: `http://127.0.0.1:${routes[host]}`,
				ws: true
			}, err => {
				console.log(err)
				res.end()
			})
		} else {
			const chunks = []
			const hookedApp = config.apps.find(app => {
				const re = new RegExp(`^/hooks/${app.name}/?([\?|#].*)?$`)
				return re.test(req.url)
			})
			if (hookedApp) {
				const branch = hookedApp.branch || 'master'
				const appPath = path.resolve(root, `${hookedApp.name}@${branch}`)
				req.on('data', chunk => chunks.push(chunk))
					.on('end', () => {
						const body = Buffer.concat(chunks).toString()
						const ghSignature = req.headers['x-hub-signature'].replace(/^sha1=/, '')
						const signature = crypto.createHmac('sha1', config.webhook.token).update(body).digest('hex')
						if (signature === ghSignature) {
							shell.exec(`cd ${appPath} && ${rootPrefix} git pull && ${rootPrefix} git checkout ${branch} && ${rootPrefix} npm install`)
						}
					})
			}
			res.end()
		}
	}
	const server = express()
	server.use('/', le.middleware())
	server.get('*', serverHandler)
	http.createServer(server).listen(port)

	if (ssl) {
		const port = ssl.port || 443
		const secureContext = {}
		const acmeDomains = []
		if (ssl.sites) {
			Object.keys(ssl.sites).forEach(domain => {
				const site = ssl.sites[domain]
				if (site === 'auto') {
					acmeDomains.push(domain)
				} else {
					const context = {}
					;['key', 'cert', 'ca'].forEach(key => {
						if (site[key]) {
							context[key] = fs.readFileSync(path.resolve(configPath, '..', resolveHome(site[key])), 'utf8')
						}
					})
					secureContext[domain] = tls.createSecureContext(context)
				}
			})
		}
		const options = {
			SNICallback(domain, callback) {
				if (secureContext[domain]) {
					if (callback) {
						callback(null, secureContext[domain])
					} else {
						return secureContext[domain]
					}
				} else {
					throw new Error(`https not supported for ${domain}`)
				}
			}
		}
		if (acmeDomains.length <= 0) {
			spdy.createServer(options, server).listen(port)
			return
		}

		return Promise.all(acmeDomains.map(domain => le.check({ domains: [domain] })))
			.then(results => {
				if (results.reduce((prev, current) => prev && current, true)) {
					return results
				}
				return Promise.all(acmeDomains.map(domain => le.register({
					domains: [domain],
					email: ssl.email || 'me@imsun.net',
					agreeTos: true,
					rsaKeySize: ssl.rsaKeySize || 2048,
					challengeType: 'http-01'
				})))
			})
			.then(results => {
				acmeDomains.forEach((domain, index) => {
					secureContext[domain] = tls.createSecureContext({
						key: results[index].privkey,
						cert: results[index].cert
					})
				})
				spdy.createServer(options, server).listen(port)
			})
	}
}<|endoftext|>


<commit_before>/*
 * Translated default messages for bootstrap-select.
 * Locale: RU (Russian; Русский)
 * Region: RU (Russian Federation)
 */
(function ($) {
  $.fn.selectpicker.defaults = {
    noneSelectedText: 'Ничего не выбрано',
    noneResultsText: 'Совпадений не найдено {0}',
    countSelectedText: 'Выбрано {0} из {1}',
    maxOptionsText: ['Достигнут предел ({n} {var} максимум)', 'Достигнут предел в группе ({n} {var} максимум)', ['items', 'item']],
    doneButtonText: 'Закрыть',
    multipleSeparator: ', '
  };
})(jQuery);
<commit_msg>Update defaults-ru_RU.js<commit_after>/*
 * Translated default messages for bootstrap-select.
 * Locale: RU (Russian; Русский)
 * Region: RU (Russian Federation)
 */
(function ($) {
  $.fn.selectpicker.defaults = {
    noneSelectedText: 'Ничего не выбрано',
    noneResultsText: 'Совпадений не найдено {0}',
    countSelectedText: 'Выбрано {0} из {1}',
    maxOptionsText: ['Достигнут предел ({n} {var} максимум)', 'Достигнут предел в группе ({n} {var} максимум)', ['шт.', 'шт.']],
    doneButtonText: 'Закрыть',
    selectAllText: 'Выбрать все',
    deselectAllText: 'Отменить все',
    multipleSeparator: ', '
  };
})(jQuery);
<|endoftext|>


<commit_before>import * as storage from '@octolinker/helper-settings';
import { showNotification } from '@octolinker/user-interface';

const pkgVersion = require('./package.json')
  .version.split('.')
  .slice(0, -1)
  .join('.');

export default async function() {
  const showUpdateNotification = storage.get('showUpdateNotification');

  if (!showUpdateNotification) {
    return;
  }

  const installedVersion = storage.get('version');

  storage.set('version', pkgVersion);

  if (installedVersion && installedVersion !== pkgVersion) {
    const response = await fetch(
      'https://api.github.com/repos/OctoLinker/OctoLinker/releases/latest',
    );
    const json = await response.json();
    const description = (json.body && json.body.split('\n')[0]) || '';
    const url = json.html_url;
    const version = json.tag_name.replace('v', '');

    const body = `New in OctoLinker ${version}: <b>${description}</b> – <a href="${url}" target="_blank">Release Notes</a>`;

    showNotification({ body });
  }
}
<commit_msg>Add survey promo box<commit_after>import * as storage from '@octolinker/helper-settings';
import { showNotification } from '@octolinker/user-interface';

const pkgVersion = require('./package.json')
  .version.split('.')
  .slice(0, -1)
  .join('.');

const showPromo = function(promoVersion) {
  const lastpromoVersion = storage.get('promoVersion');

  storage.set('promoVersion', promoVersion);

  if (lastpromoVersion !== promoVersion) {
    const body = `

      We're super interested to learn what you think about OctoLinker!
Please fill out this two questions survey so that we can make it better!

`;
    showNotification({ body });
  }
};

export default async function() {
  const showUpdateNotification = storage.get('showUpdateNotification');

  showPromo(2019);

  if (!showUpdateNotification) {
    return;
  }

  const installedVersion = storage.get('version');

  storage.set('version', pkgVersion);

  if (installedVersion && installedVersion !== pkgVersion) {
    const response = await fetch(
      'https://api.github.com/repos/OctoLinker/OctoLinker/releases/latest',
    );
    const json = await response.json();
    const description = (json.body && json.body.split('\n')[0]) || '';
    const url = json.html_url;
    const version = json.tag_name.replace('v', '');

    const body = `New in OctoLinker ${version}: <b>${description}</b> – <a href="${url}" target="_blank">Release Notes</a>`;

    showNotification({ body });
  }
}
<|endoftext|>


<commit_before>var mongoose = require('mongoose'),
    express = require('express'),
    app = express(),
    passport = require('passport'),
    expressSession = require('express-session'),
    MongoStore = require('connect-mongo')(expressSession),
    AWS = require("aws-sdk"),
    request = require("request"),
    bodyParser = require('body-parser');

//var mode = "release";
var mode = "debug";

config = require('config');

AWS.config.loadFromPath("./credentials.json");

var envconfig = require('./config')

mongoose.connect(envconfig.mongoconnectionstring);

if (envconfig.prerenderServiceUrl != null && envconfig.prerenderServiceUrl !== "")
  app.use(require('prerender-node').set("prerenderServiceUrl",envconfig.prerenderServiceUrl));

if (envconfig.twitterHandle != null && envconfig.twitterHandle !== "")
  twitterHandle = envconfig.twitterHandle;
else
  twitterHandle = "";

//load the models
require('./server/models/project.js');
require('./server/models/blog.js');
require('./server/models/resource.js');
require('./server/models/projectcategory.js');
require('./server/models/user.js');
require('./server/models/userrole.js');
require('./server/models/feature.js');
require('./server/models/product.js');
require('./server/models/rating.js');
require('./server/models/views.js');
require('./server/models/subscription.js');
require('./server/models/article.js');
require('./server/models/attachment.js');
require('./server/models/comment.js');
require('./server/models/picklist.js');
require('./server/models/picklistitem.js');

var Error = require('./server/controllers/error');

//configure passport strategies
require('./server/controllers/passport/passport.js')(passport);

//route controllers
var apiRoutes = require(__dirname+'/server/routes/api/api');
var gitRoutes = require(__dirname+'/server/routes/git/git');
var authRoutes = require(__dirname+'/server/routes/auth');
var systemRoutes = require(__dirname+'/server/routes/system/system');
const recaptchaRoutes = require(__dirname+'/server/routes/recaptcha')

app.use('/bower_components', express.static(__dirname + '/bower_components'));
app.use('/node_modules', express.static(__dirname + '/node_modules'));
app.use('/js', express.static(__dirname + '/public/scripts/build'));
app.use('/debug', express.static(__dirname + '/public/scripts/raw'));
app.use('/views', express.static(__dirname + '/public/views'));
app.use('/css', express.static(__dirname + '/public/styles/css'));
app.use('/maps', express.static(__dirname + '/public/styles/maps'));
app.use('/resources', express.static(__dirname + '/public/resources'));
app.use('/attachments', express.static(__dirname + '/public/attachments'));
app.use("/qsocks", express.static(__dirname + "/node_modules/qsocks"));
app.use("/configs", express.static(__dirname + "/public/configs"));
app.use("/images", (req, res, next) => {
  const url = "http://s3.amazonaws.com/" + envconfig.s3.bucket + "/images" + req.url
  request.get(url).pipe(res)
})
app.use((req, res, next) => {
  res.header("Cache-Control", "no-cache, no-store, must-revalidate");
  res.header("Pragma", "no-cache");
  res.header("Expires", 0);
  next();
});

app.use(expressSession({
  secret: 'mySecretKey',
  store: new MongoStore({ mongooseConnection: mongoose.connection}),
  cookie: {path:"/", domain:config.cookieDomain, httpOnly: true}
}));
app.use(passport.initialize());
app.use(passport.session());

app.use(bodyParser.json({limit: '10mb'}));
app.use(bodyParser.urlencoded({limit: '10mb'}));

app.get('/', function(req, res){
  res.render(__dirname+'/server/views/index.jade', {isAuthenticated: req.isAuthenticated(), user: req.user, mode: mode});
});

//This route is to accommodate for links from Old Branch (only applied to projects)
//The Url parameter is used to obtain the old projectId which is then used to get the new projectId
//The server then redirects to /#projects/:id

var Project = require('./server/models/project.js');

app.get("/projects/showthread.php", function(req, res, next){
  console.log(req.query);
  for(var key in req.query){
    var oldId = key.split("-")[0].trim();
    var query = {};
    break;
  }
  query.threadid = parseInt(oldId);
  Project.findOne(query, function(err, result){
    if(err){
      res.redirect('/404');
    }
    else{
      if(result && result._id){
        res.redirect("/#!project/"+result._id);
      }
      else{
        res.redirect('/404');
      }
    }
  });
});

app.use('/api', apiRoutes);
app.use('/git', gitRoutes);
app.use('/auth', authRoutes);
app.use('/system', systemRoutes);
app.use('/recaptcha', recaptchaRoutes)

app.use('/404', function(req, res){
  res.render(__dirname+'/server/views/404.jade');
});
app.use('/*', function(req, res){
  res.redirect('/404');
});

app.listen(process.env.PORT || 3001);
<commit_msg>fixing the server to output as release, not debug<commit_after>var mongoose = require('mongoose'),
    express = require('express'),
    app = express(),
    passport = require('passport'),
    expressSession = require('express-session'),
    MongoStore = require('connect-mongo')(expressSession),
    AWS = require("aws-sdk"),
    request = require("request"),
    bodyParser = require('body-parser');

var mode = "release";
//var mode = "debug";

config = require('config');

AWS.config.loadFromPath("./credentials.json");

var envconfig = require('./config')

mongoose.connect(envconfig.mongoconnectionstring);

if (envconfig.prerenderServiceUrl != null && envconfig.prerenderServiceUrl !== "")
  app.use(require('prerender-node').set("prerenderServiceUrl",envconfig.prerenderServiceUrl));

if (envconfig.twitterHandle != null && envconfig.twitterHandle !== "")
  twitterHandle = envconfig.twitterHandle;
else
  twitterHandle = "";

//load the models
require('./server/models/project.js');
require('./server/models/blog.js');
require('./server/models/resource.js');
require('./server/models/projectcategory.js');
require('./server/models/user.js');
require('./server/models/userrole.js');
require('./server/models/feature.js');
require('./server/models/product.js');
require('./server/models/rating.js');
require('./server/models/views.js');
require('./server/models/subscription.js');
require('./server/models/article.js');
require('./server/models/attachment.js');
require('./server/models/comment.js');
require('./server/models/picklist.js');
require('./server/models/picklistitem.js');

var Error = require('./server/controllers/error');

//configure passport strategies
require('./server/controllers/passport/passport.js')(passport);

//route controllers
var apiRoutes = require(__dirname+'/server/routes/api/api');
var gitRoutes = require(__dirname+'/server/routes/git/git');
var authRoutes = require(__dirname+'/server/routes/auth');
var systemRoutes = require(__dirname+'/server/routes/system/system');
const recaptchaRoutes = require(__dirname+'/server/routes/recaptcha')

app.use('/bower_components', express.static(__dirname + '/bower_components'));
app.use('/node_modules', express.static(__dirname + '/node_modules'));
app.use('/js', express.static(__dirname + '/public/scripts/build'));
app.use('/debug', express.static(__dirname + '/public/scripts/raw'));
app.use('/views', express.static(__dirname + '/public/views'));
app.use('/css', express.static(__dirname + '/public/styles/css'));
app.use('/maps', express.static(__dirname + '/public/styles/maps'));
app.use('/resources', express.static(__dirname + '/public/resources'));
app.use('/attachments', express.static(__dirname + '/public/attachments'));
app.use("/qsocks", express.static(__dirname + "/node_modules/qsocks"));
app.use("/configs", express.static(__dirname + "/public/configs"));
app.use("/images", (req, res, next) => {
  const url = "http://s3.amazonaws.com/" + envconfig.s3.bucket + "/images" + req.url
  request.get(url).pipe(res)
})
app.use((req, res, next) => {
  res.header("Cache-Control", "no-cache, no-store, must-revalidate");
  res.header("Pragma", "no-cache");
  res.header("Expires", 0);
  next();
});

app.use(expressSession({
  secret: 'mySecretKey',
  store: new MongoStore({ mongooseConnection: mongoose.connection}),
  cookie: {path:"/", domain:config.cookieDomain, httpOnly: true}
}));
app.use(passport.initialize());
app.use(passport.session());

app.use(bodyParser.json({limit: '10mb'}));
app.use(bodyParser.urlencoded({limit: '10mb'}));

app.get('/', function(req, res){
  res.render(__dirname+'/server/views/index.jade', {isAuthenticated: req.isAuthenticated(), user: req.user, mode: mode});
});

//This route is to accommodate for links from Old Branch (only applied to projects)
//The Url parameter is used to obtain the old projectId which is then used to get the new projectId
//The server then redirects to /#projects/:id

var Project = require('./server/models/project.js');

app.get("/projects/showthread.php", function(req, res, next){
  console.log(req.query);
  for(var key in req.query){
    var oldId = key.split("-")[0].trim();
    var query = {};
    break;
  }
  query.threadid = parseInt(oldId);
  Project.findOne(query, function(err, result){
    if(err){
      res.redirect('/404');
    }
    else{
      if(result && result._id){
        res.redirect("/#!project/"+result._id);
      }
      else{
        res.redirect('/404');
      }
    }
  });
});

app.use('/api', apiRoutes);
app.use('/git', gitRoutes);
app.use('/auth', authRoutes);
app.use('/system', systemRoutes);
app.use('/recaptcha', recaptchaRoutes)

app.use('/404', function(req, res){
  res.render(__dirname+'/server/views/404.jade');
});
app.use('/*', function(req, res){
  res.redirect('/404');
});

app.listen(process.env.PORT || 3001);
<|endoftext|>


<commit_before>var fs = require('fs'),
    compression = require('compression'),
    cookieParser = require('cookie-parser'),
    cookieSession = require('cookie-session'),
    express = require('express'),
    request = require('request'),
    bodyParser = require('body-parser'),
    compression = require('compression'),
    cookieParser = require('cookie-parser'),
    cookieSession = require('cookie-session'),
    url = require('url'),
    lessMiddleware = require('less-middleware'),
    config = require('config'),
    controllers = require('./app/controllers'),
    resources = require('./app/resources'),
    requestHandlers = require('./lib/request_handlers');

var app = express()

app.locals.config = config

app.use(bodyParser.json())
app.use(compression())
app.use(cookieParser())
app.use(cookieSession({ secret: config.cookie_session_secret }))

app.use(requestHandlers.login_token_parser)
app.use(requestHandlers.request_log)
app.use(requestHandlers.request_id_echo)

app.use(app.router);

app.use(lessMiddleware(__dirname + '/public', { once: (process.env.NODE_ENV == 'production') }, {}, { compress: (process.env.NODE_ENV == 'production') }))

app.set('view engine', 'ejs')
app.set('views', __dirname + '/app/views')

// Redirect to environment-appropriate domain, if necessary
app.all('*', function(req, res, next) {
  if (config.header_host_port != req.headers.host) {
    var redirectUrl = 'http://' + config.header_host_port + req.url
    console.log('req.host = %s but config.header_host_port = %s; redirecting to %s...', req.host, config.header_host_port, redirectUrl)
    res.redirect(301, redirectUrl)
  } else {
    next('route')
  }
})

app.use(express.static(__dirname + '/public'))

app.get('/twitter-sign-in', controllers.twitter_sign_in.get)
app.get(config.twitter.oauth_callback_uri, controllers.twitter_sign_in_callback.get)

app.post('/api/v1/users', resources.v1.users.post)
app.get('/api/v1/users/:user_id', resources.v1.users.get)
app.put('/api/v1/users/:user_id', resources.v1.users.put)
app.del('/api/v1/users/:user_id/login-token', resources.v1.users.delete)
app.get('/api/v1/users/:user_id/streets', resources.v1.users_streets.get)

app.post('/api/v1/streets', resources.v1.streets.post)
app.get('/api/v1/streets', resources.v1.streets.find)
app.head('/api/v1/streets', resources.v1.streets.find)

app.del('/api/v1/streets/:street_id', resources.v1.streets.delete)
app.head('/api/v1/streets/:street_id', resources.v1.streets.get)
app.get('/api/v1/streets/:street_id', resources.v1.streets.get)
app.put('/api/v1/streets/:street_id', resources.v1.streets.put)

app.post('/v1/feedback', resources.v1.feedback.post)

app.get('/.well-known/status', resources.well_known_status.get)

app.get('/help/about-beta', function(req, res) {
  res.sendfile(__dirname + '/public/help/about-beta/index.html')
})

app.get('/favicon.ico', function(req, res) {
  res.sendfile(__dirname + '/public/favicon.ico')
})

// Catch-all
app.use(function(req, res) {
  res.render('index', {})
})

app.listen(config.port, null, null, function() {
  console.log('Listening on port ' + config.port)
});
<commit_msg>fix URL for POST feedback endpoint<commit_after>var fs = require('fs'),
    compression = require('compression'),
    cookieParser = require('cookie-parser'),
    cookieSession = require('cookie-session'),
    express = require('express'),
    request = require('request'),
    bodyParser = require('body-parser'),
    compression = require('compression'),
    cookieParser = require('cookie-parser'),
    cookieSession = require('cookie-session'),
    url = require('url'),
    lessMiddleware = require('less-middleware'),
    config = require('config'),
    controllers = require('./app/controllers'),
    resources = require('./app/resources'),
    requestHandlers = require('./lib/request_handlers');

var app = express()

app.locals.config = config

app.use(bodyParser.json())
app.use(compression())
app.use(cookieParser())
app.use(cookieSession({ secret: config.cookie_session_secret }))

app.use(requestHandlers.login_token_parser)
app.use(requestHandlers.request_log)
app.use(requestHandlers.request_id_echo)

app.use(app.router);

app.use(lessMiddleware(__dirname + '/public', { once: (process.env.NODE_ENV == 'production') }, {}, { compress: (process.env.NODE_ENV == 'production') }))

app.set('view engine', 'ejs')
app.set('views', __dirname + '/app/views')

// Redirect to environment-appropriate domain, if necessary
app.all('*', function(req, res, next) {
  if (config.header_host_port != req.headers.host) {
    var redirectUrl = 'http://' + config.header_host_port + req.url
    console.log('req.host = %s but config.header_host_port = %s; redirecting to %s...', req.host, config.header_host_port, redirectUrl)
    res.redirect(301, redirectUrl)
  } else {
    next('route')
  }
})

app.use(express.static(__dirname + '/public'))

app.get('/twitter-sign-in', controllers.twitter_sign_in.get)
app.get(config.twitter.oauth_callback_uri, controllers.twitter_sign_in_callback.get)

app.post('/api/v1/users', resources.v1.users.post)
app.get('/api/v1/users/:user_id', resources.v1.users.get)
app.put('/api/v1/users/:user_id', resources.v1.users.put)
app.del('/api/v1/users/:user_id/login-token', resources.v1.users.delete)
app.get('/api/v1/users/:user_id/streets', resources.v1.users_streets.get)

app.post('/api/v1/streets', resources.v1.streets.post)
app.get('/api/v1/streets', resources.v1.streets.find)
app.head('/api/v1/streets', resources.v1.streets.find)

app.del('/api/v1/streets/:street_id', resources.v1.streets.delete)
app.head('/api/v1/streets/:street_id', resources.v1.streets.get)
app.get('/api/v1/streets/:street_id', resources.v1.streets.get)
app.put('/api/v1/streets/:street_id', resources.v1.streets.put)

app.post('/api/v1/feedback', resources.v1.feedback.post)

app.get('/.well-known/status', resources.well_known_status.get)

app.get('/help/about-beta', function(req, res) {
  res.sendfile(__dirname + '/public/help/about-beta/index.html')
})

app.get('/favicon.ico', function(req, res) {
  res.sendfile(__dirname + '/public/favicon.ico')
})

// Catch-all
app.use(function(req, res) {
  res.render('index', {})
})

app.listen(config.port, null, null, function() {
  console.log('Listening on port ' + config.port)
});
<|endoftext|>


<commit_before>(function() {
	// Insert a script tag pointing at the desired version of jQuery

	// Get the version from the url
	var jqueryRE = /[\\?&]jquery=([^&#]*)/,
		results = jqueryRE.exec( location.search ),
		version = "",
		jq,
		myScriptTag = document.getElementsByTagName( "script" )[document.getElementsByTagName( "script" ).length - 1],
		baseUrl = myScriptTag.src.replace( /(.*)\/.*$/, "$1/" ),
		url = baseUrl + "jquery-1.7.1.js";

	if( results ) {
		version = decodeURIComponent(results[results.length - 1].replace(/\+/g, " "));
	}

	switch( version ) {
		case "1.6.4":
			url = baseUrl + "jquery-1.6.4.js";
			break;
	}

	document.write( "<script src='" + url + "'></script>" );

	if ( parseInt( version.replace( /\./g, "" ), 10 ) < 170 && window.define && window.define.amd ) {
		document.write( '<script>define( "jquery", [], function () { return jQuery; } );</script>' );
	}
}());<commit_msg>Allow core jquery-git to be used for unit tests.<commit_after>(function() {
	// Insert a script tag pointing at the desired version of jQuery

	// Get the version from the url
	var jqueryRE = /[\\?&]jquery=([^&#]*)/,
		results = jqueryRE.exec( location.search ),
		version = "",
		jq,
		myScriptTag = document.getElementsByTagName( "script" )[document.getElementsByTagName( "script" ).length - 1],
		baseUrl = myScriptTag.src.replace( /(.*)\/.*$/, "$1/" ),
		url = baseUrl + "jquery-1.7.1.js";

	if( results ) {
		version = decodeURIComponent(results[results.length - 1].replace(/\+/g, " "));
	}

	switch( version ) {
		case "1.6.4":
			url = baseUrl + "jquery-1.6.4.js";
			break;
		case "git":
			url = "http://code.jquery.com/jquery-git.js";
			break;
	}

	document.write( "<script src='" + url + "'></script>" );

	if ( parseInt( version.replace( /\./g, "" ), 10 ) < 170 && window.define && window.define.amd ) {
		document.write( '<script>define( "jquery", [], function () { return jQuery; } );</script>' );
	}
}());<|endoftext|>


<commit_before>'use strict'

const express     = require('express')
const request     = require('request')

/******************************
slack imports
*******************************/
const slapp       = require('./slackSetup.js').slapp
const slashCommands = require('./slashCommands/slashIndex.js')
const botConvos   = require('./botConvos/convoIndex.js')

/******************************
firebase imports
*******************************/
// const firebase    = require('./firebaseSetup.js'),
//       slackDb = firebase.slackDb

/******************************
console.log testing area
*******************************/
// console.log(firebase.users)
// request('http://dev.jaywalk.me/api/spots/?lat=52.00000000000000000/?lng=-40.00000000000000000',function(err,res,body){
// console.log(res.statusCode)
// console.log(body)
// })

// attach Slapp to express server
const server = slapp.attachToExpress(express())
const port = process.env.PORT || 3000
// start http server
server.listen(port, (err) => {
  if (err) {
    return console.error(err)
  }

  console.log(`Listening on port ${port}`)
})
<commit_msg>process.env check log<commit_after>'use strict'

const express     = require('express')
const request     = require('request')

/******************************
slack imports
*******************************/
const slapp       = require('./slackSetup.js').slapp
const slashCommands = require('./slashCommands/slashIndex.js')
const botConvos   = require('./botConvos/convoIndex.js')

/******************************
firebase imports
*******************************/
// const firebase    = require('./firebaseSetup.js'),
//       slackDb = firebase.slackDb

/******************************
console.log testing area
*******************************/
// console.log(firebase.users)
// request('http://dev.jaywalk.me/api/spots/?lat=52.00000000000000000/?lng=-40.00000000000000000',function(err,res,body){
// console.log(res.statusCode)
// console.log(body)
// })
console.log(process.env.fire_email)

// attach Slapp to express server
const server = slapp.attachToExpress(express())
const port = process.env.PORT || 3000
// start http server
server.listen(port, (err) => {
  if (err) {
    return console.error(err)
  }

  console.log(`Listening on port ${port}`)
})
<|endoftext|>


<commit_before>(function() {
	// Insert a script tag pointing at the desired version of jQuery

	// Get the version from the url
	var jqueryRE = /[\\?&]jquery=([^&#]*)/,
		results = jqueryRE.exec( location.search ),
		version = "",
		myScriptTag = document.getElementsByTagName( "script" )[document.getElementsByTagName( "script" ).length - 1],
		baseUrl = myScriptTag.src.replace( /(.*)\/.*$/, "$1/" ),
		url;

	if( results ) {
		version = decodeURIComponent(results[results.length - 1].replace(/\+/g, " "));
	}

	switch( version ) {
		// Local versions
		case "1.6.4":
		case "1.7.1":
		case "1.7.2":
			url = baseUrl + "jquery-" + version + ".js";
			break;
		default:
			url = baseUrl + "jquery.js";
			break;
	}

	document.write( "<script src='" + url + "'></script>" );

	if ( parseInt( version.replace( /\./g, "" ), 10 ) < 170 && window.define && window.define.amd ) {
		document.write( '<script>define( "jquery", [], function () { return jQuery; } );</script>' );
	}
}());<commit_msg>Revert "default to unversioned vendored jquery.js for ci"<commit_after>(function() {
	// Insert a script tag pointing at the desired version of jQuery

	// Get the version from the url
	var jqueryRE = /[\\?&]jquery=([^&#]*)/,
		results = jqueryRE.exec( location.search ),
		version = "1.7.1",
		myScriptTag = document.getElementsByTagName( "script" )[document.getElementsByTagName( "script" ).length - 1],
		baseUrl = myScriptTag.src.replace( /(.*)\/.*$/, "$1/" ),
		url;

	if( results ) {
		version = decodeURIComponent(results[results.length - 1].replace(/\+/g, " "));
	}

	switch( version ) {
		// Local versions
		case "1.6.4":
		case "1.7.1":
		case "1.7.2":
			url = baseUrl + "jquery-" + version + ".js";
			break;
		// CDN versions
		default:
			url = "http://code.jquery.com/jquery-"+version+".js";
			break;
	}

	document.write( "<script src='" + url + "'></script>" );

	if ( parseInt( version.replace( /\./g, "" ), 10 ) < 170 && window.define && window.define.amd ) {
		document.write( '<script>define( "jquery", [], function () { return jQuery; } );</script>' );
	}
}());<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var express = require('express');

var app = module.exports = express.createServer();

// Configuration

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(require('stylus').middleware({ src: __dirname + '/public' }));
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});

// Routes

app.get('/', function(req, res){

    var url = app.address().address;
    var port = app.address().port;
    if(app.settings.env !== "development"){
        url = "http://www.twittertoplus.com";
        port = "80";
    }

  res.render('index', {
    title: 'Twitter To Google+',
    url:url,
    port:port,
    env:env
  });

});

app.get('/search/:criteria', function(req, res){
    // todo expect that this is called via ajax, and we want to return some

   //console.log('at server');

    var twitterId = req.params.criteria.split('~')[0];
    var screenName = req.params.criteria.split('~')[1];
    console.log('incoming criteria: ' + twitterId+','+screenName);

    var wasFound = false;

    var options = {
        host: 'www.gplus.to',
        port: 80,
        path: '/'+screenName,
        method: 'HEAD'

    };

    var http = require('http');
    var innerReq = http.request(options, function(innerRes){
        //console.log('STATUS: ' + innerRes.statusCode);

        // if statusCode == 302, then we found a google plus profile
        // if 200, we didn't find one
        if(parseInt(innerRes.statusCode,10)==302){
            console.log("found: "+twitterId+","+screenName);
            wasFound = true;
        }

        res.writeHead(200, {'content-type': 'text/json' });
        res.write( JSON.stringify({"twittedId":twitterId, "screenName":screenName, "wasFound":wasFound}) );
        res.end('\n');

    }).on('error', function(e) {
        console.log('problem with request: ' + e.message);
    });
    innerReq.end();

});

var port = process.env.PORT || 3000;
app.listen(port);

console.log("Express server listening at address %s on port %d in %s mode", app.address().address, app.address().port, app.settings.env);
<commit_msg>Sloppy bug<commit_after>
/**
 * Module dependencies.
 */

var express = require('express');

var app = module.exports = express.createServer();

// Configuration

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(require('stylus').middleware({ src: __dirname + '/public' }));
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});

// Routes

app.get('/', function(req, res){

    var url = app.address().address;
    var port = app.address().port;
    if(app.settings.env !== "development"){
        url = "http://www.twittertoplus.com";
        port = "80";
    }

  res.render('index', {
    title: 'Twitter To Google+',
    url:url,
    port:port,
    env:app.settings.env
  });

});

app.get('/search/:criteria', function(req, res){
    // todo expect that this is called via ajax, and we want to return some

   //console.log('at server');

    var twitterId = req.params.criteria.split('~')[0];
    var screenName = req.params.criteria.split('~')[1];
    console.log('incoming criteria: ' + twitterId+','+screenName);

    var wasFound = false;

    var options = {
        host: 'www.gplus.to',
        port: 80,
        path: '/'+screenName,
        method: 'HEAD'

    };

    var http = require('http');
    var innerReq = http.request(options, function(innerRes){
        //console.log('STATUS: ' + innerRes.statusCode);

        // if statusCode == 302, then we found a google plus profile
        // if 200, we didn't find one
        if(parseInt(innerRes.statusCode,10)==302){
            console.log("found: "+twitterId+","+screenName);
            wasFound = true;
        }

        res.writeHead(200, {'content-type': 'text/json' });
        res.write( JSON.stringify({"twittedId":twitterId, "screenName":screenName, "wasFound":wasFound}) );
        res.end('\n');

    }).on('error', function(e) {
        console.log('problem with request: ' + e.message);
    });
    innerReq.end();

});

var port = process.env.PORT || 3000;
app.listen(port);

console.log("Express server listening at address %s on port %d in %s mode", app.address().address, app.address().port, app.settings.env);
<|endoftext|>


<commit_before>// Namespace
this.Fizz = this.Fizz || { };

(function() {

	var EntityPool = Object.extend({

		init: function(prototypeEntity, size, sizeIsDynamic, resetFn) {

			if(false === prototypeEntity instanceof Fizz.Entity) {
				throw new Error("EntityPool must be instantiated with a valid Entity prototype");
			}

			this._pool = [ ];
			this._firstAvailable = null;
			this._prototypeEntity = null;
			this._resetFn = null;
			this._size = 0;
			this._sizeIsDynamic = false;

			this._activeEntityCount = 0;

			// Update the prototype reference
			this._prototypeEntity = prototypeEntity;
			this._prototypeEntity.exists = false;

			// Set 'reset' callback for entity recycling
			if(typeof resetFn === "function") {
				this._resetFn = resetFn;
			} else if(this._prototypeEntity.reset) {
				this._resetFn = this._prototypeEntity.reset;
			} else {
				this._resetFn = Fizz.noop;
			}

			// Via dynamic setters
			this.resetFn = resetFn;
			this.sizeIsDynamic = sizeIsDynamic;

			size = (typeof size === "number") ? size : EntityPool.DEFAULT_SIZE;

			this._grow(size); // zero to 'size'

		},

		reserve: function() {

			var node = this._firstAvailable; // may be null!

			// If all entities are reserved (or pool is empty)
			if(null === node) {
				if(this._sizeIsDynamic) {
					this._grow(this._size); // double the pool size – O(ln)
					node = this._firstAvailable;
				} else {
					return node;
				}
			}

			// Update the unused list head
			this._firstAvailable = node._next;
			node._next = null;

			// Configure entity to be rendered, and return
			this._resetFn.call(node);
			node.exists = true;

			this._activeEntityCount += 1;

			return node;

		},

		release: function(entity) {
			// Add the released entity to the front of the list
			entity._next = this._firstAvailable;
			this._firstAvailable = entity;
			this._activeEntityCount -= 1;
		},

		toString: function() {
			return "[EntityPool (size='" + this._size + "')]";
		},

		// Private methods

		_grow: function(size) {

			// Allow pool to grow based on an incremental value
			if(arguments.length && typeof size === "number" && size) {
				console.log("Growing pool to size ", this._size + size);
				for(var i = 0; i < size; i++) {
					this._grow();
				}
				return;
			}

			// Use metadata to create a linked list of free entities
			clone = this._prototypeEntity.clone();
			clone._next = this._firstAvailable;

			// Add the 'node' to the pool
			this._firstAvailable = clone;
			this._pool.push(clone);
			this._size += 1;

		}

	});

	// Static class members

	EntityPool.DEFAULT_SIZE = 100;

	// Public properties

	EntityPool.prototype.exposeProperty("size");
	EntityPool.prototype.exposeProperty("activeEntityCount");

	EntityPool.prototype.exposeProperty("sizeIsDynamic", "_sizeIsDynamic",
		Fizz.restrict.toBoolean("_sizeIsDynamic"));

	EntityPool.prototype.exposeProperty("resetFn", "_resetFn",
		Fizz.restrict.toDataType("_resetFn", "function"));

	// EntityPool export
	Fizz.EntityPool = EntityPool;

}());<commit_msg>Modified EntityPool class; class now defaults to zero-size pool, auto-grows to meet resource demand;<commit_after>// Namespace
this.Fizz = this.Fizz || { };

(function() {

	var EntityPool = Object.extend({

		init: function(prototypeEntity, size, sizeIsDynamic, resetFn) {

			if(false === prototypeEntity instanceof Fizz.Entity) {
				throw new Error("EntityPool must be instantiated with a valid Entity prototype");
			}

			this._pool = [ ];
			this._firstAvailable = null;
			this._prototypeEntity = null;
			this._resetFn = null;
			this._size = 0;
			this._sizeIsDynamic = true;

			this._activeEntityCount = 0;

			// Update the prototype reference
			this._prototypeEntity = prototypeEntity;
			this._prototypeEntity.exists = false;

			// Set 'reset' callback for entity recycling
			if(typeof resetFn === "function") {
				this._resetFn = resetFn;
			} else if(this._prototypeEntity.reset) {
				this._resetFn = this._prototypeEntity.reset;
			} else {
				this._resetFn = Fizz.noop;
			}

			// Via dynamic setters
			this.resetFn = resetFn;
			this.sizeIsDynamic = sizeIsDynamic;

			size = (typeof size === "number") ? size : EntityPool.DEFAULT_SIZE;

			this._grow(size); // zero to 'size'

		},

		reserve: function() {

			var node = this._firstAvailable; // may be null!

			// If all entities are reserved (or pool is empty)
			if(null === node) {
				if(this._sizeIsDynamic) {
					this._grow(this._size); // double the pool size – O(ln)
					node = this._firstAvailable;
				} else {
					return node;
				}
			}

			// Update the unused list head
			this._firstAvailable = node._next;
			node._next = null;

			// Configure entity to be rendered, and return
			this._resetFn.call(node);
			node.exists = true;

			this._activeEntityCount += 1;

			return node;

		},

		release: function(entity) {
			// Add the released entity to the front of the list
			entity._next = this._firstAvailable;
			this._firstAvailable = entity;
			this._activeEntityCount -= 1;
		},

		toString: function() {
			return "[EntityPool (size='" + this._size + "')]";
		},

		// Private methods

		_grow: function(size) {

			// Allow pool to grow based on an incremental value
			if(arguments.length && typeof size === "number" && size) {
				console.log("Growing pool to size ", this._size + size);
				for(var i = 0; i < size; i++) {
					this._grow();
				}
				return;
			}

			// Use metadata to create a linked list of free entities
			clone = this._prototypeEntity.clone();
			clone._next = this._firstAvailable;

			// Add the 'node' to the pool
			this._firstAvailable = clone;
			this._pool.push(clone);
			this._size += 1;

		}

	});

	// Static class members

	EntityPool.DEFAULT_SIZE = 0;

	// Public properties

	EntityPool.prototype.exposeProperty("size");
	EntityPool.prototype.exposeProperty("activeEntityCount");

	EntityPool.prototype.exposeProperty("sizeIsDynamic", "_sizeIsDynamic",
		Fizz.restrict.toBoolean("_sizeIsDynamic"));

	EntityPool.prototype.exposeProperty("resetFn", "_resetFn",
		Fizz.restrict.toDataType("_resetFn", "function"));

	// EntityPool export
	Fizz.EntityPool = EntityPool;

}());<|endoftext|>


<commit_before>import styled from 'styled-components'
import {theme} from 'styled-system'

const StyledButtonGroup = styled.div`
  && {
    display: flex;
    flex-flow: row wrap;
    width: fit-content;
    margin-bottom: -${props => props.melt ? 0 : theme('space.4')};

    a,
    button {
      margin-bottom: ${props => props.melt ? 0 : theme('space.4')};
    }

    border-radius: ${props => props.melt === true ? theme('radii.3') : 0};
  }
`

export default StyledButtonGroup
<commit_msg>chore(tocco-ui): shorten conditional check<commit_after>import styled from 'styled-components'
import {theme} from 'styled-system'

const StyledButtonGroup = styled.div`
  && {
    display: flex;
    flex-flow: row wrap;
    width: fit-content;
    margin-bottom: -${props => props.melt ? 0 : theme('space.4')};

    a,
    button {
      margin-bottom: ${props => props.melt ? 0 : theme('space.4')};
    }

    border-radius: ${props => props.melt ? theme('radii.3') : 0};
  }
`

export default StyledButtonGroup
<|endoftext|>


<commit_before>var mongo      = require("./helpers/mongo"),
    validator  = require("./validator"),
    collection = require("mongo-col"),
    pd         = require("pd");

var Repositories = {
  // creates a basic repository
  createRepository: function(name, content) {
    // connection to the collection
    var db = collection(name, process.env["MONGODB_DATABASE"]);

    return pd.extend(Object.create(db), content, {
      // reference to MongoDB's ObjectID
      ObjectId: require("mongodb").ObjectID,

      // basic findOne
      baseFindOne: function(conditions, callback) {
        db.findOne(conditions, function(err, object) {
          if (err) console.log(err);
          return callback(object ? object : null);
        });
      },

      // basic object delete with existence verification
      baseDelete: function(conditions, callback) {
        var that = this;

        this.baseFindOne(conditions, function(object) {
          if (object) {
            db.remove(conditions, function(err) {
              return callback((err) ? false : true, object);
            });
          } else {
            return callback(false, null);
          }
        });
      },

      // basic save with validation handling
      baseSave: function(object, callback) {
        var that = this;

        // validates the object
        validator.validate(name, object, function(errors) {
          // if there's errors, return the object with its validation errors
          if (errors) {
            return callback(object, errors);

          } else {
            // if it's a new object, save it and return it with its new ObjectID
            if (mongo.isNew(object)) {
              object._id = new that.ObjectId();
              db.insert(object, function(err) {
                if (err) {
                  console.log(err);
                  return callback(object, err);
                }
              });

            } else {
              // if the object already exists, update it and return the object
              var objectToUpdate = pd.extend({}, object);
              delete objectToUpdate._id;

              db.update({ _id: that.ObjectId(object._id) }, { $set: objectToUpdate }, function(err) {
                if (err) {
                  console.log(err);
                  return callback(object, err);
                }
              });
            }

            return callback(object);
          }
        });
      }
    });
  }
};

module.exports = Repositories;<commit_msg>testing added/updated dates<commit_after>var mongo      = require("./helpers/mongo"),
    validator  = require("./validator"),
    collection = require("mongo-col"),
    pd         = require("pd");

var Repositories = {
  // creates a basic repository
  createRepository: function(name, content) {
    // connection to the collection
    var db = collection(name, process.env["MONGODB_DATABASE"]);

    return pd.extend(Object.create(db), content, {
      // reference to MongoDB's ObjectID
      ObjectId: require("mongodb").ObjectID,

      // basic findOne
      baseFindOne: function(conditions, callback) {
        db.findOne(conditions, function(err, object) {
          if (err) console.log(err);
          return callback(object ? object : null);
        });
      },

      // basic object delete with existence verification
      baseDelete: function(conditions, callback) {
        var that = this;

        this.baseFindOne(conditions, function(object) {
          if (object) {
            db.remove(conditions, function(err) {
              return callback((err) ? false : true, object);
            });
          } else {
            return callback(false, null);
          }
        });
      },

      // basic save with validation handling
      baseSave: function(object, callback) {
        var that = this;

        // validates the object
        validator.validate(name, object, function(errors) {
          // if there's errors, return the object with its validation errors
          if (errors) {
            return callback(object, errors);

          } else {
            // if it's a new object, save it and return it with its new ObjectID
            if (mongo.isNew(object)) {
              object._id = new that.ObjectId();
              object.addedDate = new Date();
              db.insert(object, function(err) {
                if (err) {
                  console.log(err);
                  return callback(object, err);
                }
              });

            } else {
              // if the object already exists, update it and return the object
              var objectToUpdate = pd.extend({}, object);
              delete objectToUpdate._id;
              object.updatedDate = new Date();

              db.update({ _id: that.ObjectId(object._id) }, { $set: objectToUpdate }, function(err) {
                if (err) {
                  console.log(err);
                  return callback(object, err);
                }
              });
            }

            return callback(object);
          }
        });
      }
    });
  }
};

module.exports = Repositories;<|endoftext|>


<commit_before>var flownetwork = require("flownetwork");
var createGraph = require("ngraph.graph");
var createPixiGraphics = require('ngraph.pixi');

window.buildGuestsNetwork = function(){
	n = new flownetwork();

	// individuals

	// friends
	n.addEdge('s', 'or', 1, 0);
	n.addEdge('s', 'guy', 1, 0);
	n.addEdge('s', 'eran', 1, 0);
	n.addEdge('s', 'yoni', 1, 0);
	n.addEdge('s', 'alon', 1, 0);
	n.addEdge('s', 'ailon', 1, 0);
	n.addEdge('s', 'omri', 1, 0);
	n.addEdge('s', 'tal', 1, 0);
	n.addEdge('s', 'ori', 1, 0);
	n.addEdge('s', 'elad', 1, 0);
	n.addEdge('s', 'udi', 1, 0);

	// family
	n.addEdge('s', 'jacob', 1, 0);
	n.addEdge('s', 'sari', 1, 0);
	n.addEdge('s', 'zipi', 1, 0);
	n.addEdge('s', 'efraim', 1, 0);
	n.addEdge('s', 'nava', 1, 0);
	n.addEdge('s', 'oded', 1, 0);
	n.addEdge('s', 'david', 1, 0);
	n.addEdge('s', 'shifra', 1, 0);
	n.addEdge('s', 'sharona', 1, 0);
	n.addEdge('s', 'albert', 1, 0);

	// exceptions: guests which won't sit at the same table.
	n.addEdge('ex1', 'table1', 1, 0);
	n.addEdge('ex1', 'table2', 1, 0);
	n.addEdge('ex1', 'table3', 1, 0);
	n.addEdge('ex1', 'table4', 1, 0);

	n.addEdge('oded', 'ex1', 1, 0);
	n.addEdge('david', 'ex1', 1, 0);

	// tables
	n.addEdge('table1', 't', 8, 0);
	n.addEdge('table2', 't', 8, 0);
	n.addEdge('table3', 't', 8, 0);
	n.addEdge('table4', 't', 8, 0);

	// connect individuals to tables.
	n.addEdge('or', 'table1', 1, 0);
	n.addEdge('or', 'table2', 1, 0);
	n.addEdge('or', 'table3', 1, 0);
	n.addEdge('or', 'table4', 1, 0);

	n.addEdge('guy', 'table1', 1, 0);
	n.addEdge('guy', 'table2', 1, 0);
	n.addEdge('guy', 'table3', 1, 0);
	n.addEdge('guy', 'table4', 1, 0);

	n.addEdge('eran', 'table1', 1, 0);
	n.addEdge('eran', 'table2', 1, 0);
	n.addEdge('eran', 'table3', 1, 0);
	n.addEdge('eran', 'table4', 1, 0);

	n.addEdge('yoni', 'table1', 1, 0);
	n.addEdge('yoni', 'table2', 1, 0);
	n.addEdge('yoni', 'table3', 1, 0);
	n.addEdge('yoni', 'table4', 1, 0);

	n.addEdge('alon', 'table1', 1, 0);
	n.addEdge('alon', 'table2', 1, 0);
	n.addEdge('alon', 'table3', 1, 0);
	n.addEdge('alon', 'table4', 1, 0);

	n.addEdge('ailon', 'table1', 1, 0);
	n.addEdge('ailon', 'table2', 1, 0);
	n.addEdge('ailon', 'table3', 1, 0);
	n.addEdge('ailon', 'table4', 1, 0);

	n.addEdge('omri', 'table1', 1, 0);
	n.addEdge('omri', 'table2', 1, 0);
	n.addEdge('omri', 'table3', 1, 0);
	n.addEdge('omri', 'table4', 1, 0);

	n.addEdge('tal', 'table1', 1, 0);
	n.addEdge('tal', 'table2', 1, 0);
	n.addEdge('tal', 'table3', 1, 0);
	n.addEdge('tal', 'table4', 1, 0);

	n.addEdge('ori', 'table1', 1, 0);
	n.addEdge('ori', 'table2', 1, 0);
	n.addEdge('ori', 'table3', 1, 0);
	n.addEdge('ori', 'table4', 1, 0);

	n.addEdge('elad', 'table1', 1, 0);
	n.addEdge('elad', 'table2', 1, 0);
	n.addEdge('elad', 'table3', 1, 0);
	n.addEdge('elad', 'table4', 1, 0);

	n.addEdge('udi', 'table1', 1, 0);
	n.addEdge('udi', 'table2', 1, 0);
	n.addEdge('udi', 'table3', 1, 0);
	n.addEdge('udi', 'table4', 1, 0);

	n.addEdge('jacob', 'table1', 1, 0);
	n.addEdge('jacob', 'table2', 1, 0);
	n.addEdge('jacob', 'table3', 1, 0);
	n.addEdge('jacob', 'table4', 1, 0);

	n.addEdge('sari', 'table1', 1, 0);
	n.addEdge('sari', 'table2', 1, 0);
	n.addEdge('sari', 'table3', 1, 0);
	n.addEdge('sari', 'table4', 1, 0);

	n.addEdge('zipi', 'table1', 1, 0);
	n.addEdge('zipi', 'table2', 1, 0);
	n.addEdge('zipi', 'table3', 1, 0);
	n.addEdge('zipi', 'table4', 1, 0);

	n.addEdge('efraim', 'table1', 1, 0);
	n.addEdge('efraim', 'table2', 1, 0);
	n.addEdge('efraim', 'table3', 1, 0);
	n.addEdge('efraim', 'table4', 1, 0);

	n.addEdge('nava', 'table1', 1, 0);
	n.addEdge('nava', 'table2', 1, 0);
	n.addEdge('nava', 'table3', 1, 0);
	n.addEdge('nava', 'table4', 1, 0);

	// n.addEdge('oded', 'table1', 1, 0);
	// n.addEdge('oded', 'table2', 1, 0);
	// n.addEdge('oded', 'table3', 1, 0);
	// n.addEdge('oded', 'table4', 1, 0);

	// n.addEdge('david', 'table1', 1, 0);
	// n.addEdge('david', 'table2', 1, 0);
	// n.addEdge('david', 'table3', 1, 0);
	// n.addEdge('david', 'table4', 1, 0);

	n.addEdge('shifra', 'table1', 1, 0);
	n.addEdge('shifra', 'table2', 1, 0);
	n.addEdge('shifra', 'table3', 1, 0);
	n.addEdge('shifra', 'table4', 1, 0);

	n.addEdge('sharona', 'table1', 1, 0);
	n.addEdge('sharona', 'table2', 1, 0);
	n.addEdge('sharona', 'table3', 1, 0);
	n.addEdge('sharona', 'table4', 1, 0);

	n.addEdge('albert', 'table1', 1, 0);
	n.addEdge('albert', 'table2', 1, 0);
	n.addEdge('albert', 'table3', 1, 0);
	n.addEdge('albert', 'table4', 1, 0);

	return n;
}

window.computeFlow = function(n) {
	return n.maxFlow('s', 't');
}

window.computeFlowWithCost = function(n) {
	return n.maxFlowMinCost('s', 't');
}

window.convertNetworks = function (n) {
	var g = createGraph();
	var vertices = Object.keys(n.edges);

	for(var i = 0; i < vertices.length; i++) {
    	var edges = n.edges[vertices[i]];
    	 for(var j = 0; j < edges.length; j++) {
        	var edge = edges[j];
        	if(edge.capacity == 0) { continue; }
        	g.addLink(edge.source, edge.sink, {flow: edge.flow, capacity: edge.capacity, cost: edge.cost});
        }
    }

    return g;
}

window.displayNetwork = function(n) {
	var graph = convertNetworks(n);
	// Create a pixi renderer:
	var pixiGraphics = createPixiGraphics(graph, {
		physics: {
	      springLength: window.innerWidth/6,
	      springCoeff: 0.00005,
	      dragCoeff: 0.06,
	      gravity: -1.2,
	    }
	});

	// setup our custom looking nodes and links:
	pixiGraphics.createNodeUI(require('./lib/createNodeUI'))
  	.renderNode(require('./lib/renderNode'))
  	.createLinkUI(require('./lib/createLinkUI'))
  	.renderLink(require('./lib/renderLink'));

  	// just make sure first node does not move:
	var layout = pixiGraphics.layout;
	layout.setNodePosition('s', -window.innerWidth/3, 0);
	layout.pinNode(graph.getNode('s'), true);

	layout.setNodePosition('t', window.innerWidth/3, 0);
	layout.pinNode(graph.getNode('t'), true);

	pixiGraphics.run();

	return graph;
}<commit_msg>redeclare functions<commit_after>var flownetwork = require("flownetwork");
var createGraph = require("ngraph.graph");
var createPixiGraphics = require('ngraph.pixi');

function buildGuestsNetwork() {
	n = new flownetwork();

	// individuals

	// friends
	n.addEdge('s', 'or', 1, 0);
	n.addEdge('s', 'guy', 1, 0);
	n.addEdge('s', 'eran', 1, 0);
	n.addEdge('s', 'yoni', 1, 0);
	n.addEdge('s', 'alon', 1, 0);
	n.addEdge('s', 'ailon', 1, 0);
	n.addEdge('s', 'omri', 1, 0);
	n.addEdge('s', 'tal', 1, 0);
	n.addEdge('s', 'ori', 1, 0);
	n.addEdge('s', 'elad', 1, 0);
	n.addEdge('s', 'udi', 1, 0);

	// family
	n.addEdge('s', 'jacob', 1, 0);
	n.addEdge('s', 'sari', 1, 0);
	n.addEdge('s', 'zipi', 1, 0);
	n.addEdge('s', 'efraim', 1, 0);
	n.addEdge('s', 'nava', 1, 0);
	n.addEdge('s', 'oded', 1, 0);
	n.addEdge('s', 'david', 1, 0);
	n.addEdge('s', 'shifra', 1, 0);
	n.addEdge('s', 'sharona', 1, 0);
	n.addEdge('s', 'albert', 1, 0);

	// exceptions: guests which won't sit at the same table.
	n.addEdge('ex1', 'table1', 1, 0);
	n.addEdge('ex1', 'table2', 1, 0);
	n.addEdge('ex1', 'table3', 1, 0);
	n.addEdge('ex1', 'table4', 1, 0);

	n.addEdge('oded', 'ex1', 1, 0);
	n.addEdge('david', 'ex1', 1, 0);

	// tables
	n.addEdge('table1', 't', 8, 0);
	n.addEdge('table2', 't', 8, 0);
	n.addEdge('table3', 't', 8, 0);
	n.addEdge('table4', 't', 8, 0);

	// connect individuals to tables.
	n.addEdge('or', 'table1', 1, 0);
	n.addEdge('or', 'table2', 1, 0);
	n.addEdge('or', 'table3', 1, 0);
	n.addEdge('or', 'table4', 1, 0);

	n.addEdge('guy', 'table1', 1, 0);
	n.addEdge('guy', 'table2', 1, 0);
	n.addEdge('guy', 'table3', 1, 0);
	n.addEdge('guy', 'table4', 1, 0);

	n.addEdge('eran', 'table1', 1, 0);
	n.addEdge('eran', 'table2', 1, 0);
	n.addEdge('eran', 'table3', 1, 0);
	n.addEdge('eran', 'table4', 1, 0);

	n.addEdge('yoni', 'table1', 1, 0);
	n.addEdge('yoni', 'table2', 1, 0);
	n.addEdge('yoni', 'table3', 1, 0);
	n.addEdge('yoni', 'table4', 1, 0);

	n.addEdge('alon', 'table1', 1, 0);
	n.addEdge('alon', 'table2', 1, 0);
	n.addEdge('alon', 'table3', 1, 0);
	n.addEdge('alon', 'table4', 1, 0);

	n.addEdge('ailon', 'table1', 1, 0);
	n.addEdge('ailon', 'table2', 1, 0);
	n.addEdge('ailon', 'table3', 1, 0);
	n.addEdge('ailon', 'table4', 1, 0);

	n.addEdge('omri', 'table1', 1, 0);
	n.addEdge('omri', 'table2', 1, 0);
	n.addEdge('omri', 'table3', 1, 0);
	n.addEdge('omri', 'table4', 1, 0);

	n.addEdge('tal', 'table1', 1, 0);
	n.addEdge('tal', 'table2', 1, 0);
	n.addEdge('tal', 'table3', 1, 0);
	n.addEdge('tal', 'table4', 1, 0);

	n.addEdge('ori', 'table1', 1, 0);
	n.addEdge('ori', 'table2', 1, 0);
	n.addEdge('ori', 'table3', 1, 0);
	n.addEdge('ori', 'table4', 1, 0);

	n.addEdge('elad', 'table1', 1, 0);
	n.addEdge('elad', 'table2', 1, 0);
	n.addEdge('elad', 'table3', 1, 0);
	n.addEdge('elad', 'table4', 1, 0);

	n.addEdge('udi', 'table1', 1, 0);
	n.addEdge('udi', 'table2', 1, 0);
	n.addEdge('udi', 'table3', 1, 0);
	n.addEdge('udi', 'table4', 1, 0);

	n.addEdge('jacob', 'table1', 1, 0);
	n.addEdge('jacob', 'table2', 1, 0);
	n.addEdge('jacob', 'table3', 1, 0);
	n.addEdge('jacob', 'table4', 1, 0);

	n.addEdge('sari', 'table1', 1, 0);
	n.addEdge('sari', 'table2', 1, 0);
	n.addEdge('sari', 'table3', 1, 0);
	n.addEdge('sari', 'table4', 1, 0);

	n.addEdge('zipi', 'table1', 1, 0);
	n.addEdge('zipi', 'table2', 1, 0);
	n.addEdge('zipi', 'table3', 1, 0);
	n.addEdge('zipi', 'table4', 1, 0);

	n.addEdge('efraim', 'table1', 1, 0);
	n.addEdge('efraim', 'table2', 1, 0);
	n.addEdge('efraim', 'table3', 1, 0);
	n.addEdge('efraim', 'table4', 1, 0);

	n.addEdge('nava', 'table1', 1, 0);
	n.addEdge('nava', 'table2', 1, 0);
	n.addEdge('nava', 'table3', 1, 0);
	n.addEdge('nava', 'table4', 1, 0);

	// n.addEdge('oded', 'table1', 1, 0);
	// n.addEdge('oded', 'table2', 1, 0);
	// n.addEdge('oded', 'table3', 1, 0);
	// n.addEdge('oded', 'table4', 1, 0);

	// n.addEdge('david', 'table1', 1, 0);
	// n.addEdge('david', 'table2', 1, 0);
	// n.addEdge('david', 'table3', 1, 0);
	// n.addEdge('david', 'table4', 1, 0);

	n.addEdge('shifra', 'table1', 1, 0);
	n.addEdge('shifra', 'table2', 1, 0);
	n.addEdge('shifra', 'table3', 1, 0);
	n.addEdge('shifra', 'table4', 1, 0);

	n.addEdge('sharona', 'table1', 1, 0);
	n.addEdge('sharona', 'table2', 1, 0);
	n.addEdge('sharona', 'table3', 1, 0);
	n.addEdge('sharona', 'table4', 1, 0);

	n.addEdge('albert', 'table1', 1, 0);
	n.addEdge('albert', 'table2', 1, 0);
	n.addEdge('albert', 'table3', 1, 0);
	n.addEdge('albert', 'table4', 1, 0);

	return n;
}

function computeFlow(n) {
	return n.maxFlow('s', 't');
}

function computeFlowWithCost(n) {
	return n.maxFlowMinCost('s', 't');
}

function convertNetworks(n) {
	var g = createGraph();
	var vertices = Object.keys(n.edges);

	for(var i = 0; i < vertices.length; i++) {
    	var edges = n.edges[vertices[i]];
    	 for(var j = 0; j < edges.length; j++) {
        	var edge = edges[j];
        	if(edge.capacity == 0) { continue; }
        	g.addLink(edge.source, edge.sink, {flow: edge.flow, capacity: edge.capacity, cost: edge.cost});
        }
    }

    return g;
}

function displayNetwork(n) {
	var graph = convertNetworks(n);
	// Create a pixi renderer:
	var pixiGraphics = createPixiGraphics(graph, {
		physics: {
	      springLength: window.innerWidth/6,
	      springCoeff: 0.00005,
	      dragCoeff: 0.06,
	      gravity: -1.2,
	    }
	});

	// setup our custom looking nodes and links:
	pixiGraphics.createNodeUI(require('./lib/createNodeUI'))
  	.renderNode(require('./lib/renderNode'))
  	.createLinkUI(require('./lib/createLinkUI'))
  	.renderLink(require('./lib/renderLink'));

  	// just make sure first node does not move:
	var layout = pixiGraphics.layout;
	layout.setNodePosition('s', -window.innerWidth/3, 0);
	layout.pinNode(graph.getNode('s'), true);

	layout.setNodePosition('t', window.innerWidth/3, 0);
	layout.pinNode(graph.getNode('t'), true);

	pixiGraphics.run();

	return graph;
}<|endoftext|>


<commit_before>import L from 'leaflet';
import featureLayerService from '../../Services/FeatureLayerService';
import { cleanUrl, warn, setEsriAttribution } from '../../Util';
import VirtualGrid from 'leaflet-virtual-grid';
import BinarySearchIndex from 'tiny-binary-search';

export var FeatureManager = VirtualGrid.extend({
  /**
   * Options
   */

  options: {
    attribution: null,
    where: '1=1',
    fields: ['*'],
    from: false,
    to: false,
    timeField: false,
    timeFilterMode: 'server',
    simplifyFactor: 0,
    precision: 6
  },

  /**
   * Constructor
   */

  initialize: function (options) {
    VirtualGrid.prototype.initialize.call(this, options);

    options.url = cleanUrl(options.url);
    options = L.setOptions(this, options);

    this.service = featureLayerService(options);
    this.service.addEventParent(this);

    // use case insensitive regex to look for common fieldnames used for indexing
    if (this.options.fields[0] !== '*') {
      var oidCheck = false;
      for (var i = 0; i < this.options.fields.length; i++) {
        if (this.options.fields[i].match(/^(OBJECTID|FID|OID|ID)$/i)) {
          oidCheck = true;
        }
      }
      if (oidCheck === false) {
        warn('no known esriFieldTypeOID field detected in fields Array.  Please add an attribute field containing unique IDs to ensure the layer can be drawn correctly.');
      }
    }

    if (this.options.timeField.start && this.options.timeField.end) {
      this._startTimeIndex = new BinarySearchIndex();
      this._endTimeIndex = new BinarySearchIndex();
    } else if (this.options.timeField) {
      this._timeIndex = new BinarySearchIndex();
    }

    this._cache = {};
    this._currentSnapshot = []; // cache of what layers should be active
    this._activeRequests = 0;
  },

  /**
   * Layer Interface
   */

  onAdd: function (map) {
    // include 'Powered by Esri' in map attribution
    setEsriAttribution(map);

    this.service.metadata(function (err, metadata) {
      if (!err) {
        var supportedFormats = metadata.supportedQueryFormats;
        // check to see whether service can emit GeoJSON natively
        if (supportedFormats && supportedFormats.indexOf('geoJSON') !== -1) {
          this.service.options.isModern = true;
        }
        // add copyright text listed in service metadata
        if (!this.options.attribution && map.attributionControl && metadata.copyrightText) {
          this.options.attribution = metadata.copyrightText;
          map.attributionControl.addAttribution(this.getAttribution());
        }
      }
    }, this);

    map.on('zoomend', this._handleZoomChange, this);

    return VirtualGrid.prototype.onAdd.call(this, map);
  },

  onRemove: function (map) {
    map.off('zoomend', this._handleZoomChange, this);

    return VirtualGrid.prototype.onRemove.call(this, map);
  },

  getAttribution: function () {
    return this.options.attribution;
  },

  /**
   * Feature Management
   */

  createCell: function (bounds, coords) {
    this._requestFeatures(bounds, coords);
  },

  _requestFeatures: function (bounds, coords, callback) {
    this._activeRequests++;

    // our first active request fires loading
    if (this._activeRequests === 1) {
      this.fire('loading', {
        bounds: bounds
      }, true);
    }

    return this._buildQuery(bounds).run(function (error, featureCollection, response) {
      if (response && response.exceededTransferLimit) {
        this.fire('drawlimitexceeded');
      }

      // no error, features
      if (!error && featureCollection && featureCollection.features.length) {
        // schedule adding features until the next animation frame
        L.Util.requestAnimFrame(L.Util.bind(function () {
          this._addFeatures(featureCollection.features, coords);
          this._postProcessFeatures(bounds);
        }, this));
      }

      // no error, no features
      if (!error && featureCollection && !featureCollection.features.length) {
        this._postProcessFeatures(bounds);
      }

      if (error) {
        this._postProcessFeatures(bounds);
      }

      if (callback) {
        callback.call(this, error, featureCollection);
      }
    }, this);
  },

  _postProcessFeatures: function (bounds) {
    // deincriment the request counter now that we have processed features
    this._activeRequests--;

    // if there are no more active requests fire a load event for this view
    if (this._activeRequests <= 0) {
      this.fire('load', {
        bounds: bounds
      });
    }
  },

  _cacheKey: function (coords) {
    return coords.z + ':' + coords.x + ':' + coords.y;
  },

  _addFeatures: function (features, coords) {
    var key = this._cacheKey(coords);
    this._cache[key] = this._cache[key] || [];

    for (var i = features.length - 1; i >= 0; i--) {
      var id = features[i].id;
      this._currentSnapshot.push(id);
      this._cache[key].push(id);
    }

    if (this.options.timeField) {
      this._buildTimeIndexes(features);
    }

    // need to PR removal of the logic below too...
    // https://github.com/patrickarlt/leaflet-virtual-grid/blob/master/src/virtual-grid.js#L100-L102

    this.createLayers(features);
  },

  _buildQuery: function (bounds) {
    var query = this.service.query()
      .intersects(bounds)
      .where(this.options.where)
      .fields(this.options.fields)
      .precision(this.options.precision);

    if (this.options.simplifyFactor) {
      query.simplify(this._map, this.options.simplifyFactor);
    }

    if (this.options.timeFilterMode === 'server' && this.options.from && this.options.to) {
      query.between(this.options.from, this.options.to);
    }

    return query;
  },

  /**
   * Where Methods
   */

  setWhere: function (where, callback, context) {
    this.options.where = (where && where.length) ? where : '1=1';

    var oldSnapshot = [];
    var newSnapshot = [];
    var pendingRequests = 0;
    var requestError = null;
    var requestCallback = L.Util.bind(function (error, featureCollection) {
      if (error) {
        requestError = error;
      }

      if (featureCollection) {
        for (var i = featureCollection.features.length - 1; i >= 0; i--) {
          newSnapshot.push(featureCollection.features[i].id);
        }
      }

      pendingRequests--;

      if (pendingRequests <= 0) {
        this._currentSnapshot = newSnapshot;
        // schedule adding features for the next animation frame
        L.Util.requestAnimFrame(L.Util.bind(function () {
          this.removeLayers(oldSnapshot);
          this.addLayers(newSnapshot);
          if (callback) {
            callback.call(context, requestError);
          }
        }, this));
      }
    }, this);

    for (var i = this._currentSnapshot.length - 1; i >= 0; i--) {
      oldSnapshot.push(this._currentSnapshot[i]);
    }

    for (var key in this._activeCells) {
      pendingRequests++;
      var coords = this._keyToCellCoords(key);
      var bounds = this._cellCoordsToBounds(coords);
      this._requestFeatures(bounds, key, requestCallback);
    }

    return this;
  },

  getWhere: function () {
    return this.options.where;
  },

  /**
   * Time Range Methods
   */

  getTimeRange: function () {
    return [this.options.from, this.options.to];
  },

  setTimeRange: function (from, to, callback, context) {
    var oldFrom = this.options.from;
    var oldTo = this.options.to;
    var pendingRequests = 0;
    var requestError = null;
    var requestCallback = L.Util.bind(function (error) {
      if (error) {
        requestError = error;
      }
      this._filterExistingFeatures(oldFrom, oldTo, from, to);

      pendingRequests--;

      if (callback && pendingRequests <= 0) {
        callback.call(context, requestError);
      }
    }, this);

    this.options.from = from;
    this.options.to = to;

    this._filterExistingFeatures(oldFrom, oldTo, from, to);

    if (this.options.timeFilterMode === 'server') {
      for (var key in this._activeCells) {
        pendingRequests++;
        var coords = this._keyToCellCoords(key);
        var bounds = this._cellCoordsToBounds(coords);
        this._requestFeatures(bounds, key, requestCallback);
      }
    }

    return this;
  },

  refresh: function () {
    for (var key in this._activeCells) {
      var coords = this._keyToCellCoords(key);
      var bounds = this._cellCoordsToBounds(coords);
      this._requestFeatures(bounds, key);
    }

    if (this.redraw) {
      this.once('load', function () {
        this.eachFeature(function (layer) {
          this._redraw(layer.feature.id);
        }, this);
      }, this);
    }
  },

  _filterExistingFeatures: function (oldFrom, oldTo, newFrom, newTo) {
    var layersToRemove = (oldFrom && oldTo) ? this._getFeaturesInTimeRange(oldFrom, oldTo) : this._currentSnapshot;
    var layersToAdd = this._getFeaturesInTimeRange(newFrom, newTo);

    if (layersToAdd.indexOf) {
      for (var i = 0; i < layersToAdd.length; i++) {
        var shouldRemoveLayer = layersToRemove.indexOf(layersToAdd[i]);
        if (shouldRemoveLayer >= 0) {
          layersToRemove.splice(shouldRemoveLayer, 1);
        }
      }
    }

    // schedule adding features until the next animation frame
    L.Util.requestAnimFrame(L.Util.bind(function () {
      this.removeLayers(layersToRemove);
      this.addLayers(layersToAdd);
    }, this));
  },

  _getFeaturesInTimeRange: function (start, end) {
    var ids = [];
    var search;

    if (this.options.timeField.start && this.options.timeField.end) {
      var startTimes = this._startTimeIndex.between(start, end);
      var endTimes = this._endTimeIndex.between(start, end);
      search = startTimes.concat(endTimes);
    } else {
      search = this._timeIndex.between(start, end);
    }

    for (var i = search.length - 1; i >= 0; i--) {
      ids.push(search[i].id);
    }

    return ids;
  },

  _buildTimeIndexes: function (geojson) {
    var i;
    var feature;
    if (this.options.timeField.start && this.options.timeField.end) {
      var startTimeEntries = [];
      var endTimeEntries = [];
      for (i = geojson.length - 1; i >= 0; i--) {
        feature = geojson[i];
        startTimeEntries.push({
          id: feature.id,
          value: new Date(feature.properties[this.options.timeField.start])
        });
        endTimeEntries.push({
          id: feature.id,
          value: new Date(feature.properties[this.options.timeField.end])
        });
      }
      this._startTimeIndex.bulkAdd(startTimeEntries);
      this._endTimeIndex.bulkAdd(endTimeEntries);
    } else {
      var timeEntries = [];
      for (i = geojson.length - 1; i >= 0; i--) {
        feature = geojson[i];
        timeEntries.push({
          id: feature.id,
          value: new Date(feature.properties[this.options.timeField])
        });
      }

      this._timeIndex.bulkAdd(timeEntries);
    }
  },

  _featureWithinTimeRange: function (feature) {
    if (!this.options.from || !this.options.to) {
      return true;
    }

    var from = +this.options.from.valueOf();
    var to = +this.options.to.valueOf();

    if (typeof this.options.timeField === 'string') {
      var date = +feature.properties[this.options.timeField];
      return (date >= from) && (date <= to);
    }

    if (this.options.timeField.start && this.options.timeField.end) {
      var startDate = +feature.properties[this.options.timeField.start];
      var endDate = +feature.properties[this.options.timeField.end];
      return ((startDate >= from) && (startDate <= to)) || ((endDate >= from) && (endDate <= to));
    }
  },

  _visibleZoom: function () {
    // check to see whether the current zoom level of the map is within the optional limit defined for the FeatureLayer
    if (!this._map) {
      return false;
    }
    var zoom = this._map.getZoom();
    if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
      return false;
    } else { return true; }
  },

  _handleZoomChange: function () {
    if (!this._visibleZoom()) {
      this.removeLayers(this._currentSnapshot);
      this._currentSnapshot = [];
    } else {
      /*
      for every cell in this._activeCells
        1. Get the cache key for the coords of the cell
        2. If this._cache[key] exists it will be an array of feature IDs.
        3. Call this.addLayers(this._cache[key]) to instruct the feature layer to add the layers back.
      */
      for (var i in this._activeCells) {
        var coords = this._activeCells[i].coords;
        var key = this._cacheKey(coords);
        if (this._cache[key]) {
          this.addLayers(this._cache[key]);
        }
      }
    }
  },

  /**
   * Service Methods
   */

  authenticate: function (token) {
    this.service.authenticate(token);
    return this;
  },

  metadata: function (callback, context) {
    this.service.metadata(callback, context);
    return this;
  },

  query: function () {
    return this.service.query();
  },

  _getMetadata: function (callback) {
    if (this._metadata) {
      var error;
      callback(error, this._metadata);
    } else {
      this.metadata(L.Util.bind(function (error, response) {
        this._metadata = response;
        callback(error, this._metadata);
      }, this));
    }
  },

  addFeature: function (feature, callback, context) {
    this._getMetadata(L.Util.bind(function (error, metadata) {
      if (error) {
        if (callback) { callback.call(this, error, null); }
        return;
      }

      this.service.addFeature(feature, L.Util.bind(function (error, response) {
        if (!error) {
          // assign ID from result to appropriate objectid field from service metadata
          feature.properties[metadata.objectIdField] = response.objectId;

          // we also need to update the geojson id for createLayers() to function
          feature.id = response.objectId;
          this.createLayers([feature]);
        }

        if (callback) {
          callback.call(context, error, response);
        }
      }, this));
    }, this));
  },

  updateFeature: function (feature, callback, context) {
    this.service.updateFeature(feature, function (error, response) {
      if (!error) {
        this.removeLayers([feature.id], true);
        this.createLayers([feature]);
      }

      if (callback) {
        callback.call(context, error, response);
      }
    }, this);
  },

  deleteFeature: function (id, callback, context) {
    this.service.deleteFeature(id, function (error, response) {
      if (!error && response.objectId) {
        this.removeLayers([response.objectId], true);
      }
      if (callback) {
        callback.call(context, error, response);
      }
    }, this);
  },

  deleteFeatures: function (ids, callback, context) {
    return this.service.deleteFeatures(ids, function (error, response) {
      if (!error && response.length > 0) {
        for (var i = 0; i < response.length; i++) {
          this.removeLayers([response[i].objectId], true);
        }
      }
      if (callback) {
        callback.call(context, error, response);
      }
    }, this);
  }
});
<commit_msg>dont query outside FeatureLayer visible scale (#928)<commit_after>import L from 'leaflet';
import featureLayerService from '../../Services/FeatureLayerService';
import { cleanUrl, warn, setEsriAttribution } from '../../Util';
import VirtualGrid from 'leaflet-virtual-grid';
import BinarySearchIndex from 'tiny-binary-search';

export var FeatureManager = VirtualGrid.extend({
  /**
   * Options
   */

  options: {
    attribution: null,
    where: '1=1',
    fields: ['*'],
    from: false,
    to: false,
    timeField: false,
    timeFilterMode: 'server',
    simplifyFactor: 0,
    precision: 6
  },

  /**
   * Constructor
   */

  initialize: function (options) {
    VirtualGrid.prototype.initialize.call(this, options);

    options.url = cleanUrl(options.url);
    options = L.setOptions(this, options);

    this.service = featureLayerService(options);
    this.service.addEventParent(this);

    // use case insensitive regex to look for common fieldnames used for indexing
    if (this.options.fields[0] !== '*') {
      var oidCheck = false;
      for (var i = 0; i < this.options.fields.length; i++) {
        if (this.options.fields[i].match(/^(OBJECTID|FID|OID|ID)$/i)) {
          oidCheck = true;
        }
      }
      if (oidCheck === false) {
        warn('no known esriFieldTypeOID field detected in fields Array.  Please add an attribute field containing unique IDs to ensure the layer can be drawn correctly.');
      }
    }

    if (this.options.timeField.start && this.options.timeField.end) {
      this._startTimeIndex = new BinarySearchIndex();
      this._endTimeIndex = new BinarySearchIndex();
    } else if (this.options.timeField) {
      this._timeIndex = new BinarySearchIndex();
    }

    this._cache = {};
    this._currentSnapshot = []; // cache of what layers should be active
    this._activeRequests = 0;
  },

  /**
   * Layer Interface
   */

  onAdd: function (map) {
    // include 'Powered by Esri' in map attribution
    setEsriAttribution(map);

    this.service.metadata(function (err, metadata) {
      if (!err) {
        var supportedFormats = metadata.supportedQueryFormats;
        // check to see whether service can emit GeoJSON natively
        if (supportedFormats && supportedFormats.indexOf('geoJSON') !== -1) {
          this.service.options.isModern = true;
        }
        // add copyright text listed in service metadata
        if (!this.options.attribution && map.attributionControl && metadata.copyrightText) {
          this.options.attribution = metadata.copyrightText;
          map.attributionControl.addAttribution(this.getAttribution());
        }
      }
    }, this);

    map.on('zoomend', this._handleZoomChange, this);

    return VirtualGrid.prototype.onAdd.call(this, map);
  },

  onRemove: function (map) {
    map.off('zoomend', this._handleZoomChange, this);

    return VirtualGrid.prototype.onRemove.call(this, map);
  },

  getAttribution: function () {
    return this.options.attribution;
  },

  /**
   * Feature Management
   */

  createCell: function (bounds, coords) {
    // dont fetch features outside the scale range defined for the layer
    if (this._visibleZoom()) {
      this._requestFeatures(bounds, coords);
    }
  },

  _requestFeatures: function (bounds, coords, callback) {
    this._activeRequests++;

    // our first active request fires loading
    if (this._activeRequests === 1) {
      this.fire('loading', {
        bounds: bounds
      }, true);
    }

    return this._buildQuery(bounds).run(function (error, featureCollection, response) {
      if (response && response.exceededTransferLimit) {
        this.fire('drawlimitexceeded');
      }

      // no error, features
      if (!error && featureCollection && featureCollection.features.length) {
        // schedule adding features until the next animation frame
        L.Util.requestAnimFrame(L.Util.bind(function () {
          this._addFeatures(featureCollection.features, coords);
          this._postProcessFeatures(bounds);
        }, this));
      }

      // no error, no features
      if (!error && featureCollection && !featureCollection.features.length) {
        this._postProcessFeatures(bounds);
      }

      if (error) {
        this._postProcessFeatures(bounds);
      }

      if (callback) {
        callback.call(this, error, featureCollection);
      }
    }, this);
  },

  _postProcessFeatures: function (bounds) {
    // deincriment the request counter now that we have processed features
    this._activeRequests--;

    // if there are no more active requests fire a load event for this view
    if (this._activeRequests <= 0) {
      this.fire('load', {
        bounds: bounds
      });
    }
  },

  _cacheKey: function (coords) {
    return coords.z + ':' + coords.x + ':' + coords.y;
  },

  _addFeatures: function (features, coords) {
    var key = this._cacheKey(coords);
    this._cache[key] = this._cache[key] || [];

    for (var i = features.length - 1; i >= 0; i--) {
      var id = features[i].id;
      this._currentSnapshot.push(id);
      this._cache[key].push(id);
    }

    if (this.options.timeField) {
      this._buildTimeIndexes(features);
    }

    // need to PR removal of the logic below too...
    // https://github.com/patrickarlt/leaflet-virtual-grid/blob/master/src/virtual-grid.js#L100-L102

    this.createLayers(features);
  },

  _buildQuery: function (bounds) {
    var query = this.service.query()
      .intersects(bounds)
      .where(this.options.where)
      .fields(this.options.fields)
      .precision(this.options.precision);

    if (this.options.simplifyFactor) {
      query.simplify(this._map, this.options.simplifyFactor);
    }

    if (this.options.timeFilterMode === 'server' && this.options.from && this.options.to) {
      query.between(this.options.from, this.options.to);
    }

    return query;
  },

  /**
   * Where Methods
   */

  setWhere: function (where, callback, context) {
    this.options.where = (where && where.length) ? where : '1=1';

    var oldSnapshot = [];
    var newSnapshot = [];
    var pendingRequests = 0;
    var requestError = null;
    var requestCallback = L.Util.bind(function (error, featureCollection) {
      if (error) {
        requestError = error;
      }

      if (featureCollection) {
        for (var i = featureCollection.features.length - 1; i >= 0; i--) {
          newSnapshot.push(featureCollection.features[i].id);
        }
      }

      pendingRequests--;

      if (pendingRequests <= 0) {
        this._currentSnapshot = newSnapshot;
        // schedule adding features for the next animation frame
        L.Util.requestAnimFrame(L.Util.bind(function () {
          this.removeLayers(oldSnapshot);
          this.addLayers(newSnapshot);
          if (callback) {
            callback.call(context, requestError);
          }
        }, this));
      }
    }, this);

    for (var i = this._currentSnapshot.length - 1; i >= 0; i--) {
      oldSnapshot.push(this._currentSnapshot[i]);
    }

    for (var key in this._activeCells) {
      pendingRequests++;
      var coords = this._keyToCellCoords(key);
      var bounds = this._cellCoordsToBounds(coords);
      this._requestFeatures(bounds, key, requestCallback);
    }

    return this;
  },

  getWhere: function () {
    return this.options.where;
  },

  /**
   * Time Range Methods
   */

  getTimeRange: function () {
    return [this.options.from, this.options.to];
  },

  setTimeRange: function (from, to, callback, context) {
    var oldFrom = this.options.from;
    var oldTo = this.options.to;
    var pendingRequests = 0;
    var requestError = null;
    var requestCallback = L.Util.bind(function (error) {
      if (error) {
        requestError = error;
      }
      this._filterExistingFeatures(oldFrom, oldTo, from, to);

      pendingRequests--;

      if (callback && pendingRequests <= 0) {
        callback.call(context, requestError);
      }
    }, this);

    this.options.from = from;
    this.options.to = to;

    this._filterExistingFeatures(oldFrom, oldTo, from, to);

    if (this.options.timeFilterMode === 'server') {
      for (var key in this._activeCells) {
        pendingRequests++;
        var coords = this._keyToCellCoords(key);
        var bounds = this._cellCoordsToBounds(coords);
        this._requestFeatures(bounds, key, requestCallback);
      }
    }

    return this;
  },

  refresh: function () {
    for (var key in this._activeCells) {
      var coords = this._keyToCellCoords(key);
      var bounds = this._cellCoordsToBounds(coords);
      this._requestFeatures(bounds, key);
    }

    if (this.redraw) {
      this.once('load', function () {
        this.eachFeature(function (layer) {
          this._redraw(layer.feature.id);
        }, this);
      }, this);
    }
  },

  _filterExistingFeatures: function (oldFrom, oldTo, newFrom, newTo) {
    var layersToRemove = (oldFrom && oldTo) ? this._getFeaturesInTimeRange(oldFrom, oldTo) : this._currentSnapshot;
    var layersToAdd = this._getFeaturesInTimeRange(newFrom, newTo);

    if (layersToAdd.indexOf) {
      for (var i = 0; i < layersToAdd.length; i++) {
        var shouldRemoveLayer = layersToRemove.indexOf(layersToAdd[i]);
        if (shouldRemoveLayer >= 0) {
          layersToRemove.splice(shouldRemoveLayer, 1);
        }
      }
    }

    // schedule adding features until the next animation frame
    L.Util.requestAnimFrame(L.Util.bind(function () {
      this.removeLayers(layersToRemove);
      this.addLayers(layersToAdd);
    }, this));
  },

  _getFeaturesInTimeRange: function (start, end) {
    var ids = [];
    var search;

    if (this.options.timeField.start && this.options.timeField.end) {
      var startTimes = this._startTimeIndex.between(start, end);
      var endTimes = this._endTimeIndex.between(start, end);
      search = startTimes.concat(endTimes);
    } else {
      search = this._timeIndex.between(start, end);
    }

    for (var i = search.length - 1; i >= 0; i--) {
      ids.push(search[i].id);
    }

    return ids;
  },

  _buildTimeIndexes: function (geojson) {
    var i;
    var feature;
    if (this.options.timeField.start && this.options.timeField.end) {
      var startTimeEntries = [];
      var endTimeEntries = [];
      for (i = geojson.length - 1; i >= 0; i--) {
        feature = geojson[i];
        startTimeEntries.push({
          id: feature.id,
          value: new Date(feature.properties[this.options.timeField.start])
        });
        endTimeEntries.push({
          id: feature.id,
          value: new Date(feature.properties[this.options.timeField.end])
        });
      }
      this._startTimeIndex.bulkAdd(startTimeEntries);
      this._endTimeIndex.bulkAdd(endTimeEntries);
    } else {
      var timeEntries = [];
      for (i = geojson.length - 1; i >= 0; i--) {
        feature = geojson[i];
        timeEntries.push({
          id: feature.id,
          value: new Date(feature.properties[this.options.timeField])
        });
      }

      this._timeIndex.bulkAdd(timeEntries);
    }
  },

  _featureWithinTimeRange: function (feature) {
    if (!this.options.from || !this.options.to) {
      return true;
    }

    var from = +this.options.from.valueOf();
    var to = +this.options.to.valueOf();

    if (typeof this.options.timeField === 'string') {
      var date = +feature.properties[this.options.timeField];
      return (date >= from) && (date <= to);
    }

    if (this.options.timeField.start && this.options.timeField.end) {
      var startDate = +feature.properties[this.options.timeField.start];
      var endDate = +feature.properties[this.options.timeField.end];
      return ((startDate >= from) && (startDate <= to)) || ((endDate >= from) && (endDate <= to));
    }
  },

  _visibleZoom: function () {
    // check to see whether the current zoom level of the map is within the optional limit defined for the FeatureLayer
    if (!this._map) {
      return false;
    }
    var zoom = this._map.getZoom();
    if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
      return false;
    } else { return true; }
  },

  _handleZoomChange: function () {
    if (!this._visibleZoom()) {
      this.removeLayers(this._currentSnapshot);
      this._currentSnapshot = [];
    } else {
      /*
      for every cell in this._activeCells
        1. Get the cache key for the coords of the cell
        2. If this._cache[key] exists it will be an array of feature IDs.
        3. Call this.addLayers(this._cache[key]) to instruct the feature layer to add the layers back.
      */
      for (var i in this._activeCells) {
        var coords = this._activeCells[i].coords;
        var key = this._cacheKey(coords);
        if (this._cache[key]) {
          this.addLayers(this._cache[key]);
        }
      }
    }
  },

  /**
   * Service Methods
   */

  authenticate: function (token) {
    this.service.authenticate(token);
    return this;
  },

  metadata: function (callback, context) {
    this.service.metadata(callback, context);
    return this;
  },

  query: function () {
    return this.service.query();
  },

  _getMetadata: function (callback) {
    if (this._metadata) {
      var error;
      callback(error, this._metadata);
    } else {
      this.metadata(L.Util.bind(function (error, response) {
        this._metadata = response;
        callback(error, this._metadata);
      }, this));
    }
  },

  addFeature: function (feature, callback, context) {
    this._getMetadata(L.Util.bind(function (error, metadata) {
      if (error) {
        if (callback) { callback.call(this, error, null); }
        return;
      }

      this.service.addFeature(feature, L.Util.bind(function (error, response) {
        if (!error) {
          // assign ID from result to appropriate objectid field from service metadata
          feature.properties[metadata.objectIdField] = response.objectId;

          // we also need to update the geojson id for createLayers() to function
          feature.id = response.objectId;
          this.createLayers([feature]);
        }

        if (callback) {
          callback.call(context, error, response);
        }
      }, this));
    }, this));
  },

  updateFeature: function (feature, callback, context) {
    this.service.updateFeature(feature, function (error, response) {
      if (!error) {
        this.removeLayers([feature.id], true);
        this.createLayers([feature]);
      }

      if (callback) {
        callback.call(context, error, response);
      }
    }, this);
  },

  deleteFeature: function (id, callback, context) {
    this.service.deleteFeature(id, function (error, response) {
      if (!error && response.objectId) {
        this.removeLayers([response.objectId], true);
      }
      if (callback) {
        callback.call(context, error, response);
      }
    }, this);
  },

  deleteFeatures: function (ids, callback, context) {
    return this.service.deleteFeatures(ids, function (error, response) {
      if (!error && response.length > 0) {
        for (var i = 0; i < response.length; i++) {
          this.removeLayers([response[i].objectId], true);
        }
      }
      if (callback) {
        callback.call(context, error, response);
      }
    }, this);
  }
});
<|endoftext|>