├── .gitignore ├── LICENSE ├── README.md ├── bower.json ├── demo ├── demo.response.json ├── index.htm ├── jquery.profanityfilter.js ├── mit-license.html ├── styles.css └── swearWords.json ├── jquery.profanityfilter.js ├── jquery.profanityfilter.min.js ├── localResources ├── de.json ├── en.json ├── es.json ├── fr.json ├── it.json ├── nl.json ├── pt.json └── ru.json ├── package.json └── swearWords.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Chase Florell 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## jQuery.profanityFilter plug-in ## 2 | 3 | [demo](https://chaseflorell.github.io/jQuery.ProfanityFilter/demo/) 4 | 5 | The `jQuery.profanityFilter` has the ability to filter out profane words on the client side. 6 | 7 | This was built in order to allow users to "Opt-in" to profanity filtering, and offload all of the work to the client, saving the headache on the server. The `jQuery.profanityFilter` also attempts to use Local Storage on the client machine in order to reduce lookups at the `externalSwears` URL. 8 | 9 | ***Obvious warning:*** *The `swearWord` lists as well as code examples contain material that many will find offensive. (But that's the point!)* 10 | *note: localization support provided by shutterstock's [List of Dirty, Naughty, Obscene, and Otherwise Bad Words](https://github.com/shutterstock/List-of-Dirty-Naughty-Obscene-and-Otherwise-Bad-Words)* 11 | 12 | ### Usage: ### 13 | 14 | --- 15 | 16 | **Option 1:** 17 | 18 | // Filter the word "shit" every time it shows up inside the element "SomeElement" 19 | $('#SomeElement').profanityFilter({ 20 | customSwears: ['shit'] 21 | }); 22 | 23 | **Option 2:** 24 | 25 | // Filter an external array of words on the entire document 26 | $(document).profanityFilter({ 27 | externalSwears: '/Path/To/Json/Swears/' 28 | }); 29 | 30 | **Option 3:** 31 | 32 | // Change the replacement character from an astrisk (*) to a pound sign (#) 33 | $('#SomeElement').profanityFilter({ 34 | externalSwears: '/Path/To/Json/Swears/', 35 | replaceWith: '#' 36 | }); 37 | 38 | **Option 4:** 39 | 40 | // Change the replacement from astrisks (*) to random words 41 | $('#SomeElement').profanityFilter({ 42 | externalSwears: '/Path/To/Json/Swears/', 43 | replaceWith: ['fiddle', 'fun', 'stupendous'] 44 | }); 45 | 46 | **Option 5:** 47 | 48 | // Combine an externl Swear list with a custom list (don't worry, we'll remove duplicates) 49 | $('#SomeElement').profanityFilter({ 50 | customSwears: ['monkeyass'], 51 | externalSwears: '/Path/To/Json/Swears/' 52 | }); 53 | 54 | **Option 6:** 55 | 56 | // Don't filter anything. Useful in conjunction with the profaneText callback (which only 57 | // fires when profanity exists). 58 | // Ex. Check if the element contains profanity and throw an alert. 59 | $('#SomeElement').profanityFilter({ 60 | customSwears: ['shit'], 61 | filter: false, 62 | profaneText: function(data) { 63 | alert('That is vulgar!'); 64 | console.log(data); 65 | } 66 | }); 67 | 68 | 69 | ### Get It: ### 70 | 71 | - bower `> bower install jQuery.ProfanityFilter` 72 | 73 | ### Bug Tracker: ### 74 | 75 | --- 76 | 77 | Find a bug? Please create an issue here on GitHub! 78 | https://github.com/ChaseFlorell/jQuery.profanityFilter/issues 79 | 80 | 81 | ### Dependencies ### 82 | 83 | ---- 84 | 85 | This plugin requires: 86 | 87 | - [jQuery](http://jquery.com/) (tested 1.7.1) 88 | 89 | ### Author: ### 90 | 91 | --- 92 | 93 | Chase Florell 94 | 95 | - http://github.com/ChaseFlorell 96 | - http://twitter.com/ChaseFlorell 97 | 98 | ### Copyright and License ### 99 | 100 | --- 101 | 102 | Copyright (c) 2012 Chase Florell 103 | 104 | Permission is hereby granted, free of charge, to any person obtaining 105 | a copy of this software and associated documentation files (the 106 | "Software"), to deal in the Software without restriction, including 107 | without limitation the rights to use, copy, modify, merge, publish, 108 | distribute, sublicense, and/or sell copies of the Software, and to 109 | permit persons to whom the Software is furnished to do so, subject to 110 | the following conditions: 111 | 112 | The above copyright notice and this permission notice shall be 113 | included in all copies or substantial portions of the Software. 114 | 115 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 116 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 117 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 118 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 119 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 120 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 121 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 122 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jQuery.ProfanityFilter", 3 | "main": "jquery.profanityfilter.js", 4 | "version": "1.0.0", 5 | "homepage": "https://github.com/ChaseFlorell/jQuery.ProfanityFilter", 6 | "authors": [ 7 | "Chase Florell " 8 | ], 9 | "description": "A simple cleint side profanity filter", 10 | "keywords": [ 11 | "profanity", 12 | "jquery" 13 | ], 14 | "license": "MIT", 15 | "ignore": [ 16 | "**/.*", 17 | "node_modules", 18 | "bower_components", 19 | "test", 20 | "tests" 21 | ], 22 | "dependencies": { 23 | "jquery": ">=1.7.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /demo/demo.response.json: -------------------------------------------------------------------------------- 1 | ["shit"] 2 | -------------------------------------------------------------------------------- /demo/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery.profanityFilter Demo 5 | 6 | 7 | 8 | 9 | 10 | 19 | 20 | 21 |
22 |
23 |

jQuery.profanityFilter

24 | 25 |

a simple client side profanity filter

26 |
27 | 28 |
29 |
 30 | // code:
 31 | $('#one').profanityFilter({
 32 |     customSwears: ['ass']
 33 | }) 
34 |
35 |
36 | This container will filter the word "ass" but will not filter "shit" 37 |
38 |
39 | Often when I go to the bathroom, shit comes out of my ass. 40 |
41 | 46 |
47 |
48 | 49 |
50 |
 51 | // code:
 52 | $('#two').profanityFilter({
 53 |     customSwears: ['ass'],
 54 |     externalSwears: 'swearWords.json'
 55 | })
56 |
57 |
58 | This container will filter both the words "shit" and "ass" 59 |
60 |
61 | Often when I go to the bathroom, shit comes out of my ass. 62 |
63 | 69 |
70 |
71 | 72 |
73 |
 74 | // code:
 75 | $('#three').profanityFilter({
 76 |     replaceWith: ['fun', 'stuff'],
 77 |     customSwears: ['ass'],
 78 |     externalSwears: 'swearWords.json'
 79 | })
80 |
81 |
82 | This container will filter both the words "shit" and "ass", but will also change the replacement 83 | character from an asterisk (*) a random word listed in the `replaceWith` array. 84 |
85 |
86 | Often when I go to the bathroom, shit comes out of my ass. 87 |
88 | 95 |
96 |
97 | 98 |
99 |
100 | // code:
101 | $('#four').profanityFilter({
102 |     replaceWith:'#',
103 |     customSwears: ['ass'],
104 |     externalSwears: 'swearWords.json'
105 | })
106 |
107 |
108 | This container will filter both the words "shit" and "ass", but will also change the replacement 109 | character from an asterisk (*) to a pound sign (#) 110 |
111 |
112 | Often when I go to the bathroom, shit comes out of my ass. 113 |
114 | 121 |
122 |
123 |
124 |
125 | // code:
126 | $('#five').profanityFilter({
127 |     customSwears: ['ass'],
128 |     filter: true,
129 |     externalSwears: 'swearWords.json',
130 |     profaneText: function(data) {
131 |         $('#five')
132 |             .css('color', 'blue');
133 |             console.log(data);
134 |     }
135 | });
136 |
137 |
138 | This container won't filter anything, but it will do something clever because 139 | it contains the word "ass" 140 |
141 |
142 | Often when I go to the bathroom, shit comes out of my ass. 143 |
144 | 155 |
156 |
157 | 158 | 159 |
160 |
161 | // code:
162 | $('#six').profanityFilter({
163 |     customSwears: ['ass', 'shit'],
164 |     filter: false,
165 |     profaneText: function (data) {
166 |         data.forEach(function (element, index) {
167 |             var str = '' + element + '';
168 |             $('#six').html($("#six").html().replace(element, str));
169 |         });
170 |     }
171 | });
172 |
173 |
174 | This container won't filter anything, but it will do something clever with the swears that it finds. 175 |
176 |
177 | Often when I go to the bathroom, shit comes out of my ass. 178 |
179 | 192 |
193 |
194 | 195 | 196 |
197 |

Like what you see?
198 | Download: on GitHub
199 | Bower Install: bower install jQuery.profanityFilter

200 | 201 |

Find a bug?
202 | Issues: Issue tracker

203 | 204 |

Who's the author?
205 | GitHub: Chase Florell
206 | Twitter: @ChaseFlorell

207 |
208 | 209 |
210 | Fork me on GitHub 211 |
212 | 213 |
214 | 215 | -------------------------------------------------------------------------------- /demo/jquery.profanityfilter.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | "use strict"; 3 | /// takes a string and repeats it "n" times. 4 | /// times to repeat the string 5 | /// rep = '*'.repeat(5); // rep = '*****' 6 | String.prototype.repeat = function (num) { 7 | return new Array(num + 1).join(this); 8 | }; 9 | 10 | /// Removes duplicates from concatenated strings 11 | /// Array 12 | Array.prototype.unique = function () { 13 | var a, // array 14 | i, // incremental counter 15 | j; // next incremental counter 16 | 17 | a = this.concat(); 18 | for (i = 0; i < a.length; ++i) { 19 | for (j = i + 1; j < a.length; ++j) { 20 | if (a[i] === a[j]){ 21 | a.splice(j, 1); 22 | } 23 | } 24 | } 25 | 26 | return a; 27 | }; 28 | 29 | /// Default settings for profanityFilter plugin 30 | var defaults = { 31 | replaceWith: '*', 32 | customSwears: null, 33 | externalSwears: null, 34 | filter: true, 35 | profaneText: function () {} 36 | }; 37 | 38 | 39 | /// jQuery plugin used to filter profanity on the attached element 40 | /// user overridden settings 41 | /// text from an element but blots out the swear words 42 | $.fn.profanityFilter = function (settings, callback) { 43 | 44 | var options = $.extend({}, defaults, settings), 45 | localStorageIsEnabled; 46 | 47 | localStorageIsEnabled = function() { 48 | var uid = new Date(), 49 | result; 50 | 51 | try { 52 | localStorage.setItem("uid", uid); 53 | result = localStorage.getItem("uid") === uid; 54 | localStorage.removeItem("uid"); 55 | return result && localStorage; 56 | } catch(e) {} 57 | }(); 58 | 59 | function allTextNodes(parent) { 60 | function getChildNodes(parent) { 61 | var x, 62 | out = []; 63 | 64 | for (x = 0; x < parent.childNodes.length; x += 1) { 65 | out[x] = parent.childNodes[x]; 66 | } 67 | 68 | return out; 69 | } 70 | 71 | var cursor, 72 | closed = [], 73 | open = getChildNodes(parent); 74 | 75 | while (open.length) { 76 | cursor = open.shift(); 77 | if (cursor.nodeType === 1) { 78 | open.unshift.apply(open, getChildNodes(cursor)); 79 | } 80 | if (cursor.nodeType === 3) { 81 | closed.push(cursor); 82 | } 83 | } 84 | 85 | return closed; 86 | } 87 | 88 | function readJsonFromController(file) { 89 | var request = new XMLHttpRequest(); 90 | request.open('GET', file, false); 91 | request.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 92 | request.send(null); 93 | try { 94 | return JSON.parse(request.responseText); 95 | } catch (e) { 96 | return ''; 97 | } 98 | } 99 | 100 | var lastRandomNumber = null; 101 | function generateRandomNumber(max) { 102 | var randomNumber = Math.floor((Math.random()*(max))); 103 | if (lastRandomNumber == null) { 104 | lastRandomNumber = randomNumber; 105 | } else { 106 | if (randomNumber == lastRandomNumber && max !=0) { 107 | randomNumber +=1; 108 | } 109 | } 110 | 111 | if (randomNumber > max) { 112 | //set it back to zero 113 | randomNumber = 0; 114 | } 115 | 116 | lastRandomNumber = randomNumber; 117 | 118 | return randomNumber; 119 | } 120 | 121 | 122 | return this.each(function () { 123 | 124 | var badWords, 125 | i, 126 | nodes = allTextNodes(this), 127 | re, 128 | rep, 129 | x, 130 | inputs = $(this).find(':input'), 131 | profane = false, 132 | data = []; 133 | 134 | if (options.externalSwears !== null) { 135 | if (localStorageIsEnabled) { 136 | if (localStorage.getItem('localSwears') === null) { 137 | // stringify the array so that it can be stored in local storage 138 | localStorage.setItem('localSwears', JSON.stringify(readJsonFromController(options.externalSwears))); 139 | } 140 | badWords = JSON.parse(localStorage.getItem('localSwears')); 141 | } else { 142 | badWords = readJsonFromController(options.externalSwears); 143 | } 144 | if (options.customSwears !== null) { 145 | badWords = badWords.concat(options.customSwears).unique(); 146 | } 147 | } else { 148 | if (options.customSwears !== null) { 149 | badWords = options.customSwears; 150 | } 151 | } 152 | 153 | // GET OUT, there are no Swears set either custom, external OR local. 154 | if (badWords === null) { 155 | return; 156 | } 157 | 158 | // We've got an array of swears, let's proceed with removing them from the element. 159 | for (i = 0; i < badWords.length; i += 1) { 160 | re = new RegExp('\\b' + badWords[i] + '\\b', 'gi'); 161 | 162 | var rand = generateRandomNumber(options.replaceWith.length -1); 163 | 164 | rep = options.replaceWith[rand]; 165 | if (typeof options.replaceWith == 'string') { 166 | rep = options.replaceWith[rand].repeat(badWords[i].length); 167 | } 168 | 169 | // Text nodes 170 | for (x = 0; x < nodes.length; x += 1) { 171 | if (re.test(nodes[x].nodeValue)) { 172 | profane = true; 173 | data.push(badWords[i]); 174 | if (options.filter) { 175 | nodes[x].nodeValue = nodes[x].nodeValue.replace(re, rep); 176 | } 177 | } 178 | } 179 | 180 | // Text input values 181 | for (var x = 0; x < inputs.length; x++) { 182 | if (re.test(inputs[x].value)) { 183 | profane = true; 184 | data.push(badWords[i]); 185 | if (options.filter) { 186 | $(inputs[x]).val(inputs[x].value.replace(re, rep)); 187 | } 188 | } 189 | } 190 | } 191 | 192 | if (profane) { 193 | options.profaneText(data.unique()); 194 | }; 195 | }); 196 | }; 197 | })(jQuery); -------------------------------------------------------------------------------- /demo/mit-license.html: -------------------------------------------------------------------------------- 1 | 2 | MIT License 3 |

Copyright (c) 2012 Chase Florell

4 | 5 |

Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions:

12 | 13 |

The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software.

15 | 16 |

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

-------------------------------------------------------------------------------- /demo/styles.css: -------------------------------------------------------------------------------- 1 | html { 2 | min-height: 100%; 3 | } 4 | 5 | body { 6 | font-family: monospace; 7 | margin: 0, auto; 8 | background: -webkit-linear-gradient(top, rgba(76, 76, 76, 1) 0%, rgba(38, 38, 38, 1) 100%); 9 | background: -moz-linear-gradient(top, rgba(76, 76, 76, 1) 0%, rgba(38, 38, 38, 1) 100%); 10 | background: -o-linear-gradient(top, rgba(76, 76, 76, 1) 0%, rgba(38, 38, 38, 1) 100%); 11 | background: -ms-linear-gradient(top, rgba(76, 76, 76, 1) 0%, rgba(38, 38, 38, 1) 100%); 12 | background: linear-gradient(top, rgba(76, 76, 76, 1) 0%, rgba(38, 38, 38, 1) 100%); 13 | } 14 | 15 | h1 { 16 | font-size: 5em; 17 | color: #ccc; 18 | -moz-text-shadow: 3px 3px 2px #333; 19 | -webkit-text-shadow: 3px 3px 2px #333; 20 | text-shadow: 3px 3px 2px #333; 21 | } 22 | 23 | h2 { 24 | font-size: 2em; 25 | margin-top: -30px; 26 | color: #ccc; 27 | -moz-text-shadow: 3px 3px 2px #333; 28 | -webkit-text-shadow: 3px 3px 2px #333; 29 | text-shadow: 3px 3px 2px #333; 30 | } 31 | 32 | footer { 33 | width: 760px; 34 | margin: 0 auto; 35 | font-size: .9em; 36 | } 37 | 38 | #wrapper { 39 | text-align: center; 40 | } 41 | 42 | .box { 43 | background-color: #ccc; 44 | text-align: left; 45 | font-family: monospace, serif; 46 | color: #444; 47 | border: 1px solid #333; 48 | padding: 10px; 49 | -webkit-box-shadow: 0 0 5px #333; 50 | -moz-box-shadow: 0 0 5px #333; 51 | box-shadow: 0 0 5px #333; 52 | } 53 | 54 | section { 55 | /*background-color: orange;*/ 56 | margin: 30px auto; 57 | width: 760px; 58 | overflow: hidden; 59 | padding: 0; 60 | min-height: 100px; 61 | } 62 | 63 | section.description-example-wrapper { 64 | float: left; 65 | } 66 | 67 | section pre { 68 | float: right; 69 | } 70 | 71 | .description { 72 | width: 400px; 73 | font-family: verdana, sans-serif; 74 | font-size: .9em; 75 | margin-bottom: 10px; 76 | } 77 | 78 | .code { 79 | margin: 0; 80 | width: 300px; 81 | height: 100%; 82 | font-size: .9em; 83 | padding: 10px; 84 | } 85 | 86 | .example { 87 | width: 400px; 88 | font-family: arial, sans-serif; 89 | font-size: .9em; 90 | font-style: italic; 91 | height: 100%; 92 | } 93 | 94 | .github { 95 | position: absolute; 96 | top: 0px; 97 | right: 0px 98 | } 99 | 100 | #githubRibbon{ 101 | height: 149px; 102 | width: 149px; 103 | overflow:hidden; 104 | padding: 0; 105 | margin: 0; 106 | position: absolute; 107 | top:0; 108 | right:0; 109 | } 110 | 111 | #githubRibbon a{ 112 | 113 | display: block; 114 | width: 190px; 115 | font-size: 14px; 116 | font-family: Frutiger, "Frutiger Linotype", Univers, Calibri, "Gill Sans", "Gill Sans MT", 117 | "Myriad Pro", Myriad, "DejaVu Sans Condensed", "Liberation Sans", "Nimbus Sans L", Tahoma, 118 | Geneva, "Helvetica Neue", Helvetica, Arial, sans serif; 119 | background-color: #333; 120 | color: #FFF; 121 | word-spacing: 2px; 122 | text-decoration: none; 123 | padding: 5px 15px 5px 25px; 124 | 125 | position:relative; 126 | left: 20px; 127 | top: -37px; 128 | text-align: center; 129 | 130 | -moz-transform-origin: 0 0 ; 131 | -moz-transform:rotate(45deg); 132 | -moz-box-shadow: 1px 1px 5px 1px #666; 133 | 134 | -webkit-transform-origin: 0 0 ; 135 | -webkit-transform:rotate(45deg); 136 | -webkit-box-shadow: 1px 1px 5px 1px #666; 137 | 138 | -ms-transform-origin: 0 0 ; 139 | -ms-transform:rotate(45deg); 140 | -ms-box-shadow: 1px 1px 5px 1px #666; 141 | 142 | transform-origin: 0 0 ; 143 | transform:rotate(45deg); 144 | box-shadow: 1px 1px 5px 1px #666; 145 | 146 | background-image: linear-gradient(bottom, #000000 3%, #666666 5%, #000000 7%, #000000 93%, #666666 95%, #000000 97%); 147 | background-image: -o-linear-gradient(bottom, #000000 3%, #666666 5%, #000000 7%, #000000 93%, #666666 95%, #000000 97%); 148 | background-image: -moz-linear-gradient(bottom, #000000 3%, #666666 5%, #000000 7%, #000000 93%, #666666 95%, #000000 97%); 149 | background-image: -webkit-linear-gradient(bottom, #000000 3%, #666666 5%, #000000 7%, #000000 93%, #666666 95%, #000000 97%); 150 | background-image: -ms-linear-gradient(bottom, #000000 3%, #666666 5%, #000000 7%, #000000 93%, #666666 95%, #000000 97%); 151 | 152 | background-image: -webkit-gradient( 153 | linear, 154 | left bottom, 155 | left top, 156 | color-stop(0.03, #000000), 157 | color-stop(0.05, #666666), 158 | color-stop(0.07, #000000), 159 | color-stop(0.93, #000000), 160 | color-stop(0.95, #666666), 161 | color-stop(0.97, #000000) 162 | ); 163 | 164 | } 165 | -------------------------------------------------------------------------------- /demo/swearWords.json: -------------------------------------------------------------------------------- 1 | ["shit"] -------------------------------------------------------------------------------- /jquery.profanityfilter.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | "use strict"; 3 | /// takes a string and repeats it "n" times. 4 | /// times to repeat the string 5 | /// rep = '*'.repeat(5); // rep = '*****' 6 | String.prototype.repeat = function (num) { 7 | return new Array(num + 1).join(this); 8 | }; 9 | 10 | /// Removes duplicates from concatenated strings 11 | /// Array 12 | Array.prototype.unique = function () { 13 | var a, // array 14 | i, // incremental counter 15 | j; // next incremental counter 16 | 17 | a = this.concat(); 18 | for (i = 0; i < a.length; ++i) { 19 | for (j = i + 1; j < a.length; ++j) { 20 | if (a[i] === a[j]){ 21 | a.splice(j, 1); 22 | } 23 | } 24 | } 25 | 26 | return a; 27 | }; 28 | 29 | /// Default settings for profanityFilter plugin 30 | var defaults = { 31 | replaceWith: '*', 32 | customSwears: null, 33 | externalSwears: null, 34 | filter: true, 35 | profaneText: function () {} 36 | }; 37 | 38 | 39 | /// jQuery plugin used to filter profanity on the attached element 40 | /// user overridden settings 41 | /// text from an element but blots out the swear words 42 | $.fn.profanityFilter = function (settings, callback) { 43 | 44 | var options = $.extend({}, defaults, settings), 45 | localStorageIsEnabled; 46 | 47 | localStorageIsEnabled = function() { 48 | var uid = new Date(), 49 | result; 50 | 51 | try { 52 | localStorage.setItem("uid", uid); 53 | result = localStorage.getItem("uid") == uid; 54 | localStorage.removeItem("uid"); 55 | return result && localStorage; 56 | } catch(e) {} 57 | }(); 58 | 59 | function allTextNodes(parent) { 60 | function getChildNodes(parent) { 61 | var x, 62 | out = []; 63 | 64 | for (x = 0; x < parent.childNodes.length; x += 1) { 65 | out[x] = parent.childNodes[x]; 66 | } 67 | 68 | return out; 69 | } 70 | 71 | var cursor, 72 | closed = [], 73 | open = getChildNodes(parent); 74 | 75 | while (open.length) { 76 | cursor = open.shift(); 77 | if (cursor.nodeType === 1) { 78 | open.unshift.apply(open, getChildNodes(cursor)); 79 | } 80 | if (cursor.nodeType === 3) { 81 | closed.push(cursor); 82 | } 83 | } 84 | 85 | return closed; 86 | } 87 | 88 | function readJsonFromController(file) { 89 | var request = new XMLHttpRequest(); 90 | request.open('GET', file, false); 91 | request.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 92 | request.send(null); 93 | try { 94 | return JSON.parse(request.responseText); 95 | } catch (e) { 96 | return ''; 97 | } 98 | } 99 | 100 | var lastRandomNumber = null; 101 | function generateRandomNumber(max) { 102 | var randomNumber = Math.floor((Math.random()*(max))); 103 | if (lastRandomNumber == null) { 104 | lastRandomNumber = randomNumber; 105 | } else { 106 | if (randomNumber == lastRandomNumber && max !=0) { 107 | randomNumber +=1; 108 | } 109 | } 110 | 111 | if (randomNumber > max) { 112 | //set it back to zero 113 | randomNumber = 0; 114 | } 115 | 116 | lastRandomNumber = randomNumber; 117 | 118 | return randomNumber; 119 | } 120 | 121 | 122 | return this.each(function () { 123 | 124 | var badWords, 125 | i, 126 | nodes = allTextNodes(this), 127 | re, 128 | rep, 129 | x, 130 | inputs = $(this).find(':input'), 131 | profane = false, 132 | data = [], 133 | localSwearsKey = 'localSwears' + options.externalSwears; 134 | 135 | if (options.externalSwears !== null) { 136 | if (localStorageIsEnabled) { 137 | var badWordsJSON = localStorage.getItem(localSwearsKey); 138 | // check for non-existence or an empty set 139 | if (badWordsJSON === null || badWordsJSON == "\"\"") { 140 | // stringify the array so that it can be stored in local storage 141 | badWordsJSON = JSON.stringify(readJsonFromController(options.externalSwears)); 142 | localStorage.setItem(localSwearsKey, badWordsJSON); 143 | } 144 | badWords = JSON.parse(badWordsJSON); 145 | } else { 146 | badWords = readJsonFromController(options.externalSwears); 147 | } 148 | if (options.customSwears !== null) { 149 | badWords = badWords.concat(options.customSwears).unique(); 150 | } 151 | } else { 152 | if (options.customSwears !== null) { 153 | badWords = options.customSwears; 154 | } 155 | } 156 | 157 | // GET OUT, there are no Swears set either custom, external OR local. 158 | if (badWords === null) { 159 | return; 160 | } 161 | 162 | // We've got an array of swears, let's proceed with removing them from the element. 163 | for (i = 0; i < badWords.length; i += 1) { 164 | re = new RegExp('\\b' + badWords[i] + '\\b', 'gi'); 165 | 166 | var rand = generateRandomNumber(options.replaceWith.length -1); 167 | 168 | rep = options.replaceWith[rand]; 169 | if (typeof options.replaceWith == 'string') { 170 | rep = options.replaceWith[rand].repeat(badWords[i].length); 171 | } 172 | 173 | // Text nodes 174 | for (x = 0; x < nodes.length; x += 1) { 175 | if (re.test(nodes[x].nodeValue)) { 176 | profane = true; 177 | data.push(badWords[i]); 178 | if (options.filter) { 179 | nodes[x].nodeValue = nodes[x].nodeValue.replace(re, rep); 180 | } 181 | } 182 | } 183 | 184 | // Text input values 185 | for (var x = 0; x < inputs.length; x++) { 186 | if (re.test(inputs[x].value)) { 187 | profane = true; 188 | data.push(badWords[i]); 189 | if (options.filter) { 190 | $(inputs[x]).val(inputs[x].value.replace(re, rep)); 191 | } 192 | } 193 | } 194 | } 195 | 196 | if (profane) { 197 | options.profaneText(data.unique()); 198 | }; 199 | }); 200 | }; 201 | })(jQuery); 202 | -------------------------------------------------------------------------------- /jquery.profanityfilter.min.js: -------------------------------------------------------------------------------- 1 | !function(e){"use strict";String.prototype.repeat=function(e){return new Array(e+1).join(this)},Array.prototype.unique=function(){var e,t,r;for(e=this.concat(),t=0;te&&(t=0),i=t,t}var o,u=e.extend({},t,r);o=function(){var e,t=new Date;try{return localStorage.setItem("uid",t),e=localStorage.getItem("uid")===t,localStorage.removeItem("uid"),e&&localStorage}catch(r){}}();var i=null;return this.each(function(){var t,r,i,s,c,p=n(this),f=e(this).find(":input"),h=!1,g=[];if(null!==u.externalSwears?(o?(null===localStorage.getItem("localSwears")&&localStorage.setItem("localSwears",JSON.stringify(a(u.externalSwears))),t=JSON.parse(localStorage.getItem("localSwears"))):t=a(u.externalSwears),null!==u.customSwears&&(t=t.concat(u.customSwears).unique())):null!==u.customSwears&&(t=u.customSwears),null!==t){for(r=0;r