<commit_before>import Pusher from 'pusher-js';

export default function(socketRetryInterceptorProvider, socketCacheBusterProvider) {
  'ngInject';
  var PUSHER_API_KEY;
  var WS_PREF;

  this.setPusherApiKey = setPusherApiKey;
  this.setApiEndpoint = setApiEndpoint;

  function setApiEndpoint(endpoint) {
    WS_PREF = endpoint;
    socketRetryInterceptorProvider.setApiEndpoint(endpoint);
    socketCacheBusterProvider.setApiEndpoint(endpoint);
  }

  function setPusherApiKey(key) {
    PUSHER_API_KEY = key;
  }

  this.$get = function socket($rootScope,
                              $log,
                              $location,
                              $http,
                              $q) {
    'ngInject';

    var allSockets = {};
    var pusherSupported = true;
    var socketId = null;

    var pusher;
    if (Pusher && PUSHER_API_KEY) {
      pusher = new Pusher(PUSHER_API_KEY, {
        encrypted: true,
        cluster: 'eu'
      });

      pusher.connection.bind('failed', function failed() {
        pusherSupported = false;
        SocketFactory.pushSupported = false;
      });

      pusher.connection.bind('connected', function connected() {
        socketId = SocketFactory.socketId = pusher.connection.socket_id;
        var channel = pusher.subscribe('or2-socket-' + socketId);

        // Start listening for events on the channel
        channel.bind('object updated', function(data) {
          runSocketHandler(data.model, 'modified get', function(handler) {
            handler(data._id);
          });
        });

        channel.bind('query updated', function(data) {
          runSocketHandler(data.model, 'modified query', function(handler) {
            handler(data.data.qryId, {
              ids: data.data.result,
              pagingOpts: data.data.pagingOpts
            });
          });
        });
      });
    } else {
      pusherSupported = false;
    }

    function runSocketHandler(model, eventName, callback) {
      var socket = allSockets[model];
      if (socket && socket.handlers[eventName]) {
        socket.handlers[eventName].forEach(function(handler) {
          callback(handler);
        });
      }
    }

    var handleErr = function handleErr(data) {
      var err = data && data.err;

      if (err) {
        return $log.error(err);
      }
    };

    var connectionEvents = ['initialized', 'connecting', 'connected', 'unavailable', 'failed',
    'disconnected'];

    function SocketDefer() {

      var deferred = $q.defer();
      var promise = deferred.promise;
      promise['catch'](handleErr);

      return deferred;
    }

    function Or2ComboSocket(namespace, rootKey, rootKeyPlural) {

      this.namespace = namespace;
      this.rootKey = rootKey;
      this.rootKeyPlural = rootKeyPlural;
      this.endpoint = WS_PREF + this.namespace;
      this.handlers = {};
      this.subscriptions = {};

      var self = this;

      this.query = function query(qryObj, replaces) {

        var deferred = SocketDefer();
        var params = {
          _idsonly: true,
          _spaging: true
        };

        if (socketId) {
          params._sid = socketId;
        }

        // Are we replacing an existing qryId?
        if (replaces) {
          params._replace = replaces;
        }

        // Explicityl stringify the queryObj here because we don't want angular removing
        // any of our keys (they're likely to have '$' in them)
        var req = $http.post(this.endpoint + '/query', JSON.stringify(qryObj), {
          params: params
        });

        req.success(function(data, statusCode) {

          // A statusCode of 0 or lower means (essentially) we're offline. Resolve as successful
          // but with no data
          if (statusCode <= 0) {
            deferred.resolve();
            return;
          } else if (statusCode !== 200) {
            deferred.reject(data);
            return;
          }

          var ids = data[self.rootKeyPlural];

          var pagingOpts = {
            next: data.next,
            prev: data.prev
          };

          var qryId = data.qryId;

          return deferred.resolve({
            qryId: qryId,
            data:  {
              ids: ids,
              pagingOpts: pagingOpts
            }
          });
        });

        req.error(function(err) {
          deferred.reject(err);
        });

        return deferred.promise;
      };

      this.get = function get(ids) {
        var params, req;

        var deferred = SocketDefer();

        if (Array.isArray(ids)) {
          params = {};

          if (socketId) {
            params._sid = socketId;
          }

          req = $http.post(this.endpoint + '/bulk', {
            ids: ids
          }, {
            params: params
          });

          req.success(function(resdata, statusCode) {

            // A statusCode of 0 or lower means (essentially) we're offline. Resolve as successful
            // but with no data
            if (statusCode <= 0) {
              deferred.resolve();
              return;
            } else if (statusCode !== 200) {
              deferred.reject(resdata);
              return;
            }

            var data = resdata[self.rootKeyPlural];
            deferred.resolve(data);
          });

          req.error(function(err) {
            deferred.reject(err);
          });
        } else {
          var id = ids;
          params = {};

          if (socketId) {
            params._sid = socketId;
          }

          req = $http.get(this.endpoint + '/' + id, {
            params: params
          });

          req.success(function success(resdata, statusCode) {

            // A statusCode of 0 or lower means (essentially) we're offline. Resolve as successful
            // but with no data
            if (statusCode <= 0) {
              deferred.resolve();
              return;
            } else if (statusCode !== 200) {
              deferred.reject(resdata);
              return;
            }

            var data = resdata[self.rootKey];
            deferred.resolve(data);
          });

          req.error(function(err) {
            deferred.reject(err);
          });
        }

        return deferred.promise;
      };

      this.create = function create(data) {

        var deferred = SocketDefer();
        var params = {};
        var payload = {};

        if (socketId) {
          params._sid = socketId;
        }

        payload[this.rootKey] = data;

        var req = $http.post(this.endpoint, payload, {
          params: params
        });

        req.success(function success(resdata, statusCode) {

          if (statusCode !== 200) {
            deferred.reject(resdata);
            return;
          }

          data = resdata[self.rootKey];
          deferred.resolve(data);
        });

        req.error(function(err) {
          deferred.reject(err);
        });

        return deferred.promise;
      };

      this.patch = function patch(id, patchData) {
        var deferred = SocketDefer();
        var payload = {};
        var params = {};

        payload[this.rootKey] = patchData;

        if (socketId) {
          params._sid = socketId;
        }

        var req = $http.patch(this.endpoint + '/' + id, payload, {
          params: params
        });

        req.success(function(response, statusCode) {

          if (statusCode !== 200) {
            deferred.reject(response);
            return;
          }

          var data = response[self.rootKey];
          deferred.resolve(data);
        });

        req.error(function(err) {
          deferred.reject(err);
        });

        return deferred.promise;
      };

      this.update = function update(id, data) {

        var deferred = SocketDefer();
        var payload = {};
        var params = {};

        payload[this.rootKey] = data;

        if (socketId) {
          params._sid = socketId;
        }

        var req = $http.post(this.endpoint + '/' + id, payload, {
          params: params
        });

        req.success(function(response, statusCode) {

          if (statusCode !== 200) {
            deferred.reject(response);
            return;
          }

          data = response[self.rootKey];
          deferred.resolve(data);
        });

        req.error(function(err) {
          deferred.reject(err);
        });

        return deferred.promise;
      };

      this.remove = function remove(id) {
        var deferred = SocketDefer();
        var params = {};

        if (socketId) {
          params._sid = socketId;
        }

        var req = $http['delete'](this.endpoint + '/' + id, {
          params: params
        });

        req.success(function(data, statusCode) {

          if (statusCode !== 200) {
            deferred.reject(data);
            return;
          }

          deferred.resolve(data.success);
        });

        req.error(function(err) {
          deferred.reject(err);
        });

        return deferred.promise;
      };

      this.on = function on(eventName, callback) {

        if (!pusherSupported) {
          return;
        }

        if (~connectionEvents.indexOf(eventName)) {
          return pusher.connection.bind(eventName, callback);
        } else {
          var handlers;
          if(this.handlers[eventName]) {
            handlers = this.handlers[eventName];
          } else {
            handlers = this.handlers[eventName] = [];
          }

          handlers.push(callback);
        }
      };

      this.reset = function reset() {
        var handlers = this.handlers.reset;
        if (handlers) {
          handlers.forEach(function(handler) {
            handler();
          });
        }
      };
    }

    // Create the socket factory
    function SocketFactory(namespace, rootKey, rootKeyPlural) {
      // Have we already got one for this namespace?
      if (!allSockets[rootKey]) {
        allSockets[rootKey] = new Or2ComboSocket(namespace, rootKey, rootKeyPlural);
      }

      return allSockets[rootKey];
    }

    SocketFactory.pushSupported = pusherSupported;

    SocketFactory.resetAll = function resetAll() {
      allSockets.forEach(function(socket){
        socket.reset();
      });
    };

    return SocketFactory;

  };

}
<commit_msg>dont use specific cluster<commit_after>import Pusher from 'pusher-js';

export default function(socketRetryInterceptorProvider, socketCacheBusterProvider) {
  'ngInject';
  var PUSHER_API_KEY;
  var WS_PREF;

  this.setPusherApiKey = setPusherApiKey;
  this.setApiEndpoint = setApiEndpoint;

  function setApiEndpoint(endpoint) {
    WS_PREF = endpoint;
    socketRetryInterceptorProvider.setApiEndpoint(endpoint);
    socketCacheBusterProvider.setApiEndpoint(endpoint);
  }

  function setPusherApiKey(key) {
    PUSHER_API_KEY = key;
  }

  this.$get = function socket($rootScope,
                              $log,
                              $location,
                              $http,
                              $q) {
    'ngInject';

    var allSockets = {};
    var pusherSupported = true;
    var socketId = null;

    var pusher;
    if (Pusher && PUSHER_API_KEY) {
      pusher = new Pusher(PUSHER_API_KEY, {
        encrypted: true
      });

      pusher.connection.bind('failed', function failed() {
        pusherSupported = false;
        SocketFactory.pushSupported = false;
      });

      pusher.connection.bind('connected', function connected() {
        socketId = SocketFactory.socketId = pusher.connection.socket_id;
        var channel = pusher.subscribe('or2-socket-' + socketId);

        // Start listening for events on the channel
        channel.bind('object updated', function(data) {
          runSocketHandler(data.model, 'modified get', function(handler) {
            handler(data._id);
          });
        });

        channel.bind('query updated', function(data) {
          runSocketHandler(data.model, 'modified query', function(handler) {
            handler(data.data.qryId, {
              ids: data.data.result,
              pagingOpts: data.data.pagingOpts
            });
          });
        });
      });
    } else {
      pusherSupported = false;
    }

    function runSocketHandler(model, eventName, callback) {
      var socket = allSockets[model];
      if (socket && socket.handlers[eventName]) {
        socket.handlers[eventName].forEach(function(handler) {
          callback(handler);
        });
      }
    }

    var handleErr = function handleErr(data) {
      var err = data && data.err;

      if (err) {
        return $log.error(err);
      }
    };

    var connectionEvents = ['initialized', 'connecting', 'connected', 'unavailable', 'failed',
    'disconnected'];

    function SocketDefer() {

      var deferred = $q.defer();
      var promise = deferred.promise;
      promise['catch'](handleErr);

      return deferred;
    }

    function Or2ComboSocket(namespace, rootKey, rootKeyPlural) {

      this.namespace = namespace;
      this.rootKey = rootKey;
      this.rootKeyPlural = rootKeyPlural;
      this.endpoint = WS_PREF + this.namespace;
      this.handlers = {};
      this.subscriptions = {};

      var self = this;

      this.query = function query(qryObj, replaces) {

        var deferred = SocketDefer();
        var params = {
          _idsonly: true,
          _spaging: true
        };

        if (socketId) {
          params._sid = socketId;
        }

        // Are we replacing an existing qryId?
        if (replaces) {
          params._replace = replaces;
        }

        // Explicityl stringify the queryObj here because we don't want angular removing
        // any of our keys (they're likely to have '$' in them)
        var req = $http.post(this.endpoint + '/query', JSON.stringify(qryObj), {
          params: params
        });

        req.success(function(data, statusCode) {

          // A statusCode of 0 or lower means (essentially) we're offline. Resolve as successful
          // but with no data
          if (statusCode <= 0) {
            deferred.resolve();
            return;
          } else if (statusCode !== 200) {
            deferred.reject(data);
            return;
          }

          var ids = data[self.rootKeyPlural];

          var pagingOpts = {
            next: data.next,
            prev: data.prev
          };

          var qryId = data.qryId;

          return deferred.resolve({
            qryId: qryId,
            data:  {
              ids: ids,
              pagingOpts: pagingOpts
            }
          });
        });

        req.error(function(err) {
          deferred.reject(err);
        });

        return deferred.promise;
      };

      this.get = function get(ids) {
        var params, req;

        var deferred = SocketDefer();

        if (Array.isArray(ids)) {
          params = {};

          if (socketId) {
            params._sid = socketId;
          }

          req = $http.post(this.endpoint + '/bulk', {
            ids: ids
          }, {
            params: params
          });

          req.success(function(resdata, statusCode) {

            // A statusCode of 0 or lower means (essentially) we're offline. Resolve as successful
            // but with no data
            if (statusCode <= 0) {
              deferred.resolve();
              return;
            } else if (statusCode !== 200) {
              deferred.reject(resdata);
              return;
            }

            var data = resdata[self.rootKeyPlural];
            deferred.resolve(data);
          });

          req.error(function(err) {
            deferred.reject(err);
          });
        } else {
          var id = ids;
          params = {};

          if (socketId) {
            params._sid = socketId;
          }

          req = $http.get(this.endpoint + '/' + id, {
            params: params
          });

          req.success(function success(resdata, statusCode) {

            // A statusCode of 0 or lower means (essentially) we're offline. Resolve as successful
            // but with no data
            if (statusCode <= 0) {
              deferred.resolve();
              return;
            } else if (statusCode !== 200) {
              deferred.reject(resdata);
              return;
            }

            var data = resdata[self.rootKey];
            deferred.resolve(data);
          });

          req.error(function(err) {
            deferred.reject(err);
          });
        }

        return deferred.promise;
      };

      this.create = function create(data) {

        var deferred = SocketDefer();
        var params = {};
        var payload = {};

        if (socketId) {
          params._sid = socketId;
        }

        payload[this.rootKey] = data;

        var req = $http.post(this.endpoint, payload, {
          params: params
        });

        req.success(function success(resdata, statusCode) {

          if (statusCode !== 200) {
            deferred.reject(resdata);
            return;
          }

          data = resdata[self.rootKey];
          deferred.resolve(data);
        });

        req.error(function(err) {
          deferred.reject(err);
        });

        return deferred.promise;
      };

      this.patch = function patch(id, patchData) {
        var deferred = SocketDefer();
        var payload = {};
        var params = {};

        payload[this.rootKey] = patchData;

        if (socketId) {
          params._sid = socketId;
        }

        var req = $http.patch(this.endpoint + '/' + id, payload, {
          params: params
        });

        req.success(function(response, statusCode) {

          if (statusCode !== 200) {
            deferred.reject(response);
            return;
          }

          var data = response[self.rootKey];
          deferred.resolve(data);
        });

        req.error(function(err) {
          deferred.reject(err);
        });

        return deferred.promise;
      };

      this.update = function update(id, data) {

        var deferred = SocketDefer();
        var payload = {};
        var params = {};

        payload[this.rootKey] = data;

        if (socketId) {
          params._sid = socketId;
        }

        var req = $http.post(this.endpoint + '/' + id, payload, {
          params: params
        });

        req.success(function(response, statusCode) {

          if (statusCode !== 200) {
            deferred.reject(response);
            return;
          }

          data = response[self.rootKey];
          deferred.resolve(data);
        });

        req.error(function(err) {
          deferred.reject(err);
        });

        return deferred.promise;
      };

      this.remove = function remove(id) {
        var deferred = SocketDefer();
        var params = {};

        if (socketId) {
          params._sid = socketId;
        }

        var req = $http['delete'](this.endpoint + '/' + id, {
          params: params
        });

        req.success(function(data, statusCode) {

          if (statusCode !== 200) {
            deferred.reject(data);
            return;
          }

          deferred.resolve(data.success);
        });

        req.error(function(err) {
          deferred.reject(err);
        });

        return deferred.promise;
      };

      this.on = function on(eventName, callback) {

        if (!pusherSupported) {
          return;
        }

        if (~connectionEvents.indexOf(eventName)) {
          return pusher.connection.bind(eventName, callback);
        } else {
          var handlers;
          if(this.handlers[eventName]) {
            handlers = this.handlers[eventName];
          } else {
            handlers = this.handlers[eventName] = [];
          }

          handlers.push(callback);
        }
      };

      this.reset = function reset() {
        var handlers = this.handlers.reset;
        if (handlers) {
          handlers.forEach(function(handler) {
            handler();
          });
        }
      };
    }

    // Create the socket factory
    function SocketFactory(namespace, rootKey, rootKeyPlural) {
      // Have we already got one for this namespace?
      if (!allSockets[rootKey]) {
        allSockets[rootKey] = new Or2ComboSocket(namespace, rootKey, rootKeyPlural);
      }

      return allSockets[rootKey];
    }

    SocketFactory.pushSupported = pusherSupported;

    SocketFactory.resetAll = function resetAll() {
      allSockets.forEach(function(socket){
        socket.reset();
      });
    };

    return SocketFactory;

  };

}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013, ESHA Research
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */
;(function(window, define) {
    var _ = {
        version: "<%= pkg.version %>",
        areas: {},
        apis: {},

        // utilities
        inherit: function(api, o) {
            for (var p in api) {
                if (!o.hasOwnProperty(p)) {
                    o[p] = api[p];
                }
            }
            return o;
        },
        stringify: function(d) {
            return d === undefined || typeof d === "function" ? d+'' : JSON.stringify(d);
        },
        parse: function(s) {
            // if it doesn't parse, return as is
            try{ return JSON.parse(s); }catch(e){ return s; }
        },

        // extension hooks
        fn: function(name, fn) {
            _.storeAPI[name] = fn;
            for (var api in _.apis) {
                _.apis[api][name] = fn;
            }
        },
        get: function(area, key){ return area.getItem(key); },
        set: function(area, key, string){ area.setItem(key, string); },
        remove: function(area, key){ area.removeItem(key); },
        key: function(area, i){ return area.key(i); },
        length: function(area){ return area.length; },
        clear: function(area){ area.clear(); },

        // core functions
        Store: function(id, area, namespace) {
            var store = _.inherit(_.storeAPI, function(key, data, overwrite) {
                if (arguments.length === 0){ return store.getAll(); }
                if (typeof data === "function"){ return store.transact(key, data, overwrite); }// fn=data, alt=overwrite
                if (data !== undefined){ return store.set(key, data, overwrite); }
                if (typeof key === "string" || typeof key === "number"){ return store.get(key); }
                if (!key){ return store.clear(); }
                return store.setAll(key, data);// overwrite=data, data=key
            });
            store._id = id;
            try {
                var testKey = '_safariPrivate_';
                area.setItem(testKey, 'sucks');
                store._area = area;
                area.removeItem(testKey);
            } catch (e) {}
            if (!store._area) {
                store._area = _.inherit(_.storageAPI, { items: {}, name: 'fake' });
            }
            store._ns = namespace || '';
            if (!_.areas[id]) {
                _.areas[id] = store._area;
            }
            if (!_.apis[store._ns+store._id]) {
                _.apis[store._ns+store._id] = store;
            }
            return store;
        },
        storeAPI: {
            // admin functions
            area: function(id, area) {
                var store = this[id];
                if (!store || !store.area) {
                    store = _.Store(id, area, this._ns);//new area-specific api in this namespace
                    if (!this[id]){ this[id] = store; }
                }
                return store;
            },
            namespace: function(namespace, noSession) {
                if (!namespace){
                    return this._ns ? this._ns.substring(0,this._ns.length-1) : '';
                }
                var ns = namespace, store = this[ns];
                if (!store || !store.namespace) {
                    store = _.Store(this._id, this._area, this._ns+ns+'.');//new namespaced api
                    if (!this[ns]){ this[ns] = store; }
                    if (!noSession){ store.area('session', _.areas.session); }
                }
                return store;
            },
            isFake: function(){ return this._area.name === 'fake'; },
            toString: function() {
                return 'store'+(this._ns?'.'+this.namespace():'')+'['+this._id+']';
            },

            // storage functions
            has: function(key) {
                if (this._area.has) {
                    return this._area.has(this._in(key));//extension hook
                }
                return !!(this._in(key) in this._area);
            },
            size: function(){ return this.keys().length; },
            each: function(fn, and) {
                for (var i=0, m=_.length(this._area); i<m; i++) {
                    var key = this._out(_.key(this._area, i));
                    if (key !== undefined) {
                        if (fn.call(this, key, and || this.get(key)) === false) {
                            break;
                        }
                    }
                    if (m > _.length(this._area)) { m--; i--; }// in case of removeItem
                }
                return and || this;
            },
            keys: function() {
                return this.each(function(k, list){ list.push(k); }, []);
            },
            get: function(key, alt) {
                var s = _.get(this._area, this._in(key));
                return s !== null ? _.parse(s) : alt || s;// support alt for easy default mgmt
            },
            getAll: function() {
                return this.each(function(k, all){ all[k] = this.get(k); }, {});
            },
            transact: function(key, fn, alt) {
                var val = this.get(key, alt),
                    ret = fn(val);
                this.set(key, ret === undefined ? val : ret);
                return this;
            },
            set: function(key, data, overwrite) {
                var d = this.get(key);
                if (d != null && overwrite === false) {
                    return data;
                }
                return _.set(this._area, this._in(key), _.stringify(data), overwrite) || d;
            },
            setAll: function(data, overwrite) {
                var changed, val;
                for (var key in data) {
                    val = data[key];
                    if (this.set(key, val, overwrite) !== val) {
                        changed = true;
                    }
                }
                return changed;
            },
            remove: function(key) {
                var d = this.get(key);
                _.remove(this._area, this._in(key));
                return d;
            },
            clear: function() {
                if (!this._ns) {
                    _.clear(this._area);
                } else {
                    this.each(function(k){ _.remove(this._area, this._in(k)); }, 1);
                }
                return this;
            },
            clearAll: function() {
                var area = this._area;
                for (var id in _.areas) {
                    if (_.areas.hasOwnProperty(id)) {
                        this._area = _.areas[id];
                        this.clear();
                    }
                }
                this._area = area;
                return this;
            },

            // internal use functions
            _in: function(k) {
                if (typeof k !== "string"){ k = _.stringify(k); }
                return this._ns ? this._ns + k : k;
            },
            _out: function(k) {
                return this._ns ?
                    k && k.indexOf(this._ns) === 0 ?
                        k.substring(this._ns.length) :
                        undefined : // so each() knows to skip it
                    k;
            }
        },// end _.storeAPI
        storageAPI: {
            length: 0,
            has: function(k){ return this.items.hasOwnProperty(k); },
            key: function(i) {
                var c = 0;
                for (var k in this.items){
                    if (this.has(k) && i === c++) {
                        return k;
                    }
                }
            },
            setItem: function(k, v) {
                if (!this.has(k)) {
                    this.length++;
                }
                this.items[k] = v;
            },
            removeItem: function(k) {
                if (this.has(k)) {
                    delete this.items[k];
                    this.length--;
                }
            },
            getItem: function(k){ return this.has(k) ? this.items[k] : null; },
            clear: function(){ for (var k in this.items){ this.removeItem(k); } },
            toString: function(){ return this.length+' items in '+this.name+'Storage'; }
        }// end _.storageAPI
    };

    var store =
        // safely set this up (throws error in IE10/32bit mode for local files)
        _.Store("local", (function(){try{ return localStorage; }catch(e){}})());
    store.local = store;// for completeness
    store._ = _;// for extenders and debuggers...
    // safely setup store.session (throws exception in FF for file:/// urls)
    store.area("session", (function(){try{ return sessionStorage; }catch(e){}})());

    if (typeof define === 'function' && define.amd !== undefined) {
        define('store2', [], function () {
            return store;
        });
    } else if (typeof module !== 'undefined' && module.exports) {
        module.exports = store;
    } else {
        // expose the primary store fn to the global object and save conflicts
        if (window.store){ _.conflict = window.store; }
        window.store = store;
    }

})(this, this.define);
<commit_msg>make it clear that the 'and' arg in each() is for internal use<commit_after>/**
 * Copyright (c) 2013, ESHA Research
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */
;(function(window, define) {
    var _ = {
        version: "<%= pkg.version %>",
        areas: {},
        apis: {},

        // utilities
        inherit: function(api, o) {
            for (var p in api) {
                if (!o.hasOwnProperty(p)) {
                    o[p] = api[p];
                }
            }
            return o;
        },
        stringify: function(d) {
            return d === undefined || typeof d === "function" ? d+'' : JSON.stringify(d);
        },
        parse: function(s) {
            // if it doesn't parse, return as is
            try{ return JSON.parse(s); }catch(e){ return s; }
        },

        // extension hooks
        fn: function(name, fn) {
            _.storeAPI[name] = fn;
            for (var api in _.apis) {
                _.apis[api][name] = fn;
            }
        },
        get: function(area, key){ return area.getItem(key); },
        set: function(area, key, string){ area.setItem(key, string); },
        remove: function(area, key){ area.removeItem(key); },
        key: function(area, i){ return area.key(i); },
        length: function(area){ return area.length; },
        clear: function(area){ area.clear(); },

        // core functions
        Store: function(id, area, namespace) {
            var store = _.inherit(_.storeAPI, function(key, data, overwrite) {
                if (arguments.length === 0){ return store.getAll(); }
                if (typeof data === "function"){ return store.transact(key, data, overwrite); }// fn=data, alt=overwrite
                if (data !== undefined){ return store.set(key, data, overwrite); }
                if (typeof key === "string" || typeof key === "number"){ return store.get(key); }
                if (!key){ return store.clear(); }
                return store.setAll(key, data);// overwrite=data, data=key
            });
            store._id = id;
            try {
                var testKey = '_safariPrivate_';
                area.setItem(testKey, 'sucks');
                store._area = area;
                area.removeItem(testKey);
            } catch (e) {}
            if (!store._area) {
                store._area = _.inherit(_.storageAPI, { items: {}, name: 'fake' });
            }
            store._ns = namespace || '';
            if (!_.areas[id]) {
                _.areas[id] = store._area;
            }
            if (!_.apis[store._ns+store._id]) {
                _.apis[store._ns+store._id] = store;
            }
            return store;
        },
        storeAPI: {
            // admin functions
            area: function(id, area) {
                var store = this[id];
                if (!store || !store.area) {
                    store = _.Store(id, area, this._ns);//new area-specific api in this namespace
                    if (!this[id]){ this[id] = store; }
                }
                return store;
            },
            namespace: function(namespace, noSession) {
                if (!namespace){
                    return this._ns ? this._ns.substring(0,this._ns.length-1) : '';
                }
                var ns = namespace, store = this[ns];
                if (!store || !store.namespace) {
                    store = _.Store(this._id, this._area, this._ns+ns+'.');//new namespaced api
                    if (!this[ns]){ this[ns] = store; }
                    if (!noSession){ store.area('session', _.areas.session); }
                }
                return store;
            },
            isFake: function(){ return this._area.name === 'fake'; },
            toString: function() {
                return 'store'+(this._ns?'.'+this.namespace():'')+'['+this._id+']';
            },

            // storage functions
            has: function(key) {
                if (this._area.has) {
                    return this._area.has(this._in(key));//extension hook
                }
                return !!(this._in(key) in this._area);
            },
            size: function(){ return this.keys().length; },
            each: function(fn, _and) {// _and is purely for internal use (see keys())
                for (var i=0, m=_.length(this._area); i<m; i++) {
                    var key = this._out(_.key(this._area, i));
                    if (key !== undefined) {
                        if (fn.call(this, key, _and || this.get(key)) === false) {
                            break;
                        }
                    }
                    if (m > _.length(this._area)) { m--; i--; }// in case of removeItem
                }
                return _and || this;
            },
            keys: function() {
                return this.each(function(k, list){ list.push(k); }, []);
            },
            get: function(key, alt) {
                var s = _.get(this._area, this._in(key));
                return s !== null ? _.parse(s) : alt || s;// support alt for easy default mgmt
            },
            getAll: function() {
                return this.each(function(k, all){ all[k] = this.get(k); }, {});
            },
            transact: function(key, fn, alt) {
                var val = this.get(key, alt),
                    ret = fn(val);
                this.set(key, ret === undefined ? val : ret);
                return this;
            },
            set: function(key, data, overwrite) {
                var d = this.get(key);
                if (d != null && overwrite === false) {
                    return data;
                }
                return _.set(this._area, this._in(key), _.stringify(data), overwrite) || d;
            },
            setAll: function(data, overwrite) {
                var changed, val;
                for (var key in data) {
                    val = data[key];
                    if (this.set(key, val, overwrite) !== val) {
                        changed = true;
                    }
                }
                return changed;
            },
            remove: function(key) {
                var d = this.get(key);
                _.remove(this._area, this._in(key));
                return d;
            },
            clear: function() {
                if (!this._ns) {
                    _.clear(this._area);
                } else {
                    this.each(function(k){ _.remove(this._area, this._in(k)); }, 1);
                }
                return this;
            },
            clearAll: function() {
                var area = this._area;
                for (var id in _.areas) {
                    if (_.areas.hasOwnProperty(id)) {
                        this._area = _.areas[id];
                        this.clear();
                    }
                }
                this._area = area;
                return this;
            },

            // internal use functions
            _in: function(k) {
                if (typeof k !== "string"){ k = _.stringify(k); }
                return this._ns ? this._ns + k : k;
            },
            _out: function(k) {
                return this._ns ?
                    k && k.indexOf(this._ns) === 0 ?
                        k.substring(this._ns.length) :
                        undefined : // so each() knows to skip it
                    k;
            }
        },// end _.storeAPI
        storageAPI: {
            length: 0,
            has: function(k){ return this.items.hasOwnProperty(k); },
            key: function(i) {
                var c = 0;
                for (var k in this.items){
                    if (this.has(k) && i === c++) {
                        return k;
                    }
                }
            },
            setItem: function(k, v) {
                if (!this.has(k)) {
                    this.length++;
                }
                this.items[k] = v;
            },
            removeItem: function(k) {
                if (this.has(k)) {
                    delete this.items[k];
                    this.length--;
                }
            },
            getItem: function(k){ return this.has(k) ? this.items[k] : null; },
            clear: function(){ for (var k in this.items){ this.removeItem(k); } },
            toString: function(){ return this.length+' items in '+this.name+'Storage'; }
        }// end _.storageAPI
    };

    var store =
        // safely set this up (throws error in IE10/32bit mode for local files)
        _.Store("local", (function(){try{ return localStorage; }catch(e){}})());
    store.local = store;// for completeness
    store._ = _;// for extenders and debuggers...
    // safely setup store.session (throws exception in FF for file:/// urls)
    store.area("session", (function(){try{ return sessionStorage; }catch(e){}})());

    if (typeof define === 'function' && define.amd !== undefined) {
        define('store2', [], function () {
            return store;
        });
    } else if (typeof module !== 'undefined' && module.exports) {
        module.exports = store;
    } else {
        // expose the primary store fn to the global object and save conflicts
        if (window.store){ _.conflict = window.store; }
        window.store = store;
    }

})(this, this.define);
<|endoftext|>


<commit_before>import { observable, toJS, autorun } from "mobx";
import { groupTypes, physicalTypes, entitySchemas } from "./constants";
import ice from "icepick";
import update from "immutability-helper";

const baseLevel = [
  { group: "TARGET", id: 15, position: { y: 3, x: 5 } },
  { group: "TARGET", id: 10, position: { y: 2, x: 3 } },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 0,
    position: { y: 0, x: 1 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 1,
    position: { y: 0, x: 2 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 2,
    position: { y: 0, x: 3 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 3,
    position: { y: 0, x: 4 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 4,
    position: { y: 0, x: 5 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 5,
    position: { y: 0, x: 6 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 6,
    position: { y: 1, x: 1 }
  },
  {
    isPlayer: true,
    group: "PLAYER",
    physicalType: "OBSTACLE",
    id: 7,
    position: { y: 1, x: 2 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 8,
    position: { y: 1, x: 6 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 9,
    position: { y: 2, x: 1 }
  },
  {
    group: "BOX",
    physicalType: "PUSHABLE",
    id: 11,
    position: { y: 2, x: 3 }
  },
  {
    group: "BOX",
    physicalType: "PUSHABLE",
    id: 12,
    position: { y: 2, x: 5 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 13,
    position: { y: 2, x: 6 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 14,
    position: { y: 3, x: 1 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 16,
    position: { y: 3, x: 6 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 17,
    position: { y: 4, x: 1 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 18,
    position: { y: 4, x: 2 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 19,
    position: { y: 4, x: 3 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 20,
    position: { y: 4, x: 4 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 21,
    position: { y: 4, x: 5 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 22,
    position: { y: 4, x: 6 }
  }
];

export const getLevelPlayStore = (initialState = {}, goBack = () => {}) => {
  const {
    levelStart = baseLevel || [],
    moves = []
  } = initialState;

  const state = observable({
    levelStart,

    moves,

    get entities() {
      const result = state.moves.reduce(
        (levelAtMove, nextMove) => {
          return Object.entries(nextMove).reduce(
            (levelAtSubMove, [id, position]) => {
              const indexToMove = levelAtSubMove.findIndex(ent => ent.id == id);

              if (indexToMove === -1) {
                return levelAtSubMove;
              }

              const newPos = {
                ...levelAtSubMove[indexToMove].position,
                ...nextMove[id]
              };

              return update(levelAtSubMove, {
                [indexToMove]: {
                  position: { $set: newPos }
                }
              });
            },
            levelAtMove
          );
        },
        state.levelStart
      );
      //console.log(result[11].position);
      return result;
    },

    get playerIndex() {
      return state.entities.findIndex(ent => ent.isPlayer);
    },

    get player() {
      return state.entities[state.playerIndex];
    },

    get moveCount() {
      return state.moves.length;
    }
  });

  const tryMove = (axis, step) => {
    if (!state.player) {
      return;
    }

    const playerPos = state.player.position;
    //console.log(axis, step, playerPos.x, playerPos);
    const positionToTry = {
      ...playerPos,
      [axis]: playerPos[axis] + step
    };

    const { entities } = state;
    const entityThereIndex = entities.findIndex(
      ent =>
        ent.physicalType &&
        ent.position.x === positionToTry.x &&
        ent.position.y === positionToTry.y
    );
    const entityThere = entities[entityThereIndex];
    if (entityThere) {
      if (entityThere.physicalType === physicalTypes.obstacle) {
        return;
      }

      if (entityThere.physicalType === physicalTypes.pushable) {
        const nextPositionOver = {
          ...playerPos,
          [axis]: playerPos[axis] + step * 2
        };
        const nextEntityOver = entities.find(
          ent =>
            ent.physicalType &&
            ent.position.x === nextPositionOver.x &&
            ent.position.y === nextPositionOver.y
        );

        if (nextEntityOver && nextEntityOver.physicalType) {
          return;
        }

        state.moves.push({
          [entityThere.id]: {
            ...entityThere.position,
            [axis]: entityThere.position[axis] + step
          },
          [state.player.id]: {
            ...playerPos,
            [axis]: playerPos[axis] + step
          }
        });
        return;
      }
    }
    state.moves.push({
      [state.player.id]: {
        ...playerPos,
        [axis]: playerPos[axis] + step
      }
    });
    //console.log(state.player.position.x);
  };

  return {
    state,

    tryMoveLeft: () => tryMove("x", -1),
    tryMoveDown: () => tryMove("y", +1),
    tryMoveUp: () => tryMove("y", -1),
    tryMoveRight: () => tryMove("x", +1),
    undo() {
      if (state.moves.length) {
        state.moves.pop();
      }
    },
    reset() {
      state.moves = [];
    },
    goBack
  };
};
export const getLevelRecordStore = (initialState = {}) => {
  const {
    records = [
      { id: 0, level: baseLevel },
      ...[1, 2, 3, 4, 5, 6, 7].map(x => ({
        id: x,
        level: baseLevel.filter(o => Math.random() < 0.5)
      })),
      { id: 8, level: baseLevel.filter(ent => ent.position.x < 3) },
      { id: 9, level: baseLevel.filter(ent => ent.position.y < 3) }
    ]
  } = initialState;

  const state = observable({
    records
  });

  const createLevel = e => {
    e.preventDefault();
    const formData = serialize(e.target, { hash: true });
    formData.doc = { id: "stupid", level: [] };
    const body = JSON.stringify(formData);
    fetch(submitUrl, {
      method: "POST",
      body,
      headers: new Headers({ "Content-Type": "application/json" })
    })
      .then(res => res.json())
      .then(console.log);
  };

  // TODO: db logic
  return { state };
};

export const getMenuStore = (initial = {}) => {
  const {
    initialState = {},
    levelRecordStore = getLevelRecordStore(),
    goBack = () => {}
  } = initial;

  const state = observable({
    ...initialState,
    highlightedLevelId: -1
  });

  const selectLevel = id => {
    state.highlightedLevelId = id == state.highlightedLevelId ? -1 : id;
  };

  return {
    state,
    levelRecordStore,
    selectLevel
  };
};

export const getGameStore = (initial = {}) => {
  const gotoView = viewType => state.currentView = viewType;

  const {
    initialState,
    levelRecordStore = getLevelRecordStore(),
    menuStore = getMenuStore({ levelRecordStore }),
    levelPlayStore = getLevelPlayStore(undefined, () => gotoView("MENU"))
  } = initial;

  const state = observable({
    ...initialState,
    currentView: "MENU"
  });

  const loadLevel = id => {
    id = id === null ? menuStore.state.highlightedLevelId : id;
    const recordToLoad = levelRecordStore.state.records.find(r => r.id == id);

    if (!recordToLoad) {
      console.warn("Level not found!");
      return;
    }
    console.log(id, toJS(recordToLoad));

    levelPlayStore.state.moves = [];
    levelPlayStore.state.levelStart = recordToLoad.level;
    state.currentView = "PLAY";
  };

  menuStore.loadLevel = loadLevel;

  return {
    state,
    menuStore,
    levelRecordStore,
    levelPlayStore,
    gotoView
  };
};
<commit_msg>feat: pulling from db<commit_after>import { observable, toJS, autorun } from "mobx";
import { groupTypes, physicalTypes, entitySchemas } from "./constants";
import ice from "icepick";
import update from "immutability-helper";

const baseLevel = [
  { group: "TARGET", id: 15, position: { y: 3, x: 5 } },
  { group: "TARGET", id: 10, position: { y: 2, x: 3 } },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 0,
    position: { y: 0, x: 1 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 1,
    position: { y: 0, x: 2 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 2,
    position: { y: 0, x: 3 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 3,
    position: { y: 0, x: 4 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 4,
    position: { y: 0, x: 5 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 5,
    position: { y: 0, x: 6 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 6,
    position: { y: 1, x: 1 }
  },
  {
    isPlayer: true,
    group: "PLAYER",
    physicalType: "OBSTACLE",
    id: 7,
    position: { y: 1, x: 2 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 8,
    position: { y: 1, x: 6 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 9,
    position: { y: 2, x: 1 }
  },
  {
    group: "BOX",
    physicalType: "PUSHABLE",
    id: 11,
    position: { y: 2, x: 3 }
  },
  {
    group: "BOX",
    physicalType: "PUSHABLE",
    id: 12,
    position: { y: 2, x: 5 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 13,
    position: { y: 2, x: 6 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 14,
    position: { y: 3, x: 1 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 16,
    position: { y: 3, x: 6 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 17,
    position: { y: 4, x: 1 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 18,
    position: { y: 4, x: 2 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 19,
    position: { y: 4, x: 3 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 20,
    position: { y: 4, x: 4 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 21,
    position: { y: 4, x: 5 }
  },
  {
    group: "WALL",
    physicalType: "OBSTACLE",
    id: 22,
    position: { y: 4, x: 6 }
  }
];

export const getLevelPlayStore = (initialState = {}, goBack = () => {}) => {
  const {
    levelStart = baseLevel || [],
    moves = []
  } = initialState;

  const state = observable({
    levelStart,

    moves,

    get entities() {
      const result = state.moves.reduce(
        (levelAtMove, nextMove) => {
          return Object.entries(nextMove).reduce(
            (levelAtSubMove, [id, position]) => {
              const indexToMove = levelAtSubMove.findIndex(ent => ent.id == id);

              if (indexToMove === -1) {
                return levelAtSubMove;
              }

              const newPos = {
                ...levelAtSubMove[indexToMove].position,
                ...nextMove[id]
              };

              return update(levelAtSubMove, {
                [indexToMove]: {
                  position: { $set: newPos }
                }
              });
            },
            levelAtMove
          );
        },
        state.levelStart
      );
      //console.log(result[11].position);
      return result;
    },

    get playerIndex() {
      return state.entities.findIndex(ent => ent.isPlayer);
    },

    get player() {
      return state.entities[state.playerIndex];
    },

    get moveCount() {
      return state.moves.length;
    }
  });

  const tryMove = (axis, step) => {
    if (!state.player) {
      return;
    }

    const playerPos = state.player.position;
    //console.log(axis, step, playerPos.x, playerPos);
    const positionToTry = {
      ...playerPos,
      [axis]: playerPos[axis] + step
    };

    const { entities } = state;
    const entityThereIndex = entities.findIndex(
      ent =>
        ent.physicalType &&
        ent.position.x === positionToTry.x &&
        ent.position.y === positionToTry.y
    );
    const entityThere = entities[entityThereIndex];
    if (entityThere) {
      if (entityThere.physicalType === physicalTypes.obstacle) {
        return;
      }

      if (entityThere.physicalType === physicalTypes.pushable) {
        const nextPositionOver = {
          ...playerPos,
          [axis]: playerPos[axis] + step * 2
        };
        const nextEntityOver = entities.find(
          ent =>
            ent.physicalType &&
            ent.position.x === nextPositionOver.x &&
            ent.position.y === nextPositionOver.y
        );

        if (nextEntityOver && nextEntityOver.physicalType) {
          return;
        }

        state.moves.push({
          [entityThere.id]: {
            ...entityThere.position,
            [axis]: entityThere.position[axis] + step
          },
          [state.player.id]: {
            ...playerPos,
            [axis]: playerPos[axis] + step
          }
        });
        return;
      }
    }
    state.moves.push({
      [state.player.id]: {
        ...playerPos,
        [axis]: playerPos[axis] + step
      }
    });
    //console.log(state.player.position.x);
  };

  return {
    state,

    tryMoveLeft: () => tryMove("x", -1),
    tryMoveDown: () => tryMove("y", +1),
    tryMoveUp: () => tryMove("y", -1),
    tryMoveRight: () => tryMove("x", +1),
    undo() {
      if (state.moves.length) {
        state.moves.pop();
      }
    },
    reset() {
      state.moves = [];
    },
    goBack
  };
};
export const getLevelRecordStore = (initialState = {}) => {
  const {
    records = [
      { id: 0, level: baseLevel },
      ...[1, 2, 3, 4, 5, 6, 7].map(x => ({
        id: x,
        level: baseLevel.filter(o => Math.random() < 0.5)
      })),
      { id: 8, level: baseLevel.filter(ent => ent.position.x < 3) },
      { id: 9, level: baseLevel.filter(ent => ent.position.y < 3) }
    ]
  } = initialState;

  const state = observable({
    records
  });

  const pullRecords = o => {
    fetch(
      "https://qlrvsjbsr3.execute-api.us-west-2.amazonaws.com/prod/getSokobanLevels"
    )
      .then(res => res.json())
      .then(records => {
        state.records = records;
      });
  };
  pullRecords();

  const createLevel = e => {
    e.preventDefault();
    const formData = serialize(e.target, { hash: true });
    formData.doc = { id: "stupid", level: [] };
    const body = JSON.stringify(formData);
    fetch(submitUrl, {
      method: "POST",
      body,
      headers: new Headers({ "Content-Type": "application/json" })
    })
      .then(res => res.json())
      .then(console.log);
  };

  // TODO: db logic
  return { state };
};

export const getMenuStore = (initial = {}) => {
  const {
    initialState = {},
    levelRecordStore = getLevelRecordStore(),
    goBack = () => {}
  } = initial;

  const state = observable({
    ...initialState,
    highlightedLevelId: -1
  });

  const selectLevel = id => {
    state.highlightedLevelId = id == state.highlightedLevelId ? -1 : id;
  };

  return {
    state,
    levelRecordStore,
    selectLevel
  };
};

export const getGameStore = (initial = {}) => {
  const gotoView = viewType => state.currentView = viewType;

  const {
    initialState,
    levelRecordStore = getLevelRecordStore(),
    menuStore = getMenuStore({ levelRecordStore }),
    levelPlayStore = getLevelPlayStore(undefined, () => gotoView("MENU"))
  } = initial;

  const state = observable({
    ...initialState,
    currentView: "MENU"
  });

  const loadLevel = id => {
    id = id === null ? menuStore.state.highlightedLevelId : id;
    const recordToLoad = levelRecordStore.state.records.find(r => r.id == id);

    if (!recordToLoad) {
      console.warn("Level not found!");
      return;
    }
    console.log(id, toJS(recordToLoad));

    levelPlayStore.state.moves = [];
    levelPlayStore.state.levelStart = recordToLoad.level;
    state.currentView = "PLAY";
  };

  menuStore.loadLevel = loadLevel;

  return {
    state,
    menuStore,
    levelRecordStore,
    levelPlayStore,
    gotoView
  };
};
<|endoftext|>


<commit_before>// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
//
// Copyright (c) 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
//
// Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are met:
//   * Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//   * Neither the name of the GNOME Foundation nor the
//     names of its contributors may be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Params = imports.params;
const CategoryList = imports.categoryList;
const Character = imports.character;
const CharacterList = imports.characterList;
const Pango = imports.gi.Pango;
const Gc = imports.gi.Gc;
const Gettext = imports.gettext;

const Util = imports.util;

const MAX_SEARCH_RESULTS = 100;

const MainWindow = new Lang.Class({
    Name: 'MainWindow',
    Extends: Gtk.ApplicationWindow,
    Properties: {
        'search-active': GObject.ParamSpec.boolean(
            'search-active', '', '',
            GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, false)
    },

    _init: function(params) {
        params = Params.fill(params, { title: GLib.get_application_name(),
                                       default_width: 640,
                                       default_height: 480 });
        this.parent(params);

        this._searchActive = false;
        this._searchKeywords = [];

        Util.initActions(this,
                         [{ name: 'about',
                            activate: this._about },
                          { name: 'search-active',
                            activate: this._toggleSearch,
                            parameter_type: new GLib.VariantType('b'),
                            state: new GLib.Variant('b', false) },
                          { name: 'category',
                            activate: this._category,
                            parameter_type: new GLib.VariantType('s'),
                            state: new GLib.Variant('s', 'punctuation') },
                          { name: 'character',
                            activate: this._character,
                            parameter_type: new GLib.VariantType('s') }]);

        let builder = new Gtk.Builder();
        builder.add_from_resource('/org/gnome/Characters/main.ui');
        builder.add_from_resource('/org/gnome/Characters/sidebar.ui');

        this._headerBar = builder.get_object('main-header');
        this.set_titlebar(this._headerBar);

        let searchBtn = builder.get_object('search-active-button');
        this.bind_property('search-active', searchBtn, 'active',
                           GObject.BindingFlags.SYNC_CREATE |
                           GObject.BindingFlags.BIDIRECTIONAL);
        this._searchBar = builder.get_object('search-bar');
        this.bind_property('search-active',
                           this._searchBar,
                           'search-mode-enabled',
                           GObject.BindingFlags.SYNC_CREATE |
                           GObject.BindingFlags.BIDIRECTIONAL);
        let searchEntry = builder.get_object('search-entry');
        this._searchBar.connect_entry(searchEntry);
        searchEntry.connect('search-changed',
                            Lang.bind(this, this._handleSearchChanged));

        let grid = builder.get_object('main-grid');
        let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL });

        let sidebarGrid = builder.get_object('sidebar-grid');
        this._categoryList = new CategoryList.CategoryListWidget();
        sidebarGrid.add(this._categoryList);
        hbox.pack_start(sidebarGrid, false, false, 2);

        this._mainView = new MainView();
        hbox.pack_start(this._mainView, true, true, 0);

        grid.add(hbox);

        this.add(grid);
        grid.show_all();

        // Due to limitations of gobject-introspection wrt GdkEvent
        // and GdkEventKey, this needs to be a signal handler
        this.connect('key-press-event', Lang.bind(this, this._handleKeyPress));
    },

    get search_active() {
        return this._searchActive;
    },

    set search_active(v) {
        if (this._searchActive == v)
            return;

        this._searchActive = v;
        this.notify('search-active');
    },

    _handleSearchChanged: function(entry) {
        let text = entry.get_text().replace(/^\s+|\s+$/g, '');
        let keywords = text == '' ? [] : text.split(/\s+/);
        keywords = keywords.map(String.toUpperCase);
        if (keywords != this._searchKeywords) {
            this._searchKeywords = keywords;
            if (this._searchKeywords.length > 0)
                this._mainView.startSearch(this._searchKeywords);
            else
                this._mainView.cancelSearch();
        }
        return true;
    },

    _handleKeyPress: function(self, event) {
        return this._searchBar.handle_event(event);
    },

    _about: function() {
        let aboutDialog = new Gtk.AboutDialog(
            { authors: [ 'Daiki Ueno <dueno@src.gnome.org>' ],
              translator_credits: _("translator-credits"),
              program_name: _("GNOME Characters"),
              comments: _("Character Map"),
              copyright: 'Copyright 2014 Daiki Ueno',
              license_type: Gtk.License.GPL_2_0,
              logo_icon_name: 'org.gnome.Characters',
              version: pkg.version,
              website: 'http://www.example.com/gnome-characters/',
              wrap_license: true,
              modal: true,
              transient_for: this
            });

        aboutDialog.show();
        aboutDialog.connect('response', function() {
            aboutDialog.destroy();
        });
    },

    _category: function(action, v) {
        let [name, length] = v.get_string()
        let category = null;
        for (let index in CategoryList.Category) {
            category = CategoryList.Category[index];
            if (category.name == name)
                break;
        }

        Util.assertNotEqual(category, null);
        this._mainView.setPage(category.name);
        this._headerBar.title = Gettext.gettext(category.label);
    },

    _character: function(action, v) {
        let [uc, length] = v.get_string()
        this._mainView.selectCharacter(uc);
    },
});

const MainView = new Lang.Class({
    Name: 'MainView',
    Extends: Gtk.Stack,

    _init: function(params) {
        params = Params.fill(params, { hexpand: true, vexpand: true });
        this.parent(params);

        this._characterListWidgets = {};

        let characterList;
        for (let index in CategoryList.Category) {
            let category = CategoryList.Category[index];
            characterList = this._createCharacterList(category.category);
            this._characterListWidgets[category.name] = characterList;
            this.add_named(this._createScrolledWindow(characterList),
                           category.name);
        }

        characterList = this._createCharacterList(Gc.Category.NONE);
        this.add_named(this._createScrolledWindow(characterList),
                       'search-result');
        this._characterListWidgets['search-result'] = characterList;

        let builder = new Gtk.Builder();
        builder.add_from_resource('/org/gnome/Characters/main.ui');
        let searchBannerGrid = builder.get_object('search-banner-grid');
        this.add_named(searchBannerGrid, 'search-banner');
        let loadingBannerGrid = builder.get_object('loading-banner-grid');
        this._spinner = builder.get_object('loading-banner-spinner');
        this.add_named(loadingBannerGrid, 'loading-banner');

        this._recentCharacters = [];
        this._cancellable = new Gio.Cancellable();
    },

    _createCharacterList: function(category) {
        let widget = new CharacterList.CharacterListWidget({ hexpand: true,
                                                             vexpand: true });
        widget.connect('character-selected',
                       Lang.bind(this, this._handleCharacterSelected));
        return widget;
    },

    _createScrolledWindow: function(widget) {
        let viewport = new Gtk.Viewport({});
        viewport.add(widget);

        let scroll = new Gtk.ScrolledWindow({
            hscrollbar_policy: Gtk.PolicyType.NEVER
        });
        scroll.add(viewport);
        return scroll;
    },

    _startSearch: function() {
        this._cancellable.cancel();
        this._cancellable.reset();
        this._spinner.start();
        this.visible_child_name = 'loading-banner';
        this.show_all();
    },

    _finishSearch: function(name, result) {
        this._spinner.stop();

        let characters = [];
        for (let index = 0; index < result.len; index++) {
            characters.push(Gc.search_result_get(result, index));
        }

        let characterList = this._characterListWidgets[name];
        characterList.setCharacters(characters);
        if (result.length == 0)
            this.visible_child_name = 'search-banner';
        else {
            this.visible_child_name = name;
        }
        this.show_all();
    },

    startSearch: function(keywords) {
        this._startSearch();
        Gc.search_by_keywords(
            keywords,
            MAX_SEARCH_RESULTS,
            this._cancellable,
            Lang.bind(this, function(source_object, res, user_data) {
                try {
                    let result = Gc.search_finish(res);
                    this._finishSearch('search-result', result);
                } catch (e) {
                    log("Failed to search by keywords: " + e);
                }
            }));
    },

    cancelSearch: function() {
        this._cancellable.cancel();
        this._finishSearch('search-result', []);
    },

    setPage: function(name) {
        if (!(name in this._characterListWidgets))
            return;

        let characterList = this._characterListWidgets[name];
        if (name == 'recent') {
            if (this._recentCharacters.length == 0)
                this.visible_child_name = 'search-banner';
            else {
                characterList.setCharacters(this._recentCharacters);
                characterList.show_all();
                this.visible_child_name = name;
            }
            this.show_all();
        } else {
            let category = null;
            for (let index in CategoryList.Category) {
                category = CategoryList.Category[index];
                if (category.name == name)
                    break;
            }

            Util.assertNotEqual(category, null);
            this._startSearch();
            Gc.search_by_category(
                category.category,
                -1,
                this._cancellable,
                Lang.bind(this,
                          function(source_object, res, user_data) {
                              try {
                                  let result = Gc.search_finish(res);
                                  this._finishSearch(name, result);
                              } catch (e) {
                                  log("Failed to search by category: " + e);
                              }
                          }));
        }
    },

    selectCharacter: function(uc) {
        if (this._recentCharacters.indexOf(uc) < 0) {
            this._recentCharacters.push(uc);

            if (this.visible_child_name == 'recent')
                this.setPage('recent');
        }
    },

    _handleCharacterSelected: function(widget, uc) {
        this.selectCharacter(uc);

        let dialog = new Character.CharacterDialog({
            character: uc,
            modal: true,
            transient_for: this.get_toplevel()
        });

        dialog.show();
        dialog.connect('response', function(self, response_id) {
            if (response_id == Gtk.ResponseType.CLOSE)
                dialog.destroy();
        });
    }
});
<commit_msg>Remove unused argument from MainView._createCharacterList<commit_after>// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
//
// Copyright (c) 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
//
// Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are met:
//   * Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//   * Neither the name of the GNOME Foundation nor the
//     names of its contributors may be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Params = imports.params;
const CategoryList = imports.categoryList;
const Character = imports.character;
const CharacterList = imports.characterList;
const Pango = imports.gi.Pango;
const Gc = imports.gi.Gc;
const Gettext = imports.gettext;

const Util = imports.util;

const MAX_SEARCH_RESULTS = 100;

const MainWindow = new Lang.Class({
    Name: 'MainWindow',
    Extends: Gtk.ApplicationWindow,
    Properties: {
        'search-active': GObject.ParamSpec.boolean(
            'search-active', '', '',
            GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, false)
    },

    _init: function(params) {
        params = Params.fill(params, { title: GLib.get_application_name(),
                                       default_width: 640,
                                       default_height: 480 });
        this.parent(params);

        this._searchActive = false;
        this._searchKeywords = [];

        Util.initActions(this,
                         [{ name: 'about',
                            activate: this._about },
                          { name: 'search-active',
                            activate: this._toggleSearch,
                            parameter_type: new GLib.VariantType('b'),
                            state: new GLib.Variant('b', false) },
                          { name: 'category',
                            activate: this._category,
                            parameter_type: new GLib.VariantType('s'),
                            state: new GLib.Variant('s', 'punctuation') },
                          { name: 'character',
                            activate: this._character,
                            parameter_type: new GLib.VariantType('s') }]);

        let builder = new Gtk.Builder();
        builder.add_from_resource('/org/gnome/Characters/main.ui');
        builder.add_from_resource('/org/gnome/Characters/sidebar.ui');

        this._headerBar = builder.get_object('main-header');
        this.set_titlebar(this._headerBar);

        let searchBtn = builder.get_object('search-active-button');
        this.bind_property('search-active', searchBtn, 'active',
                           GObject.BindingFlags.SYNC_CREATE |
                           GObject.BindingFlags.BIDIRECTIONAL);
        this._searchBar = builder.get_object('search-bar');
        this.bind_property('search-active',
                           this._searchBar,
                           'search-mode-enabled',
                           GObject.BindingFlags.SYNC_CREATE |
                           GObject.BindingFlags.BIDIRECTIONAL);
        let searchEntry = builder.get_object('search-entry');
        this._searchBar.connect_entry(searchEntry);
        searchEntry.connect('search-changed',
                            Lang.bind(this, this._handleSearchChanged));

        let grid = builder.get_object('main-grid');
        let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL });

        let sidebarGrid = builder.get_object('sidebar-grid');
        this._categoryList = new CategoryList.CategoryListWidget();
        sidebarGrid.add(this._categoryList);
        hbox.pack_start(sidebarGrid, false, false, 2);

        this._mainView = new MainView();
        hbox.pack_start(this._mainView, true, true, 0);

        grid.add(hbox);

        this.add(grid);
        grid.show_all();

        // Due to limitations of gobject-introspection wrt GdkEvent
        // and GdkEventKey, this needs to be a signal handler
        this.connect('key-press-event', Lang.bind(this, this._handleKeyPress));
    },

    get search_active() {
        return this._searchActive;
    },

    set search_active(v) {
        if (this._searchActive == v)
            return;

        this._searchActive = v;
        this.notify('search-active');
    },

    _handleSearchChanged: function(entry) {
        let text = entry.get_text().replace(/^\s+|\s+$/g, '');
        let keywords = text == '' ? [] : text.split(/\s+/);
        keywords = keywords.map(String.toUpperCase);
        if (keywords != this._searchKeywords) {
            this._searchKeywords = keywords;
            if (this._searchKeywords.length > 0)
                this._mainView.startSearch(this._searchKeywords);
            else
                this._mainView.cancelSearch();
        }
        return true;
    },

    _handleKeyPress: function(self, event) {
        return this._searchBar.handle_event(event);
    },

    _about: function() {
        let aboutDialog = new Gtk.AboutDialog(
            { authors: [ 'Daiki Ueno <dueno@src.gnome.org>' ],
              translator_credits: _("translator-credits"),
              program_name: _("GNOME Characters"),
              comments: _("Character Map"),
              copyright: 'Copyright 2014 Daiki Ueno',
              license_type: Gtk.License.GPL_2_0,
              logo_icon_name: 'org.gnome.Characters',
              version: pkg.version,
              website: 'http://www.example.com/gnome-characters/',
              wrap_license: true,
              modal: true,
              transient_for: this
            });

        aboutDialog.show();
        aboutDialog.connect('response', function() {
            aboutDialog.destroy();
        });
    },

    _category: function(action, v) {
        let [name, length] = v.get_string()
        let category = null;
        for (let index in CategoryList.Category) {
            category = CategoryList.Category[index];
            if (category.name == name)
                break;
        }

        Util.assertNotEqual(category, null);
        this._mainView.setPage(category.name);
        this._headerBar.title = Gettext.gettext(category.label);
    },

    _character: function(action, v) {
        let [uc, length] = v.get_string()
        this._mainView.selectCharacter(uc);
    },
});

const MainView = new Lang.Class({
    Name: 'MainView',
    Extends: Gtk.Stack,

    _init: function(params) {
        params = Params.fill(params, { hexpand: true, vexpand: true });
        this.parent(params);

        this._characterListWidgets = {};

        let characterList;
        for (let index in CategoryList.Category) {
            let category = CategoryList.Category[index];
            characterList = this._createCharacterList();
            this._characterListWidgets[category.name] = characterList;
            this.add_named(this._createScrolledWindow(characterList),
                           category.name);
        }

        characterList = this._createCharacterList();
        this.add_named(this._createScrolledWindow(characterList),
                       'search-result');
        this._characterListWidgets['search-result'] = characterList;

        let builder = new Gtk.Builder();
        builder.add_from_resource('/org/gnome/Characters/main.ui');
        let searchBannerGrid = builder.get_object('search-banner-grid');
        this.add_named(searchBannerGrid, 'search-banner');
        let loadingBannerGrid = builder.get_object('loading-banner-grid');
        this._spinner = builder.get_object('loading-banner-spinner');
        this.add_named(loadingBannerGrid, 'loading-banner');

        this._recentCharacters = [];
        this._cancellable = new Gio.Cancellable();
    },

    _createCharacterList: function() {
        let widget = new CharacterList.CharacterListWidget({ hexpand: true,
                                                             vexpand: true });
        widget.connect('character-selected',
                       Lang.bind(this, this._handleCharacterSelected));
        return widget;
    },

    _createScrolledWindow: function(widget) {
        let viewport = new Gtk.Viewport({});
        viewport.add(widget);

        let scroll = new Gtk.ScrolledWindow({
            hscrollbar_policy: Gtk.PolicyType.NEVER
        });
        scroll.add(viewport);
        return scroll;
    },

    _startSearch: function() {
        this._cancellable.cancel();
        this._cancellable.reset();
        this._spinner.start();
        this.visible_child_name = 'loading-banner';
        this.show_all();
    },

    _finishSearch: function(name, result) {
        this._spinner.stop();

        let characters = [];
        for (let index = 0; index < result.len; index++) {
            characters.push(Gc.search_result_get(result, index));
        }

        let characterList = this._characterListWidgets[name];
        characterList.setCharacters(characters);
        if (result.length == 0)
            this.visible_child_name = 'search-banner';
        else {
            this.visible_child_name = name;
        }
        this.show_all();
    },

    startSearch: function(keywords) {
        this._startSearch();
        Gc.search_by_keywords(
            keywords,
            MAX_SEARCH_RESULTS,
            this._cancellable,
            Lang.bind(this, function(source_object, res, user_data) {
                try {
                    let result = Gc.search_finish(res);
                    this._finishSearch('search-result', result);
                } catch (e) {
                    log("Failed to search by keywords: " + e);
                }
            }));
    },

    cancelSearch: function() {
        this._cancellable.cancel();
        this._finishSearch('search-result', []);
    },

    setPage: function(name) {
        if (!(name in this._characterListWidgets))
            return;

        let characterList = this._characterListWidgets[name];
        if (name == 'recent') {
            if (this._recentCharacters.length == 0)
                this.visible_child_name = 'search-banner';
            else {
                characterList.setCharacters(this._recentCharacters);
                characterList.show_all();
                this.visible_child_name = name;
            }
            this.show_all();
        } else {
            let category = null;
            for (let index in CategoryList.Category) {
                category = CategoryList.Category[index];
                if (category.name == name)
                    break;
            }

            Util.assertNotEqual(category, null);
            this._startSearch();
            Gc.search_by_category(
                category.category,
                -1,
                this._cancellable,
                Lang.bind(this,
                          function(source_object, res, user_data) {
                              try {
                                  let result = Gc.search_finish(res);
                                  this._finishSearch(name, result);
                              } catch (e) {
                                  log("Failed to search by category: " + e);
                              }
                          }));
        }
    },

    selectCharacter: function(uc) {
        if (this._recentCharacters.indexOf(uc) < 0) {
            this._recentCharacters.push(uc);

            if (this.visible_child_name == 'recent')
                this.setPage('recent');
        }
    },

    _handleCharacterSelected: function(widget, uc) {
        this.selectCharacter(uc);

        let dialog = new Character.CharacterDialog({
            character: uc,
            modal: true,
            transient_for: this.get_toplevel()
        });

        dialog.show();
        dialog.connect('response', function(self, response_id) {
            if (response_id == Gtk.ResponseType.CLOSE)
                dialog.destroy();
        });
    }
});
<|endoftext|>


<commit_before>'use strict'

if(process.env.NEW_RELIC_LICENSE_KEY) { require('newrelic') }

const _ = require('lodash')
const async = require('async')
const bparser = require('body-parser')
const cparser = require('cookie-parser')
const express = require('express')
const jwt = require('jsonwebtoken')
const mongo = require('mongodb')
const passport = require('passport')
const raven = require('raven')

// MONGODB
// ACCOUNTS
// JWT_SECRET

// GOOGLE_ID
// GOOGLE_SECRET

// FACEBOOK_ID
// FACEBOOK_SECRET

// TWITTER_KEY
// TWITTER_SECRET

// LIVE_ID
// LIVE_SECRET

// TAAT_ENTRYPOINT
// TAAT_ISSUER
// TAAT_CERT
// TAAT_PRIVATECERT

// passport (de)serialize
passport.serializeUser((user, done) => {
    done(null, user)
})

passport.deserializeUser((user, done) => {
    done(null, user)
})

// initialize getsentry.com client
if(process.env.SENTRY_DSN) {
    raven.config(process.env.SENTRY_DSN, {
        release: process.env.VERSION || process.env.HEROKU_SLUG_COMMIT.substr(0, 7) || require('./package').version,
        dataCallback: (data) => {
            _.unset(data, 'request.env')
            return data
        }
    }).install()
}

// start express app
const app = express()

// returns db connection (creates if not set)
app.locals.dbs = {}
app.locals.db = (account, callback) => {
    if(_.has(app, ['locals', 'dbs', account])) {
        return callback(null, app.locals.dbs[account].db(account))
    } else {
        var entuDb
        async.waterfall([
            (callback) => {
                mongo.MongoClient.connect(process.env.MONGODB, { ssl: true, sslValidate: true }, callback)
            },
            (connection, callback) => {
                entuDb = connection
                entuDb.collection('entity').findOne({ 'database_name.string': account, 'mongodb.string': { $exists: true } }, { _id: false, 'mongodb.string': true }, callback)
            },
            (url, callback) => {
                if (!_.has(url, 'mongodb.0.string')) { return callback('No MongoDb url') }

                mongo.MongoClient.connect(_.get(url, 'mongodb.0.string'), { ssl: true, sslValidate: true }, callback)
            },
            (connection, callback) => {
                console.log(`Connected to ${account}`)

                connection.on('close', () => {
                    _.unset(app, ['locals', 'dbs', account])
                    console.log(`Disconnected from ${account}`)
                })

                app.locals.dbs[account] = connection

                entuDb.close(callback)
            }
        ], (err) => {
            if(err) { return callback(err) }

            return callback(null, app.locals.dbs[account].db(account))
        })
    }
}

// Hide Powered By
app.disable('x-powered-by')

// get correct client IP behind nginx
app.set('trust proxy', true)

// logs to getsentry.com - start
if(process.env.SENTRY_DSN) { app.use(raven.requestHandler()) }

// Initialize Passport
app.use(passport.initialize())

// parse Cookies
app.use(cparser())

// parse POST/PUT requests body
app.use(bparser.json())

// save request info to request collection
app.use((req, res, next) => {
    req.startDt = Date.now()

    res.on('finish', () => {
        var request = {}

        _.set(request, 'date', new Date())
        _.set(request, 'ms', Date.now() - _.get(req, 'startDt'))
        _.set(request, 'status', res.statusCode)
        _.set(request, 'method', _.get(req, 'method'))
        _.set(request, 'path', _.get(req, 'originalUrl').split('?')[0])
        _.set(request, 'query', _.get(req, 'originalUrl').split('?')[1])
        _.set(request, 'body', _.get(req, 'body'))
        _.set(request, 'user.ip', _.get(req, 'ip'))
        _.set(request, 'user.account', _.get(req, 'account'))
        _.set(request, 'user.user', _.get(req, 'user'))
        _.set(request, 'user.browser', _.get(req, 'headers.user-agent'))

        if (_.isEmpty(request.body)) {
            _.unset(request, 'body')
        } else {
            request.body = JSON.stringify(request.body)
        }
        request.user = _.pickBy(request.user, _.identity)

        async.waterfall([
            (callback) => {
                req.app.locals.db(req.account || 'entu', callback)
            },
            (connection, callback) => {
                connection.collection('request').insertOne(_.pickBy(request, _.identity), callback)
            },
        ], (err) => {
            if(err) {
                console.error(`Can't save request: ${err.toString()}`)
            }
        })
    })

    next(null)
})

// redirect HTTP to HTTPS
app.use((req, res, next) => {
    if (req.hostname !== 'localhost' && req.protocol.toLowerCase() !== 'https') { return next([418, 'I\'m a teapot']) } else { next() }
})

// check JWT
const jwtCheck = (req, res, next) => {
    const parts = _.get(req, 'headers.authorization', '').split(' ')

    if(parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') { return next(null) }

    req.account = _.get(req, 'query.account')

    const jwtConf = {
        issuer: req.account,
        audience: _.get(req, 'ip')
    }

    jwt.verify(parts[1], process.env.JWT_SECRET, jwtConf, (err, decoded) => {
        if(err) { return next([401, err]) }

        if(decoded.aud !== _.get(req, 'ip')) { return next([403, 'Invalid JWT audience']) }

        _.set(req, 'user', decoded.sub)
        _.set(req, 'account', decoded.iss)

        next(null)
    })
}

// routes mapping
app.use('/', require('./routes/index'))
app.use('/auth', require('./routes/auth/index'))
app.use('/auth/id-card', require('./routes/auth/id-card'))

app.use('/account', jwtCheck, require('./routes/account'))
app.use('/entity', jwtCheck, require('./routes/entity'))
app.use('/property', jwtCheck, require('./routes/property'))

// provider mapping (only if configured)
if(process.env.GOOGLE_ID && process.env.GOOGLE_SECRET) { app.use('/auth/google', require('./routes/auth/google')) }
if(process.env.FACEBOOK_ID && process.env.FACEBOOK_SECRET) { app.use('/auth/facebook', require('./routes/auth/facebook')) }
if(process.env.TWITTER_KEY && process.env.TWITTER_SECRET) { app.use('/auth/twitter', require('./routes/auth/twitter')) }
if(process.env.LIVE_ID && process.env.LIVE_SECRET) { app.use('/auth/live', require('./routes/auth/live')) }
if(process.env.TAAT_ENTRYPOINT && process.env.TAAT_CERT && process.env.TAAT_PRIVATECERT) { app.use('/auth/taat', require('./routes/auth/taat')) }

// logs to getsentry.com - error
if(process.env.SENTRY_DSN) {
    app.use(raven.errorHandler())
}

// show 404
app.use((req, res, next) => {
    next([404, 'Not found'])
})

// show error
app.use((err, req, res, next) => {
    if (err.constructor === Array) {
        res.status(err[0]).json({ error: err[1] })
    } else {
        res.status(500).json({ error: err.toString() })
        console.error(err)
    }
})

// start server
app.listen(process.env.PORT, () => {
    console.log(`${new Date().toString()} started listening port ${process.env.PORT}`)
})
<commit_msg>Account parameter check fix<commit_after>'use strict'

if(process.env.NEW_RELIC_LICENSE_KEY) { require('newrelic') }

const _ = require('lodash')
const async = require('async')
const bparser = require('body-parser')
const cparser = require('cookie-parser')
const express = require('express')
const jwt = require('jsonwebtoken')
const mongo = require('mongodb')
const passport = require('passport')
const raven = require('raven')

// MONGODB
// ACCOUNTS
// JWT_SECRET

// GOOGLE_ID
// GOOGLE_SECRET

// FACEBOOK_ID
// FACEBOOK_SECRET

// TWITTER_KEY
// TWITTER_SECRET

// LIVE_ID
// LIVE_SECRET

// TAAT_ENTRYPOINT
// TAAT_ISSUER
// TAAT_CERT
// TAAT_PRIVATECERT

// passport (de)serialize
passport.serializeUser((user, done) => {
    done(null, user)
})

passport.deserializeUser((user, done) => {
    done(null, user)
})

// initialize getsentry.com client
if(process.env.SENTRY_DSN) {
    raven.config(process.env.SENTRY_DSN, {
        release: process.env.VERSION || process.env.HEROKU_SLUG_COMMIT.substr(0, 7) || require('./package').version,
        dataCallback: (data) => {
            _.unset(data, 'request.env')
            return data
        }
    }).install()
}

// start express app
const app = express()

// returns db connection (creates if not set)
app.locals.dbs = {}
app.locals.db = (account, callback) => {
    if(_.has(app, ['locals', 'dbs', account])) {
        return callback(null, app.locals.dbs[account].db(account))
    } else {
        var entuDb
        async.waterfall([
            (callback) => {
                mongo.MongoClient.connect(process.env.MONGODB, { ssl: true, sslValidate: true }, callback)
            },
            (connection, callback) => {
                entuDb = connection
                entuDb.collection('entity').findOne({ 'database_name.string': account, 'mongodb.string': { $exists: true } }, { _id: false, 'mongodb.string': true }, callback)
            },
            (url, callback) => {
                if (!_.has(url, 'mongodb.0.string')) { return callback('No MongoDb url') }

                mongo.MongoClient.connect(_.get(url, 'mongodb.0.string'), { ssl: true, sslValidate: true }, callback)
            },
            (connection, callback) => {
                console.log(`Connected to ${account}`)

                connection.on('close', () => {
                    _.unset(app, ['locals', 'dbs', account])
                    console.log(`Disconnected from ${account}`)
                })

                app.locals.dbs[account] = connection

                entuDb.close(callback)
            }
        ], (err) => {
            if(err) { return callback(err) }

            return callback(null, app.locals.dbs[account].db(account))
        })
    }
}

// Hide Powered By
app.disable('x-powered-by')

// get correct client IP behind nginx
app.set('trust proxy', true)

// logs to getsentry.com - start
if(process.env.SENTRY_DSN) { app.use(raven.requestHandler()) }

// Initialize Passport
app.use(passport.initialize())

// parse Cookies
app.use(cparser())

// parse POST/PUT requests body
app.use(bparser.json())

// save request info to request collection
app.use((req, res, next) => {
    req.startDt = Date.now()

    res.on('finish', () => {
        var request = {}

        _.set(request, 'date', new Date())
        _.set(request, 'ms', Date.now() - _.get(req, 'startDt'))
        _.set(request, 'status', res.statusCode)
        _.set(request, 'method', _.get(req, 'method'))
        _.set(request, 'path', _.get(req, 'originalUrl').split('?')[0])
        _.set(request, 'query', _.get(req, 'originalUrl').split('?')[1])
        _.set(request, 'body', _.get(req, 'body'))
        _.set(request, 'user.ip', _.get(req, 'ip'))
        _.set(request, 'user.account', _.get(req, 'account'))
        _.set(request, 'user.user', _.get(req, 'user'))
        _.set(request, 'user.browser', _.get(req, 'headers.user-agent'))

        if (_.isEmpty(request.body)) {
            _.unset(request, 'body')
        } else {
            request.body = JSON.stringify(request.body)
        }
        request.user = _.pickBy(request.user, _.identity)

        async.waterfall([
            (callback) => {
                req.app.locals.db(req.account || 'entu', callback)
            },
            (connection, callback) => {
                connection.collection('request').insertOne(_.pickBy(request, _.identity), callback)
            },
        ], (err) => {
            if(err) {
                console.error(`Can't save request: ${err.toString()}`)
            }
        })
    })

    next(null)
})

// redirect HTTP to HTTPS
app.use((req, res, next) => {
    if (req.hostname !== 'localhost' && req.protocol.toLowerCase() !== 'https') { return next([418, 'I\'m a teapot']) } else { next() }
})

// check JWT
const jwtCheck = (req, res, next) => {
    req.account = _.get(req, 'query.account')

    const parts = _.get(req, 'headers.authorization', '').split(' ')
    if(parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') { return next(null) }

    const jwtConf = {
        issuer: req.account,
        audience: _.get(req, 'ip')
    }

    jwt.verify(parts[1], process.env.JWT_SECRET, jwtConf, (err, decoded) => {
        if(err) { return next([401, err]) }

        if(decoded.aud !== _.get(req, 'ip')) { return next([403, 'Invalid JWT audience']) }

        _.set(req, 'user', decoded.sub)
        _.set(req, 'account', decoded.iss)

        next(null)
    })
}

// routes mapping
app.use('/', require('./routes/index'))
app.use('/auth', require('./routes/auth/index'))
app.use('/auth/id-card', require('./routes/auth/id-card'))

app.use('/account', jwtCheck, require('./routes/account'))
app.use('/entity', jwtCheck, require('./routes/entity'))
app.use('/property', jwtCheck, require('./routes/property'))

// provider mapping (only if configured)
if(process.env.GOOGLE_ID && process.env.GOOGLE_SECRET) { app.use('/auth/google', require('./routes/auth/google')) }
if(process.env.FACEBOOK_ID && process.env.FACEBOOK_SECRET) { app.use('/auth/facebook', require('./routes/auth/facebook')) }
if(process.env.TWITTER_KEY && process.env.TWITTER_SECRET) { app.use('/auth/twitter', require('./routes/auth/twitter')) }
if(process.env.LIVE_ID && process.env.LIVE_SECRET) { app.use('/auth/live', require('./routes/auth/live')) }
if(process.env.TAAT_ENTRYPOINT && process.env.TAAT_CERT && process.env.TAAT_PRIVATECERT) { app.use('/auth/taat', require('./routes/auth/taat')) }

// logs to getsentry.com - error
if(process.env.SENTRY_DSN) {
    app.use(raven.errorHandler())
}

// show 404
app.use((req, res, next) => {
    next([404, 'Not found'])
})

// show error
app.use((err, req, res, next) => {
    if (err.constructor === Array) {
        res.status(err[0]).json({ error: err[1] })
    } else {
        res.status(500).json({ error: err.toString() })
        console.error(err)
    }
})

// start server
app.listen(process.env.PORT, () => {
    console.log(`${new Date().toString()} started listening port ${process.env.PORT}`)
})
<|endoftext|>