├── .npmignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── build.js ├── build.sh ├── index.js ├── package.json ├── swot-data-bool.json ├── swot-data.json ├── swot-simple.js ├── test.js └── tlds.json /.npmignore: -------------------------------------------------------------------------------- 1 | swot.zip 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.8" 4 | - "0.10" 5 | before_install: 6 | - npm install -g npm@~1.4.6 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | ISC License 3 | 4 | Copyright (c) 2017, Mapbox 5 | 6 | Permission to use, copy, modify, and/or distribute this software for any 7 | purpose with or without fee is hereby granted, provided that the above 8 | copyright notice and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # swot-simple 2 | 3 | [![build status](https://secure.travis-ci.org/mapbox/swot-simple.png)](http://travis-ci.org/mapbox/swot-simple) 4 | 5 | Simple & fast JavaScript implementation of [Swot](https://github.com/leereilly/swot) 6 | 7 | * only depends on tldjs 8 | * compiles swot data into a single json file, so bootup is fast 9 | * passes all of Swot's tests. 10 | * fast 11 | 12 | ## Install 13 | 14 | npm install swot-simple 15 | 16 | ### `isAcademic(email)` 17 | 18 | Check an email for whether it is from an educational domain or not. 19 | 20 | 21 | ### Parameters 22 | 23 | | parameter | type | description | 24 | | --------- | ------ | -------------------- | 25 | | `email` | String | a full email address | 26 | 27 | 28 | ### Example 29 | 30 | ```js 31 | swot.isAcademic('me@gmail.com'); // false 32 | swot.isAcademic('lee@harvard.edu'); // true 33 | ``` 34 | 35 | 36 | **Returns** `boolean`, whether the email is educational 37 | 38 | 39 | ### `getInstitutionName(email)` 40 | 41 | Check an email for whether it is from an educational domain or not, 42 | and if it is a known educational institution, return its name. 43 | 44 | 45 | ### Parameters 46 | 47 | | parameter | type | description | 48 | | --------- | ------ | -------------------- | 49 | | `email` | String | a full email address | 50 | 51 | 52 | ### Example 53 | 54 | ```js 55 | swot.getInstitutionName('lreilly@cs.strath.ac.uk'); 56 | // "University of Strathclyde" 57 | ``` 58 | 59 | 60 | **Returns** `boolean,String`, false, if the email is not educational. otherwise, a string describing the domain. 61 | 62 | ## Build Process 63 | 64 | swot's main data is transformed by `build.js`. The list of tlds is transformed 65 | from the [Ruby](https://github.com/leereilly/swot/blob/master/lib/swot/academic_tlds.rb) to 66 | [json](tlds.json) by hand. 67 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | var ProgressBar = require('progress'), 2 | glob = require('glob'), 3 | fs = require('fs'); 4 | 5 | var names = glob 6 | .sync('./swot-build/swot-master/lib/domains/**/*.txt'); 7 | 8 | console.log('%s to run', names.length); 9 | 10 | var bar = new ProgressBar(':bar :percent | :current/:total', { 11 | total: names.length 12 | }); 13 | 14 | var reduced = names.reduce(function(memo, name) { 15 | var reduced_name = name 16 | .replace('./swot-build/swot-master/lib/domains/', '') 17 | .replace(/\.txt$/, ''); 18 | var label = fs.readFileSync(name); 19 | var domain = reduced_name.split('/').reverse().join('.'); 20 | memo[domain] = label.toString().trim(); 21 | bar.tick(1); 22 | return memo; 23 | }, {}); 24 | 25 | fs.writeFileSync('swot-data.json', JSON.stringify(reduced, null, 2)); 26 | fs.writeFileSync('swot-data-bool.json', JSON.stringify(Object.keys(reduced))); 27 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf swot.zip 4 | rm -rf swot-build 5 | 6 | wget https://codeload.github.com/leereilly/swot/zip/master -O swot.zip 7 | mkdir swot-build 8 | unzip swot.zip -d swot-build 9 | 10 | node build.js 11 | 12 | npm run build 13 | 14 | npm run test 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var data = require('./swot-data.json'), 2 | tldjs = require('tldjs'); 3 | 4 | var tlds = require('./tlds.json').reduce(function(memo, _) { 5 | memo[_] = true; 6 | return memo; 7 | }, {}); 8 | 9 | var blacklist = { 10 | 'si.edu': true, 11 | 'america.edu': true 12 | }; 13 | 14 | /** 15 | * Check an email for whether it is from an educational domain or not. 16 | * 17 | * @param {String} email a full email address 18 | * @returns {boolean} whether the email is educational 19 | * @example 20 | * swot.isAcademic('me@gmail.com'); // false 21 | * swot.isAcademic('lee@harvard.edu'); // true 22 | */ 23 | function isAcademic(email) { 24 | if (typeof email !== 'string') return false; 25 | 26 | var parts = email.split('@'), 27 | domain = tldjs.getDomain(parts[parts.length - 1]); 28 | 29 | return !!(domain && 30 | blacklist[domain] === undefined && 31 | (data[domain] !== undefined || 32 | tlds[tldjs.getPublicSuffix(domain)])); 33 | } 34 | 35 | /** 36 | * Check an email for whether it is from an educational domain or not, 37 | * and if it is a known educational institution, return its name. 38 | * 39 | * @param {String} email a full email address 40 | * @returns {(boolean|String)} false, if the email is not educational. 41 | * otherwise, a string describing the domain. 42 | * @example 43 | * swot.getInstitutionName('lreilly@cs.strath.ac.uk'); 44 | * // "University of Strathclyde" 45 | */ 46 | function getInstitutionName(email) { 47 | if (typeof email !== 'string') return false; 48 | 49 | var parts = email.split('@'), 50 | domain = tldjs.getDomain(parts[parts.length - 1]); 51 | 52 | return data[domain]; 53 | } 54 | 55 | module.exports.isAcademic = isAcademic; 56 | module.exports.getInstitutionName = getInstitutionName; 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mapbox/swot-simple", 3 | "version": "1.0.0", 4 | "description": "detect whether an email address is under an edu domain", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "tape test.js", 8 | "build": "browserify -s swot index.js | uglifyjs -c -m > swot-simple.js", 9 | "doc": "dox -r < index.js | doxme" 10 | }, 11 | "keywords": [ 12 | "swot", 13 | "email", 14 | "edu" 15 | ], 16 | "author": "Tom MacWright, Contributors, SWOT originally by Lee Reilly", 17 | "license": "ISC", 18 | "devDependencies": { 19 | "browserify": "^9.0.3", 20 | "dox": "^0.6.1", 21 | "doxme": "^1.4.2", 22 | "progress": "^1.1.8", 23 | "tape": "^3.5.0" 24 | }, 25 | "dependencies": { 26 | "glob": "^5.0.3", 27 | "tldjs": "^1.5.2", 28 | "uglify-js": "^2.4.16" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "git@github.com:mapbox/swot-simple.git" 33 | } 34 | } -------------------------------------------------------------------------------- /swot-data-bool.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /swot-data.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'), 2 | swot = require('./'); 3 | 4 | test('swot', function(t) { 5 | function testAcademic(name, value) { 6 | t.equal(swot.isAcademic(name), value, name); 7 | } 8 | 9 | t.test('isAcademic', function(t) { 10 | t.equal(testAcademic('lreilly@stanford.edu', true)); 11 | t.equal(testAcademic('LREILLY@STANFORD.EDU', true)); 12 | t.equal(testAcademic('Lreilly@Stanford.Edu', true)); 13 | t.equal(testAcademic('lreilly@slac.stanford.edu', true)); 14 | t.equal(testAcademic('lreilly@strath.ac.uk', true)); 15 | t.equal(testAcademic('lreilly@soft-eng.strath.ac.uk', true)); 16 | t.equal(testAcademic('lee@ugr.es', true)); 17 | t.equal(testAcademic('lee@uottawa.ca', true)); 18 | t.equal(testAcademic('lee@mother.edu.ru', true)); 19 | t.equal(testAcademic('lee@ucy.ac.cy', true)); 20 | t.equal(testAcademic('lee@leerilly.net', false)); 21 | t.equal(testAcademic('lee@gmail.com', false)); 22 | t.equal(testAcademic('lee@stanford.edu.com', false)); 23 | t.equal(testAcademic('lee@strath.ac.uk.com', false)); 24 | t.equal(testAcademic('stanford.edu', true)); 25 | t.equal(testAcademic('slac.stanford.edu', true)); 26 | t.equal(testAcademic('www.stanford.edu', true)); 27 | t.equal(testAcademic('http://www.stanford.edu', true)); 28 | t.equal(testAcademic('http://www.stanford.edu:9393', true)); 29 | t.equal(testAcademic('strath.ac.uk', true)); 30 | t.equal(testAcademic('soft-eng.strath.ac.uk', true)); 31 | t.equal(testAcademic('ugr.es', true)); 32 | t.equal(testAcademic('uottawa.ca', true)); 33 | t.equal(testAcademic('mother.edu.ru', true)); 34 | t.equal(testAcademic('ucy.ac.cy', true)); 35 | t.equal(testAcademic('leerilly.net', false)); 36 | t.equal(testAcademic('gmail.com', false)); 37 | t.equal(testAcademic('stanford.edu.com', false)); 38 | t.equal(testAcademic('strath.ac.uk.com', false)); 39 | t.equal(testAcademic(null, false)); 40 | t.equal(testAcademic('', false)); 41 | t.equal(testAcademic('the', false)); 42 | t.equal(testAcademic(' stanford.edu', true)); 43 | t.equal(testAcademic('lee@strath.ac.uk ', true)); 44 | t.equal(testAcademic(' gmail.com ', false)); 45 | t.equal(testAcademic('lee@stud.uni-corvinus.hu', true)); 46 | t.equal(testAcademic('lee@harvard.edu', true)); 47 | t.equal(testAcademic('lee@mail.harvard.edu', true)); 48 | t.end(); 49 | }); 50 | 51 | t.test('getInstitutionName', function(t) { 52 | t.equal(swot.getInstitutionName('lreilly@cs.strath.ac.uk'), 'University of Strathclyde'); 53 | t.equal(swot.getInstitutionName('lreilly@fadi.at'), 'BRG Fadingerstraße Linz, Austria'); 54 | t.end(); 55 | }); 56 | 57 | t.test('blacklist', function(t) { 58 | ["si.edu", " si.edu ", "imposter@si.edu", "foo.si.edu", "america.edu"].forEach(function(bad) { 59 | t.equal(swot.isAcademic(bad), false, bad); 60 | }); 61 | t.end(); 62 | }); 63 | 64 | t.test('invalid', function(t) { 65 | t.equal(swot.isAcademic('foo@bar.invalid'), false); 66 | t.equal(swot.isAcademic('.com'), false); 67 | t.equal(swot.isAcademic(false), false); 68 | t.end(); 69 | }); 70 | 71 | t.end(); 72 | }); 73 | -------------------------------------------------------------------------------- /tlds.json: -------------------------------------------------------------------------------- 1 | [ 2 | "ac.ae", 3 | "ac.at", 4 | "ac.bd", 5 | "ac.be", 6 | "ac.cn", 7 | "ac.cr", 8 | "ac.cy", 9 | "ac.fj", 10 | "ac.gg", 11 | "ac.gn", 12 | "ac.id", 13 | "ac.il", 14 | "ac.in", 15 | "ac.ir", 16 | "ac.jp", 17 | "ac.ke", 18 | "ac.kr", 19 | "ac.ma", 20 | "ac.me", 21 | "ac.mu", 22 | "ac.mw", 23 | "ac.mz", 24 | "ac.ni", 25 | "ac.nz", 26 | "ac.om", 27 | "ac.pa", 28 | "ac.pg", 29 | "ac.pr", 30 | "ac.rs", 31 | "ac.ru", 32 | "ac.rw", 33 | "ac.sz", 34 | "ac.th", 35 | "ac.tz", 36 | "ac.ug", 37 | "ac.uk", 38 | "ac.yu", 39 | "ac.za", 40 | "ac.zm", 41 | "ac.zw", 42 | "ed.ao", 43 | "ed.cr", 44 | "ed.jp", 45 | "edu", 46 | "edu.af", 47 | "edu.al", 48 | "edu.ar", 49 | "edu.au", 50 | "edu.az", 51 | "edu.ba", 52 | "edu.bb", 53 | "edu.bd", 54 | "edu.bh", 55 | "edu.bi", 56 | "edu.bn", 57 | "edu.bo", 58 | "edu.br", 59 | "edu.bs", 60 | "edu.bt", 61 | "edu.bz", 62 | "edu.ck", 63 | "edu.cn", 64 | "edu.co", 65 | "edu.cu", 66 | "edu.do", 67 | "edu.dz", 68 | "edu.ec", 69 | "edu.ee", 70 | "edu.eg", 71 | "edu.er", 72 | "edu.es", 73 | "edu.et", 74 | "edu.ge", 75 | "edu.gh", 76 | "edu.gr", 77 | "edu.gt", 78 | "edu.hk", 79 | "edu.hn", 80 | "edu.ht", 81 | "edu.in", 82 | "edu.iq", 83 | "edu.jm", 84 | "edu.jo", 85 | "edu.kg", 86 | "edu.kh", 87 | "edu.kn", 88 | "edu.kw", 89 | "edu.ky", 90 | "edu.kz", 91 | "edu.la", 92 | "edu.lb", 93 | "edu.lr", 94 | "edu.lv", 95 | "edu.ly", 96 | "edu.me", 97 | "edu.mg", 98 | "edu.mk", 99 | "edu.ml", 100 | "edu.mm", 101 | "edu.mn", 102 | "edu.mo", 103 | "edu.mt", 104 | "edu.mv", 105 | "edu.mw", 106 | "edu.mx", 107 | "edu.my", 108 | "edu.ni", 109 | "edu.np", 110 | "edu.om", 111 | "edu.pa", 112 | "edu.pe", 113 | "edu.ph", 114 | "edu.pk", 115 | "edu.pl", 116 | "edu.pr", 117 | "edu.ps", 118 | "edu.pt", 119 | "edu.pw", 120 | "edu.py", 121 | "edu.qa", 122 | "edu.rs", 123 | "edu.ru", 124 | "edu.sa", 125 | "edu.sc", 126 | "edu.sd", 127 | "edu.sg", 128 | "edu.sh", 129 | "edu.sl", 130 | "edu.sv", 131 | "edu.sy", 132 | "edu.tr", 133 | "edu.tt", 134 | "edu.tw", 135 | "edu.ua", 136 | "edu.uy", 137 | "edu.ve", 138 | "edu.vn", 139 | "edu.ws", 140 | "edu.ye", 141 | "edu.zm", 142 | "es.kr", 143 | "g12.br", 144 | "hs.kr", 145 | "ms.kr", 146 | "sc.kr", 147 | "sc.ug", 148 | "sch.ae", 149 | "sch.gg", 150 | "sch.id", 151 | "sch.ir", 152 | "sch.je", 153 | "sch.jo", 154 | "sch.lk", 155 | "sch.ly", 156 | "sch.my", 157 | "sch.om", 158 | "sch.ps", 159 | "sch.sa", 160 | "sch.uk", 161 | "school.nz", 162 | "school.za", 163 | "vic.edu.au"] 164 | --------------------------------------------------------------------------------