├── .gitignore ├── lib ├── seeds │ └── profanity.json └── filter.js ├── .travis.yml ├── package.json ├── LICENSE ├── README.md └── test └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules -------------------------------------------------------------------------------- /lib/seeds/profanity.json: -------------------------------------------------------------------------------- 1 | { 2 | "shit": "poop", 3 | "damn": "darn" 4 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" 4 | - "0.10" 5 | - "0.8" -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "profanity-filter", 3 | "description": "A node.js utility for masking words or phrases in strings that aren't allowed.", 4 | "version": "0.2.1", 5 | "homepage": "https://github.com/jwils0n/profanity-filter", 6 | "author": { 7 | "name": "James Wilson", 8 | "email": "james@jwilson.us", 9 | "url": "https://github.com/jwils0n" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/jwils0n/profanity-filter.git" 14 | }, 15 | "licenses": [ 16 | { 17 | "type": "MIT", 18 | "url": "https://github.com/jwils0n/profanity-filter/LICENSE" 19 | } 20 | ], 21 | "main": "lib/filter", 22 | "engines": { 23 | "node": ">= 0.8.0" 24 | }, 25 | "devDependencies":{ 26 | "mocha":"1.16.2" 27 | }, 28 | "scripts": { 29 | "test": "mocha" 30 | }, 31 | "keywords": ["curse", "words", "string", "bad", "unallowed", "profanity", "filter", "clean"] 32 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 James Wilson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # profanity-filter 2 | 3 | [![Build Status](https://travis-ci.org/jwils0n/profanity-filter.png)](https://travis-ci.org/jwils0n/profanity-filter) 4 | 5 | A static node.js utility for masking words or phrases in strings that aren't allowed. 6 | 7 | ## configuration 8 | 9 | There are three methods of replacement, outlined below ('word' requires you specify a replacement for each word): 10 | 11 | ```javascript 12 | stars - That **** UX change was such a pain in the *** 13 | grawlix - That &!%$ UX change was such a pain in the #@% 14 | word - That darn UX change was such a pain in the badonkadonk 15 | ``` 16 | 17 | Note: 'stars' is the default method of replacement 18 | 19 | ## methods 20 | 21 | ### filter.clean(string) 22 | 23 | Takes supplied string and runs the filter based on the current dictionary of unallowed words and replacement method. Returns the filtered string. 24 | 25 | ```javascript 26 | var filter = require('profanity-filter'); 27 | console.log(filter.clean('String I\'d like to filter for inappropriate words.')); 28 | ``` 29 | 30 | ### filter.seed(name) 31 | 32 | Populates the internal filter dictionary using a seed data JSON file (must live in lib/seeds). 33 | 34 | ```javascript 35 | var filter = require('profanity-filter'); 36 | filter.seed('profanity'); 37 | ``` 38 | 39 | ###filter.debug() 40 | 41 | Returns the dictionary, replacementMethod, and grawlixChars internal properties for debugging purposes. 42 | 43 | ```javascript 44 | var filter = require('profanity-filter'); 45 | filter.debug() 46 | ``` 47 | 48 | ### filter.setReplacementMethod(string) 49 | 50 | Globally sets the method of replacement. Accepts 'stars', 'word', and 'grawlix'. 51 | 52 | ```javascript 53 | var filter = require('profanity-filter'); 54 | filter.setReplacementMethod('grawlix'); 55 | ``` 56 | 57 | ### filter.setGrawlixChars(array) 58 | 59 | Globally sets the grawlix characters to be used as replacements, if grawlix is the current replacementMethod. 60 | 61 | ```javascript 62 | var filter = require('profanity-filter'); 63 | filter.setGrawlixChars(['1', '2', '3', '4', '5', '6']); 64 | ``` 65 | 66 | ### filter.addWord(string, [string]) 67 | 68 | Adds a word to the internal replacement dictionary. The optional second parameter is used if the replacementMethod is set to 'word'. If the word method is set and no replacement is passed, the filter will default to 'BLEEP'. 69 | 70 | ```javascript 71 | var filter = require('profanity-filter'); 72 | filter.addWord('ass', 'badonkadonk'); 73 | ``` 74 | 75 | ### filter.removeWord(string) 76 | 77 | Removes a word from the internal replacement dicitonary. 78 | 79 | ```javascript 80 | var filter = require('profanity-filter'); 81 | filter.removeWord('ass'); 82 | ``` 83 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var filter = require('../lib/filter.js'); 5 | 6 | var fixtures = { 7 | string: 'That damn UX change was such a pain in the ass.', 8 | seed: { 9 | 'damn': 'dad-gum', 10 | 'ass': 'badonkadonk' 11 | } 12 | }; 13 | 14 | describe('clean', function () { 15 | 16 | filter.addWord('damn', 'dad-gum'); 17 | filter.addWord('ass', 'badonkadonk'); 18 | 19 | it('should replace the unallowed words', function () { 20 | var filteredString = filter.clean(fixtures.string); 21 | 22 | assert.equal(filteredString.indexOf('damn'), -1); 23 | assert.equal(filteredString.indexOf('ass'), -1); 24 | }); 25 | 26 | it('should replace the correct number of characters for grawlix/stars methods', function () { 27 | filter.setReplacementMethod('stars'); 28 | assert.equal(filter.clean(fixtures.string).length, fixtures.string.length); 29 | 30 | filter.setReplacementMethod('grawlix'); 31 | assert.equal(filter.clean(fixtures.string).length, fixtures.string.length); 32 | }); 33 | 34 | it('should replace the unallowed words with the replacement for "word" method', function () { 35 | filter.setReplacementMethod('word'); 36 | 37 | var filteredString = filter.clean(fixtures.string); 38 | 39 | assert.notEqual(filteredString.indexOf('dad-gum'), -1); 40 | assert.notEqual(filteredString.indexOf('badonkadonk'), -1); 41 | }); 42 | }); 43 | 44 | describe('config', function () { 45 | 46 | beforeEach(function () { 47 | filter.removeWord('damn'); 48 | filter.removeWord('ass'); 49 | filter.setReplacementMethod('stars'); 50 | filter.setGrawlixChars(['!','@','#','$','%','&','*']); 51 | }); 52 | 53 | it('should be able to add words to the unallowed list', function () { 54 | assert.notEqual(filter.clean(fixtures.string).indexOf('damn'), -1); 55 | 56 | filter.addWord('damn'); 57 | 58 | assert.equal(filter.clean(fixtures.string).indexOf('damn'), -1); 59 | }); 60 | 61 | it('should be able to remove words from the unallowed list', function () { 62 | filter.addWord('damn'); 63 | assert.equal(filter.clean(fixtures.string).indexOf('damn'), -1); 64 | 65 | filter.removeWord('damn'); 66 | assert.notEqual(filter.clean(fixtures.string).indexOf('damn'), -1); 67 | }); 68 | 69 | it('should be able to change the replacement method', function () { 70 | var filteredStrings = {}; 71 | 72 | filter.addWord('damn', 'dad-gum'); 73 | 74 | filter.setReplacementMethod('stars'); 75 | filteredStrings.stars = filter.clean(fixtures.string); 76 | 77 | filter.setReplacementMethod('grawlix'); 78 | filteredStrings.grawlix = filter.clean(fixtures.string); 79 | 80 | filter.setReplacementMethod('word'); 81 | filteredStrings.word = filter.clean(fixtures.string); 82 | 83 | assert.notEqual(filteredStrings.stars, filteredStrings.grawlix); 84 | assert.notEqual(filteredStrings.grawlix, filteredStrings.word); 85 | assert.notEqual(filteredStrings.word, filteredStrings.stars); 86 | }); 87 | 88 | it('should be able to change the grawlix characters', function () { 89 | filter.addWord('damn'); 90 | filter.setReplacementMethod('grawlix'); 91 | 92 | assert.equal(filter.clean(fixtures.string).indexOf('++++'), -1); 93 | 94 | filter.setGrawlixChars(['+']); 95 | assert.notEqual(filter.clean(fixtures.string).indexOf('++++'), -1); 96 | }); 97 | 98 | }); -------------------------------------------------------------------------------- /lib/filter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var replacementMethod = 'stars'; 4 | var grawlixChars = ['!','@','#','$','%','&','*']; 5 | var dictionary = {}; 6 | 7 | var replacement = { 8 | stars: function (key) { 9 | var keyReplacement = '', i, len; 10 | 11 | for (i = 0, len = key.length; i < len; i++) { 12 | keyReplacement += '*'; 13 | } 14 | 15 | return keyReplacement; 16 | }, 17 | word: function (key) { 18 | return dictionary[key]; 19 | }, 20 | grawlix: function (key) { 21 | var keyReplacement = '', 22 | grawlixLen = grawlixChars.length, 23 | wordLen = key.length, 24 | rand, 25 | i; 26 | 27 | for (i = 0; i < wordLen; i++) { 28 | rand = Math.floor(Math.random() * grawlixLen); 29 | keyReplacement += grawlixChars[rand]; 30 | } 31 | 32 | return keyReplacement; 33 | } 34 | }; 35 | 36 | module.exports = { 37 | 38 | /** 39 | * Clean the supplied string of all words in the internal dictionary 40 | * @method clean 41 | * @param {String} string The phrase to be cleaned 42 | * @return {String} The phrase with all words in the dictionary filtered 43 | */ 44 | clean: function (string) { 45 | var key, keyReplacement, lowerString; 46 | 47 | lowerString = string.toLowerCase(); 48 | // loop through each key in the dictionary and search for matches 49 | // (seems like it'd be faster to indexOf on all keys and run replace on matches, rather than replace all) 50 | for (key in dictionary) { 51 | var index = lowerString.indexOf(key); 52 | if (index !== -1) { 53 | keyReplacement = replacement[replacementMethod](key); 54 | 55 | string = string.substr(0, index) + keyReplacement + string.substr(index + key.length); 56 | lowerString = string.toLowerCase(); 57 | } 58 | } 59 | 60 | return string; 61 | }, 62 | 63 | /** 64 | * Populate the dictionary with words 65 | * @method seed 66 | * @param {Object|String} name Either an object containing all dictionary key/values or the name of a preset seed data file 67 | */ 68 | seed: function (name) { 69 | if (typeof name === 'object') { 70 | dictionary = name; 71 | } else { 72 | try { 73 | dictionary = require('./seeds/' + name); 74 | } catch (err) { 75 | console.warn('Couldn\'t load profanity filter seed file: ' + name, err); 76 | } 77 | } 78 | 79 | return this; 80 | }, 81 | 82 | /** 83 | * Set the method of replacement for the clean() method 84 | * @method setReplacementMethod 85 | * @param {String} method The replacement method (stars, grawlix, word) 86 | */ 87 | setReplacementMethod: function (method) { 88 | if (typeof replacement[method] === 'undefined') { 89 | throw 'Replacement method "' + method + '" not valid.'; 90 | } 91 | replacementMethod = method; 92 | 93 | return this; 94 | }, 95 | 96 | /** 97 | * Set the characters to be used for grawlix filtering 98 | * @setGrawlixChars 99 | * @param {Array} chars An array of strings that will be used at random for grawlix filtering 100 | */ 101 | setGrawlixChars: function (chars) { 102 | grawlixChars = chars; 103 | 104 | return this; 105 | }, 106 | 107 | /** 108 | * Adds a word to the dictionary 109 | * @method addWord 110 | * @param {String} word The word to search for during clean() 111 | * @param {String} [replacement] The string to replace the unallowed word, if the 'word' replacementMethod is being used 112 | */ 113 | addWord: function (word, replacement) { 114 | dictionary[word] = replacement || 'BLEEP'; 115 | 116 | return this; 117 | }, 118 | 119 | /** 120 | * Remove a word from the dictionary 121 | * @method removeWord 122 | * @param {String} word The word to be removed 123 | */ 124 | removeWord: function (word) { 125 | if (dictionary[word]) { 126 | delete dictionary[word]; 127 | } 128 | 129 | return this; 130 | }, 131 | 132 | /** 133 | * Obtain the internal dictionary, replacementMethod, and grawlixChars properties for debugging purposes 134 | * @method debug 135 | * @return {Object} The debugging data 136 | */ 137 | debug: function () { 138 | return { 139 | dictionary: dictionary, 140 | replacementMethod: replacementMethod, 141 | grawlixChars: grawlixChars 142 | }; 143 | } 144 | }; 145 | --------------------------------------------------------------------------------