{"_id":"snapshot-js","_rev":"31-099151ed373ff796cfd046a1edf515e2","name":"snapshot-js","description":"Node.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.","dist-tags":{"latest":"0.2.0"},"versions":{"0.0.1":{"name":"snapshot-js","buildName":"snapshot","version":"0.0.1","description":"Snapshot.js","main":"index.js","scripts":{"test":"grunt test"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"},"author":{"name":"Adam Timberlake"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/Wildhoney/Snapshot.js/issues"},"dependencies":{"crossfilter":"~1.3.5","underscore":"~1.5.2","grunt-cli":"~0.1.9","bower":"~1.2.7","grunt":"~0.4.1","grunt-contrib-uglify":"~0.2.4","grunt-contrib-jshint":"~0.6.4","socket.io":"~0.9.16","request":"~2.27.0","should":"~2.0.1","socket.io-client":"~0.9.16","grunt-mocha-test":"~0.7.0","colors":"~0.6.2","grunt-contrib-copy":"~0.4.1"},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot what collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event with a simple hash. If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', {\n    key         : property,\n    direction   : 'descending'\n});\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` filter method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(data) {\n\n    _.forEach(data.models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nUnit Testing\n-----------\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\nAngular\n-----------\n\n<img src=\"https://lh6.googleusercontent.com/-DtPuPzooNEY/AAAAAAAAAAI/AAAAAAAAAC4/6Y1jxzshd4g/photo.jpg?sz=48\" alt=\"Angular.js\" />\n\nSnapshot also comes with a bundled Angular module for easier interaction. Simply add `$snapshot` as a dependency and you have everything you need.","readmeFilename":"README.md","_id":"snapshot-js@0.0.1","dist":{"shasum":"cb44b1b13f492077aadf15c7bdb224c1eeb2e430","tarball":"https://registry.npmjs.org/snapshot-js/-/snapshot-js-0.0.1.tgz","integrity":"sha512-OJ6ovG529rNYvRRaAhLweJus3vauoB0SWHdplaVAklCck+yAxZkSYMbmZDRB740+aFe8kJ4L4ZyBNv/Dfw33QA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3pyPREKQilrbd2WS/RGwrnKsDjwhBbpP2EJ1y8GA1GgIgX6rlOvoCpMGLmFkWWGxNQDxXYExFxuOT8Mih3nSMaR4="}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"wildhoney","email":"adam.timberlake@gmail.com"},"maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}]},"0.0.2":{"name":"snapshot-js","buildName":"snapshot","version":"0.0.2","description":"Snapshot.js","main":"index.js","scripts":{"test":"grunt test"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"},"author":{"name":"Adam Timberlake"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/Wildhoney/Snapshot.js/issues"},"dependencies":{"crossfilter":"~1.3.5","underscore":"~1.5.2","grunt-cli":"~0.1.9","bower":"~1.2.7","grunt":"~0.4.1","grunt-contrib-uglify":"~0.2.4","grunt-contrib-jshint":"~0.6.4","socket.io":"~0.9.16","request":"~2.27.0","should":"~2.0.1","socket.io-client":"~0.9.16","grunt-mocha-test":"~0.7.0","colors":"~0.6.2","grunt-contrib-copy":"~0.4.1"},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n\nInstalling via npm\n-----------\n\nSnapshot is added to the npm registry, and can therefore be downloaded with `npm install snapshot-js`.\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot what collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event passing in the `key` and `direction` (ascending/descending). If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', property, 'descending');\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` filter method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering &ndash; or you can use Snapshot's <a href=\"#in-built-filters\">primitive in-built filters</a>.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\n<h3>In-Built Filters</h3>\n\nIn light of Crossfilter's learning curve, Snapshot comes bundled with a handful of in-built filters for common filtering techniques. These can all be invoked by emitting an event with a corresponding value.\n\n * `snapshot/:namespace/fuzzyFilter` `{String}`\n * `snapshot/:namespace/exactFilter` `{String}`\n * `snapshot/:namespace/rangeFilter` `{Array}`\n\n```javascript\nsocket.emit('snapshot/default/fuzzyFilter', 'word', 'abc');\n```\n\nEach in-built filter expects the event name (`snapshot/default/fuzzyFilter`), the key (`word`), and value (`abc`).\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(data) {\n\n    _.forEach(data.models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nUnit Testing\n-----------\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\nAngular\n-----------\n\n<img src=\"https://lh6.googleusercontent.com/-DtPuPzooNEY/AAAAAAAAAAI/AAAAAAAAAC4/6Y1jxzshd4g/photo.jpg?sz=48\" alt=\"Angular.js\" />\n\nSnapshot also comes with a bundled Angular module for easier interaction. Simply add `$snapshot` as a dependency and you have everything you need.","readmeFilename":"README.md","_id":"snapshot-js@0.0.2","dist":{"shasum":"5b6a6998b63bc2a9820feef7d8bb39bf44547e7a","tarball":"https://registry.npmjs.org/snapshot-js/-/snapshot-js-0.0.2.tgz","integrity":"sha512-hlpAx1XxnIA7ZIAz3Kg8mT2+Mduo1vhGUxxvi3CRaI0wnRK2tI0UiUENobckB+9IGDz9LmnDHj83NL37PuVX7Q==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDRI1jlOuM623phjoikHYI7y1dosG91OXzt+Uj3OM2yBwIgfaET+OlQeh3+PYzk6XHVRJ0Vaqha5rTP93AFH+H3BCc="}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"wildhoney","email":"adam.timberlake@gmail.com"},"maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}]},"0.0.3":{"name":"snapshot-js","buildName":"snapshot","version":"0.0.3","description":"Snapshot.js","main":"index.js","scripts":{"test":"grunt test"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"},"author":{"name":"Adam Timberlake"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/Wildhoney/Snapshot.js/issues"},"dependencies":{"crossfilter":"~1.3.5","underscore":"~1.5.2","grunt-cli":"~0.1.9","bower":"~1.2.7","grunt":"~0.4.1","grunt-contrib-uglify":"~0.2.4","grunt-contrib-jshint":"~0.6.4","socket.io":"~0.9.16","request":"~2.27.0","should":"~2.0.1","socket.io-client":"~0.9.16","grunt-mocha-test":"~0.7.0","colors":"~0.6.2","grunt-contrib-copy":"~0.4.1"},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n\nInstalling via npm\n-----------\n\nSnapshot is added to the npm registry, and can therefore be downloaded with `npm install snapshot-js`.\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot what collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event passing in the `key` and `direction` (ascending/descending). If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', property, 'descending');\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` filter method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering &ndash; or you can use Snapshot's <a href=\"#in-built-filters\">primitive in-built filters</a>.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\n<h3>In-Built Filters</h3>\n\nIn light of Crossfilter's learning curve, Snapshot comes bundled with a handful of in-built filters for common filtering techniques. These can all be invoked by emitting an event with a corresponding value.\n\n * `snapshot/:namespace/fuzzyFilter` `{String}`\n * `snapshot/:namespace/exactFilter` `{String}`\n * `snapshot/:namespace/rangeFilter` `{Array}`\n\n```javascript\nsocket.emit('snapshot/default/fuzzyFilter', 'word', 'abc');\n```\n\nEach in-built filter expects the event name (`snapshot/default/fuzzyFilter`), the key (`word`), and value (`abc`).\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(data) {\n\n    _.forEach(data.models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nExample\n-----------\n\nSnapshot comes bundled with an example to get you started.\n\n * Navigate to `example/server` and run `node default.js`;\n * Open `example/client/index.html` in your browser;\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nUnit Testing\n-----------\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\nPhilosophy\n-----------\n\nLoading a large collection of models into the browser is slow and unnecessary, instead Snapshot uses WebSockets to serve snapshots of those models to the browser when requested. It retains the state of the models, and so if a filter is changed, or the page number incremented, it will modify the snapshot <strong>only</strong> for that client.\n\nSnapshot is also tremendously fast because of its use of Socket.io and Crossfilter. Snapshot listens for events to change the state of the collection of models, and fires another event to let the client know the snapshot was updated. Crossfilter allows Snapshot to quickly slice and dice models &ndash; in the example, slicing and dicing takes 0-1 milliseconds for 1,000 models.\n\nSince Snapshot uses Node.js, the browser support is that of Socket.io, which essentially means Snapshot supports Internet Explorer 5.5+.\n\n <h3>Example</h3>\n\n * Browser connects to Snapshot on Node.js server;\n * Snapshot emits `snapshot/default/contentUpdated` with first page's 50 models;\n * Browser increments the page number;\n * Snapshot emits `snapshot/default/contentUpdated` with second page's 50 models;\n * Browser applies filter to select only red items;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models;\n * Browser sorts the models by their colour;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models ordered globally by colour;\n\nAngular\n-----------\n\n<img src=\"https://lh6.googleusercontent.com/-DtPuPzooNEY/AAAAAAAAAAI/AAAAAAAAAC4/6Y1jxzshd4g/photo.jpg?sz=48\" alt=\"Angular.js\" />\n\nSnapshot also comes with a bundled Angular module for easier interaction. Simply add `$snapshot` as a dependency and you have everything you need.","readmeFilename":"README.md","_id":"snapshot-js@0.0.3","dist":{"shasum":"97b07659d267d04ea5e3f2700df7bdc71fb53c72","tarball":"https://registry.npmjs.org/snapshot-js/-/snapshot-js-0.0.3.tgz","integrity":"sha512-ZMYZ6O49ZKn570uzGrVT+ZT2SbOXF8qUcXc1tBTIhfKq7sAK5LpA7iujRkjLGxh8iXg1e6JLqck6G5ZmpHJAxQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID5+U6XsyNIioEqZM05v8kQQ+KcDY3iDsvlsKKsJIVaGAiAy+oGtMFMpR7IPJrmUywbO+XJAH76JWsvbtzynEuP+SA=="}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"wildhoney","email":"adam.timberlake@gmail.com"},"maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}]},"0.0.4":{"name":"snapshot-js","buildName":"snapshot","version":"0.0.4","description":"Snapshot.js","main":"index.js","scripts":{"test":"grunt test"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"},"author":{"name":"Adam Timberlake"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/Wildhoney/Snapshot.js/issues"},"dependencies":{"crossfilter":"~1.3.5","underscore":"~1.5.2","grunt-cli":"~0.1.9","bower":"~1.2.7","grunt":"~0.4.1","grunt-contrib-uglify":"~0.2.4","grunt-contrib-jshint":"~0.6.4","socket.io":"~0.9.16","request":"~2.27.0","should":"~2.0.1","socket.io-client":"~0.9.16","grunt-mocha-test":"~0.7.0","colors":"~0.6.2","grunt-contrib-copy":"~0.4.1"},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n&nbsp;\n<img src=\"https://badge.fury.io/js/snapshot-js.png\" alt=\"NPM Version\" />\n\nInstalling via npm\n-----------\n\nSnapshot is added to the npm registry, and can therefore be downloaded with `npm install snapshot-js`.\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot what collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event passing in the `key` and `direction` (ascending/descending). If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', property, 'descending');\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` filter method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering &ndash; or you can use Snapshot's <a href=\"#in-built-filters\">primitive in-built filters</a>.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\n<h3>In-Built Filters</h3>\n\nIn light of Crossfilter's learning curve, Snapshot comes bundled with a handful of in-built filters for common filtering techniques. These can all be invoked by emitting an event with a corresponding value.\n\n * `snapshot/:namespace/fuzzyFilter` `{String}`\n * `snapshot/:namespace/exactFilter` `{String}`\n * `snapshot/:namespace/rangeFilter` `{Array}`\n\n```javascript\nsocket.emit('snapshot/default/fuzzyFilter', 'word', 'abc');\n```\n\nEach in-built filter expects the event name (`snapshot/default/fuzzyFilter`), the key (`word`), and value (`abc`).\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(data) {\n\n    _.forEach(data.models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nExample\n-----------\n\nSnapshot comes bundled with an example to get you started.\n\n * Navigate to `example/server` and run `node default.js`;\n * Open `example/client/index.html` in your browser;\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nUnit Testing\n-----------\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\nPhilosophy\n-----------\n\nLoading a large collection of models into the browser is slow and unnecessary, instead Snapshot uses WebSockets to serve snapshots of those models to the browser when requested. It retains the state of the models, and so if a filter is changed, or the page number incremented, it will modify the snapshot <strong>only</strong> for that client.\n\nSnapshot is also tremendously fast because of its use of Socket.io and Crossfilter. Snapshot listens for events to change the state of the collection of models, and fires another event to let the client know the snapshot was updated. Crossfilter allows Snapshot to quickly slice and dice models &ndash; in the example, slicing and dicing takes 0-1 milliseconds for 1,000 models.\n\nSince Snapshot uses Node.js, the browser support is that of Socket.io, which essentially means Snapshot supports Internet Explorer 5.5+.\n\n <h3>Example</h3>\n\n * Browser connects to Snapshot on Node.js server;\n * Snapshot emits `snapshot/default/contentUpdated` with first page's 50 models;\n * Browser increments the page number;\n * Snapshot emits `snapshot/default/contentUpdated` with second page's 50 models;\n * Browser applies filter to select only red items;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models;\n * Browser sorts the models by their colour;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models ordered globally by colour;\n\nAngular\n-----------\n\n<img src=\"https://lh6.googleusercontent.com/-DtPuPzooNEY/AAAAAAAAAAI/AAAAAAAAAC4/6Y1jxzshd4g/photo.jpg?sz=48\" alt=\"Angular.js\" />\n\nSnapshot also comes with a bundled Angular module for easier interaction. Simply add `$snapshot` as a dependency and you have everything you need.","readmeFilename":"README.md","_id":"snapshot-js@0.0.4","dist":{"shasum":"0017435545d40b3e674448cf8195e3bbc2ba612b","tarball":"https://registry.npmjs.org/snapshot-js/-/snapshot-js-0.0.4.tgz","integrity":"sha512-Geo96wfNII/UVPMMRv/tfXSJUyfey2V/4BQiSMrBAeM1pXKtpqsxvVOjvwWMjQsZHRv//5luki2QMiQhtsNaiQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDCPsy8xglKKSNzXprOC3MGSKQNBgBlbWAxKh3cJZn99wIgQO2MBB0jk2+cB3cP/jMRtedszFVJCczecAO/8TuMt0w="}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"wildhoney","email":"adam.timberlake@gmail.com"},"maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}]},"0.0.5":{"name":"snapshot-js","buildName":"snapshot","version":"0.0.5","description":"Snapshot.js","main":"index.js","scripts":{"test":"grunt test"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"},"author":{"name":"Adam Timberlake"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/Wildhoney/Snapshot.js/issues"},"dependencies":{"crossfilter":"~1.3.5","underscore":"~1.5.2","grunt-cli":"~0.1.9","bower":"~1.2.7","grunt":"~0.4.1","grunt-contrib-uglify":"~0.2.4","grunt-contrib-jshint":"~0.6.4","socket.io":"~0.9.16","request":"~2.27.0","should":"~2.0.1","socket.io-client":"~0.9.16","grunt-mocha-test":"~0.7.0","colors":"~0.6.2","grunt-contrib-copy":"~0.4.1"},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n&nbsp;\n<img src=\"https://badge.fury.io/js/snapshot-js.png\" alt=\"NPM Version\" />\n\nInstalling via npm\n-----------\n\nSnapshot is added to the npm registry, and can therefore be downloaded with `npm install snapshot-js`.\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot which collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument, and statistics relating to the request as the second argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event passing in the `key` and `direction` (ascending/descending). If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', property, 'descending');\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering &ndash; or for simple filtering you can use Snapshot's <a href=\"#in-built-filters\">primitive in-built filters</a>.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\n<h3>In-Built Filters</h3>\n\nIn light of Crossfilter's learning curve, Snapshot comes bundled with a handful of in-built filters for common filtering techniques. These can all be invoked by emitting an event with a corresponding value.\n\n * `snapshot/:namespace/fuzzyFilter` `{String}`\n * `snapshot/:namespace/exactFilter` `{String}`\n * `snapshot/:namespace/rangeFilter` `{Array}`\n\n```javascript\nsocket.emit('snapshot/default/fuzzyFilter', 'word', 'abc');\n```\n\nEach in-built filter expects the event name (`snapshot/default/fuzzyFilter`), the key (`word`), and value (`abc`).\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(models, stats) {\n\n    _.forEach(models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nExample\n-----------\n\nSnapshot comes bundled with an example to get you started.\n\n * Navigate to `example/server` and run `node default.js`;\n * Open `example/client/index.html` in your browser;\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nUnit Testing\n-----------\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\nPhilosophy\n-----------\n\nLoading a large collection of models into the browser is slow and unnecessary, instead Snapshot uses WebSockets to serve snapshots of those models to the browser when requested. It retains the state of the models, and so if a filter is changed, or the page number incremented, it will modify the snapshot <strong>only</strong> for that client.\n\nSnapshot is also tremendously fast because of its use of Socket.io and Crossfilter. Snapshot listens for events to change the state of the collection of models, and fires another event to let the client know the snapshot was updated. Crossfilter allows Snapshot to quickly slice and dice models &ndash; in the example, slicing and dicing takes 0-1 milliseconds for 1,000 models.\n\nSince Snapshot uses Node.js, the browser support is that of Socket.io, which essentially means Snapshot supports Internet Explorer 5.5+.\n\n <h3>Example</h3>\n\n * Browser connects to Snapshot on Node.js server;\n * Snapshot emits `snapshot/default/contentUpdated` with first page's 50 models;\n * Browser increments the page number;\n * Snapshot emits `snapshot/default/contentUpdated` with second page's 50 models;\n * Browser applies filter to select only red items;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models;\n * Browser sorts the models by their colour;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models ordered globally by colour;\n\nAngular\n-----------\n\n<img src=\"https://lh6.googleusercontent.com/-DtPuPzooNEY/AAAAAAAAAAI/AAAAAAAAAC4/6Y1jxzshd4g/photo.jpg?sz=48\" alt=\"Angular.js\" />\n\nSnapshot also comes with a bundled Angular module for easier interaction. Simply add `$snapshot` as a dependency and you have everything you need.","readmeFilename":"README.md","_id":"snapshot-js@0.0.5","dist":{"shasum":"944ee8a89caf7ecc767489d57ae1abebc9b98dce","tarball":"https://registry.npmjs.org/snapshot-js/-/snapshot-js-0.0.5.tgz","integrity":"sha512-YTMSlqAYUexzHeh9PfgoxprH2VCBsoOptc6Dn+Pl7L4GKhjMbeuimhNtgjZlNGcW8dFOV8ou9pNdTma+pXxEdA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDhm+8xAtv2i1pP176tOMJQP08R+h6Tr2oXMuzF6LvoCAIgednkzYOM8Bx1JTfmsTBnKraBirjPfwK0WvKRdM5nv6U="}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"wildhoney","email":"adam.timberlake@gmail.com"},"maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}]},"0.0.6":{"name":"snapshot-js","buildName":"snapshot","version":"0.0.6","description":"Node.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.","main":"index.js","scripts":{"test":"grunt test"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"},"author":{"name":"Adam Timberlake"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/Wildhoney/Snapshot.js/issues"},"dependencies":{"crossfilter":"~1.3.5","underscore":"~1.5.2","grunt-cli":"~0.1.9","bower":"~1.2.7","grunt":"~0.4.1","grunt-contrib-uglify":"~0.2.4","grunt-contrib-jshint":"~0.6.4","socket.io":"~0.9.16","should":"~2.0.1","socket.io-client":"~0.9.16","grunt-mocha-test":"~0.7.0","colors":"~0.6.2","grunt-contrib-copy":"~0.4.1"},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n&nbsp;\n<img src=\"https://badge.fury.io/js/snapshot-js.png\" alt=\"NPM Version\" />\n\nInstalling via npm\n-----------\n\nSnapshot is added to the npm registry, and can therefore be downloaded with `npm install snapshot-js`.\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot which collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument, and statistics relating to the request as the second argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event passing in the `key` and `direction` (ascending/descending). If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', property, 'descending');\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering &ndash; or for simple filtering you can use Snapshot's <a href=\"#in-built-filters\">primitive in-built filters</a>.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\n<h3>In-Built Filters</h3>\n\nIn light of Crossfilter's learning curve, Snapshot comes bundled with a handful of in-built filters for common filtering techniques. These can all be invoked by emitting an event with a corresponding value.\n\n * `snapshot/:namespace/fuzzyFilter` `{String}`\n * `snapshot/:namespace/exactFilter` `{String}`\n * `snapshot/:namespace/rangeFilter` `{Array}`\n\n```javascript\nsocket.emit('snapshot/default/fuzzyFilter', 'word', 'abc');\n```\n\nEach in-built filter expects the event name (`snapshot/default/fuzzyFilter`), the key (`word`), and value (`abc`).\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(models, stats) {\n\n    _.forEach(models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nExample\n-----------\n\nSnapshot comes bundled with an example to get you started.\n\n * Navigate to `example/server` and run `node default.js`;\n * Open `example/client/index.html` in your browser;\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nTesting\n-----------\n\n<h3>Unit Testing</h3>\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\n<h3>Cucumber</h3>\n\nSnapshot also comes bundled with a handful of Cucumber tests.\n\n * `cd tests/cucumber`\n * `bundle install`\n * `bundle exec cucumber`\n\nPhilosophy\n-----------\n\nLoading a large collection of models into the browser is slow and unnecessary, instead Snapshot uses WebSockets to serve snapshots of those models to the browser when requested. It retains the state of the models, and so if a filter is changed, or the page number incremented, it will modify the snapshot <strong>only</strong> for that client.\n\nSnapshot is also tremendously fast because of its use of Socket.io and Crossfilter. Snapshot listens for events to change the state of the collection of models, and fires another event to let the client know the snapshot was updated. Crossfilter allows Snapshot to quickly slice and dice models &ndash; in the example, slicing and dicing takes 0-1 milliseconds for 1,000 models.\n\nSince Snapshot uses Node.js, the browser support is that of Socket.io, which essentially means Snapshot supports Internet Explorer 5.5+.\n\n <h3>Example</h3>\n\n * Browser connects to Snapshot on Node.js server;\n * Snapshot emits `snapshot/default/contentUpdated` with first page's 50 models;\n * Browser increments the page number;\n * Snapshot emits `snapshot/default/contentUpdated` with second page's 50 models;\n * Browser applies filter to select only red items;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models;\n * Browser sorts the models by their colour;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models ordered globally by colour;\n\nAngular\n-----------\n\n<img src=\"https://lh6.googleusercontent.com/-DtPuPzooNEY/AAAAAAAAAAI/AAAAAAAAAC4/6Y1jxzshd4g/photo.jpg?sz=48\" alt=\"Angular.js\" />\n\nSnapshot also comes with a bundled Angular module for easier interaction. Simply add `$snapshot` as a dependency and you have everything you need.","readmeFilename":"README.md","_id":"snapshot-js@0.0.6","dist":{"shasum":"757f58d2ceda0e82e92a615c05a3e8cd31fd39c6","tarball":"https://registry.npmjs.org/snapshot-js/-/snapshot-js-0.0.6.tgz","integrity":"sha512-0idmyQ1HdEuBjQ903smCPJBYuN8s+ekOXUwaDXebYbvjj4OC23kN7z/wP4kX3k4A5NxhD5pFMrEGAwoWHdcrDg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDMbBJ6kKE8/Kp8HAf0Bqoa+NawZNnqYdrxPOacDpWZOAiBzpttmnBhE8AU7PdmljQgpBVPq91xSMmpjFZJPwpsLhA=="}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"wildhoney","email":"adam.timberlake@gmail.com"},"maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}]},"0.0.7":{"name":"snapshot-js","buildName":"snapshot","version":"0.0.7","description":"Node.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.","main":"index.js","scripts":{"test":"grunt test"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"},"author":{"name":"Adam Timberlake"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/Wildhoney/Snapshot.js/issues"},"dependencies":{"crossfilter":"~1.3.5","underscore":"~1.5.2","grunt-cli":"~0.1.9","bower":"~1.2.7","grunt":"~0.4.1","grunt-contrib-uglify":"~0.2.4","grunt-contrib-jshint":"~0.6.4","socket.io":"~0.9.16","should":"~2.0.1","socket.io-client":"~0.9.16","grunt-mocha-test":"~0.7.0","colors":"~0.6.2","grunt-contrib-copy":"~0.4.1"},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n&nbsp;\n<img src=\"https://badge.fury.io/js/snapshot-js.png\" alt=\"NPM Version\" />\n\nInstalling via npm\n-----------\n\nSnapshot is added to the npm registry, and can therefore be downloaded with `npm install snapshot-js`.\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot which collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument, and statistics relating to the request as the second argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event passing in the `key` and `direction` (ascending/descending). If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', property, 'descending');\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering &ndash; or for simple filtering you can use Snapshot's <a href=\"#in-built-filters\">primitive in-built filters</a>.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\n<h3>In-Built Filters</h3>\n\nIn light of Crossfilter's learning curve, Snapshot comes bundled with a handful of in-built filters for common filtering techniques. These can all be invoked by emitting an event with a corresponding value.\n\n * `snapshot/:namespace/fuzzyFilter` `{String}`\n * `snapshot/:namespace/exactFilter` `{String}`\n * `snapshot/:namespace/rangeFilter` `{Array}`\n\n```javascript\nsocket.emit('snapshot/default/fuzzyFilter', 'word', 'abc');\n```\n\nEach in-built filter expects the event name (`snapshot/default/fuzzyFilter`), the key (`word`), and value (`abc`).\n\nRanges\n-----------\n\nSince the browser does not download every model into the browser, it's impossible to determine what the minimum/maximum for any given key is without loading all models. Snapshot therefore allows you to specify which columns you wish to generate minimum/maximum ranges for.\n\nPlease be careful with these as too many may noticeably slow down your Snapshots.\n\nThe following would specify that you wish to retrieve the ranges for the `id` property on every content change.\n\n```javascript\n$snapshot.setRanges(['id']);\n```\n\nWhen the content changes you can access the range with `stats.ranges.id.min` and `stats.ranges.id.max`.\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(models, stats) {\n\n    _.forEach(models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nExample\n-----------\n\nSnapshot comes bundled with an example to get you started.\n\n * Navigate to `example/server` and run `node default.js`;\n * Open `example/client/index.html` in your browser;\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nTesting\n-----------\n\n<h3>Unit Testing</h3>\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\n<h3>Cucumber</h3>\n\nSnapshot also comes bundled with a handful of Cucumber tests.\n\n * `cd tests/cucumber`\n * `bundle install`\n * `bundle exec cucumber`\n\nPhilosophy\n-----------\n\nLoading a large collection of models into the browser is slow and unnecessary, instead Snapshot uses WebSockets to serve snapshots of those models to the browser when requested. It retains the state of the models, and so if a filter is changed, or the page number incremented, it will modify the snapshot <strong>only</strong> for that client.\n\nSnapshot is also tremendously fast because of its use of Socket.io and Crossfilter. Snapshot listens for events to change the state of the collection of models, and fires another event to let the client know the snapshot was updated. Crossfilter allows Snapshot to quickly slice and dice models &ndash; in the example, slicing and dicing takes 0-1 milliseconds for 1,000 models.\n\nSince Snapshot uses Node.js, the browser support is that of Socket.io, which essentially means Snapshot supports Internet Explorer 5.5+.\n\n <h3>Example</h3>\n\n * Browser connects to Snapshot on Node.js server;\n * Snapshot emits `snapshot/default/contentUpdated` with first page's 50 models;\n * Browser increments the page number;\n * Snapshot emits `snapshot/default/contentUpdated` with second page's 50 models;\n * Browser applies filter to select only red items;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models;\n * Browser sorts the models by their colour;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models ordered globally by colour;\n\nCaching\n-----------\n\nYou may wish to cache the collection loaded into Snapshot &ndash; for this we recommend something like <a href=\"https://github.com/Wildhoney/RedisCache\" target=\"_blank\">RedisCache</a>.","readmeFilename":"README.md","_id":"snapshot-js@0.0.7","dist":{"shasum":"ce66df0f5ca74adb12d0968d4ea1dfa90482be88","tarball":"https://registry.npmjs.org/snapshot-js/-/snapshot-js-0.0.7.tgz","integrity":"sha512-d2hREFxyNd/M+T2LhXmazAcNiFY8mtSUuqZ3jruU3Xpu286XEdb27sDbUSz0g264YVjZVrMpJGyxcbQzV8Ziaw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCfPUaYzfonMbDaF/FQMWffutVlziQzeqLnDxz3fD1tOQIhAIPbu9G32oCz5x1+HGVIiKSHkLyHbtMwxnad+IGiTA2h"}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"wildhoney","email":"adam.timberlake@gmail.com"},"maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}]},"0.0.8":{"name":"snapshot-js","buildName":"snapshot","version":"0.0.8","description":"Node.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.","main":"index.js","scripts":{"test":"grunt test"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"},"author":{"name":"Adam Timberlake"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/Wildhoney/Snapshot.js/issues"},"dependencies":{"crossfilter":"~1.3.5","underscore":"~1.5.2","grunt-cli":"~0.1.9","bower":"~1.2.7","grunt":"~0.4.1","grunt-contrib-uglify":"~0.2.4","grunt-contrib-jshint":"~0.6.4","socket.io":"~0.9.16","should":"~2.0.1","socket.io-client":"~0.9.16","grunt-mocha-test":"~0.7.0","colors":"~0.6.2","grunt-contrib-copy":"~0.4.1"},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n&nbsp;\n<img src=\"https://badge.fury.io/js/snapshot-js.png\" alt=\"NPM Version\" />\n\nInstalling via npm\n-----------\n\nSnapshot is added to the npm registry, and can therefore be downloaded with `npm install snapshot-js`.\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot which collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument, and statistics relating to the request as the second argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event passing in the `key` and `direction` (ascending/descending). If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', property, 'descending');\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering &ndash; or for simple filtering you can use Snapshot's <a href=\"#in-built-filters\">primitive in-built filters</a>.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\n<h3>In-Built Filters</h3>\n\nIn light of Crossfilter's learning curve, Snapshot comes bundled with a handful of in-built filters for common filtering techniques. These can all be invoked by emitting an event with a corresponding value.\n\n * `snapshot/:namespace/fuzzyFilter` `{String}`\n * `snapshot/:namespace/exactFilter` `{String}`\n * `snapshot/:namespace/rangeFilter` `{Array}`\n\n```javascript\nsocket.emit('snapshot/default/fuzzyFilter', 'word', 'abc');\n```\n\nEach in-built filter expects the event name (`snapshot/default/fuzzyFilter`), the key (`word`), and value (`abc`).\n\n<h3>Filtering Types</h3>\n\nBy default when you apply a filter, the previous filter will be cleared which is mostly likely the behaviour you're looking for. However, what if the user clicks **red**, and then clicks **blue**? Wouldn't it be nice if we could filter by both **red** and **blue**? In that case you're looking for the third argument of the `applyFilter` method.\n\n```javascript\n$snapshot.applyFilter('word', ..., 'reduce');\n```\n\n * `default` &ndash; filtering cleared before each filter;\n * `reduce` &ndash; filtering applied on current collection;\n\nRanges\n-----------\n\nSince the browser does not download every model into the browser, it's impossible to determine what the minimum/maximum for any given key is without loading all models. Snapshot therefore allows you to specify which columns you wish to generate minimum/maximum ranges for.\n\nPlease be careful with these as too many may noticeably slow down your Snapshots.\n\nThe following would specify that you wish to retrieve the ranges for the `id` property on every content change.\n\n```javascript\n$snapshot.setRanges(['id']);\n```\n\nWhen the content changes you can access the range with `stats.ranges.id.min` and `stats.ranges.id.max`.\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(models, stats) {\n\n    _.forEach(models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nExample\n-----------\n\nSnapshot comes bundled with an example to get you started.\n\n * Navigate to `example/server` and run `node default.js`;\n * Open `example/client/index.html` in your browser;\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nTesting\n-----------\n\n<h3>Unit Testing</h3>\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\n<h3>Cucumber</h3>\n\nSnapshot also comes bundled with a handful of Cucumber tests.\n\n * `cd tests/cucumber`\n * `bundle install`\n * `bundle exec cucumber`\n\nPhilosophy\n-----------\n\nLoading a large collection of models into the browser is slow and unnecessary, instead Snapshot uses WebSockets to serve snapshots of those models to the browser when requested. It retains the state of the models, and so if a filter is changed, or the page number incremented, it will modify the snapshot <strong>only</strong> for that client.\n\nSnapshot is also tremendously fast because of its use of Socket.io and Crossfilter. Snapshot listens for events to change the state of the collection of models, and fires another event to let the client know the snapshot was updated. Crossfilter allows Snapshot to quickly slice and dice models &ndash; in the example, slicing and dicing takes 0-1 milliseconds for 1,000 models.\n\nSince Snapshot uses Node.js, the browser support is that of Socket.io, which essentially means Snapshot supports Internet Explorer 5.5+.\n\n <h3>Example</h3>\n\n * Browser connects to Snapshot on Node.js server;\n * Snapshot emits `snapshot/default/contentUpdated` with first page's 50 models;\n * Browser increments the page number;\n * Snapshot emits `snapshot/default/contentUpdated` with second page's 50 models;\n * Browser applies filter to select only red items;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models;\n * Browser sorts the models by their colour;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models ordered globally by colour;\n\nCaching\n-----------\n\nYou may wish to cache the collection loaded into Snapshot &ndash; for this we recommend something like <a href=\"https://github.com/Wildhoney/RedisCache\" target=\"_blank\">RedisCache</a>.","readmeFilename":"README.md","_id":"snapshot-js@0.0.8","dist":{"shasum":"1fe058aacf9309b563af81477c683c287dade107","tarball":"https://registry.npmjs.org/snapshot-js/-/snapshot-js-0.0.8.tgz","integrity":"sha512-9XIyvWK3OrqmNdYhQjsdmo//JKTtoHwTv3udc9WlrrE4Eha7jdeaNmTwVqzF5e3kLK2XtyE8XAYAkIFfVoc3Mg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCNj7OQqDUGzJVUA9EaXUxN4za9/z8cJgbwhYM4z4nrHgIhALnCNEuisSrLOpGL8EWlPANA1qgQg6I0AACLoXTlD/WF"}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"wildhoney","email":"adam.timberlake@gmail.com"},"maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}]},"0.0.9":{"name":"snapshot-js","buildName":"snapshot","version":"0.0.9","description":"Node.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.","main":"index.js","scripts":{"test":"grunt test"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"},"author":{"name":"Adam Timberlake"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/Wildhoney/Snapshot.js/issues"},"dependencies":{"crossfilter":"~1.3.5","underscore":"~1.5.2","grunt-cli":"~0.1.9","bower":"~1.2.7","grunt":"~0.4.1","grunt-contrib-uglify":"~0.2.4","grunt-contrib-jshint":"~0.6.4","socket.io":"~0.9.16","should":"~2.0.1","socket.io-client":"~0.9.16","grunt-mocha-test":"~0.7.0","colors":"~0.6.2","grunt-contrib-copy":"~0.4.1"},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n&nbsp;\n<img src=\"https://badge.fury.io/js/snapshot-js.png\" alt=\"NPM Version\" />\n\nInstalling via npm\n-----------\n\nSnapshot is added to the npm registry, and can therefore be downloaded with `npm install snapshot-js`.\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot which collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument, and statistics relating to the request as the second argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event passing in the `key` and `direction` (ascending/descending). If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', property, 'descending');\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering &ndash; or for simple filtering you can use Snapshot's <a href=\"#in-built-filters\">primitive in-built filters</a>.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\n<h3>In-Built Filters</h3>\n\nIn light of Crossfilter's learning curve, Snapshot comes bundled with a handful of in-built filters for common filtering techniques. These can all be invoked by emitting an event with a corresponding value.\n\n * `snapshot/:namespace/fuzzyFilter` `{String}`\n * `snapshot/:namespace/exactFilter` `{String}`\n * `snapshot/:namespace/rangeFilter` `{Array}`\n\n```javascript\nsocket.emit('snapshot/default/fuzzyFilter', 'word', 'abc');\n```\n\nEach in-built filter expects the event name (`snapshot/default/fuzzyFilter`), the key (`word`), and value (`abc`).\n\n<h3>Filtering Types</h3>\n\nBy default when you apply a filter, the previous filter will be cleared which is mostly likely the behaviour you're looking for. However, what if the user clicks **red**, and then clicks **blue**? Wouldn't it be nice if we could filter by both **red** and **blue**? In that case you're looking for the third argument of the `applyFilter` method.\n\n```javascript\n$snapshot.applyFilter('word', ..., 'reduce');\n```\n\n * `default` &ndash; filtering cleared before each filter;\n * `reduce` &ndash; filtering applied on current collection;\n\nRanges\n-----------\n\nSince the browser does not download every model into the browser, it's impossible to determine what the minimum/maximum for any given key is without loading all models. Snapshot therefore allows you to specify which columns you wish to generate minimum/maximum ranges for.\n\nPlease be careful with these as too many may noticeably slow down your Snapshots.\n\nThe following would specify that you wish to retrieve the ranges for the `id` property on every content change.\n\n```javascript\n$snapshot.setRanges(['id']);\n```\n\nWhen the content changes you can access the range with `stats.ranges.id.min` and `stats.ranges.id.max`.\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(models, stats) {\n\n    _.forEach(models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nExample\n-----------\n\nSnapshot comes bundled with an example to get you started.\n\n * Navigate to `example/server` and run `node default.js`;\n * Open `example/client/index.html` in your browser;\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nTesting\n-----------\n\n<h3>Unit Testing</h3>\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\n<h3>Cucumber</h3>\n\nSnapshot also comes bundled with a handful of Cucumber tests.\n\n * `cd tests/cucumber`\n * `bundle install`\n * `bundle exec cucumber`\n\nPhilosophy\n-----------\n\nLoading a large collection of models into the browser is slow and unnecessary, instead Snapshot uses WebSockets to serve snapshots of those models to the browser when requested. It retains the state of the models, and so if a filter is changed, or the page number incremented, it will modify the snapshot <strong>only</strong> for that client.\n\nSnapshot is also tremendously fast because of its use of Socket.io and Crossfilter. Snapshot listens for events to change the state of the collection of models, and fires another event to let the client know the snapshot was updated. Crossfilter allows Snapshot to quickly slice and dice models &ndash; in the example, slicing and dicing takes 0-1 milliseconds for 1,000 models.\n\nSince Snapshot uses Node.js, the browser support is that of Socket.io, which essentially means Snapshot supports Internet Explorer 5.5+.\n\n <h3>Example</h3>\n\n * Browser connects to Snapshot on Node.js server;\n * Snapshot emits `snapshot/default/contentUpdated` with first page's 50 models;\n * Browser increments the page number;\n * Snapshot emits `snapshot/default/contentUpdated` with second page's 50 models;\n * Browser applies filter to select only red items;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models;\n * Browser sorts the models by their colour;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models ordered globally by colour;\n\nCaching\n-----------\n\nYou may wish to cache the collection loaded into Snapshot &ndash; for this we recommend something like <a href=\"https://github.com/Wildhoney/RedisCache\" target=\"_blank\">RedisCache</a>.","readmeFilename":"README.md","_id":"snapshot-js@0.0.9","dist":{"shasum":"8e4b3adfcec90a1ef64c8222ba3d9bb83507c489","tarball":"https://registry.npmjs.org/snapshot-js/-/snapshot-js-0.0.9.tgz","integrity":"sha512-148HDmAKFlCWk5+ciE4qzHR82Ec1p4CFeGSwEzIng+pfgszfDqMJ+wNFCAEuzFzzmKJJmm3auMMtxBs+PKStXA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC1hgOKQr8XrghHC7n/vtBnRkdw4TQKeIcd2cVfuQ7xAAIhAOc7WMIfLOd9nxSf8OisZGaCMYzLzCiEYDG8b1IbdI0Q"}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"wildhoney","email":"adam.timberlake@gmail.com"},"maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}]},"0.0.10":{"name":"snapshot-js","buildName":"snapshot","version":"0.0.10","description":"Node.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.","main":"index.js","scripts":{"test":"grunt test"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"},"author":{"name":"Adam Timberlake"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/Wildhoney/Snapshot.js/issues"},"dependencies":{"crossfilter":"~1.3.5","underscore":"~1.5.2","grunt-cli":"~0.1.9","bower":"~1.2.7","grunt":"~0.4.1","grunt-contrib-uglify":"~0.2.4","grunt-contrib-jshint":"~0.6.4","socket.io":"~0.9.16","should":"~2.0.1","socket.io-client":"~0.9.16","grunt-mocha-test":"~0.7.0","colors":"~0.6.2","grunt-contrib-copy":"~0.4.1"},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n&nbsp;\n<img src=\"https://badge.fury.io/js/snapshot-js.png\" alt=\"NPM Version\" />\n\nInstalling via npm\n-----------\n\nSnapshot is added to the npm registry, and can therefore be downloaded with `npm install snapshot-js`.\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot which collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument, and statistics relating to the request as the second argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event passing in the `key` and `direction` (ascending/descending). If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', property, 'descending');\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering &ndash; or for simple filtering you can use Snapshot's <a href=\"#in-built-filters\">primitive in-built filters</a>.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\n<h3>In-Built Filters</h3>\n\nIn light of Crossfilter's learning curve, Snapshot comes bundled with a handful of in-built filters for common filtering techniques. These can all be invoked by emitting an event with a corresponding value.\n\n * `snapshot/:namespace/fuzzyFilter` `{String}`\n * `snapshot/:namespace/exactFilter` `{String}`\n * `snapshot/:namespace/rangeFilter` `{Array}`\n\n```javascript\nsocket.emit('snapshot/default/fuzzyFilter', 'word', 'abc');\n```\n\nEach in-built filter expects the event name (`snapshot/default/fuzzyFilter`), the key (`word`), and value (`abc`).\n\n<h3>Filtering Types</h3>\n\nBy default when you apply a filter, the previous filter will be cleared which is mostly likely the behaviour you're looking for. However, what if the user clicks **red**, and then clicks **blue**? Wouldn't it be nice if we could filter by both **red** and **blue**? In that case you're looking for the third argument of the `applyFilter` method.\n\n```javascript\n$snapshot.applyFilter('word', ..., 'reduce');\n```\n\n * `afresh` &ndash; filtering cleared before each filter;\n * `reduce` &ndash; filtering applied on current collection;\n\nRanges\n-----------\n\nSince the browser does not download every model into the browser, it's impossible to determine what the minimum/maximum for any given key is without loading all models. Snapshot therefore allows you to specify which columns you wish to generate minimum/maximum ranges for.\n\nPlease be careful with these as too many may noticeably slow down your Snapshots.\n\nThe following would specify that you wish to retrieve the ranges for the `id` property on every content change.\n\n```javascript\n$snapshot.setRanges(['id']);\n```\n\nWhen the content changes you can access the range with `stats.ranges.id.min` and `stats.ranges.id.max`.\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(models, stats) {\n\n    _.forEach(models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nExample\n-----------\n\nSnapshot comes bundled with an example to get you started.\n\n * Navigate to `example/server` and run `node default.js`;\n * Open `example/client/index.html` in your browser;\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nTesting\n-----------\n\n<h3>Unit Testing</h3>\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\n<h3>Cucumber</h3>\n\nSnapshot also comes bundled with a handful of Cucumber tests.\n\n * `cd tests/cucumber`\n * `bundle install`\n * `bundle exec cucumber`\n\nPhilosophy\n-----------\n\nLoading a large collection of models into the browser is slow and unnecessary, instead Snapshot uses WebSockets to serve snapshots of those models to the browser when requested. It retains the state of the models, and so if a filter is changed, or the page number incremented, it will modify the snapshot <strong>only</strong> for that client.\n\nSnapshot is also tremendously fast because of its use of Socket.io and Crossfilter. Snapshot listens for events to change the state of the collection of models, and fires another event to let the client know the snapshot was updated. Crossfilter allows Snapshot to quickly slice and dice models &ndash; in the example, slicing and dicing takes 0-1 milliseconds for 1,000 models.\n\nSince Snapshot uses Node.js, the browser support is that of Socket.io, which essentially means Snapshot supports Internet Explorer 5.5+.\n\n <h3>Example</h3>\n\n * Browser connects to Snapshot on Node.js server;\n * Snapshot emits `snapshot/default/contentUpdated` with first page's 50 models;\n * Browser increments the page number;\n * Snapshot emits `snapshot/default/contentUpdated` with second page's 50 models;\n * Browser applies filter to select only red items;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models;\n * Browser sorts the models by their colour;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models ordered globally by colour;\n\nCaching\n-----------\n\nYou may wish to cache the collection loaded into Snapshot &ndash; for this we recommend something like <a href=\"https://github.com/Wildhoney/RedisCache\" target=\"_blank\">RedisCache</a>.","readmeFilename":"README.md","_id":"snapshot-js@0.0.10","dist":{"shasum":"87f649efee397c102b542a1ead6a125125c86271","tarball":"https://registry.npmjs.org/snapshot-js/-/snapshot-js-0.0.10.tgz","integrity":"sha512-7jplJQq6QZKYbHA3iy3H0mHbNs4MXoW756Bq3+LomJgckje6rylYOY+IdmUFEfg6zERGP28UREzD2Pcroy5noA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICDwVOPvcPvMKwIgWdSnJqFSAjGkhXLCSd2m4DSbHkfIAiEAsEvh69JkP9BwL4tC+7m/sNJslBh0SguhA//dp51MIPs="}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"wildhoney","email":"adam.timberlake@gmail.com"},"maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}]},"0.1.0":{"name":"snapshot-js","buildName":"snapshot","version":"0.1.0","description":"Node.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.","main":"index.js","scripts":{"test":"grunt test"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"},"author":{"name":"Adam Timberlake"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/Wildhoney/Snapshot.js/issues"},"dependencies":{"crossfilter":"~1.3.5","underscore":"~1.5.2","grunt-cli":"~0.1.9","bower":"~1.2.7","grunt":"~0.4.1","grunt-contrib-uglify":"~0.2.4","grunt-contrib-jshint":"~0.6.4","socket.io":"~0.9.16","should":"~2.0.1","socket.io-client":"~0.9.16","grunt-mocha-test":"~0.7.0","colors":"~0.6.2","grunt-contrib-copy":"~0.4.1"},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n&nbsp;\n<img src=\"https://badge.fury.io/js/snapshot-js.png\" alt=\"NPM Version\" />\n\nInstalling via npm\n-----------\n\nSnapshot is added to the npm registry, and can therefore be downloaded with `npm install snapshot-js`.\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot which collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument, and statistics relating to the request as the second argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event passing in the `key` and `direction` (ascending/descending). If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', property, 'descending');\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering &ndash; or for simple filtering you can use Snapshot's <a href=\"#in-built-filters\">primitive in-built filters</a>.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\n<h3>In-Built Filters</h3>\n\nIn light of Crossfilter's learning curve, Snapshot comes bundled with a handful of in-built filters for common filtering techniques. These can all be invoked by emitting an event with a corresponding value.\n\n * `snapshot/:namespace/fuzzyFilter` `{String}`\n * `snapshot/:namespace/exactFilter` `{String}`\n * `snapshot/:namespace/rangeFilter` `{Array}`\n\n```javascript\nsocket.emit('snapshot/default/fuzzyFilter', 'word', 'abc');\n```\n\nEach in-built filter expects the event name (`snapshot/default/fuzzyFilter`), the key (`word`), and value (`abc`).\n\n<h3>Filtering Types</h3>\n\nBy default when you apply a filter, the previous filter will be cleared which is mostly likely the behaviour you're looking for. However, what if the user clicks **red**, and then clicks **blue**? Wouldn't it be nice if we could filter by both **red** and **blue**? In that case you're looking for the third argument of the `applyFilter` method.\n\n```javascript\n$snapshot.applyFilter('word', ..., 'reduce');\n```\n\n * `afresh` &ndash; filtering cleared before each filter;\n * `reduce` &ndash; filtering applied on current collection;\n\nRanges\n-----------\n\nSince the browser does not download every model into the browser, it's impossible to determine what the minimum/maximum for any given key is without loading all models. Snapshot therefore allows you to specify which columns you wish to generate minimum/maximum ranges for.\n\nPlease be careful with these as too many may noticeably slow down your Snapshots.\n\nThe following would specify that you wish to retrieve the ranges for the `id` property on every content change.\n\n```javascript\n$snapshot.setRanges(['id']);\n```\n\nWhen the content changes you can access the range with `stats.ranges.id.min` and `stats.ranges.id.max`.\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(models, stats) {\n\n    _.forEach(models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nExample\n-----------\n\nSnapshot comes bundled with an example to get you started.\n\n * Navigate to `example/server` and run `node default.js`;\n * Open `example/client/index.html` in your browser;\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nTesting\n-----------\n\n<h3>Unit Testing</h3>\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\n<h3>Cucumber</h3>\n\nSnapshot also comes bundled with a handful of Cucumber tests.\n\n * `cd tests/cucumber`\n * `bundle install`\n * `bundle exec cucumber`\n\nPhilosophy\n-----------\n\nLoading a large collection of models into the browser is slow and unnecessary, instead Snapshot uses WebSockets to serve snapshots of those models to the browser when requested. It retains the state of the models, and so if a filter is changed, or the page number incremented, it will modify the snapshot <strong>only</strong> for that client.\n\nSnapshot is also tremendously fast because of its use of Socket.io and Crossfilter. Snapshot listens for events to change the state of the collection of models, and fires another event to let the client know the snapshot was updated. Crossfilter allows Snapshot to quickly slice and dice models &ndash; in the example, slicing and dicing takes 0-1 milliseconds for 1,000 models.\n\nSince Snapshot uses Node.js, the browser support is that of Socket.io, which essentially means Snapshot supports Internet Explorer 5.5+.\n\n <h3>Example</h3>\n\n * Browser connects to Snapshot on Node.js server;\n * Snapshot emits `snapshot/default/contentUpdated` with first page's 50 models;\n * Browser increments the page number;\n * Snapshot emits `snapshot/default/contentUpdated` with second page's 50 models;\n * Browser applies filter to select only red items;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models;\n * Browser sorts the models by their colour;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models ordered globally by colour;\n\nCaching\n-----------\n\nYou may wish to cache the collection loaded into Snapshot &ndash; for this we recommend something like <a href=\"https://github.com/Wildhoney/RedisCache\" target=\"_blank\">RedisCache</a>.","readmeFilename":"README.md","_id":"snapshot-js@0.1.0","dist":{"shasum":"ae0e008dde378dbfce4694f2d7328793e6aaddbb","tarball":"https://registry.npmjs.org/snapshot-js/-/snapshot-js-0.1.0.tgz","integrity":"sha512-VuQwETn3d2F62dzLWAZAeWdOTKRK3KBDkTbE2msIKEkJYkaveF4Ra0/aw1L6oFrMQ1Y66up35pdVtxTA8gbZLw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCzQaKQ/KZLbCBj1lD/f83G5Ti5W/A841G9r4ryGqDEkAIhAJDE6c1RrbrEgNx1H/5nk7qsAXXAL9mFXRhyqdx7AGWX"}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"wildhoney","email":"adam.timberlake@gmail.com"},"maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}]},"0.1.1":{"name":"snapshot-js","buildName":"snapshot","version":"0.1.1","description":"Node.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.","main":"index.js","scripts":{"test":"grunt test"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"},"author":{"name":"Adam Timberlake"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/Wildhoney/Snapshot.js/issues"},"dependencies":{"crossfilter":"~1.3.5","underscore":"~1.5.2","grunt-cli":"~0.1.9","bower":"~1.2.7","grunt":"~0.4.1","grunt-contrib-uglify":"~0.2.4","grunt-contrib-jshint":"~0.6.4","socket.io":"~0.9.16","should":"~2.0.1","socket.io-client":"~0.9.16","grunt-mocha-test":"~0.7.0","colors":"~0.6.2","grunt-contrib-copy":"~0.4.1"},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n&nbsp;\n<img src=\"https://badge.fury.io/js/snapshot-js.png\" alt=\"NPM Version\" />\n\nInstalling via npm\n-----------\n\nSnapshot is added to the npm registry, and can therefore be downloaded with `npm install snapshot-js`.\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot which collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument, and statistics relating to the request as the second argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event passing in the `key` and `direction` (ascending/descending). If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', property, 'descending');\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering &ndash; or for simple filtering you can use Snapshot's <a href=\"#in-built-filters\">primitive in-built filters</a>.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\n<h3>In-Built Filters</h3>\n\nIn light of Crossfilter's learning curve, Snapshot comes bundled with a handful of in-built filters for common filtering techniques. These can all be invoked by emitting an event with a corresponding value.\n\n * `snapshot/:namespace/fuzzyFilter` `{String}`\n * `snapshot/:namespace/exactFilter` `{String}`\n * `snapshot/:namespace/rangeFilter` `{Array}`\n * `snapshot/:namespace/regexpFilter` `{String}` `{String}`\n\n```javascript\nsocket.emit('snapshot/default/fuzzyFilter', 'word', 'abc');\nsocket.emit('snapshot/default/regExpFilter', 'word', '[a-z0-9]+', 'ig');\n```\n\nEach in-built filter expects the event name (`snapshot/default/fuzzyFilter`), the key (`word`), and value (`abc`).\n\n<h3>Filtering Types</h3>\n\nBy default when you apply a filter, the previous filter will be cleared which is mostly likely the behaviour you're looking for. However, what if the user clicks **red**, and then clicks **blue**? Wouldn't it be nice if we could filter by both **red** and **blue**? In that case you're looking for the third argument of the `applyFilter` method.\n\n```javascript\n$snapshot.applyFilter('word', ..., 'reduce');\n```\n\n * `afresh` &ndash; filtering cleared before each filter;\n * `reduce` &ndash; filtering applied on current collection;\n\nRanges\n-----------\n\nSince the browser does not download every model into the browser, it's impossible to determine what the minimum/maximum for any given key is without loading all models. Snapshot therefore allows you to specify which columns you wish to generate minimum/maximum ranges for.\n\nPlease be careful with these as too many may noticeably slow down your Snapshots.\n\nThe following would specify that you wish to retrieve the ranges for the `id` property on every content change.\n\n```javascript\n$snapshot.setRanges(['id']);\n```\n\nWhen the content changes you can access the range with `stats.ranges.id.min` and `stats.ranges.id.max`.\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(models, stats) {\n\n    _.forEach(models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nExample\n-----------\n\nSnapshot comes bundled with an example to get you started.\n\n * Navigate to `example/server` and run `node default.js`;\n * Open `example/client/index.html` in your browser;\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nTesting\n-----------\n\n<h3>Unit Testing</h3>\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\n<h3>Cucumber</h3>\n\nSnapshot also comes bundled with a handful of Cucumber tests.\n\n * `cd tests/cucumber`\n * `bundle install`\n * `bundle exec cucumber`\n\nPhilosophy\n-----------\n\nLoading a large collection of models into the browser is slow and unnecessary, instead Snapshot uses WebSockets to serve snapshots of those models to the browser when requested. It retains the state of the models, and so if a filter is changed, or the page number incremented, it will modify the snapshot <strong>only</strong> for that client.\n\nSnapshot is also tremendously fast because of its use of Socket.io and Crossfilter. Snapshot listens for events to change the state of the collection of models, and fires another event to let the client know the snapshot was updated. Crossfilter allows Snapshot to quickly slice and dice models &ndash; in the example, slicing and dicing takes 0-1 milliseconds for 1,000 models.\n\nSince Snapshot uses Node.js, the browser support is that of Socket.io, which essentially means Snapshot supports Internet Explorer 5.5+.\n\n <h3>Example</h3>\n\n * Browser connects to Snapshot on Node.js server;\n * Snapshot emits `snapshot/default/contentUpdated` with first page's 50 models;\n * Browser increments the page number;\n * Snapshot emits `snapshot/default/contentUpdated` with second page's 50 models;\n * Browser applies filter to select only red items;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models;\n * Browser sorts the models by their colour;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models ordered globally by colour;\n\nCaching\n-----------\n\nYou may wish to cache the collection loaded into Snapshot &ndash; for this we recommend something like <a href=\"https://github.com/Wildhoney/RedisCache\" target=\"_blank\">RedisCache</a>.","readmeFilename":"README.md","_id":"snapshot-js@0.1.1","dist":{"shasum":"d4f929a7ff636b523ca3a55e8894ab45379e4281","tarball":"https://registry.npmjs.org/snapshot-js/-/snapshot-js-0.1.1.tgz","integrity":"sha512-GdCuEEVsLTbE1Z6GDBCwsCAZXrCmyIJYkWwv2SEO0wr0DozqtIAXkotj2Wh9FNdpVz3s9mxKBeFp25eUkBO4+w==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBGW05/TrTbbYEVRN5FwEbf2WqS2oyRVxz+sGM73AXQNAiBzFoumdWO/ZzVd5BsGgz+K9dXPW8oNi31MO162+MeYdg=="}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"wildhoney","email":"adam.timberlake@gmail.com"},"maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}]},"0.2.0":{"name":"snapshot-js","buildName":"snapshot","version":"0.2.0","description":"Node.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.","main":"index.js","scripts":{"test":"grunt test"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"},"author":{"name":"Adam Timberlake"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/Wildhoney/Snapshot.js/issues"},"dependencies":{"crossfilter":"~1.3.5","underscore":"~1.5.2","grunt-cli":"~0.1.9","bower":"~1.2.7","grunt":"~0.4.1","grunt-contrib-uglify":"~0.2.4","grunt-contrib-jshint":"~0.6.4","socket.io":"~0.9.16","should":"~2.0.1","socket.io-client":"~0.9.16","grunt-mocha-test":"~0.7.0","colors":"~0.6.2","grunt-contrib-copy":"~0.4.1"},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n&nbsp;\n<img src=\"https://badge.fury.io/js/snapshot-js.png\" alt=\"NPM Version\" />\n\nInstalling via npm\n-----------\n\nSnapshot is added to the npm registry, and can therefore be downloaded with `npm install snapshot-js`.\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot which collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument, and statistics relating to the request as the second argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event passing in the `key` and `direction` (ascending/descending). If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', property, 'descending');\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering &ndash; or for simple filtering you can use Snapshot's <a href=\"#in-built-filters\">primitive in-built filters</a>.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\n<h3>In-Built Filters</h3>\n\nIn light of Crossfilter's learning curve, Snapshot comes bundled with a handful of in-built filters for common filtering techniques. These can all be invoked by emitting an event with a corresponding value.\n\n * `snapshot/:namespace/fuzzyFilter` `{String}`\n * `snapshot/:namespace/exactFilter` `{String}`\n * `snapshot/:namespace/rangeFilter` `{Array}`\n * `snapshot/:namespace/regexpFilter` `{String}` `{String}`\n\n```javascript\nsocket.emit('snapshot/default/fuzzyFilter', 'word', 'abc');\nsocket.emit('snapshot/default/regExpFilter', 'word', '[a-z0-9]+', 'ig');\n```\n\nEach in-built filter expects the event name (`snapshot/default/fuzzyFilter`), the key (`word`), and value (`abc`).\n\n<h3>Filtering Types</h3>\n\nBy default when you apply a filter, the previous filter will be cleared which is mostly likely the behaviour you're looking for. However, what if the user clicks **red**, and then clicks **blue**? Wouldn't it be nice if we could filter by both **red** and **blue**? In that case you're looking for the third argument of the `applyFilter` method.\n\n```javascript\n$snapshot.applyFilter('word', ..., 'reduce');\n```\n\n * `afresh` &ndash; filtering cleared before each filter;\n * `reduce` &ndash; filtering applied on current collection;\n\nRanges\n-----------\n\nSince the browser does not download every model into the browser, it's impossible to determine what the minimum/maximum for any given key is without loading all models. Snapshot therefore allows you to specify which columns you wish to generate minimum/maximum ranges for.\n\nPlease be careful with these as too many may noticeably slow down your Snapshots.\n\nThe following would specify that you wish to retrieve the ranges for the `id` property on every content change.\n\n```javascript\n$snapshot.setRanges(['id']);\n```\n\nWhen the content changes you can access the range with `stats.ranges.id.min` and `stats.ranges.id.max`.\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(models, stats) {\n\n    _.forEach(models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nExample\n-----------\n\nSnapshot comes bundled with an example to get you started.\n\n * Navigate to `example/server` and run `node default.js`;\n * Open `example/client/index.html` in your browser;\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nTesting\n-----------\n\n<h3>Unit Testing</h3>\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\n<h3>Cucumber</h3>\n\nSnapshot also comes bundled with a handful of Cucumber tests.\n\n * `cd tests/cucumber`\n * `bundle install`\n * `bundle exec cucumber`\n\nPhilosophy\n-----------\n\nLoading a large collection of models into the browser is slow and unnecessary, instead Snapshot uses WebSockets to serve snapshots of those models to the browser when requested. It retains the state of the models, and so if a filter is changed, or the page number incremented, it will modify the snapshot <strong>only</strong> for that client.\n\nSnapshot is also tremendously fast because of its use of Socket.io and Crossfilter. Snapshot listens for events to change the state of the collection of models, and fires another event to let the client know the snapshot was updated. Crossfilter allows Snapshot to quickly slice and dice models &ndash; in the example, slicing and dicing takes 0-1 milliseconds for 1,000 models.\n\nSince Snapshot uses Node.js, the browser support is that of Socket.io, which essentially means Snapshot supports Internet Explorer 5.5+.\n\n <h3>Example</h3>\n\n * Browser connects to Snapshot on Node.js server;\n * Snapshot emits `snapshot/default/contentUpdated` with first page's 50 models;\n * Browser increments the page number;\n * Snapshot emits `snapshot/default/contentUpdated` with second page's 50 models;\n * Browser applies filter to select only red items;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models;\n * Browser sorts the models by their colour;\n * Snapshot emits `snapshot/default/contentUpdated` to supply second page's red models ordered globally by colour;\n\nCaching\n-----------\n\nYou may wish to cache the collection loaded into Snapshot &ndash; for this we recommend something like <a href=\"https://github.com/Wildhoney/RedisCache\" target=\"_blank\">RedisCache</a>.","readmeFilename":"README.md","_id":"snapshot-js@0.2.0","dist":{"shasum":"08f4d2d9884bf720c1ef6918a5acf67b18a61a2c","tarball":"https://registry.npmjs.org/snapshot-js/-/snapshot-js-0.2.0.tgz","integrity":"sha512-365Z6/nEkX5Aual4iyrkai/iKUnm/63O2vOEk/gUhKeaHCa9E8ExoEJJy0VCDctnwHhUCGAzYihXF8Qt1ZHY5g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDjAIMfxuzGTGIuejqPEd6Kwv9x/WFQPnXZTmRJTGGy1AIhAJgO/SZSyL2K5AlgVW6jVx62cjAxdAykgH56nLn7dhCm"}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"wildhoney","email":"adam.timberlake@gmail.com"},"maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}]}},"readme":"Snapshot.js\n===========\n\nNode.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.\n\n<img src=\"https://travis-ci.org/Wildhoney/Snapshot.js.png?branch=master\" alt=\"Travis CI\" />\n\nDependencies\n-----------\n\nAll dependencies can be installed with `bower install` and `npm install`, however the list is as follows:\n\n * <a href=\"http://socket.io/\">Socket.IO</a> (client and server);\n * <a href=\"http://underscorejs.org/\">Underscore.js</a> (server);\n * <a href=\"http://nodejs.org/\">Node.js</a> (server);\n * <a href=\"http://square.github.io/crossfilter/\">Crossfilter</a> (server);\n * <a href=\"http://visionmedia.github.io/mocha/\">Mocha</a> &ndash; with Should.js (grunt);\n\nQuick Start\n-----------\n\nOnce a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.\n\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(false);\n```\n\nYou then need to tell Snapshot what collection it's going to be creating snapshots of. You can pass in an optional string for the `primaryKey` &ndash; if you omit the `primaryKey` then the first key of the first model is used.\n\n```javascript\n$snapshot.setCollection(collection, primaryKey);\n```\n\n<small>(`primaryKey` is an optional parameter.)</small>\n\nSnapshot listens for three events natively, and these are handled automatically.\n\n * `snapshot/:namespace/perPage` &ndash; Set amount per page;\n * `snapshot/:namespace/pageNumber` &ndash; Set current page number;\n * `snapshot/:namespace/sortBy` &ndash; Set order column and ascending/descending;\n\nAs Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be `default`. Therefore all of your events will be: `snapshot/default/perPage`, `snapshot/default/pageNumber` and `snapshot/default/sortBy`.\n\n<small>(`:namespace` is the name you provided upon instantiation of `Snapshot` &ndash; if you didn't, then it's `default`.)</small>\n\nWhen the collection has been updated, Snapshot emits the `snapshot/:namespace/contentUpdated` event, passing through the snapshot as the first argument.\n\nFor sorting by any given column, you can emit the `snapshot/:namespace/sortBy` event with a simple hash. If you omit the `direction` property (or set its value to `false`) then Snapshot will cleverly invert the current sorting direction for you.\n\n```javascript\nsocket.emit('snapshot/:namespace/sortBy', {\n    key         : property,\n    direction   : 'descending'\n});\n```\n\nFiltering\n-----------\n\nIn addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the `applyFilter` filter method. Unfortunately you will need to read <a href=\"https://github.com/square/crossfilter/wiki/API-Reference\" target=\"_blank\">Crossfilter's API Reference</a> before you begin filtering.\n\n```javascript\nsocket.emit('filterByWord', text);\n```\n\nYou can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the `filterByWord` event to the Node.js server, and then we need to listen for that event.\n\n```javascript\nsocket.on('filterByWord', function(text) {\n\n    $snapshot.applyFilter('word', function(dimension) {\n\n        dimension.filterFunction(function(d) {\n            var regExp = new RegExp(text, 'i');\n            return d.match(regExp);\n        });\n\n    });\n\n});\n```\n\nYou essentially invoke the `applyFilter` on the `snapshot` object. Snapshot will pass in the `dimension` argument to your lambda function &ndash; `this` context is preserved. It's then entirely up to you to apply that dimension to the collection.\n\nIf you would like to clear a specific dimension, then you can use the `clearFilter` method &ndash; which takes the property name as its one and only argument.\n\n```javascript\n$snapshot.clearFilter('word');\n```\n\nYou can also clear every single filter by using the `clearFilters` method.\n\n```javascript\n$snapshot.clearFilters();\n```\n\nMultiple Instances\n-----------\n\nWhen instantiating Snapshot you should pass in the namespace for the current collection &ndash; that way you could create a new instance of `Snapshot` with a unique collection of models.\n\n```javascript\nvar $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);\nvar $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);\nvar $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);\n```\n\nIn the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):\n\n * `snapshot/dogs/perPage`, `snapshot/dogs/pageNumber`, `snapshot/dogs/sortBy`\n * `snapshot/cats/perPage`, `snapshot/cats/pageNumber`, `snapshot/cats/sortBy`\n * `snapshot/rabbits/perPage`, `snapshot/rabbits/pageNumber`, `snapshot/rabbits/sortBy`\n\nAnd it will emit 3 events:\n\n * `snapshot/dogs/contentUpdated`\n * `snapshot/cats/contentUpdated`\n * `snapshot/rabbits/contentUpdated`\n\nIf you don't create a namespace then the namespace will be set to `default`.\n\nDelta Updates\n-----------\n\nThere may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates &ndash; essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.\n\n```javascript\nvar $snapshot = new Snapshot().bootstrap(socket).useDelta(true);\n```\n\nOnce you've enabled delta updates using `useDelta(true)` as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking `setCollection`, otherwise a default primary key will be assumed.\n\n```javascript\n$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');\n```\n\nSince unique models will <strong>only</strong> ever be transmitted once, it's imperative that you keep a history of all models from the `snapshot/:namespace/contentUpdated` event, and then to utilise those from your local cache when you come across a delta model.\n\nDelta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like `Number.isFinite` (or Underscore's `_.isNumber`) on the returned collection.\n\n```javascript\nsocket.on('snapshot/:namespace/contentUpdated', function(data) {\n\n    _.forEach(data.models, function(model) {\n\n        if (_.isNumber(model)) {\n            // Delta model!\n            return;\n        }\n\n        // ...\n\n    });\n\n});\n```\n\nArchitecture\n-----------\n\nBelow is a simple diagram of how Snapshot works. It demonstrates how the `snapshot/:namespace/pageNumber` event operates &ndash; which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.\n\n<img src=\"http://i.imgur.com/6o0Nw5Y.png\" alt=\"Snapshot Architecture\" />\n\n * Browser establishes a WebSocket connection to Node.js &ndash; models are added;\n * Browser emits `snapshot/:namespace/pageNumber` event with data (example);\n * Snapshot along with Crossfilter updates the collection <i>snapshot</i>;\n * Snapshot emits `snapshot/:namespace/contentUpdated` event with the updated collection;\n * Browser emits a custom event (`customFilterApplied`) with the data;\n * Node.js listens for the `customFilterApplied` event and then interacts with Snapshot;\n * Snapshot emits the `snapshot/:namespace/contentUpdated` event with the updated filter applied;\n\nUnit Testing\n-----------\n\nGrunt is a prerequisite to run the Mocha tests, which is installed when you run `npm install`. Afterwards all of Snapshot's unit tests can be run with the `grunt test` command from the terminal.\n\nAngular\n-----------\n\n<img src=\"https://lh6.googleusercontent.com/-DtPuPzooNEY/AAAAAAAAAAI/AAAAAAAAAC4/6Y1jxzshd4g/photo.jpg?sz=48\" alt=\"Angular.js\" />\n\nSnapshot also comes with a bundled Angular module for easier interaction. Simply add `$snapshot` as a dependency and you have everything you need.","maintainers":[{"name":"wildhoney","email":"adam.timberlake@gmail.com"}],"time":{"modified":"2022-06-26T21:40:28.050Z","created":"2013-10-20T13:00:20.227Z","0.0.1":"2013-10-20T13:00:23.863Z","0.0.2":"2013-10-20T14:24:35.142Z","0.0.3":"2013-10-20T17:37:38.011Z","0.0.4":"2013-10-21T18:45:02.053Z","0.0.5":"2013-10-21T22:05:37.963Z","0.0.6":"2013-10-25T23:28:18.382Z","0.0.7":"2013-10-28T21:37:29.471Z","0.0.8":"2013-10-28T22:40:52.452Z","0.0.9":"2013-10-29T00:22:27.067Z","0.0.10":"2013-11-03T02:05:31.116Z","0.1.0":"2013-11-07T21:57:23.315Z","0.1.1":"2013-11-26T00:00:59.237Z","0.2.0":"2013-11-26T00:06:26.905Z"},"author":{"name":"Adam Timberlake"},"repository":{"type":"git","url":"https://github.com/Wildhoney/Snapshot.js.git"}}