├── index.js ├── README.md ├── test ├── mocha.opts ├── testHooks.js └── index.spec.js ├── .gitignore ├── package.json ├── lib ├── utils.js └── index.js ├── LICENSE └── .eslintrc /index.js: -------------------------------------------------------------------------------- 1 | require('./lib/index'); 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | cookie-batch 2 | ============ 3 | 4 | Simple Cookie Manager 5 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require ./test/testHooks 2 | --ui tdd 3 | --reporter spec -------------------------------------------------------------------------------- /test/testHooks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | global.should = require('chai').should(); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-batch", 3 | "version": "0.0.2", 4 | "description": "Cookie Manager for client", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "mocha" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/davidchase/cookie-batch.git" 15 | }, 16 | "keywords": [ 17 | "cookie", 18 | "cookie-manager" 19 | ], 20 | "author": "David Chase", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/davidchase/cookie-batch/issues" 24 | }, 25 | "homepage": "https://github.com/davidchase/cookie-batch", 26 | "devDependencies": { 27 | "chai": "^1.10.0", 28 | "mocha": "^2.0.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var utils = {}; 3 | 4 | utils.isJSON = function(str) { 5 | try { 6 | JSON.parse(str); 7 | } catch (e) { 8 | return false; 9 | } 10 | return true; 11 | }; 12 | utils.typeCheck = function(value) { 13 | return ({}).toString.call(value).slice(8, -1).toLowerCase(); 14 | }; 15 | 16 | utils.assert = function(condition, message) { 17 | if (!condition) { 18 | message = message || "Assertion failed"; 19 | throw new Error(message); 20 | } 21 | }; 22 | 23 | utils.serialize = function(value) { 24 | if (utils.typeCheck(value) !== 'string') { 25 | value = JSON.stringify(value); 26 | } 27 | return encodeURIComponent(value); 28 | }; 29 | 30 | utils.parse = function(value) { 31 | value = decodeURIComponent(value); 32 | return utils.isJSON(value) ? JSON.parse(value) : value; 33 | }; 34 | 35 | module.exports = utils; 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 David Chase 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var utils = require('./utils'); 3 | var CookieManager = function() { 4 | this._maxExpireDate = new Date('Fri, 31 Dec 9999 23:59:59 UTC'); 5 | }; 6 | var CMProto = CookieManager.prototype; 7 | 8 | CMProto._cookieExpirationDate = function(stale) { 9 | var _this = this; 10 | var expires; 11 | var staleTypes = { 12 | 'number': function() { 13 | expires = stale === Infinity ? 14 | "expires=" + _this._maxExpireDate : 15 | "max-age=" + stale; // max-age is in seconds 16 | }, 17 | 'string': function() { 18 | expires = "expires=" + new Date(stale).toUTCString(); 19 | }, 20 | 'date': function() { 21 | expires = "expires=" + stale.toUTCString(); 22 | } 23 | }; 24 | return stale && staleTypes[utils.typeCheck(stale)].call() || expires; 25 | 26 | }; 27 | 28 | CMProto.get = function(key) { 29 | var arr = document.cookie.split(/; */); 30 | var i = arr.length; 31 | var val; 32 | var idx; 33 | utils.assert(this.exists(key), 'No cookie found'); 34 | while (i--) { 35 | idx = arr[i].indexOf('='); 36 | val = arr[i].substr(++idx, arr[i].length).trim(); 37 | if (arr[i].indexOf(key) > -1) { 38 | return utils.parse(val); 39 | } 40 | } 41 | }; 42 | 43 | CMProto.set = function(key, val, opt) { 44 | var pairs; 45 | var _this = this; 46 | opt = opt || {}; 47 | 48 | pairs = [key + '=' + utils.serialize(val)]; 49 | 50 | pairs.push(_this._cookieExpirationDate(val === undefined ? -1 : opt.staleIn)); 51 | pairs.push(opt.path && 'path=' + opt.path); 52 | pairs.push(opt.domain && 'domain=' + opt.domain); 53 | pairs.push(opt.secure && 'secure'); // only over https 54 | 55 | pairs = pairs.filter(Boolean); 56 | document.cookie = pairs.join('; '); 57 | }; 58 | 59 | CMProto.remove = function(key, opt) { 60 | this.set(key, undefined, opt); 61 | return true; 62 | }; 63 | CMProto.exists = function(key) { 64 | return document.cookie.indexOf(key + '=') >= 0; 65 | }; 66 | CMProto.keys = function() { 67 | var cookies = document.cookie.split(/; */); 68 | return cookies.map(function(cookie) { 69 | var idx = cookie.indexOf('='); 70 | return cookie.substr(0, idx).trim(); 71 | }); 72 | }; 73 | 74 | 75 | module.exports = new CookieManager(); -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "mocha": true 6 | }, 7 | 8 | "globals": { 9 | "Zombie": true 10 | }, 11 | 12 | "rules": { 13 | "block-scoped-var": 2, 14 | "complexity": [1, 5], 15 | "consistent-return": 2, 16 | "curly": 2, 17 | "default-case": 2, 18 | "dot-notation": 2, 19 | "eqeqeq": 2, 20 | "guard-for-in": 2, 21 | "no-alert": 2, 22 | "no-caller": 2, 23 | "no-comma-dangle": 2, 24 | "no-div-regex": 2, 25 | "no-dupe-keys": 2, 26 | "no-else-return": 2, 27 | "no-empty-label": 2, 28 | "no-eq-null": 2, 29 | "no-eval": 2, 30 | "no-extend-native": 2, 31 | "no-extra-bind": 2, 32 | "no-extra-boolean-cast": 2, 33 | "no-fallthrough": 2, 34 | "no-floating-decimal": 2, 35 | "no-implied-eval": 2, 36 | "no-labels": 2, 37 | "no-iterator": 2, 38 | "no-lone-blocks": 2, 39 | "no-loop-func": 2, 40 | "no-multi-str": 2, 41 | "no-native-reassign": 2, 42 | "no-new": 2, 43 | "no-new-func": 2, 44 | "no-new-wrappers": 2, 45 | "no-octal": 2, 46 | "no-octal-escape": 2, 47 | "no-proto": 2, 48 | "no-redeclare": 2, 49 | "no-return-assign": 2, 50 | "no-script-url": 2, 51 | "no-self-compare": 2, 52 | "no-sequences": 2, 53 | "no-unused-expressions": 2, 54 | "no-unused-vars": 1, 55 | "no-unreachable": 2, 56 | "no-void": 2, 57 | "no-warning-comments": 2, 58 | "no-with": 2, 59 | "radix": 2, 60 | "vars-on-top": 2, 61 | "wrap-iife": [2, "inside"], 62 | "yoda": 2, 63 | "quotes": 0, 64 | "eol-last": 0, 65 | "no-extra-strict": 2, 66 | "camelcase": 0, 67 | "consistent-this": [2, "_this"], 68 | "new-cap": 2, 69 | "new-parens": 2, 70 | "func-style": [2, "expression"], 71 | "no-lonely-if": 2, 72 | "no-new-object": 2, 73 | "no-space-before-semi": 2, 74 | "brace-style": [2, "1tbs"], 75 | "no-wrap-func": 2, 76 | "space-after-keywords": [2, "always"], 77 | "use-isnan": 2, 78 | "valid-jsdoc": [2, { 79 | "prefer": { 80 | "return": "returns" 81 | } 82 | }], 83 | "max-nested-callbacks": [2, 3], 84 | "semi": [2, "always"], 85 | "no-underscore-dangle": 0 86 | } 87 | } -------------------------------------------------------------------------------- /test/index.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var cm = require('../lib/index'); 3 | setup(function() { 4 | // create a document.cookie mock 5 | // since in node there's no 6 | // document property 7 | global.document = { 8 | _value: '', 9 | get cookie() { 10 | return this._value; 11 | }, 12 | set cookie(value) { 13 | this._value += value + ';'; 14 | } 15 | }; 16 | 17 | }); 18 | 19 | suite('set method', function() { 20 | test('setting one cookie, the name should be at position 0', function() { 21 | cm.set('myCookie', 'my cookie'); 22 | return document.cookie.indexOf('myCookie=').should.equal(0); 23 | }); 24 | 25 | 26 | test('setting values with spaces should be encoded', function() { 27 | cm.set('someCookie', 'hello world'); 28 | document.cookie.split('=')[1].should.contain('hello%20world'); 29 | }); 30 | 31 | test('setting a object value should JSON.stringify before encoding', function() { 32 | var obj = { 33 | a: 'b', 34 | d: 'e' 35 | }; 36 | var encode = function(val){ 37 | val = JSON.stringify(val); 38 | return encodeURIComponent(val); 39 | }; 40 | cm.set('jsonCookie', obj); 41 | document.cookie.split('=')[1].should.contain(encode(obj)); 42 | }); 43 | }); 44 | 45 | suite('get method', function() { 46 | test('calling get method with a value should return cookie value', function() { 47 | cm.set('someCookie', 'hello world'); 48 | return cm.get('someCookie').should.equal('hello world'); 49 | }); 50 | 51 | test('getting a json value should parse it before returning it', function(){ 52 | var obj = { 53 | a: 'b', 54 | d: 'e' 55 | }; 56 | cm.set('jsonCookie', JSON.stringify(obj)); 57 | cm.get('jsonCookie').should.eql(obj); 58 | }); 59 | }); 60 | 61 | suite('remove method', function() { 62 | test('calling remove method the mock cookie should contain epoch time', function() { 63 | cm.set('someCookie', 'hello'); 64 | cm.remove('someCookie'); 65 | return document.cookie.should.contain('max-age=-1'); 66 | }); 67 | 68 | }); 69 | 70 | suite('keys method', function() { 71 | test('calling keys method should return an array', function() { 72 | cm.set('someCookie', 'hello'); 73 | cm.set('anotherCookie', 'ahh'); 74 | return cm.keys().should.be.an.Array; 75 | }); 76 | 77 | 78 | test('calling keys method should return an array of cookie names', function() { 79 | cm.set('someCookie', 'hello'); 80 | cm.set('anotherCookie', 'ahh'); 81 | return cm.keys().should.include.members(['someCookie', 'anotherCookie']); 82 | }); 83 | }); 84 | 85 | suite('exists method', function() { 86 | test('calling exists method should return truthy if cookie is present', function() { 87 | cm.set('someCookie', 'hello'); 88 | cm.set('anotherCookie', 'ahh'); 89 | return cm.exists('anotherCookie').should.be.true; 90 | }); 91 | 92 | test('calling exists method should return falsey if cookie is not present', function() { 93 | cm.set('someCookie', 'hello'); 94 | cm.set('anotherCookie', 'ahh'); 95 | return cm.exists('cookieMonster').should.be.false; 96 | }); 97 | }); --------------------------------------------------------------------------------