<commit_before>'use strict'

var debug = require('debug')('store')

var d3 = require('d3')
var topojson = require('topojson')
var crossfilter = require('crossfilter')
var action = require('./action')

var store = {
  data: {
  },
  filters: {
    animal: crossfilter(),
    roadkill: crossfilter()
  },
  dimensions: {}
}

var dispatch = d3.dispatch(
  'loading',
  'ready',
  'update'
)
d3.rebind(store, dispatch, 'on')

store.loadGeo = function () {
  d3.json('data/twCounty2010.topo.json')
    .on('progress', function () {
      dispatch.loading(d3.event.loaded)
    })
    .get(function (err, data) {
      if (err) {
        debug(err)
        return
      }
      var topo = topojson.feature(data, data.objects.layer1)
      store.data.geo = topo.features
      dispatch.update()
    })
}

store.loadAnimal = function () {
  d3.csv('data/topic_animal.csv')
    .on('progress', function () {
      dispatch.loading(d3.event.loaded)
    })
    .get(function (err, data) {
      if (err) {
        debug(err)
        return
      }
      store.filters.animal.add(data.map(function (d) {
        var date = new Date(d.CollectedDateTime)
        return {
          id: 'tesri-' + date.getTime(),
          date: date,
          lngLat: [+d.Longitude, +d.Latitude],
          latLng: [+d.Latitude, +d.Longitude]
        }
      }))
      store.dimensions.animal = store.filters.animal.dimension(function (d) { return d.date })
      if (store.data.focused) {
        store.dimensions.animal.filter(function (d) {
          return d < store.data.focused.date
        })
      }
      dispatch.update()
    })
  d3.json('data/lin-2014.geojson')
    .get(function (err, data) {
      if (err) {
        debug(err)
        return
      }
      store.filters.animal.add(data.features.map(function (d, i) {
        return {
          id: 'lin2014-' + i,
          date: new Date(d.properties.date),
          latLng: [d.geometry.coordinates[1], d.geometry.coordinates[0]],
          lngLat: d.geometry.coordinates
        }
      }))
      store.dimensions.animal = store.filters.animal.dimension(function (d) { return d.date })
      if (store.data.focused) {
        store.dimensions.animal.filter(function (d) {
          return d < store.data.focused.date
        })
      }
      dispatch.update()
    })
}

store.loadRoadkill = function () {
  d3.csv('data/roadkill.csv')
    .on('progress', function () {
      dispatch.loading(d3.event.loaded)
    })
    .get(function (err, data) {
      if (err) {
        debug(err)
        return
      }
      store.filters.roadkill.add(data.map(function (d) {
        return {
          id: 'roadkilltw-' + d.SerialNo,
          date: new Date(d.ObserveDate),
          lngLat: [+d.WGS84Lon, +d.WGS84Lat],
          latlng: [+d.WGS84Lat, +d.WGS84Lon]
        }
      }))
      store.dimensions.roadkill = store.filters.roadkill.dimension(function (d) {
        return d.date
      })
      dispatch.update()
    })
}

store.loadTimeline = function () {
  d3.csv('http://cors.io/?u=https://docs.google.com/spreadsheets/d/1J3Sm3MURwI9ZErjcxdxNEkm9Cfactw0Na6KD65NcYcA/pub?output=csv')
    .on('progress', function () {
      dispatch.loading(d3.event.loading)
    })
    .get(function (err, data) {
      if (err) {
        debug(err)
        return
      }
      store.data.timeline = data.map(function (d) {
        return {
          date: d['日期 '],
          category: d['分類（以逗號分隔：開發案, 路殺, 衝突, 友善農耕,石虎研究）'].split(/,\s*/),
          link: d['資訊連結'],
          title: d['事件'],
          ref: d['資料來源（e-info或其他媒體）'],
          location: (d['經度（路殺或目擊事件才需登）'] ? [d['經度（路殺或目擊事件才需登）'], d['緯度（路殺或目擊事件才需登）']] : undefined)
        }
      })
      store.data.timelineFilter = crossfilter(store.data.timeline)
      store.data.timelineDate = store.data.timelineFilter.dimension(function (d) { return d.date })
      dispatch.update()
    })
}

store.handle = function (act) {
  debug('handle ' + act.name)
  if (act.name === 'focused') {
    debug(act.opts)
    store.data.focused = act.opts
    if (store.dimensions.animal) {
      store.dimensions.animal.filter(function (d) {
        return d < store.data.focused.date
      })
    }
    debug(store.dimensions.animal.top(1000))
    dispatch.update()
  } else if (act.name === 'setHeight') {
    store.data.height = act.opts
    dispatch.update()
  }
}

store.init = function () {
  store.loadGeo()
  store.loadAnimal()
  store.loadRoadkill()
  store.loadTimeline()
  action.on('run', store.handle.bind(store))
  dispatch.ready()
}

store.get = function () {
  if (arguments[0] === 'animal') {
    if (store.dimensions.animal) {
      return store.dimensions.animal.top(Infinity)
    }
    return []
  }
  if (arguments[0] === 'roadkill') {
    if (store.dimensions.roadkill) {
      return store.dimensions.roadkill.top(Infinity)
    }
    return []
  }
  if (arguments[0] === 'tag') {
    return ['開發案', '衝突', '石虎研究', '苗50線', '獸鋏', '路殺', '友善農耕', '三義外環道', '後龍殯葬園區']
  }
  if (arguments.length > 0) {
    for (var r = store.data, i = 0; i < arguments.length; i++) {
      r = r[arguments[i]]
      if (r === undefined) {
        break
      }
    }
    return r
  } else {
    return store.data
  }
}

module.exports = store
<commit_msg>adjust tags<commit_after>'use strict'

var debug = require('debug')('store')

var d3 = require('d3')
var topojson = require('topojson')
var crossfilter = require('crossfilter')
var action = require('./action')

var store = {
  data: {
  },
  filters: {
    animal: crossfilter(),
    roadkill: crossfilter()
  },
  dimensions: {}
}

var dispatch = d3.dispatch(
  'loading',
  'ready',
  'update'
)
d3.rebind(store, dispatch, 'on')

store.loadGeo = function () {
  d3.json('data/twCounty2010.topo.json')
    .on('progress', function () {
      dispatch.loading(d3.event.loaded)
    })
    .get(function (err, data) {
      if (err) {
        debug(err)
        return
      }
      var topo = topojson.feature(data, data.objects.layer1)
      store.data.geo = topo.features
      dispatch.update()
    })
}

store.loadAnimal = function () {
  d3.csv('data/topic_animal.csv')
    .on('progress', function () {
      dispatch.loading(d3.event.loaded)
    })
    .get(function (err, data) {
      if (err) {
        debug(err)
        return
      }
      store.filters.animal.add(data.map(function (d) {
        var date = new Date(d.CollectedDateTime)
        return {
          id: 'tesri-' + date.getTime(),
          date: date,
          lngLat: [+d.Longitude, +d.Latitude],
          latLng: [+d.Latitude, +d.Longitude]
        }
      }))
      store.dimensions.animal = store.filters.animal.dimension(function (d) { return d.date })
      if (store.data.focused) {
        store.dimensions.animal.filter(function (d) {
          return d < store.data.focused.date
        })
      }
      dispatch.update()
    })
  d3.json('data/lin-2014.geojson')
    .get(function (err, data) {
      if (err) {
        debug(err)
        return
      }
      store.filters.animal.add(data.features.map(function (d, i) {
        return {
          id: 'lin2014-' + i,
          date: new Date(d.properties.date),
          latLng: [d.geometry.coordinates[1], d.geometry.coordinates[0]],
          lngLat: d.geometry.coordinates
        }
      }))
      store.dimensions.animal = store.filters.animal.dimension(function (d) { return d.date })
      if (store.data.focused) {
        store.dimensions.animal.filter(function (d) {
          return d < store.data.focused.date
        })
      }
      dispatch.update()
    })
}

store.loadRoadkill = function () {
  d3.csv('data/roadkill.csv')
    .on('progress', function () {
      dispatch.loading(d3.event.loaded)
    })
    .get(function (err, data) {
      if (err) {
        debug(err)
        return
      }
      store.filters.roadkill.add(data.map(function (d) {
        return {
          id: 'roadkilltw-' + d.SerialNo,
          date: new Date(d.ObserveDate),
          lngLat: [+d.WGS84Lon, +d.WGS84Lat],
          latlng: [+d.WGS84Lat, +d.WGS84Lon]
        }
      }))
      store.dimensions.roadkill = store.filters.roadkill.dimension(function (d) {
        return d.date
      })
      dispatch.update()
    })
}

store.loadTimeline = function () {
  d3.csv('http://cors.io/?u=https://docs.google.com/spreadsheets/d/1J3Sm3MURwI9ZErjcxdxNEkm9Cfactw0Na6KD65NcYcA/pub?output=csv')
    .on('progress', function () {
      dispatch.loading(d3.event.loading)
    })
    .get(function (err, data) {
      if (err) {
        debug(err)
        return
      }
      store.data.timeline = data.map(function (d) {
        return {
          date: d['日期 '],
          category: d['分類（以逗號分隔：開發案, 路殺, 衝突, 友善農耕,石虎研究）'].split(/,\s*/),
          link: d['資訊連結'],
          title: d['事件'],
          ref: d['資料來源（e-info或其他媒體）'],
          location: (d['經度（路殺或目擊事件才需登）'] ? [d['經度（路殺或目擊事件才需登）'], d['緯度（路殺或目擊事件才需登）']] : undefined)
        }
      })
      store.data.timelineFilter = crossfilter(store.data.timeline)
      store.data.timelineDate = store.data.timelineFilter.dimension(function (d) { return d.date })
      dispatch.update()
    })
}

store.handle = function (act) {
  debug('handle ' + act.name)
  if (act.name === 'focused') {
    debug(act.opts)
    store.data.focused = act.opts
    if (store.dimensions.animal) {
      store.dimensions.animal.filter(function (d) {
        return d < store.data.focused.date
      })
    }
    debug(store.dimensions.animal.top(1000))
    dispatch.update()
  } else if (act.name === 'setHeight') {
    store.data.height = act.opts
    dispatch.update()
  }
}

store.init = function () {
  store.loadGeo()
  store.loadAnimal()
  store.loadRoadkill()
  store.loadTimeline()
  action.on('run', store.handle.bind(store))
  dispatch.ready()
}

store.get = function () {
  if (arguments[0] === 'animal') {
    if (store.dimensions.animal) {
      return store.dimensions.animal.top(Infinity)
    }
    return []
  }
  if (arguments[0] === 'roadkill') {
    if (store.dimensions.roadkill) {
      return store.dimensions.roadkill.top(Infinity)
    }
    return []
  }
  if (arguments[0] === 'tag') {
    return ['衝突', '獸鋏', '路殺', '石虎研究', '友善農耕', '苗50線', '三義外環道', '後龍殯葬園區']
  }
  if (arguments.length > 0) {
    for (var r = store.data, i = 0; i < arguments.length; i++) {
      r = r[arguments[i]]
      if (r === undefined) {
        break
      }
    }
    return r
  } else {
    return store.data
  }
}

module.exports = store
<|endoftext|>


<commit_before>var path = require('path');
var env = process.env.NODE_ENV || 'development';
var headers = { 'Content-Type': 'application/json' };

server = Bones.Server.extend({});

var options = {
    type: '.js',
    wrapper: Bones.utils.wrapClientFile,
    sort: Bones.utils.sortByLoadOrder
};

// TODO: This should be moved to the initialize method!
server.prototype.assets = {
    vendor: new mirror([
        require.resolve(path.join(__dirname, '../assets/jquery')),
        require.resolve('underscore'),
        require.resolve('backbone')
    ], { type: '.js' }),
    core: new mirror([
        require.resolve(path.join(__dirname, '../shared/utils')),
        require.resolve(path.join(__dirname, '../client/utils')),
        require.resolve(path.join(__dirname, '../shared/backbone')),
        require.resolve(path.join(__dirname, '../client/backbone'))
    ], { type: '.js' }),
    models: new mirror([], options),
    views: new mirror([], options),
    routers: new mirror([], options),
    templates: new mirror([], options)
};

if (env === 'development') {
    server.prototype.assets.core.unshift(require.resolve(path.join(__dirname, '../assets/debug')));
}

// TODO: This should be moved to the initialize method!
server.prototype.assets.all = new mirror([
    server.prototype.assets.vendor,
    server.prototype.assets.core,
    server.prototype.assets.routers,
    server.prototype.assets.models,
    server.prototype.assets.views,
    server.prototype.assets.templates
], { type: '.js' });

// Stores models, views served by this server.
// TODO: This should be moved to the initialize method!
server.prototype.models = {};
server.prototype.views = {};

// Stores instances of routers registered with this server.
// TODO: This should be moved to the initialize method!
server.prototype.routers = {};

server.prototype.initialize = function(app) {
    this.registerComponents(app);
    this.initializeAssets(app);
    this.initializeModels(app);
};

server.prototype.registerComponents = function(app) {
    var components = ['routers', 'models', 'views', 'templates'];
    components.forEach(function(kind) {
        for (var name in app[kind]) {
            app[kind][name].register(this);
        }
    }, this);
};

server.prototype.initializeAssets = function(app) {
    this.get('/assets/bones/vendor.js', this.assets.vendor.handler);
    this.get('/assets/bones/core.js', this.assets.core.handler);
    this.get('/assets/bones/routers.js', this.assets.routers.handler);
    this.get('/assets/bones/models.js', this.assets.models.handler);
    this.get('/assets/bones/views.js', this.assets.views.handler);
    this.get('/assets/bones/templates.js', this.assets.templates.handler);

    this.get('/assets/bones/all.js', this.assets.all.handler);
};

server.prototype.initializeModels = function(app) {
    this.models = app.models;
    _.bindAll(this, 'loadModel', 'getModel', 'saveModel', 'delModel', 'loadCollection');
    this.get('/api/:model/:id', [this.loadModel, this.getModel]);
    this.post('/api/:model', [this.loadModel, this.saveModel]);
    this.put('/api/:model/:id', [this.loadModel, this.saveModel]);
    this.del('/api/:model/:id', [this.loadModel, this.delModel]);
    this.get('/api/:collection', this.loadCollection.bind(this));
};

server.prototype.loadCollection = function(req, res, next) {
    var name = Bones.utils.pluralize(req.params.collection);
    if (name in this.models) {
        // Pass any querystring paramaters to the collection.
        req.collection = new this.models[name]([], req.query);
        req.collection.fetch({
            success: function(collection, resp) {
                res.send(resp, headers);
            },
            error: function(collection, err) {
                var error = err instanceof Object ? err.message : err;
                next(new Error.HTTP(error, err && err.status || 500));
            }
        });
    } else {
        next();
    }
};

server.prototype.loadModel = function(req, res, next) {
    var name = req.params.model;
    if (name in this.models) {
        // Pass any querystring paramaters to the model.
        req.model = new this.models[name]({ id: req.params.id }, req.query);
    }
    next();
};

server.prototype.getModel = function(req, res, next) {
    if (!req.model) return next();
    req.model.fetch({
        success: function(model, resp) {
            res.send(resp, headers);
        },
        error: function(model, err) {
            var error = err instanceof Object ? err.message : err;
            next(new Error.HTTP(error, err && err.status || 404));
        }
    });
};

server.prototype.saveModel = function(req, res, next) {
    if (!req.model) return next();
    req.model.save(req.body, {
        success: function(model, resp) {
            res.send(resp, headers);
        },
        error: function(model, err) {
            var error = err instanceof Object ? err.message : err;
            next(new Error.HTTP(error, err && err.status || 409));
        }
    });
};

server.prototype.delModel = function(req, res, next) {
    if (!req.model) return next();
    req.model.destroy({
        success: function(model, resp) {
            res.send({}, headers);
        },
        error: function(model, err) {
            var error = err instanceof Object ? err.message : err;
            next(new Error.HTTP(error, err && err.status || 409));
        }
    });
};
<commit_msg>Send response for DELETE requests.<commit_after>var path = require('path');
var env = process.env.NODE_ENV || 'development';
var headers = { 'Content-Type': 'application/json' };

server = Bones.Server.extend({});

var options = {
    type: '.js',
    wrapper: Bones.utils.wrapClientFile,
    sort: Bones.utils.sortByLoadOrder
};

// TODO: This should be moved to the initialize method!
server.prototype.assets = {
    vendor: new mirror([
        require.resolve(path.join(__dirname, '../assets/jquery')),
        require.resolve('underscore'),
        require.resolve('backbone')
    ], { type: '.js' }),
    core: new mirror([
        require.resolve(path.join(__dirname, '../shared/utils')),
        require.resolve(path.join(__dirname, '../client/utils')),
        require.resolve(path.join(__dirname, '../shared/backbone')),
        require.resolve(path.join(__dirname, '../client/backbone'))
    ], { type: '.js' }),
    models: new mirror([], options),
    views: new mirror([], options),
    routers: new mirror([], options),
    templates: new mirror([], options)
};

if (env === 'development') {
    server.prototype.assets.core.unshift(require.resolve(path.join(__dirname, '../assets/debug')));
}

// TODO: This should be moved to the initialize method!
server.prototype.assets.all = new mirror([
    server.prototype.assets.vendor,
    server.prototype.assets.core,
    server.prototype.assets.routers,
    server.prototype.assets.models,
    server.prototype.assets.views,
    server.prototype.assets.templates
], { type: '.js' });

// Stores models, views served by this server.
// TODO: This should be moved to the initialize method!
server.prototype.models = {};
server.prototype.views = {};

// Stores instances of routers registered with this server.
// TODO: This should be moved to the initialize method!
server.prototype.routers = {};

server.prototype.initialize = function(app) {
    this.registerComponents(app);
    this.initializeAssets(app);
    this.initializeModels(app);
};

server.prototype.registerComponents = function(app) {
    var components = ['routers', 'models', 'views', 'templates'];
    components.forEach(function(kind) {
        for (var name in app[kind]) {
            app[kind][name].register(this);
        }
    }, this);
};

server.prototype.initializeAssets = function(app) {
    this.get('/assets/bones/vendor.js', this.assets.vendor.handler);
    this.get('/assets/bones/core.js', this.assets.core.handler);
    this.get('/assets/bones/routers.js', this.assets.routers.handler);
    this.get('/assets/bones/models.js', this.assets.models.handler);
    this.get('/assets/bones/views.js', this.assets.views.handler);
    this.get('/assets/bones/templates.js', this.assets.templates.handler);

    this.get('/assets/bones/all.js', this.assets.all.handler);
};

server.prototype.initializeModels = function(app) {
    this.models = app.models;
    _.bindAll(this, 'loadModel', 'getModel', 'saveModel', 'delModel', 'loadCollection');
    this.get('/api/:model/:id', [this.loadModel, this.getModel]);
    this.post('/api/:model', [this.loadModel, this.saveModel]);
    this.put('/api/:model/:id', [this.loadModel, this.saveModel]);
    this.del('/api/:model/:id', [this.loadModel, this.delModel]);
    this.get('/api/:collection', this.loadCollection.bind(this));
};

server.prototype.loadCollection = function(req, res, next) {
    var name = Bones.utils.pluralize(req.params.collection);
    if (name in this.models) {
        // Pass any querystring paramaters to the collection.
        req.collection = new this.models[name]([], req.query);
        req.collection.fetch({
            success: function(collection, resp) {
                res.send(resp, headers);
            },
            error: function(collection, err) {
                var error = err instanceof Object ? err.message : err;
                next(new Error.HTTP(error, err && err.status || 500));
            }
        });
    } else {
        next();
    }
};

server.prototype.loadModel = function(req, res, next) {
    var name = req.params.model;
    if (name in this.models) {
        // Pass any querystring paramaters to the model.
        req.model = new this.models[name]({ id: req.params.id }, req.query);
    }
    next();
};

server.prototype.getModel = function(req, res, next) {
    if (!req.model) return next();
    req.model.fetch({
        success: function(model, resp) {
            res.send(resp, headers);
        },
        error: function(model, err) {
            var error = err instanceof Object ? err.message : err;
            next(new Error.HTTP(error, err && err.status || 404));
        }
    });
};

server.prototype.saveModel = function(req, res, next) {
    if (!req.model) return next();
    req.model.save(req.body, {
        success: function(model, resp) {
            res.send(resp, headers);
        },
        error: function(model, err) {
            var error = err instanceof Object ? err.message : err;
            next(new Error.HTTP(error, err && err.status || 409));
        }
    });
};

server.prototype.delModel = function(req, res, next) {
    if (!req.model) return next();
    req.model.destroy({
        success: function(model, resp) {
            res.send(resp, headers);
        },
        error: function(model, err) {
            var error = err instanceof Object ? err.message : err;
            next(new Error.HTTP(error, err && err.status || 409));
        }
    });
};
<|endoftext|>


<commit_before>'use strict;'

/* redux */
import {compose, createStore, applyMiddleware, combineReducers} from 'redux';
import { routerReducer } from 'react-router-redux';
import thunk from 'redux-thunk'
import persistState from 'redux-localstorage'
import reduxProviderAdapter from 'butter-redux-provider';

import persist from './redux/persist'
import cache from './redux/cache'

import {remote} from 'electron'

const Identity = a => a

const hashifyReduxers = (source, resource) => (
    source.reduce((acc, reduxer) => (
        Object.assign(acc, {
            [reduxer.provider.id]: reduxer[resource]
        })
    ), {})
)

const butterCreateStore = ({tabs, ...settings}) => {
    let providerReducers = {}
    let providerActions = {}

    const reducedTabs = Object.keys(tabs).reduce((acc, k) => {
        const tab = tabs[k]

        const providers = tab.providers.map(uri => {
            const name = uri.split('?')[0]
            let instance = null

            try {
                const Provider = remote.require(`butter-provider-${name}`)
                instance = new Provider(uri)
            } catch (e) {
                return null
n            }

            return instance
        }).filter(e => e)

        providers.forEach(provider => {
            const reduxer =  reduxProviderAdapter(provider, cache.actions)
            providerReducers[provider.id] = reduxer.reducer
            providerActions[provider.id]  = reduxer.actions
        })

        return Object.assign(acc, {
            [k]: Object.assign(tab, {
                providers: providers.map(provider => provider.id)
            })
        })
    }, {})

    const reducers = {
        collections: combineReducers({
            ...providerReducers
        }),
        settings: (state, action) => ({
            ...settings
        }),
        tabs: (state, action) => ({
            ...tabs
        })
    }

    const actions = {
        ...providerActions
    }

    const middlewares = [thunk]
    const composeEnhancers = typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
    const enhancer = composeEnhancers(
        applyMiddleware(...middlewares),
        persistState(['persist', 'cache', 'settings'])
    )

    const store = createStore(combineReducers({
        ...reducers,
        cache: cache.reducer,
        persist: persist.reducer,
        router: routerReducer
    }), enhancer)

    Object.values(actions).map(a => store.dispatch(a.FETCH()))

    return store
}

export {butterCreateStore as default}
<commit_msg>guard against provider loading errors<commit_after>'use strict;'

/* redux */
import {compose, createStore, applyMiddleware, combineReducers} from 'redux';
import { routerReducer } from 'react-router-redux';
import thunk from 'redux-thunk'
import persistState from 'redux-localstorage'
import reduxProviderAdapter from 'butter-redux-provider';

import persist from './redux/persist'
import cache from './redux/cache'

import {remote} from 'electron'

const Identity = a => a

const hashifyReduxers = (source, resource) => (
    source.reduce((acc, reduxer) => (
        Object.assign(acc, {
            [reduxer.provider.id]: reduxer[resource]
        })
    ), {})
)

const butterCreateStore = ({tabs, ...settings}) => {
    let providerReducers = {}
    let providerActions = {}

    const reducedTabs = Object.keys(tabs).reduce((acc, k) => {
        const tab = tabs[k]

        const providers = tab.providers.map(uri => {
            const name = uri.split('?')[0]
            let instance = null

            try {
                const Provider = remote.require(`butter-provider-${name}`)
                instance = new Provider(uri)
            } catch (e) {
                console.error('couldnt load provider', name, Provider)
                return null
            }

            return instance
        }).filter(e => e)

        providers.forEach(provider => {
            const reduxer =  reduxProviderAdapter(provider, cache.actions)
            providerReducers[provider.id] = reduxer.reducer
            providerActions[provider.id]  = reduxer.actions
        })

        return Object.assign(acc, {
            [k]: Object.assign(tab, {
                providers: providers.map(provider => provider.id)
            })
        })
    }, {})

    const reducers = {
        collections: combineReducers({
            ...providerReducers
        }),
        settings: (state, action) => ({
            ...settings
        }),
        tabs: (state, action) => ({
            ...tabs
        })
    }

    const actions = {
        ...providerActions
    }

    const middlewares = [thunk]
    const composeEnhancers = typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
    const enhancer = composeEnhancers(
        applyMiddleware(...middlewares),
        persistState(['persist', 'cache', 'settings'])
    )

    const store = createStore(combineReducers({
        ...reducers,
        cache: cache.reducer,
        persist: persist.reducer,
        router: routerReducer
    }), enhancer)

    Object.values(actions).map(a => store.dispatch(a.FETCH()))

    return store
}

export {butterCreateStore as default}
<|endoftext|>


<commit_before>/*
 * Had to rework original tilemap functionallity to migrate
 * to TemplateVisType. Combined pieces from
 *   plugins/kbn_vislib_vis_types/public/tileMap.js
 *   ui/public/vislib/visualizations/tile_map.js
 */
import d3 from 'd3';
import _ from 'lodash';
import $ from 'jquery';
import Binder from 'ui/binder';
import MapProvider from 'plugins/enhanced_tilemap/vislib/_map';
import VislibVisTypeBuildChartDataProvider from 'ui/vislib_vis_type/build_chart_data';

define(function (require) {
  var module = require('ui/modules').get('kibana/enhanced_tilemap', ['kibana', 'etm-ui.bootstrap.accordion']);

  module.controller('KbnEnhancedTilemapVisController', function ($scope, $rootScope, $element, Private, courier, config, getAppState) {
    let buildChartData = Private(VislibVisTypeBuildChartDataProvider);
    const queryFilter = Private(require('ui/filter_bar/query_filter'));
    const pushFilter = Private(require('ui/filter_bar/push_filter'))(getAppState());
    const callbacks = Private(require('plugins/enhanced_tilemap/callbacks'));
    const POIsProvider = Private(require('plugins/enhanced_tilemap/POIs'));
    const utils = require('plugins/enhanced_tilemap/utils');
    let TileMapMap = Private(MapProvider);
    const ResizeChecker = Private(require('ui/vislib/lib/resize_checker'));
    let map = null;
    let collar = null;
    let chartData = null;
    appendMap();
    modifyToDsl();

    const shapeFields = $scope.vis.indexPattern.fields.filter(function (field) {
      return field.type === 'geo_shape';
    }).map(function (field) {
      return field.name;
    });
    //Using $root as mechanism to pass data to vis-editor-vis-options scope
    $scope.$root.etm = {
      shapeFields: shapeFields
    };

    const binder = new Binder();
    const resizeChecker = new ResizeChecker($element);
    binder.on(resizeChecker, 'resize', function() {
      resizeArea();
    });

    const respProcessor = {
      buildChartData: buildChartData,
      process: function(resp) {
        const aggs = resp.aggregations;
        let numGeoBuckets = 0;
        _.keys(aggs).forEach(function(key) {
          if(_.has(aggs[key], "filtered_geohash")) {
            aggs[key].buckets = aggs[key].filtered_geohash.buckets;
            delete aggs[key].filtered_geohash;
            numGeoBuckets = aggs[key].buckets.length;
          }
        });
        console.log("geogrids: " + numGeoBuckets);
        if(numGeoBuckets === 0) return null;

        const chartData = this.buildChartData(resp);
        const geoMinMax = utils.getGeoExtents(chartData);
        chartData.geoJson.properties.allmin = geoMinMax.min;
        chartData.geoJson.properties.allmax = geoMinMax.max;
        return chartData;
      },
      vis: $scope.vis
    }

    function modifyToDsl() {
      $scope.vis.aggs.origToDsl = $scope.vis.aggs.toDsl;
      $scope.vis.aggs.toDsl = function() {
        resizeArea();
        const dsl = $scope.vis.aggs.origToDsl();

        //append map collar filter to geohash_grid aggregation
        _.keys(dsl).forEach(function(key) {
          if(_.has(dsl[key], "geohash_grid")) {
            const origAgg = dsl[key];
            dsl[key] = {
              filter: aggFilter(origAgg.geohash_grid.field),
              aggs: {
                filtered_geohash: origAgg
              }
            }
          }
        });
        return dsl;
      }
    }

    function aggFilter(field) {
      collar = utils.scaleBounds(
        map.mapBounds(),
        $scope.vis.params.collarScale);
      var filter = {geo_bounding_box: {}};
      filter.geo_bounding_box[field] = collar;
      return filter;
    }

    $scope.$watch('vis.aggs', function (resp) {
      //'apply changes' creates new vis.aggs object - ensure toDsl is overwritten again
      if(!_.has($scope.vis.aggs, "origToDsl")) {
        modifyToDsl();
      }
    });

    $scope.$watch('vis.params', function (visParams) {
      draw();

      map.clearPOILayers();
      visParams.overlays.savedSearches.forEach(function (layerParams) {
        const poi = new POIsProvider(layerParams);
        poi.getPOIs(points => {
          map.addPOILayer(layerParams.savedSearchId, points, layerParams.color);
        });
      });
    });

    $scope.$watch('esResponse', function (resp) {
      if(_.has(resp, 'aggregations')) {
        chartData = respProcessor.process(resp);
        draw();
      }
    });

    $scope.$on("$destroy", function() {
      binder.destroy();
      resizeChecker.destroy();
      if (map) map.destroy();
    });

    function draw() {
      if(!chartData) return;

      //add overlay layer to provide visibility of filtered area
      let fieldName;
      if ($scope.vis.params.filterByShape && $scope.vis.params.shapeField) {
        fieldName = $scope.vis.params.shapeField;
      } else {
        const agg = _.get(chartData, 'geohashGridAgg');
        if (agg) {
          fieldName = agg.fieldName();
        }
      }
      if (fieldName) {
        map.addFilters(getGeoFilters(fieldName));
      }

      drawWmsOverlays();

      map.addMarkers(
        chartData,
        $scope.vis.params,
        Private(require('ui/agg_response/geo_json/_tooltip_formatter')),
        _.get(chartData, 'valueFormatter', _.identity),
        collar);
    }

    function getGeoFilters(field) {
      let filters = [];
      _.flatten([queryFilter.getAppFilters(), queryFilter.getGlobalFilters()]).forEach(function (it) {
        if (utils.isGeoFilter(it, field) && !_.get(it, 'meta.disabled', false)) {
          const features = utils.filterToGeoJson(it, field);
          filters = filters.concat(features);
        }
      });
      return filters;
    }

    function drawWmsOverlays() {
      map.clearWMSOverlays();
      if ($scope.vis.params.overlays.wmsOverlays.length === 0) {
        return;
      }

      const source = new courier.SearchSource();
      const appState = getAppState();
      source.set('filter', queryFilter.getFilters());
      if (appState.query && !appState.linked) {
        source.set('query', appState.query);
      }
      source._flatten().then(function (fetchParams) {
        const esQuery = fetchParams.body.query;
        //remove kibana parts of query
        const cleanedMust = [];
        if (_.has(esQuery, 'bool.must')) {
          esQuery.bool.must.forEach(function(must) {
            cleanedMust.push(_.omit(must, ['$state', '$$hashKey']));
          });
        }
        esQuery.bool.must = cleanedMust;
        const cleanedMustNot = [];
        if (_.has(esQuery, 'bool.must_not')) {
          esQuery.bool.must_not.forEach(function(mustNot) {
            cleanedMustNot.push(_.omit(mustNot, ['$state', '$$hashKey']));
          });
        }
        esQuery.bool.must_not = cleanedMustNot;
        const escapedQuery = JSON.stringify(esQuery).replace(new RegExp('[,]', 'g'), '\\,');

        $scope.vis.params.overlays.wmsOverlays.forEach(function(layerParams) {
          const name = _.get(layerParams, 'displayName', layerParams.layers);
          const options = {
            format: 'image/png',
            layers: layerParams.layers,
            maxFeatures: _.get(layerParams, 'maxFeatures', 1000),
            transparent: true,
            version: '1.1.1'
          };
          if (_.get(layerParams, 'viewparams')) {
            options.viewparams = 'q:' + escapedQuery;
          }
          map.addWmsOverlay(layerParams.url, name, options);
        });
      });
    }

    function appendMap() {
      callbacks.setPushFilter(pushFilter);
      const initialMapState = utils.getMapStateFromVis($scope.vis);
      var params = $scope.vis.params;
      var container = $element[0].querySelector('.tilemap');
      map = new TileMapMap(container, {
        center: initialMapState.center,
        zoom: initialMapState.zoom,
        callbacks: callbacks,
        mapType: params.mapType,
        attr: params,
        editable: $scope.vis.getEditableVis() ? true : false
      });
    }

    function resizeArea() {
      if (map) map.updateSize();
    }
  });
});
<commit_msg>bug fix - agg results not rendered when vis first created<commit_after>/*
 * Had to rework original tilemap functionallity to migrate
 * to TemplateVisType. Combined pieces from
 *   plugins/kbn_vislib_vis_types/public/tileMap.js
 *   ui/public/vislib/visualizations/tile_map.js
 */
import d3 from 'd3';
import _ from 'lodash';
import $ from 'jquery';
import Binder from 'ui/binder';
import MapProvider from 'plugins/enhanced_tilemap/vislib/_map';
import VislibVisTypeBuildChartDataProvider from 'ui/vislib_vis_type/build_chart_data';

define(function (require) {
  var module = require('ui/modules').get('kibana/enhanced_tilemap', ['kibana', 'etm-ui.bootstrap.accordion']);

  module.controller('KbnEnhancedTilemapVisController', function ($scope, $rootScope, $element, Private, courier, config, getAppState) {
    let buildChartData = Private(VislibVisTypeBuildChartDataProvider);
    const queryFilter = Private(require('ui/filter_bar/query_filter'));
    const pushFilter = Private(require('ui/filter_bar/push_filter'))(getAppState());
    const callbacks = Private(require('plugins/enhanced_tilemap/callbacks'));
    const POIsProvider = Private(require('plugins/enhanced_tilemap/POIs'));
    const utils = require('plugins/enhanced_tilemap/utils');
    let TileMapMap = Private(MapProvider);
    const ResizeChecker = Private(require('ui/vislib/lib/resize_checker'));
    let map = null;
    let collar = null;
    let chartData = null;
    appendMap();
    modifyToDsl();

    const shapeFields = $scope.vis.indexPattern.fields.filter(function (field) {
      return field.type === 'geo_shape';
    }).map(function (field) {
      return field.name;
    });
    //Using $root as mechanism to pass data to vis-editor-vis-options scope
    $scope.$root.etm = {
      shapeFields: shapeFields
    };

    const binder = new Binder();
    const resizeChecker = new ResizeChecker($element);
    binder.on(resizeChecker, 'resize', function() {
      resizeArea();
    });

    const respProcessor = {
      buildChartData: buildChartData,
      process: function(resp) {
        const aggs = resp.aggregations;
        _.keys(aggs).forEach(function(key) {
          if(_.has(aggs[key], "filtered_geohash")) {
            aggs[key].buckets = aggs[key].filtered_geohash.buckets;
            delete aggs[key].filtered_geohash;
          }
        });

        const chartData = this.buildChartData(resp);
        const geoMinMax = utils.getGeoExtents(chartData);
        chartData.geoJson.properties.allmin = geoMinMax.min;
        chartData.geoJson.properties.allmax = geoMinMax.max;
        return chartData;
      },
      vis: $scope.vis
    }

    function modifyToDsl() {
      $scope.vis.aggs.origToDsl = $scope.vis.aggs.toDsl;
      $scope.vis.aggs.toDsl = function() {
        resizeArea();
        const dsl = $scope.vis.aggs.origToDsl();

        //append map collar filter to geohash_grid aggregation
        _.keys(dsl).forEach(function(key) {
          if(_.has(dsl[key], "geohash_grid")) {
            const origAgg = dsl[key];
            dsl[key] = {
              filter: aggFilter(origAgg.geohash_grid.field),
              aggs: {
                filtered_geohash: origAgg
              }
            }
          }
        });
        return dsl;
      }
    }

    function aggFilter(field) {
      collar = utils.scaleBounds(
        map.mapBounds(),
        $scope.vis.params.collarScale);
      var filter = {geo_bounding_box: {}};
      filter.geo_bounding_box[field] = collar;
      return filter;
    }

    $scope.$watch('vis.aggs', function (resp) {
      //'apply changes' creates new vis.aggs object - ensure toDsl is overwritten again
      if(!_.has($scope.vis.aggs, "origToDsl")) {
        modifyToDsl();
      }
    });

    $scope.$watch('vis.params', function (visParams) {
      draw();

      map.clearPOILayers();
      visParams.overlays.savedSearches.forEach(function (layerParams) {
        const poi = new POIsProvider(layerParams);
        poi.getPOIs(points => {
          map.addPOILayer(layerParams.savedSearchId, points, layerParams.color);
        });
      });
    });

    $scope.$watch('esResponse', function (resp) {
      if(_.has(resp, 'aggregations')) {
        chartData = respProcessor.process(resp);
        draw();
      }
    });

    $scope.$on("$destroy", function() {
      binder.destroy();
      resizeChecker.destroy();
      if (map) map.destroy();
    });

    function draw() {
      if(!chartData) return;

      //add overlay layer to provide visibility of filtered area
      let fieldName;
      if ($scope.vis.params.filterByShape && $scope.vis.params.shapeField) {
        fieldName = $scope.vis.params.shapeField;
      } else {
        const agg = _.get(chartData, 'geohashGridAgg');
        if (agg) {
          fieldName = agg.fieldName();
        }
      }
      if (fieldName) {
        map.addFilters(getGeoFilters(fieldName));
      }

      drawWmsOverlays();

      map.addMarkers(
        chartData,
        $scope.vis.params,
        Private(require('ui/agg_response/geo_json/_tooltip_formatter')),
        _.get(chartData, 'valueFormatter', _.identity),
        collar);
    }

    function getGeoFilters(field) {
      let filters = [];
      _.flatten([queryFilter.getAppFilters(), queryFilter.getGlobalFilters()]).forEach(function (it) {
        if (utils.isGeoFilter(it, field) && !_.get(it, 'meta.disabled', false)) {
          const features = utils.filterToGeoJson(it, field);
          filters = filters.concat(features);
        }
      });
      return filters;
    }

    function drawWmsOverlays() {
      map.clearWMSOverlays();
      if ($scope.vis.params.overlays.wmsOverlays.length === 0) {
        return;
      }

      const source = new courier.SearchSource();
      const appState = getAppState();
      source.set('filter', queryFilter.getFilters());
      if (appState.query && !appState.linked) {
        source.set('query', appState.query);
      }
      source._flatten().then(function (fetchParams) {
        const esQuery = fetchParams.body.query;
        //remove kibana parts of query
        const cleanedMust = [];
        if (_.has(esQuery, 'bool.must')) {
          esQuery.bool.must.forEach(function(must) {
            cleanedMust.push(_.omit(must, ['$state', '$$hashKey']));
          });
        }
        esQuery.bool.must = cleanedMust;
        const cleanedMustNot = [];
        if (_.has(esQuery, 'bool.must_not')) {
          esQuery.bool.must_not.forEach(function(mustNot) {
            cleanedMustNot.push(_.omit(mustNot, ['$state', '$$hashKey']));
          });
        }
        esQuery.bool.must_not = cleanedMustNot;
        const escapedQuery = JSON.stringify(esQuery).replace(new RegExp('[,]', 'g'), '\\,');

        $scope.vis.params.overlays.wmsOverlays.forEach(function(layerParams) {
          const name = _.get(layerParams, 'displayName', layerParams.layers);
          const options = {
            format: 'image/png',
            layers: layerParams.layers,
            maxFeatures: _.get(layerParams, 'maxFeatures', 1000),
            transparent: true,
            version: '1.1.1'
          };
          if (_.get(layerParams, 'viewparams')) {
            options.viewparams = 'q:' + escapedQuery;
          }
          map.addWmsOverlay(layerParams.url, name, options);
        });
      });
    }

    function appendMap() {
      callbacks.setPushFilter(pushFilter);
      const initialMapState = utils.getMapStateFromVis($scope.vis);
      var params = $scope.vis.params;
      var container = $element[0].querySelector('.tilemap');
      map = new TileMapMap(container, {
        center: initialMapState.center,
        zoom: initialMapState.zoom,
        callbacks: callbacks,
        mapType: params.mapType,
        attr: params,
        editable: $scope.vis.getEditableVis() ? true : false
      });
    }

    function resizeArea() {
      if (map) map.updateSize();
    }
  });
});
<|endoftext|>


<commit_before>import R from 'ramda';
import Bacon from 'baconjs';
import { getActionStream } from './dispatcher'

// pluggables before store reducer.
let preReduces = [];
// pluggables after store reducer.
let postReduces = [];

const setPreReduce = (middleware) => {
  if (middleware.getPreReduce) {
    preReduces = R.concat(preReduces, middleware.getPreReduce);
  }
};

const setPostReduce = (middleware) => {
  if (middleware.getPostReduce) {
    postReduces = R.concat(postReduces, middleware.getPostReduce);
  }
};

const setPrePostReduce = R.pipe(
  R.tap(setPreReduce),
  setPostReduce
);

const mapPreArgs = (...args) => (
  R.merge({
    action: args[0],
    state: args[1]
  },
  args[2])
);

const mapPostArgs = (...args) => (
  R.merge({
    action: args[1],
    state: args[2],
    nextState: args[0]
  },
  args[3])
);

const plugStreams = (fromStream, getPluggable) => {
  let pluggable = getPluggable();
  pluggable.input.plug(fromStream);
  return pluggable.output;
};

const plugPreAndReducer = (name, getReducer, reducerArgs) => {
  let reducedStream = R.reduce(plugStreams,
    // pass action and store states,
    Bacon.when(reducerArgs, mapPreArgs)
      // merge in the store name.
      .map(R.merge({ name: name })),
    // to pre-reduce middlewares and reducer.
    R.flatten([preReduces, getReducer])
  );

  return [name,
    // turn action stream to property to be sampled.
    R.concat([reducedStream, reducerArgs[0].toProperty({})],
      // store properties.
      R.slice(1, Infinity, reducerArgs))];
};

const plugPostReduce = ([name, reducerArgs]) => (
  R.reduce(plugStreams,
    // pass information to post-reduce middleware.
    Bacon.when(reducerArgs, mapPostArgs)
      // merge in the store name.
      .map(R.merge({ name: name })),
    // to post-reduce middlewares.
    postReduces
  )
  // get the reduced state.
  .map(R.prop('nextState'))
);

const getStoreProperties = R.pipe(
  R.map(R.invoker(0, 'getProperty')),
  Bacon.combineTemplate
);

export const applyMiddleware = (...args) => {
  // loop through an array of middlewares.
  R.forEach(setPrePostReduce, args);
};

export const createStore = (name, getReducer, otherStores = {}) => {
  let storeStream = new Bacon.Bus(),
      storeProperty = storeStream.toProperty(null),
      otherProperties = getStoreProperties(otherStores),
      reducerArgs = [getActionStream(), storeProperty, otherProperties];

  storeStream.plug(
    R.pipe(
      plugPreAndReducer,
      plugPostReduce
    // store name and reducer.
    // an array of action and store states.
    )(name, getReducer, reducerArgs)
  );

  return {
    getProperty: () => storeProperty
  };
};
<commit_msg>Limit direct access to middleware arrays<commit_after>import R from 'ramda';
import Bacon from 'baconjs';
import { getActionStream } from './dispatcher'

const hasMiddlewareType = (concat, type, middleware) => (
  R.is(Function, middleware[type])
);

const concatMiddlewareByType = (concat, type, middleware) => (
  concat(middleware[type])
);

const concatMiddleware = R.cond([
  [R.complement(R.nthArg(2)), R.F],
  [hasMiddlewareType, concatMiddlewareByType]
]);

// pluggables before store reducer.
const preReduces = (() => {
  let array = [];
  return (middleware) => (
    array = concatMiddleware(
      R.concat(array), 'getPreReduce', middleware) || array
  );
})();

// pluggables after store reducer.
const postReduces = (() => {
  let array = [];
  return (middleware) => (
    array = concatMiddleware(
      R.concat(array), 'getPostReduce', middleware) || array
  );
})();

const setPrePostReduce = R.pipe(
  R.tap(preReduces),
  postReduces
);

const mapPreArgs = (...args) => (
  R.merge({
    action: args[0],
    state: args[1]
  },
  args[2])
);

const mapPostArgs = (...args) => (
  R.merge({
    action: args[1],
    state: args[2],
    nextState: args[0]
  },
  args[3])
);

const plugStreams = (fromStream, getPluggable) => {
  let pluggable = getPluggable();
  pluggable.input.plug(fromStream);
  return pluggable.output;
};

const plugPreAndReducer = (name, getReducer, reducerArgs) => {
  let reducedStream = R.reduce(plugStreams,
    // pass action and store states,
    Bacon.when(reducerArgs, mapPreArgs)
      // merge in the store name.
      .map(R.merge({ name: name })),
    // to pre-reduce middlewares and reducer.
    R.flatten([preReduces(), getReducer])
  );

  return [name,
    // turn action stream to property to be sampled.
    R.concat([reducedStream, reducerArgs[0].toProperty({})],
      // store properties.
      R.slice(1, Infinity, reducerArgs))];
};

const plugPostReduce = ([name, reducerArgs]) => (
  R.reduce(plugStreams,
    // pass information to post-reduce middleware.
    Bacon.when(reducerArgs, mapPostArgs)
      // merge in the store name.
      .map(R.merge({ name: name })),
    // to post-reduce middlewares.
    postReduces()
  )
  // get the reduced state.
  .map(R.prop('nextState'))
);

const getStoreProperties = R.pipe(
  R.map(R.invoker(0, 'getProperty')),
  Bacon.combineTemplate
);

export const applyMiddleware = (...args) => {
  // loop through an array of middlewares.
  R.forEach(setPrePostReduce, args);
};

export const createStore = (name, getReducer, otherStores = {}) => {
  let storeStream = new Bacon.Bus(),
      storeProperty = storeStream.toProperty(null),
      otherProperties = getStoreProperties(otherStores),
      reducerArgs = [getActionStream(), storeProperty, otherProperties];

  storeStream.plug(
    R.pipe(
      plugPreAndReducer,
      plugPostReduce
    // store name and reducer.
    // an array of action and store states.
    )(name, getReducer, reducerArgs)
  );

  return {
    getProperty: () => storeProperty
  };
};
<|endoftext|>


<commit_before>/*
 *  Thank you!
 *  https://github.com/iambrandonn/WebcamSwiper
 *
 * */
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || undefined;
var videoElement;

function initializeWebcamSwiper() {
    if (navigator.getUserMedia === undefined) {
        if (console !== undefined) {
            console.log("Browser doesn't support getUserMedia");
            return;
        }
    }

    navigator.getUserMedia({video: true, audio: false}, function (stream) {
        window.webcamSwiperStream = stream;

        // Create a video element and set its source to the stream from the webcam
        videoElement = document.createElement("video");
        videoElement.style.display = "none";
        document.getElementsByTagName("body")[0].appendChild(videoElement);
        if (window.URL === undefined) {
            window.URL = window.webkitURL;
        }

        if (videoElement.mozSrcObject !== undefined) {
            videoElement.mozSrcObject = stream;
        }
        else {
            videoElement.src = window.URL.createObjectURL(stream);
        }
        videoElement.play();

        // Wait for the video element to initialize
        videoElement.addEventListener("loadeddata", startSwipeRecogntion);
    }, function(err) {
        console('Something went wrong in getUserMedia');
    });
}

function startSwipeRecogntion() {
    // Now that the video element has been initialized, determine the webcam resolution from it
    var horizontalResolution = videoElement.videoWidth;
    var verticalResolution = videoElement.videoHeight;

    if (horizontalResolution < 1 || horizontalResolution > 4000) {
        setTimeout(startSwipeRecogntion, 100);
        console.log('Trying again');
        return;
    }

    var canvasWidth = horizontalResolution > 320 ? 320 : horizontalResolution;
    var canvasHeight = verticalResolution > 240 ? 240 : verticalResolution;

    // Create the canvas that we will draw to
    var greyScaleCnvs = document.createElement("canvas");
    greyScaleCnvs.width = canvasWidth;
    greyScaleCnvs.height = canvasHeight;
    var greyscaleCtx = greyScaleCnvs.getContext("2d");
    var currentImageData = greyscaleCtx.createImageData(canvasWidth, canvasHeight);

    // Initialize some variables we will reference each frame
    var isActive = false;
    var remainingFrames = 14;
    var PIXEL_CHANGE_THRESHOLD = 30;
    var FRAME_THRESHOLD = 15000;
    var originalWeight = 0;
    var previousImageData;
    var lightLevel = 0;
    var scanCount = 0;
    var frameAnalysisTime = 36;

    // every ?th of a second, sample the video stream
    window.webcamSwiperInterval = setInterval(analyzeCurrentFrame, 1000/28);

    function analyzeCurrentFrame() {
        // Start the timer
        var startTime = new Date().getTime();

        scanCount++;

        previousImageData = currentImageData;

        // Draw the current video frame onto a canvas so we can desaturate the image
        greyscaleCtx.drawImage(videoElement, 0, 0, horizontalResolution, verticalResolution, 0, 0, canvasWidth, canvasHeight);

        // Desaturate it
        currentImageData = deSaturate(greyscaleCtx.getImageData(0, 0, canvasWidth, canvasHeight));

        // Make adjustments for light level and system speed
        if (scanCount % 50 === 0) {
            // Calibrate based on the current light level, if we haven't already
            lightLevel = getLightLevel(currentImageData);
            if (lightLevel > 0 && lightLevel <= 1) {
                PIXEL_CHANGE_THRESHOLD = 25;
                FRAME_THRESHOLD = 3000;
            }
            else if (lightLevel > 1 && lightLevel < 3) {
                PIXEL_CHANGE_THRESHOLD = 28;
                FRAME_THRESHOLD = 6000;
            }
            else {
                PIXEL_CHANGE_THRESHOLD = 30;
                FRAME_THRESHOLD = 15000;
            }

            // Adjust frame scan rate if needed
            if (frameAnalysisTime > 36) {
                clearInterval(window.webcamSwiperInterval);
                window.webcamSwiperInterval = setInterval(analyzeCurrentFrame, 1000 / (frameAnalysisTime * 2)); // Add some buffer
            }
        }

        // Map the pixels that are changing
        var currentWeight = getMotionWeight(previousImageData, currentImageData);

        // If we aren't actively looking for a spike the opposite direction, check if we should start
        if (!isActive) {
            if (Math.abs(currentWeight) > FRAME_THRESHOLD) {
                isActive = true;
                remainingFrames = 8;
                originalWeight = currentWeight;
            }
        }

        // If we are actively looking for a spike, see if it has occurred within the allowed number of frames
        if (isActive) {
            if (remainingFrames <= 0) {
                isActive = false;
            }
            else {
                remainingFrames--;
                if (originalWeight > 0) {
                    if (currentWeight < -FRAME_THRESHOLD) {
                        fireSwipeEvent("webcamSwipeRight");
                        isActive = false;
                    }
                }
                else {
                    if (currentWeight > FRAME_THRESHOLD) {
                        fireSwipeEvent("webcamSwipeLeft");
                        isActive = false;
                    }
                }
            }
        }

        // Stop the timer
        var endTime = new Date().getTime();
        frameAnalysisTime = endTime - startTime;
    }

    function fireSwipeEvent(eventName) {
        var swipeLeftEvent = document.createEvent("UIEvents");
        swipeLeftEvent.initEvent(eventName, false, false);
        document.getElementsByTagName("body")[0].dispatchEvent(swipeLeftEvent);
    }

    function getMotionWeight (previous, current) {
        var motionWeight = 0;
        var previousData = previous.data;
        var currentData = current.data;
        var dataLength = previousData.length;
        for (var i = 0; i < dataLength; i += 4) {
            if (Math.abs(previousData[i] - currentData[i]) > PIXEL_CHANGE_THRESHOLD) {
                motionWeight += ((i / 4) % canvasWidth) - (canvasWidth / 2);
            }
        }
        return motionWeight;
    }

    // Takes and ImageData and returns an equally sized Image Data which is desaturated
    function deSaturate (imageData) {
        var theData = imageData.data;
        var newImageData = greyscaleCtx.createImageData(imageData);
        var newData = newImageData.data;

        // Iterate through each pixel, desaturating it
        var dataLength = theData.length;
        for (var i = 0; i < dataLength; i += 4) {
            // To find the desaturated value, average the brightness of the red, green, and blue values
            var average = (theData[i] + theData[i + 1] + theData[i + 2]) / 3;
            newData[i] = newData[i+1] = newData[i+2] = average;

            // Fully opaque
            newData[i+3] = 255;
        }

        return newImageData;
    }

    // Will return the average intensity of all pixels.  Used for calibrating sensitivity based on room light level.
    function getLightLevel (imageData) {
        var theData = imageData.data;
        var dataLength = theData.length;

        var value = 0;
        for (var i = 0; i < dataLength; i += 4) {
            value += theData[i];
        }

        return value / theData.length;
    }
}

function destroyWebcamSwiper() {
    if (window.webcamSwiperInterval !== undefined) {
        clearInterval(window.webcamSwiperInterval);
        window.webcamSwiperInterval = undefined;
    }
    if (window.webcamSwiperStream !== undefined) {
        window.webcamSwiperStream.stop();
        window.webcamSwiperStream = undefined;
    }
}
<commit_msg>minor fixes<commit_after>/*
 *  Thank you!
 *  https://github.com/iambrandonn/WebcamSwiper
 *
 * */
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || undefined;
var videoElement;

function initializeWebcamSwiper() {
    if (navigator.getUserMedia === undefined) {
        if (console !== undefined) {
            $(".info").html("Browser doesn't support getUserMedia");
            return;
        }
    }

    navigator.getUserMedia({video: true, audio: false}, function (stream) {
        window.webcamSwiperStream = stream;

        // Create a video element and set its source to the stream from the webcam
        videoElement = document.createElement("video");
        videoElement.style.display = "none";
        document.getElementsByTagName("body")[0].appendChild(videoElement);
        if (window.URL === undefined) {
            window.URL = window.webkitURL;
        }

        if (videoElement.mozSrcObject !== undefined) {
            videoElement.mozSrcObject = stream;
        }
        else {
            videoElement.src = window.URL.createObjectURL(stream);
        }
        videoElement.play();

        // Wait for the video element to initialize
        videoElement.addEventListener("loadeddata", startSwipeRecogntion);
    }, function(err) {
        $(".info").html('Something went wrong in getUserMedia');
    });
}

function startSwipeRecogntion() {
    // Now that the video element has been initialized, determine the webcam resolution from it
    var horizontalResolution = videoElement.videoWidth;
    var verticalResolution = videoElement.videoHeight;

    if (horizontalResolution < 1 || horizontalResolution > 4000) {
        setTimeout(startSwipeRecogntion, 100);
        console.log('Trying again');
        return;
    }

    var canvasWidth = horizontalResolution > 320 ? 320 : horizontalResolution;
    var canvasHeight = verticalResolution > 240 ? 240 : verticalResolution;

    // Create the canvas that we will draw to
    var greyScaleCnvs = document.createElement("canvas");
    greyScaleCnvs.width = canvasWidth;
    greyScaleCnvs.height = canvasHeight;
    var greyscaleCtx = greyScaleCnvs.getContext("2d");
    var currentImageData = greyscaleCtx.createImageData(canvasWidth, canvasHeight);

    // Initialize some variables we will reference each frame
    var isActive = false;
    var remainingFrames = 14;
    var PIXEL_CHANGE_THRESHOLD = 30;
    var FRAME_THRESHOLD = 15000;
    var originalWeight = 0;
    var previousImageData;
    var lightLevel = 0;
    var scanCount = 0;
    var frameAnalysisTime = 36;

    // every ?th of a second, sample the video stream
    window.webcamSwiperInterval = setInterval(analyzeCurrentFrame, 1000/28);

    function analyzeCurrentFrame() {
        // Start the timer
        var startTime = new Date().getTime();

        scanCount++;

        previousImageData = currentImageData;

        // Draw the current video frame onto a canvas so we can desaturate the image
        greyscaleCtx.drawImage(videoElement, 0, 0, horizontalResolution, verticalResolution, 0, 0, canvasWidth, canvasHeight);

        // Desaturate it
        currentImageData = deSaturate(greyscaleCtx.getImageData(0, 0, canvasWidth, canvasHeight));

        // Make adjustments for light level and system speed
        if (scanCount % 50 === 0) {
            // Calibrate based on the current light level, if we haven't already
            lightLevel = getLightLevel(currentImageData);
            if (lightLevel > 0 && lightLevel <= 1) {
                PIXEL_CHANGE_THRESHOLD = 25;
                FRAME_THRESHOLD = 3000;
            }
            else if (lightLevel > 1 && lightLevel < 3) {
                PIXEL_CHANGE_THRESHOLD = 28;
                FRAME_THRESHOLD = 6000;
            }
            else {
                PIXEL_CHANGE_THRESHOLD = 30;
                FRAME_THRESHOLD = 15000;
            }

            // Adjust frame scan rate if needed
            if (frameAnalysisTime > 36) {
                clearInterval(window.webcamSwiperInterval);
                window.webcamSwiperInterval = setInterval(analyzeCurrentFrame, 1000 / (frameAnalysisTime * 2)); // Add some buffer
            }
        }

        // Map the pixels that are changing
        var currentWeight = getMotionWeight(previousImageData, currentImageData);

        // If we aren't actively looking for a spike the opposite direction, check if we should start
        if (!isActive) {
            if (Math.abs(currentWeight) > FRAME_THRESHOLD) {
                isActive = true;
                remainingFrames = 8;
                originalWeight = currentWeight;
            }
        }

        // If we are actively looking for a spike, see if it has occurred within the allowed number of frames
        if (isActive) {
            if (remainingFrames <= 0) {
                isActive = false;
            }
            else {
                remainingFrames--;
                if (originalWeight > 0) {
                    if (currentWeight < -FRAME_THRESHOLD) {
                        fireSwipeEvent("webcamSwipeRight");
                        isActive = false;
                    }
                }
                else {
                    if (currentWeight > FRAME_THRESHOLD) {
                        fireSwipeEvent("webcamSwipeLeft");
                        isActive = false;
                    }
                }
            }
        }

        // Stop the timer
        var endTime = new Date().getTime();
        frameAnalysisTime = endTime - startTime;
    }

    function fireSwipeEvent(eventName) {
        var swipeLeftEvent = document.createEvent("UIEvents");
        swipeLeftEvent.initEvent(eventName, false, false);
        document.getElementsByTagName("body")[0].dispatchEvent(swipeLeftEvent);
    }

    function getMotionWeight (previous, current) {
        var motionWeight = 0;
        var previousData = previous.data;
        var currentData = current.data;
        var dataLength = previousData.length;
        for (var i = 0; i < dataLength; i += 4) {
            if (Math.abs(previousData[i] - currentData[i]) > PIXEL_CHANGE_THRESHOLD) {
                motionWeight += ((i / 4) % canvasWidth) - (canvasWidth / 2);
            }
        }
        return motionWeight;
    }

    // Takes and ImageData and returns an equally sized Image Data which is desaturated
    function deSaturate (imageData) {
        var theData = imageData.data;
        var newImageData = greyscaleCtx.createImageData(imageData);
        var newData = newImageData.data;

        // Iterate through each pixel, desaturating it
        var dataLength = theData.length;
        for (var i = 0; i < dataLength; i += 4) {
            // To find the desaturated value, average the brightness of the red, green, and blue values
            var average = (theData[i] + theData[i + 1] + theData[i + 2]) / 3;
            newData[i] = newData[i+1] = newData[i+2] = average;

            // Fully opaque
            newData[i+3] = 255;
        }

        return newImageData;
    }

    // Will return the average intensity of all pixels.  Used for calibrating sensitivity based on room light level.
    function getLightLevel (imageData) {
        var theData = imageData.data;
        var dataLength = theData.length;

        var value = 0;
        for (var i = 0; i < dataLength; i += 4) {
            value += theData[i];
        }

        return value / theData.length;
    }
}

function destroyWebcamSwiper() {
    if (window.webcamSwiperInterval !== undefined) {
        clearInterval(window.webcamSwiperInterval);
        window.webcamSwiperInterval = undefined;
    }
    if (window.webcamSwiperStream !== undefined) {
        window.webcamSwiperStream.stop();
        window.webcamSwiperStream = undefined;
    }
}
<|endoftext|>


<commit_before>var apikeys = require('../apikeys');

var Promise       = require('bluebird'),
    request       = require('request'),
    //failed to promisify xml2js (not sure if by own error or by bug in either lib)
    parseXML      = require('xml2js').parseString;

var redis         = require("redis"),
    redisClient   = redis.createClient();

redisClient.on("error", function (err) {
    console.log("Redis Error: ", err);
});

var synonyms = {
    getSynonyms : function(word) {
        var self = this;
        return new Promise(function(resolve, reject) {

            var synonymsList = [];

            //check if synonym exists in cache
            var getCachedSynonymsPromise = self._getCachedSynonyms(word);

            //Handle cache repsonse
            var apiPromise = getCachedSynonymsPromise.then(function(result) {
                    if(!result) {
                        //No cached synonyms found so make api request
                        return Promise.all([self._makeApiRequest(word), self._makeWordnikApiRequest(word)]);
                    }
                    //Cached synonyms found
                    resolve(result);
                })
                .error(function(err) {
                    reject(err);
                })
                //handle the all or continue passing along the empty result
                .then(function(result) {
                    if(result) {
                        //console.log('wordnik api request', result);
                        var joinedSynonyms = result[0]
                            .concat(result[1])
                            .sort()
                            .filter(function(item, pos, arr) {
                                return !pos || item != arr[pos - 1];
                            });
                        return joinedSynonyms;
                    }
                }, function(err) {
                    console.log('error making api requests: ', err);
                });

            //Handle Api Response
            var setCachedSynonymsPromise = apiPromise.then(function(result) {
                    //check for result to see if apiRequest was even made. this fix works, but seems hacky
                    if(result) {
                        synonymsList = result;
                        return self._setCachedSynonyms(word, result);
                    }
                })
                .error(function(err) {
                    reject(err);
                });

            //Handle setting cache response
            setCachedSynonymsPromise.then(function(result) {
                    if(result) {
                        //console.log('success setting cached synonyms: ', result);
                    } else {
                        //console.log('results already cached');
                    }
                })
                .error(function(err) {
                    console.log('error setting cached synonyms: ', err);
                })
                .finally(function() {
                    resolve(synonymsList);
                });

        });
    },

    /*
     * Utility Methods
     * These methods below are exposed for unit testing purposes only
     */

    _makeApiRequest : function(word) {

        var self = this;

        return new Promise(function(resolve, reject) {
            //word was not in cache
            var url =
                'http://www.dictionaryapi.com/api/v1/references/thesaurus/xml/' +
                word +
                '?key=' +
                apikeys.thesaurus;

            request(url, function(error, response, body) {
                if(error) {
                    reject(error);
                    return;
                }
                parseXML(body, function(error, result) {
                    if(error) {
                        reject(error);
                    } else {
                        if(result.suggestion || result.entry_list.suggestion) {
                            //reject({error: 'Synonyms not found. Please check your spelling.'});
                            resolve(false);
                        }
                        var parsedSynonyms = self._parseSynonymsXML(result);
                        if(parsedSynonyms.error) {
                            reject(parsedSynonyms);
                        }

                        var synonymsList = self._synonymsList(parsedSynonyms);
                        resolve(synonymsList);
                    }
                })
            });
        });
    },

    _makeWordnikApiRequest : function(word) {

        var self = this;

        return new Promise(function(resolve, reject) {
            //word was not in cache
            var url =
                'http://api.wordnik.com:80/v4/word.json/' +
                word +
                '/relatedWords?useCanonical=false&limitPerRelationshipType=100&api_key=' +
                apikeys.wordnik;

            request(url, function(error, response, body) {
                if(error) {
                    reject(error);
                    return;
                }
                //parsing the json from wordnik is so much easier
                //console.log('Wordnik Response: ', body);
                resolve(self._parseWordnikResults(JSON.parse(body)));
            });
        });
    },

    _parseWordnikResults : function(results) {
        // hash table for fast lookups
        var wantedRelationshipTypes = {
            "verb-form": true,
            "hypernym": true,
            "etymologically-related-term": true,
            "variant": true,
            "same-context": true,
            "synonym": true
        };
        var words = [];
        for(var i = 0; i < results.length; i++) {
            if(wantedRelationshipTypes[results[i].relationshipType] === true) {
                words = words.concat(results[i].words);
            }
        }
        return words;
    },

    _getCachedSynonyms : function(word) {
        return new Promise(function(resolve, reject) {
            redisClient.get('synonyms:' + word, function(err, result) {
                if(err) {
                    reject(err);
                } else if(result) {
                    //results are stored in redis as a string. Need to blow it up into array
                    var resultArray = result.split(',');
                    resolve(resultArray);
                } else {
                    resolve(false);
                }
            });
        });
    },

    _setCachedSynonyms : function(word, synonyms) {
        return new Promise(function(resolve, reject) {
            //cache synonyms results
            redisClient.set('synonyms:' + word, synonyms, function(err, result) {
                if(err) {
                    reject(err);
                } else if(result) {
                    var secondsPerDay = 60 * 60 * 24;
                    var ttlDays = 180;
                    var ttl = ttlDays * secondsPerDay;
                    redisClient.expire('synonyms:' + word, ttl, function(err, res) {
                        //console.log('expire err:', err);
                        //console.log('expire result: ', res);
                    });
                    resolve(result);
                } else {
                    reject('Redis: Unkown error caching synonymsList');
                }
            });
        });
    },

    //this method returns an object of mapped data, or an object with an error property
    _parseSynonymsXML : function(rawSynonyms) {
        //check for needed properties
        if( !rawSynonyms ||
            !rawSynonyms.entry_list ||
            !rawSynonyms.entry_list.entry ||
            rawSynonyms.entry_list.entry.length == 0 ) {

                return {error: '_mapRawSynonyms: rawSynonyms fails validation'};
        }

        //an entry differentiates between words by type like noun or verb
        var entries = [];
        var entryCount = rawSynonyms.entry_list.entry.length;
        for(var i = 0; i < entryCount; i++) {
            var rawEntry = rawSynonyms.entry_list.entry[i];

            var entry = {};
            entry.wordType = rawEntry.fl;

            var senses = [];
            var senseCount  = rawEntry.sens.length;
            for(var j = 0; j < senseCount; j++) {
                var rawSense = rawEntry.sens[j];

                //Checking for valid Sense.
                //  The API sometimes returns invalid Senses mixed with valid ones (investigate why)
                if( !rawSense ||
                    !rawSense.mc ||
                    !rawSense.mc.length ||
                    !rawSense.rel ||
                    !rawSense.rel.length ||
                    !rawSense.syn ||
                    !rawSense.syn.length) {
                    //console.log('rawSense is null. senseCount: ', senseCount, ' j: ', j, ' i: ', i);
                    continue;
                }

                var sense = {};
                sense.meaning = rawSense.mc[0];

                var rel = rawSense.rel[0];
                if(rel._) {
                    rel = rel._;
                }
                var syn = rawSense.syn[0];
                if(syn._) {
                    syn = syn._;
                }
                var relWords = this._extractKeywords(rel);
                var synWords = this._extractKeywords(syn);
                sense.words = synWords.concat(relWords);

                senses.push(sense);
            }
            entry.senses = senses;

            entries.push(entry);
        }

        return entries;
    },

    _extractKeywords : function(str) {
        return str
            //remove whitespace
            .replace(/(\s)/g, '')
            //remove parentheses and words in them
            .replace(/(\(.*\))/g, '')
            //replace semicolons with commas
            .replace(/([;])/g, ',')
            //remove square brackets (are they empty? I forget the format that caused this check to be necessary)
            .replace(/(\[\])/g, '')
            //remove hyphens
            .replace(/(-)/g, '')
            //use commas to split words into an array
            .split(',');
    },

    //Takes in a synonyms array.
    /*
        synonyms[] {
            wordType : ...
            senses[] : {
                meaning : ...
                words[] : ...
            }
        }
    */
    _synonymsList : function(synonyms) {
        //storing domains as a hash
        var synonymsHash = {};

        //so many loops. caching lengths to squeeze performance a bit.
        var synonymsCount = synonyms.length;
        for(var i = 0; i < synonymsCount; i++) {
            var synonymSet = synonyms[i]
            var senseCount = synonymSet.senses.length;
            for(var j = 0; j < senseCount; j++) {
                var sense = synonymSet.senses[j];

                var wordCount = sense.words.length;
                for(var k = 0; k < wordCount; k++) {
                    var synonym = sense.words[k];
                    synonymsHash[synonym] = true;
                }
            }
        }

        var result = [], prop, i;

        for (prop in synonymsHash) {
            if (hasOwnProperty.call(synonymsHash, prop)) {
                result.push(prop);
            }
        }

        return result;
    }
};

module.exports = synonyms;<commit_msg>Synonyms were not being set by the cache properly.<commit_after>var apikeys = require('../apikeys');

var Promise       = require('bluebird'),
    request       = require('request'),
    //failed to promisify xml2js (not sure if by own error or by bug in either lib)
    parseXML      = require('xml2js').parseString;

var redis         = require("redis"),
    redisClient   = redis.createClient();

redisClient.on("error", function (err) {
    console.log("Redis Error: ", err);
});

var synonyms = {
    getSynonyms : function(word) {
        var self = this;
        return new Promise(function(resolve, reject) {

            var synonymsList = [];

            //check if synonym exists in cache
            var getCachedSynonymsPromise = self._getCachedSynonyms(word);

            //Handle cache repsonse
            var apiPromise = getCachedSynonymsPromise.then(function(result) {
                    if(!result) {
                        //No cached synonyms found so make api request
                        return Promise.all([self._makeApiRequest(word), self._makeWordnikApiRequest(word)]);
                    }
                    //Cached synonyms found
                    resolve(result);
                })
                .error(function(err) {
                    reject(err);
                })
                //handle the all or continue passing along the empty result
                .then(function(result) {
                    if(result) {
                        //console.log('wordnik api request', result);
                        var joinedSynonyms = result[0]
                            .concat(result[1])
                            .sort()
                            .filter(function(item, pos, arr) {
                                return !pos || item != arr[pos - 1];
                            });
                        return joinedSynonyms;
                    }
                }, function(err) {
                    console.log('error making api requests: ', err);
                });

            //Handle Api Response
            var setCachedSynonymsPromise = apiPromise.then(function(result) {
                    //check for result to see if apiRequest was even made. this fix works, but seems hacky
                    if(result) {
                        synonymsList = result;
                        return self._setCachedSynonyms(word, result);
                    }
                })
                .error(function(err) {
                    reject(err);
                });

            //Handle setting cache response
            setCachedSynonymsPromise.then(function(result) {
                    if(result) {
                        //console.log('success setting cached synonyms: ', result);
                    } else {
                        //console.log('results already cached');
                    }
                })
                .error(function(err) {
                    console.log('error setting cached synonyms: ', err);
                })
                .finally(function() {
                    resolve(synonymsList);
                });

        });
    },

    /*
     * Utility Methods
     * These methods below are exposed for unit testing purposes only
     */

    _makeApiRequest : function(word) {

        var self = this;

        return new Promise(function(resolve, reject) {
            //word was not in cache
            var url =
                'http://www.dictionaryapi.com/api/v1/references/thesaurus/xml/' +
                word +
                '?key=' +
                apikeys.thesaurus;

            request(url, function(error, response, body) {
                if(error) {
                    reject(error);
                    return;
                }
                parseXML(body, function(error, result) {
                    if(error) {
                        reject(error);
                    } else {
                        if(result.suggestion || result.entry_list.suggestion) {
                            //reject({error: 'Synonyms not found. Please check your spelling.'});
                            resolve(false);
                        }
                        var parsedSynonyms = self._parseSynonymsXML(result);
                        if(parsedSynonyms.error) {
                            reject(parsedSynonyms);
                        }

                        var synonymsList = self._synonymsList(parsedSynonyms);
                        resolve(synonymsList);
                    }
                })
            });
        });
    },

    _makeWordnikApiRequest : function(word) {

        var self = this;

        return new Promise(function(resolve, reject) {
            //word was not in cache
            var url =
                'http://api.wordnik.com:80/v4/word.json/' +
                word +
                '/relatedWords?useCanonical=false&limitPerRelationshipType=100&api_key=' +
                apikeys.wordnik;

            request(url, function(error, response, body) {
                if(error) {
                    reject(error);
                    return;
                }
                //parsing the json from wordnik is so much easier
                //console.log('Wordnik Response: ', body);
                resolve(self._parseWordnikResults(JSON.parse(body)));
            });
        });
    },

    _parseWordnikResults : function(results) {
        // hash table for fast lookups
        var wantedRelationshipTypes = {
            "verb-form": true,
            "hypernym": true,
            "etymologically-related-term": true,
            "variant": true,
            "same-context": true,
            "synonym": true
        };
        var words = [];
        for(var i = 0; i < results.length; i++) {
            if(wantedRelationshipTypes[results[i].relationshipType] === true) {
                words = words.concat(results[i].words);
            }
        }
        return words;
    },

    _getCachedSynonyms : function(word) {
        return new Promise(function(resolve, reject) {
            redisClient.get('synonyms:' + word, function(err, result) {
                if(err) {
                    reject(err);
                } else if(result) {
                    //results are stored in redis as a string. Need to blow it up into array
                    var resultArray = result.split(',');
                    resolve(resultArray);
                } else {
                    resolve(false);
                }
            });
        });
    },

    _setCachedSynonyms : function(word, synonyms) {
        return new Promise(function(resolve, reject) {
            //cache synonyms results
            redisClient.set('synonyms:' + word, synonyms.join(), function(err, result) {
                if(err) {
                    reject(err);
                } else if(result) {
                    var secondsPerDay = 60 * 60 * 24;
                    var ttlDays = 180;
                    var ttl = ttlDays * secondsPerDay;
                    redisClient.expire('synonyms:' + word, ttl, function(err, res) {
                        //console.log('expire err:', err);
                        //console.log('expire result: ', res);
                    });
                    resolve(result);
                } else {
                    reject('Redis: Unkown error caching synonymsList');
                }
            });
        });
    },

    //this method returns an object of mapped data, or an object with an error property
    _parseSynonymsXML : function(rawSynonyms) {
        //check for needed properties
        if( !rawSynonyms ||
            !rawSynonyms.entry_list ||
            !rawSynonyms.entry_list.entry ||
            rawSynonyms.entry_list.entry.length == 0 ) {

                return {error: '_mapRawSynonyms: rawSynonyms fails validation'};
        }

        //an entry differentiates between words by type like noun or verb
        var entries = [];
        var entryCount = rawSynonyms.entry_list.entry.length;
        for(var i = 0; i < entryCount; i++) {
            var rawEntry = rawSynonyms.entry_list.entry[i];

            var entry = {};
            entry.wordType = rawEntry.fl;

            var senses = [];
            var senseCount  = rawEntry.sens.length;
            for(var j = 0; j < senseCount; j++) {
                var rawSense = rawEntry.sens[j];

                //Checking for valid Sense.
                //  The API sometimes returns invalid Senses mixed with valid ones (investigate why)
                if( !rawSense ||
                    !rawSense.mc ||
                    !rawSense.mc.length ||
                    !rawSense.rel ||
                    !rawSense.rel.length ||
                    !rawSense.syn ||
                    !rawSense.syn.length) {
                    //console.log('rawSense is null. senseCount: ', senseCount, ' j: ', j, ' i: ', i);
                    continue;
                }

                var sense = {};
                sense.meaning = rawSense.mc[0];

                var rel = rawSense.rel[0];
                if(rel._) {
                    rel = rel._;
                }
                var syn = rawSense.syn[0];
                if(syn._) {
                    syn = syn._;
                }
                var relWords = this._extractKeywords(rel);
                var synWords = this._extractKeywords(syn);
                sense.words = synWords.concat(relWords);

                senses.push(sense);
            }
            entry.senses = senses;

            entries.push(entry);
        }

        return entries;
    },

    _extractKeywords : function(str) {
        return str
            //remove whitespace
            .replace(/(\s)/g, '')
            //remove parentheses and words in them
            .replace(/(\(.*\))/g, '')
            //replace semicolons with commas
            .replace(/([;])/g, ',')
            //remove square brackets (are they empty? I forget the format that caused this check to be necessary)
            .replace(/(\[\])/g, '')
            //remove hyphens
            .replace(/(-)/g, '')
            //use commas to split words into an array
            .split(',');
    },

    //Takes in a synonyms array.
    /*
        synonyms[] {
            wordType : ...
            senses[] : {
                meaning : ...
                words[] : ...
            }
        }
    */
    _synonymsList : function(synonyms) {
        //storing domains as a hash
        var synonymsHash = {};

        //so many loops. caching lengths to squeeze performance a bit.
        var synonymsCount = synonyms.length;
        for(var i = 0; i < synonymsCount; i++) {
            var synonymSet = synonyms[i]
            var senseCount = synonymSet.senses.length;
            for(var j = 0; j < senseCount; j++) {
                var sense = synonymSet.senses[j];

                var wordCount = sense.words.length;
                for(var k = 0; k < wordCount; k++) {
                    var synonym = sense.words[k];
                    synonymsHash[synonym] = true;
                }
            }
        }

        var result = [], prop, i;

        for (prop in synonymsHash) {
            if (hasOwnProperty.call(synonymsHash, prop)) {
                result.push(prop);
            }
        }

        return result;
    }
};

module.exports = synonyms;<|endoftext|>


<commit_before>'use strict'

var opts = {
  metrics: {
    emitter: {
      enabled: false
    }
  },
  seneca_metrics: {
    group: 'nodezoo',
    tag: 'info',
    pins: [
      'role:info,req:part',
      'role:info,res:part'
    ]
  },
  mesh: {
    auto: true,
    listen: [
      {pin: 'role:info,cmd:get', model: 'consume'},
      {pin: 'role:info,res:part', model: 'observe'}
    ]
  }
}

require('seneca')()
  .use('entity')
  .use('../info.js')
  .use('vidi-metrics', opts.metrics)
  .use('vidi-seneca-metrics', opts.seneca_metrics)
  .use('mesh', opts.mesh)
<commit_msg>added env variables<commit_after>'use strict'

var opts = {
  metrics: {
    emitter: {
      enabled: false
    }
  },
  seneca_metrics: {
    group: 'nodezoo',
    tag: 'info',
    pins: [
      'role:info,req:part',
      'role:info,res:part'
    ]
  },
  mesh: {
    auto: true,
    host: envs.INFO_HOST || '127.0.0.1',
    bases: [envs.BASE_HOST || '127.0.0.1:39999'],
    listen: [
      {pin: 'role:info,cmd:get', model: 'consume'},
      {pin: 'role:info,res:part', model: 'observe'}
    ]
  }
}

require('seneca')()
  .use('entity')
  .use('../info.js')
  .use('vidi-metrics', opts.metrics)
  .use('vidi-seneca-metrics', opts.seneca_metrics)
  .use('mesh', opts.mesh)
<|endoftext|>