diff --git a/data/version b/data/version
index a6bf9a74..4eb01765 100644
--- a/data/version
+++ b/data/version
@@ -1 +1 @@
-{"version":"0.7.0.2","updatedTime":"2015-10-16T07:11:51.505Z"}
\ No newline at end of file
+{"version":"0.9","updatedTime":"2015-10-16T07:11:51.505Z"}
\ No newline at end of file
diff --git a/node_modules/file.js b/node_modules/file.js
index 9adbb03e..a88677aa 100644
--- a/node_modules/file.js
+++ b/node_modules/file.js
@@ -1,5 +1,6 @@
var db = require('db');
var fs = require('fs');
+var crypto = require('crypto');
var needle = require('needle');
var path = require('path');
var Evt = require('evt');
@@ -133,6 +134,93 @@ var File = {
console.error(e);
}
},
+
+ getFileBase64: function(filePath) {
+ try {
+ // read binary data
+ var bitmap = fs.readFileSync(filePath);
+ if (!bitmap) {
+ return '';
+ }
+ return new Buffer(bitmap).toString('base64');
+ } catch(e) {
+ return '';
+ }
+ },
+
+ getFileBase64AndMd5: function(filePath) {
+ try {
+ // read binary data
+ var bitmap = fs.readFileSync(filePath);
+ if (!bitmap) {
+ return '';
+ }
+ var base64 = new Buffer(bitmap).toString('base64');
+
+ var hash = crypto.createHash('md5');
+ hash.setEncoding('hex');
+ hash.write(bitmap);
+ hash.end();
+ var md5 = hash.read();
+
+ return {base64: base64, md5: md5}
+
+ } catch(e) {
+ return '';
+ }
+ },
+
+ getFileMd5: function (filePath) {
+ try {
+ var bitmap = fs.readFileSync(filePath);
+ if (!bitmap) {
+ return;
+ }
+ var hash = crypto.createHash('md5');
+ hash.setEncoding('hex');
+ hash.write(bitmap);
+ hash.end();
+ return hash.read();
+ } catch(e) {
+ return '';
+ }
+ },
+
+ mines: {
+ "gif": "image/gif",
+ "jpeg": "image/jpeg",
+ "jpg": "image/jpeg",
+ "png": "image/png",
+
+ "css": "text/css",
+ "html": "text/html",
+ "ico": "image/x-icon",
+ "js": "text/javascript",
+ "json": "application/json",
+ "pdf": "application/pdf",
+ "svg": "image/svg+xml",
+ "swf": "application/x-shockwave-flash",
+ "tiff": "image/tiff",
+ "txt": "text/plain",
+ "wav": "audio/x-wav",
+ "wma": "audio/x-ms-wma",
+ "wmv": "video/x-ms-wmv",
+ "xml": "text/xml"
+ },
+
+ getFileType: function (ext) {
+ if (!ext) {
+ return;
+ }
+
+ ext = ext.toLowerCase();
+ var type = this.mines[ext];
+ if (type) {
+ return type;
+ }
+ return ext;
+ },
+
_addAttach: function(filePath, fileTitle, callback) {
try {
var fileStat = fs.statSync(filePath);
@@ -326,6 +414,16 @@ var File = {
});
},
+ getImageInfo: function(fileId, callback) {
+ db.images.findOne({FileId: fileId}, function(err, doc) {
+ if(!err && doc && doc.Path) {
+ callback(true, doc);
+ } else {
+ callback(false, false);
+ }
+ });
+ },
+
// 得到fileIds所有的images, 为了发送到服务器上
getAllImages: function(fileIds, callback) {
var me = this;
@@ -612,4 +710,4 @@ var File = {
}
};
-module.exports = File;
\ No newline at end of file
+module.exports = File;
diff --git a/node_modules/resanitize/LICENSE b/node_modules/resanitize/LICENSE
new file mode 100644
index 00000000..32aa8097
--- /dev/null
+++ b/node_modules/resanitize/LICENSE
@@ -0,0 +1,24 @@
+----------------------------------------------------------------------
+resanitize is released under the MIT License
+Copyright (c) 2012 Dan MacTough - http://yabfog.com
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/resanitize/README.md b/node_modules/resanitize/README.md
new file mode 100644
index 00000000..0308c4d6
--- /dev/null
+++ b/node_modules/resanitize/README.md
@@ -0,0 +1,36 @@
+# Resanitize - Regular expression-based HTML sanitizer and ad remover, geared toward RSS feed descriptions
+
+This node.js module provides functions for removing unsafe parts and ads from
+HTML. I am using it for the <description> element of RSS feeds.
+
+## Installation
+
+npm install resanitize
+
+## Usage
+
+```javascript
+
+ var resanitize = require('resanitize')
+ , html = '
Headline
'
+ ;
+
+ resanitize(html); // => 'Headline
'
+```
+
+## Notes
+
+This module's opinion of "sanitized" might not meet your security requirements.
+The mere fact that it uses regular expressions should make this disclaimer
+unnecessary, but just to be clear: if you intend to display arbitrary user input
+that includes HTML, you're going to want something more robust.
+
+As of v0.3.0, we've added [node-validator's](//github.com/chriso/node-validator) XSS
+filter. It's certainly an improvement, but still -- be careful. Any concerns
+about XSS attacks should be directered to [node-validator's issue tracker](//github.com/chriso/node-validator/issues).
+
+Note that the `stripUnsafeTags` method will loop over the strip an arbitrary
+number of times (2) to try to strip maliciously nested html tags. After the
+maximum number of iterations is reached, if the string still appears to contain
+any unsafe tags, it is deemed unsafe and set to an empty string. If this seems
+unexpected and/or is causing any problems, please raise an [issue](//github.com/danmactough/node-resanitize/issues).
\ No newline at end of file
diff --git a/node_modules/resanitize/node_modules/validator/.npmignore b/node_modules/resanitize/node_modules/validator/.npmignore
new file mode 100644
index 00000000..496ee2ca
--- /dev/null
+++ b/node_modules/resanitize/node_modules/validator/.npmignore
@@ -0,0 +1 @@
+.DS_Store
\ No newline at end of file
diff --git a/node_modules/resanitize/node_modules/validator/LICENSE b/node_modules/resanitize/node_modules/validator/LICENSE
new file mode 100755
index 00000000..c1a825a5
--- /dev/null
+++ b/node_modules/resanitize/node_modules/validator/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2010 Chris O'Hara
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/resanitize/node_modules/validator/README.md b/node_modules/resanitize/node_modules/validator/README.md
new file mode 100755
index 00000000..7dd945bd
--- /dev/null
+++ b/node_modules/resanitize/node_modules/validator/README.md
@@ -0,0 +1,249 @@
+**node-validator is a library of string validation, filtering and sanitization methods.**
+
+To install node-validator, use [npm](http://github.com/isaacs/npm):
+
+```bash
+$ npm install validator
+```
+
+To use the library in the browser, include `validator-min.js`
+
+## Example
+
+```javascript
+var check = require('validator').check,
+ sanitize = require('validator').sanitize
+
+//Validate
+check('test@email.com').len(6, 64).isEmail(); //Methods are chainable
+check('abc').isInt(); //Throws 'Invalid integer'
+check('abc', 'Please enter a number').isInt(); //Throws 'Please enter a number'
+check('abcdefghijklmnopzrtsuvqxyz').is(/^[a-z]+$/);
+
+//Set a message per validator
+check('foo', {
+ isNumeric: 'This is not a number',
+ contains: 'The value doesn\'t have a 0 in it'
+}).isNumeric().contains('0');
+
+//Referencing validator args from the message
+check('foo', 'The message needs to be between %1 and %2 characters long (you passed "%0")').len(2, 6);
+
+//Sanitize / Filter
+var int = sanitize('0123').toInt(); //123
+var bool = sanitize('true').toBoolean(); //true
+var str = sanitize(' \t\r hello \n').trim(); //'hello'
+var str = sanitize('aaaaaaaaab').ltrim('a'); //'b'
+var str = sanitize(large_input_str).xss();
+var str = sanitize('<a>').entityDecode(); //''
+```
+
+## Web development
+
+Often it's more desirable to check or automatically sanitize parameters by name (rather than the actual string). See [this gist](https://gist.github.com/752126) for instructions on binding the library to the `request` prototype.
+
+If you are using the [express.js framework](https://github.com/visionmedia/express) you can use the [express-validator middleware](https://github.com/ctavan/express-validator) to seamlessly integrate node-validator.
+
+Example `http://localhost:8080/?zip=12345&foo=1&textarea=large_string`
+
+```javascript
+get('/', function (req, res) {
+ req.onValidationError(function (msg) {
+ //Redirect the user with error 'msg'
+ });
+
+ //Validate user input
+ req.check('zip', 'Please enter a valid ZIP code').len(4,5).isInt();
+ req.check('email', 'Please enter a valid email').len(6,64).isEmail();
+ req.checkHeader('referer').contains('localhost');
+
+ //Sanitize user input
+ req.sanitize('textarea').xss();
+ req.sanitize('foo').toBoolean();
+
+ //etc.
+});
+```
+
+## An important note
+
+This library validates **strings** only. If you pass something that's not a string as input it will be coerced to a string using the following rules:
+
+- Is it an object with a `toString` property? Call `input.toString()`
+- Is it `null`, `undefined`, or `NaN`? Replace with an empty string
+- All other input? Coerce to a string using `'' + input`
+
+## List of validation methods
+
+```javascript
+is() //Alias for regex()
+not() //Alias for notRegex()
+isEmail()
+isUrl() //Accepts http, https, ftp
+isIP() //Combines isIPv4 and isIPv6
+isIPv4()
+isIPv6()
+isAlpha()
+isAlphanumeric()
+isNumeric()
+isHexadecimal()
+isHexColor() //Accepts valid hexcolors with or without # prefix
+isInt() //isNumeric accepts zero padded numbers, e.g. '001', isInt doesn't
+isLowercase()
+isUppercase()
+isDecimal()
+isFloat() //Alias for isDecimal
+notNull() //Check if length is 0
+isNull()
+notEmpty() //Not just whitespace (input.trim().length !== 0)
+equals(equals)
+contains(str)
+notContains(str)
+regex(pattern, modifiers) //Usage: regex(/[a-z]/i) or regex('[a-z]','i')
+notRegex(pattern, modifiers)
+len(min, max) //max is optional
+isUUID(version) //Version can be 3, 4 or 5 or empty, see http://en.wikipedia.org/wiki/Universally_unique_identifier
+isUUIDv3() //Alias for isUUID(3)
+isUUIDv4() //Alias for isUUID(4)
+isUUIDv5() //Alias for isUUID(5)
+isDate() //Uses Date.parse() - regex is probably a better choice
+isAfter(date) //Argument is optional and defaults to today. Comparison is non-inclusive
+isBefore(date) //Argument is optional and defaults to today. Comparison is non-inclusive
+isIn(options) //Accepts an array or string
+notIn(options)
+max(val)
+min(val)
+isCreditCard() //Will work against Visa, MasterCard, American Express, Discover, Diners Club, and JCB card numbering formats
+```
+
+## List of sanitization / filter methods
+
+```javascript
+trim(chars) //Trim optional `chars`, default is to trim whitespace (\r\n\t )
+ltrim(chars)
+rtrim(chars)
+ifNull(replace)
+toFloat()
+toInt()
+toBoolean() //True unless str = '0', 'false', or str.length == 0
+toBooleanStrict() //False unless str = '1' or 'true'
+entityDecode() //Decode HTML entities
+entityEncode()
+escape() //Escape &, <, >, and "
+xss() //Remove common XSS attack vectors from user-supplied HTML
+xss(true) //Remove common XSS attack vectors from images
+```
+
+## Extending the library
+
+When adding to the Validator prototype, use `this.str` to access the string and `this.error(this.msg || default_msg)` when the string is invalid
+
+```javascript
+var Validator = require('validator').Validator;
+Validator.prototype.contains = function(str) {
+ if (!~this.str.indexOf(str)) {
+ this.error(this.msg || this.str + ' does not contain ' + str);
+ }
+ return this; //Allow method chaining
+}
+```
+
+When adding to the Filter (sanitize) prototype, use `this.str` to access the string and `this.modify(new_str)` to update it
+
+```javascript
+var Filter = require('validator').Filter;
+Filter.prototype.removeNumbers = function() {
+ this.modify(this.str.replace(/[0-9]+/g, ''));
+ return this.str;
+}
+```
+
+## Error handling
+
+By default, the validation methods throw an exception when a check fails
+
+```javascript
+try {
+ check('abc').notNull().isInt()
+} catch (e) {
+ console.log(e.message); //Invalid integer
+}
+```
+
+To set a custom error message, set the second param of `check()`
+
+```javascript
+try {
+ check('abc', 'Please enter a valid integer').notNull().isInt()
+} catch (e) {
+ console.log(e.message); //Please enter a valid integer
+}
+```
+
+To attach a custom error handler, set the `error` method of the validator instance
+
+```javascript
+var Validator = require('validator').Validator;
+var v = new Validator();
+v.error = function(msg) {
+ console.log('Fail');
+}
+v.check('abc').isInt(); //'Fail'
+```
+
+You might want to collect errors instead of throwing each time
+
+```javascript
+Validator.prototype.error = function (msg) {
+ this._errors.push(msg);
+ return this;
+}
+
+Validator.prototype.getErrors = function () {
+ return this._errors;
+}
+
+var validator = new Validator();
+
+validator.check('abc').isEmail();
+validator.check('hello').len(10,30);
+
+var errors = validator.getErrors(); // ['Invalid email', 'String is too small']
+```
+
+## Contributors
+
+- [zero21xxx](https://github.com/zero21xxx) - Added per check messages
+- [PING](https://github.com/PlNG) - Fixed entity encoding
+- [Dan VerWeire](https://github.com/wankdanker) - Modified the behaviour of the error handler
+- [ctavan](https://github.com/ctavan) - Added isArray (since removed) and isUUID
+- [foxbunny](https://github.com/foxbunny) - Added min(), max(), isAfter(), isBefore(), and improved isDate()
+- [oris](https://github.com/orls) - Added in()
+- [mren](https://github.com/mren) - Decoupled rules
+- [Thorsten Basse](https://github.com/tbasse) - Cleanup and refinement of existing validators
+- [Neal Poole](https://github.com/nealpoole) - Port the latest xss() updates from CodeIgniter
+
+## LICENSE
+
+(MIT License)
+
+Copyright (c) 2010 Chris O'Hara
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/resanitize/node_modules/validator/index.html b/node_modules/resanitize/node_modules/validator/index.html
new file mode 100644
index 00000000..62944998
--- /dev/null
+++ b/node_modules/resanitize/node_modules/validator/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
diff --git a/node_modules/resanitize/node_modules/validator/index.js b/node_modules/resanitize/node_modules/validator/index.js
new file mode 100755
index 00000000..0f485b93
--- /dev/null
+++ b/node_modules/resanitize/node_modules/validator/index.js
@@ -0,0 +1 @@
+exports = module.exports = require('./lib');
diff --git a/node_modules/resanitize/node_modules/validator/lib/defaultError.js b/node_modules/resanitize/node_modules/validator/lib/defaultError.js
new file mode 100644
index 00000000..2980ca89
--- /dev/null
+++ b/node_modules/resanitize/node_modules/validator/lib/defaultError.js
@@ -0,0 +1,37 @@
+var defaultError = module.exports = {
+ isEmail: 'Invalid email',
+ isUrl: 'Invalid URL',
+ isIP: 'Invalid IP',
+ isAlpha: 'Invalid characters',
+ isAlphanumeric: 'Invalid characters',
+ isHexadecimal: 'Invalid hexadecimal',
+ isHexColor: 'Invalid hexcolor',
+ isNumeric: 'Invalid number',
+ isLowercase: 'Invalid characters',
+ isUppercase: 'Invalid characters',
+ isInt: 'Invalid integer',
+ isDecimal: 'Invalid decimal',
+ isDivisibleBy: 'Not divisible',
+ notNull: 'String is empty',
+ isNull: 'String is not empty',
+ notEmpty: 'String is empty',
+ equals: 'Not equal',
+ contains: 'Invalid characters',
+ notContains: 'Invalid characters',
+ regex: 'Invalid characters',
+ notRegex: 'Invalid characters',
+ len: 'String is not in range',
+ isUUID: 'Not a UUID',
+ isUUIDv3: 'Not a UUID v3',
+ isUUIDv4: 'Not a UUID v4',
+ isUUIDv5: 'Not a UUID v5',
+ isDate: 'Not a date',
+ isAfter: 'Invalid date',
+ isBefore: 'Invalid date',
+ isIn: 'Unexpected value or invalid argument',
+ notIn: 'Unexpected value or invalid argument',
+ min: 'Invalid number',
+ max: 'Invalid number',
+ isCreditCard: 'Invalid credit card'
+};
+
diff --git a/node_modules/resanitize/node_modules/validator/lib/entities.js b/node_modules/resanitize/node_modules/validator/lib/entities.js
new file mode 100755
index 00000000..ddfcf928
--- /dev/null
+++ b/node_modules/resanitize/node_modules/validator/lib/entities.js
@@ -0,0 +1,290 @@
+var entities = {
+ ' ': '\u00a0',
+ '¡': '\u00a1',
+ '¢': '\u00a2',
+ '£': '\u00a3',
+ '€': '\u20ac',
+ '¥': '\u00a5',
+ '¦': '\u0160',
+ '§': '\u00a7',
+ '¨': '\u0161',
+ '©': '\u00a9',
+ 'ª': '\u00aa',
+ '«': '\u00ab',
+ '¬': '\u00ac',
+ '': '\u00ad',
+ '®': '\u00ae',
+ '¯': '\u00af',
+ '°': '\u00b0',
+ '±': '\u00b1',
+ '²': '\u00b2',
+ '³': '\u00b3',
+ '´': '\u017d',
+ 'µ': '\u00b5',
+ '¶': '\u00b6',
+ '·': '\u00b7',
+ '¸': '\u017e',
+ '¹': '\u00b9',
+ 'º': '\u00ba',
+ '»': '\u00bb',
+ '¼': '\u0152',
+ '½': '\u00bd',
+ '¾': '\u0178',
+ '¿': '\u00bf',
+ 'À': '\u00c0',
+ 'Á': '\u00c1',
+ 'Â': '\u00c2',
+ 'Ã': '\u00c3',
+ 'Ä': '\u00c4',
+ 'Å': '\u00c5',
+ 'Æ': '\u00c6',
+ 'Ç': '\u00c7',
+ 'È': '\u00c8',
+ 'É': '\u00c9',
+ 'Ê': '\u00ca',
+ 'Ë': '\u00cb',
+ 'Ì': '\u00cc',
+ 'Í': '\u00cd',
+ 'Î': '\u00ce',
+ 'Ï': '\u00cf',
+ 'Ð': '\u00d0',
+ 'Ñ': '\u00d1',
+ 'Ò': '\u00d2',
+ 'Ó': '\u00d3',
+ 'Ô': '\u00d4',
+ 'Õ': '\u00d5',
+ 'Ö': '\u00d6',
+ '×': '\u00d7',
+ 'Ø': '\u00d8',
+ 'Ù': '\u00d9',
+ 'Ú': '\u00da',
+ 'Û': '\u00db',
+ 'Ü': '\u00dc',
+ 'Ý': '\u00dd',
+ 'Þ': '\u00de',
+ 'ß': '\u00df',
+ 'à': '\u00e0',
+ 'á': '\u00e1',
+ 'â': '\u00e2',
+ 'ã': '\u00e3',
+ 'ä': '\u00e4',
+ 'å': '\u00e5',
+ 'æ': '\u00e6',
+ 'ç': '\u00e7',
+ 'è': '\u00e8',
+ 'é': '\u00e9',
+ 'ê': '\u00ea',
+ 'ë': '\u00eb',
+ 'ì': '\u00ec',
+ 'í': '\u00ed',
+ 'î': '\u00ee',
+ 'ï': '\u00ef',
+ 'ð': '\u00f0',
+ 'ñ': '\u00f1',
+ 'ò': '\u00f2',
+ 'ó': '\u00f3',
+ 'ô': '\u00f4',
+ 'õ': '\u00f5',
+ 'ö': '\u00f6',
+ '÷': '\u00f7',
+ 'ø': '\u00f8',
+ 'ù': '\u00f9',
+ 'ú': '\u00fa',
+ 'û': '\u00fb',
+ 'ü': '\u00fc',
+ 'ý': '\u00fd',
+ 'þ': '\u00fe',
+ 'ÿ': '\u00ff',
+ '"': '\u0022',
+ '<': '\u003c',
+ '>': '\u003e',
+ ''': '\u0027',
+ '−': '\u2212',
+ 'ˆ': '\u02c6',
+ '˜': '\u02dc',
+ 'Š': '\u0160',
+ '‹': '\u2039',
+ 'Œ': '\u0152',
+ '‘': '\u2018',
+ '’': '\u2019',
+ '“': '\u201c',
+ '”': '\u201d',
+ '•': '\u2022',
+ '–': '\u2013',
+ '—': '\u2014',
+ '™': '\u2122',
+ 'š': '\u0161',
+ '›': '\u203a',
+ 'œ': '\u0153',
+ 'Ÿ': '\u0178',
+ 'ƒ': '\u0192',
+ 'Α': '\u0391',
+ 'Β': '\u0392',
+ 'Γ': '\u0393',
+ 'Δ': '\u0394',
+ 'Ε': '\u0395',
+ 'Ζ': '\u0396',
+ 'Η': '\u0397',
+ 'Θ': '\u0398',
+ 'Ι': '\u0399',
+ 'Κ': '\u039a',
+ 'Λ': '\u039b',
+ 'Μ': '\u039c',
+ 'Ν': '\u039d',
+ 'Ξ': '\u039e',
+ 'Ο': '\u039f',
+ 'Π': '\u03a0',
+ 'Ρ': '\u03a1',
+ 'Σ': '\u03a3',
+ 'Τ': '\u03a4',
+ 'Υ': '\u03a5',
+ 'Φ': '\u03a6',
+ 'Χ': '\u03a7',
+ 'Ψ': '\u03a8',
+ 'Ω': '\u03a9',
+ 'α': '\u03b1',
+ 'β': '\u03b2',
+ 'γ': '\u03b3',
+ 'δ': '\u03b4',
+ 'ε': '\u03b5',
+ 'ζ': '\u03b6',
+ 'η': '\u03b7',
+ 'θ': '\u03b8',
+ 'ι': '\u03b9',
+ 'κ': '\u03ba',
+ 'λ': '\u03bb',
+ 'μ': '\u03bc',
+ 'ν': '\u03bd',
+ 'ξ': '\u03be',
+ 'ο': '\u03bf',
+ 'π': '\u03c0',
+ 'ρ': '\u03c1',
+ 'ς': '\u03c2',
+ 'σ': '\u03c3',
+ 'τ': '\u03c4',
+ 'υ': '\u03c5',
+ 'φ': '\u03c6',
+ 'χ': '\u03c7',
+ 'ψ': '\u03c8',
+ 'ω': '\u03c9',
+ 'ϑ': '\u03d1',
+ 'ϒ': '\u03d2',
+ 'ϖ': '\u03d6',
+ ' ': '\u2002',
+ ' ': '\u2003',
+ ' ': '\u2009',
+ '': '\u200c',
+ '': '\u200d',
+ '': '\u200e',
+ '': '\u200f',
+ '‚': '\u201a',
+ '„': '\u201e',
+ '†': '\u2020',
+ '‡': '\u2021',
+ '…': '\u2026',
+ '‰': '\u2030',
+ '′': '\u2032',
+ '″': '\u2033',
+ '‾': '\u203e',
+ '⁄': '\u2044',
+ 'ℑ': '\u2111',
+ '℘': '\u2118',
+ 'ℜ': '\u211c',
+ 'ℵ': '\u2135',
+ '←': '\u2190',
+ '↑': '\u2191',
+ '→': '\u2192',
+ '↓': '\u2193',
+ '↔': '\u2194',
+ '↵': '\u21b5',
+ '⇐': '\u21d0',
+ '⇑': '\u21d1',
+ '⇒': '\u21d2',
+ '⇓': '\u21d3',
+ '⇔': '\u21d4',
+ '∀': '\u2200',
+ '∂': '\u2202',
+ '∃': '\u2203',
+ '∅': '\u2205',
+ '∇': '\u2207',
+ '∈': '\u2208',
+ '∉': '\u2209',
+ '∋': '\u220b',
+ '∏': '\u220f',
+ '∑': '\u2211',
+ '∗': '\u2217',
+ '√': '\u221a',
+ '∝': '\u221d',
+ '∞': '\u221e',
+ '∠': '\u2220',
+ '∧': '\u2227',
+ '∨': '\u2228',
+ '∩': '\u2229',
+ '∪': '\u222a',
+ '∫': '\u222b',
+ '∴': '\u2234',
+ '∼': '\u223c',
+ '≅': '\u2245',
+ '≈': '\u2248',
+ '≠': '\u2260',
+ '≡': '\u2261',
+ '≤': '\u2264',
+ '≥': '\u2265',
+ '⊂': '\u2282',
+ '⊃': '\u2283',
+ '⊄': '\u2284',
+ '⊆': '\u2286',
+ '⊇': '\u2287',
+ '⊕': '\u2295',
+ '⊗': '\u2297',
+ '⊥': '\u22a5',
+ '⋅': '\u22c5',
+ '⌈': '\u2308',
+ '⌉': '\u2309',
+ '⌊': '\u230a',
+ '⌋': '\u230b',
+ '〈': '\u2329',
+ '〉': '\u232a',
+ '◊': '\u25ca',
+ '♠': '\u2660',
+ '♣': '\u2663',
+ '♥': '\u2665',
+ '♦': '\u2666'
+};
+
+exports.decode = function (str) {
+ if (!~str.indexOf('&')) return str;
+
+ //Decode literal entities
+ for (var i in entities) {
+ str = str.replace(new RegExp(i, 'g'), entities[i]);
+ }
+
+ //Decode hex entities
+ str = str.replace(/(0*[0-9a-f]{2,5});?/gi, function (m, code) {
+ return String.fromCharCode(parseInt(+code, 16));
+ });
+
+ //Decode numeric entities
+ str = str.replace(/([0-9]{2,4});?/gi, function (m, code) {
+ return String.fromCharCode(+code);
+ });
+
+ str = str.replace(/&/g, '&');
+
+ return str;
+}
+
+exports.encode = function (str) {
+ str = str.replace(/&/g, '&');
+
+ //IE doesn't accept '
+ str = str.replace(/'/g, ''');
+
+ //Encode literal entities
+ for (var i in entities) {
+ str = str.replace(new RegExp(entities[i], 'g'), i);
+ }
+
+ return str;
+}
diff --git a/node_modules/resanitize/node_modules/validator/lib/filter.js b/node_modules/resanitize/node_modules/validator/lib/filter.js
new file mode 100755
index 00000000..f828c33e
--- /dev/null
+++ b/node_modules/resanitize/node_modules/validator/lib/filter.js
@@ -0,0 +1,109 @@
+var entities = require('./entities');
+var xss = require('./xss');
+
+var Filter = exports.Filter = function() {}
+
+var whitespace = '\\r\\n\\t\\s';
+
+Filter.prototype.modify = function(str) {
+ this.str = str;
+}
+
+Filter.prototype.wrap = function (str) {
+ return str;
+}
+
+Filter.prototype.value = function () {
+ return this.str;
+}
+
+Filter.prototype.chain = function () {
+ this.wrap = function () { return this };
+ return this;
+}
+
+//Create some aliases - may help code readability
+Filter.prototype.convert = Filter.prototype.sanitize = function(str) {
+ this.str = str == null ? '' : str + '';
+ return this;
+}
+
+Filter.prototype.xss = function(is_image) {
+ this.modify(xss.clean(this.str, is_image));
+ return this.wrap(this.str);
+}
+
+Filter.prototype.entityDecode = function() {
+ this.modify(entities.decode(this.str));
+ return this.wrap(this.str);
+}
+
+Filter.prototype.entityEncode = function() {
+ this.modify(entities.encode(this.str));
+ return this.wrap(this.str);
+}
+
+Filter.prototype.ltrim = function(chars) {
+ chars = chars || whitespace;
+ this.modify(this.str.replace(new RegExp('^['+chars+']+', 'g'), ''));
+ return this.wrap(this.str);
+}
+
+Filter.prototype.rtrim = function(chars) {
+ chars = chars || whitespace;
+ this.modify(this.str.replace(new RegExp('['+chars+']+$', 'g'), ''));
+ return this.wrap(this.str);
+}
+
+Filter.prototype.trim = function(chars) {
+ chars = chars || whitespace;
+ this.modify(this.str.replace(new RegExp('^['+chars+']+|['+chars+']+$', 'g'), ''));
+ return this.wrap(this.str);
+}
+
+Filter.prototype.escape = function() {
+ this.modify(this.str.replace(/&/g, '&')
+ .replace(/"/g, '"')
+ .replace(//g, '>'));
+ return this.wrap(this.str);
+};
+
+Filter.prototype.ifNull = function(replace) {
+ if (!this.str || this.str === '') {
+ this.modify(replace);
+ }
+ return this.wrap(this.str);
+}
+
+Filter.prototype.toFloat = function() {
+ this.modify(parseFloat(this.str));
+ return this.wrap(this.str);
+}
+
+Filter.prototype.toInt = function(radix) {
+ this.modify(parseInt(this.str, radix || 10));
+ return this.wrap(this.str);
+}
+
+//Any strings with length > 0 (except for '0' and 'false') are considered true,
+//all other strings are false
+Filter.prototype.toBoolean = function() {
+ if (!this.str || this.str == '0' || this.str == 'false' || this.str == '') {
+ this.modify(false);
+ } else {
+ this.modify(true);
+ }
+ return this.wrap(this.str);
+}
+
+//String must be equal to '1' or 'true' to be considered true, all other strings
+//are false
+Filter.prototype.toBooleanStrict = function() {
+ if (this.str == '1' || this.str == 'true') {
+ this.modify(true);
+ } else {
+ this.modify(false);
+ }
+ return this.wrap(this.str);
+}
diff --git a/node_modules/resanitize/node_modules/validator/lib/index.js b/node_modules/resanitize/node_modules/validator/lib/index.js
new file mode 100755
index 00000000..881e948a
--- /dev/null
+++ b/node_modules/resanitize/node_modules/validator/lib/index.js
@@ -0,0 +1,20 @@
+var node_validator = require('./validator');
+
+exports.Validator = node_validator.Validator;
+exports.ValidatorError = node_validator.ValidatorError;
+exports.Filter = require('./filter').Filter;
+exports.validators = require('./validators');
+exports.defaultError = require('./defaultError');
+
+exports.entities = require('./entities');
+
+//Quick access methods
+exports.sanitize = exports.convert = function(str) {
+ var filter = new exports.Filter();
+ return filter.sanitize(str);
+}
+
+exports.check = exports.validate = exports.assert = function(str, fail_msg) {
+ var validator = new exports.Validator();
+ return validator.check(str, fail_msg);
+}
diff --git a/node_modules/resanitize/node_modules/validator/lib/validator.js b/node_modules/resanitize/node_modules/validator/lib/validator.js
new file mode 100644
index 00000000..fc6ed645
--- /dev/null
+++ b/node_modules/resanitize/node_modules/validator/lib/validator.js
@@ -0,0 +1,76 @@
+var util = require('util');
+var validators = require('./validators');
+exports.defaultError = require('./defaultError');
+
+var ValidatorError = exports.ValidatorError = function(msg) {
+ Error.captureStackTrace(this, this);
+ this.name = 'ValidatorError';
+ this.message = msg;
+};
+util.inherits(ValidatorError, Error);
+
+var Validator = exports.Validator = function(options) {
+ options = options || {};
+ this.options = options;
+
+ this.options.messageBuilder = this.options.messageBuilder || function (msg, args) {
+ if (typeof msg === 'string') {
+ args.forEach(function(arg, i) { msg = msg.replace('%'+i, arg); });
+ }
+ return msg;
+ };
+};
+
+Validator.prototype.error = function (msg) {
+ throw new ValidatorError(msg);
+};
+
+Validator.prototype.check = function(str, fail_msg) {
+ if (typeof str === 'object' && str !== null && str.toString) {
+ str = str.toString();
+ }
+ this.str = (str == null || (isNaN(str) && str.length == undefined)) ? '' : str;
+ this.str += '';
+
+ // This is a key, value pair of error messages to use
+ if (typeof fail_msg === 'object') {
+ this.errorDictionary = fail_msg;
+ this.msg = null
+ }
+ else {
+ this.errorDictionary = {};
+ this.msg = fail_msg;
+ }
+
+ this._errors = this._errors || [];
+ return this;
+}
+
+for (var key in validators) {
+ if (validators.hasOwnProperty(key)) {
+ (function (key) {
+ Validator.prototype[key] = function() {
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift(this.str);
+ if(!validators[key].apply(this, args)) {
+ var msg = exports.defaultError[key];
+ if (key in this.errorDictionary) {
+ msg = this.errorDictionary[key];
+ } else if (this.msg !== null && typeof this.msg !== 'undefined') {
+ msg = this.msg;
+ }
+ msg = this.options.messageBuilder(msg, args);
+ return this.error(msg);
+ }
+ return this;
+ };
+ })(key);
+ }
+}
+
+//Create some aliases - may help code readability
+Validator.prototype.validate = Validator.prototype.check;
+Validator.prototype.assert = Validator.prototype.check;
+Validator.prototype.isFloat = Validator.prototype.isDecimal;
+Validator.prototype.is = Validator.prototype.regex;
+Validator.prototype.not = Validator.prototype.notRegex;
diff --git a/node_modules/resanitize/node_modules/validator/lib/validators.js b/node_modules/resanitize/node_modules/validator/lib/validators.js
new file mode 100644
index 00000000..7b66e195
--- /dev/null
+++ b/node_modules/resanitize/node_modules/validator/lib/validators.js
@@ -0,0 +1,240 @@
+
+// Helper function to avoid duplication of code
+function toDateTime(date) {
+ if (date instanceof Date) {
+ return date;
+ }
+ var intDate = Date.parse(date);
+ if (isNaN(intDate)) {
+ return null;
+ }
+ return new Date(intDate);
+}
+
+// Convert to date without the time component
+function toDate(date) {
+ if (!(date instanceof Date)) {
+ date = toDateTime(date);
+ }
+ if (!date) {
+ return null;
+ }
+ return date;
+}
+
+var validators = module.exports = {
+ isEmail: function(str) {
+ return str.match(/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/);
+ },
+ isUrl: function(str) {
+ //A modified version of the validator from @diegoperini / https://gist.github.com/729294
+ return str.length < 2083 && str.match(/^(?!mailto:)(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))|localhost)(?::\d{2,5})?(?:\/[^\s]*)?$/i);
+ },
+ //node-js-core
+ isIP : function(str) {
+ if (validators.isIPv4(str)) {
+ return 4;
+ } else if (validators.isIPv6(str)) {
+ return 6;
+ } else {
+ return 0;
+ }
+ },
+ //node-js-core
+ isIPv4 : function(str) {
+ if (/^(\d?\d?\d)\.(\d?\d?\d)\.(\d?\d?\d)\.(\d?\d?\d)$/.test(str)) {
+ var parts = str.split('.').sort();
+ // no need to check for < 0 as regex won't match in that case
+ if (parts[3] > 255) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ },
+ //node-js-core
+ isIPv6 : function(str) {
+ if (/^::|^::1|^([a-fA-F0-9]{1,4}::?){1,7}([a-fA-F0-9]{1,4})$/.test(str)) {
+ return true;
+ }
+ return false;
+ },
+ isIPNet: function(str) {
+ return validators.isIP(str) !== 0;
+ },
+ isAlpha: function(str) {
+ return str.match(/^[a-zA-Z]+$/);
+ },
+ isAlphanumeric: function(str) {
+ return str.match(/^[a-zA-Z0-9]+$/);
+ },
+ isNumeric: function(str) {
+ return str.match(/^-?[0-9]+$/);
+ },
+ isHexadecimal: function(str) {
+ return str.match(/^[0-9a-fA-F]+$/);
+ },
+ isHexColor: function(str) {
+ return str.match(/^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/);
+ },
+ isLowercase: function(str) {
+ return str === str.toLowerCase();
+ },
+ isUppercase: function(str) {
+ return str === str.toUpperCase();
+ },
+ isInt: function(str) {
+ return str.match(/^(?:-?(?:0|[1-9][0-9]*))$/);
+ },
+ isDecimal: function(str) {
+ return str !== '' && str.match(/^(?:-?(?:[0-9]+))?(?:\.[0-9]*)?(?:[eE][\+\-]?(?:[0-9]+))?$/);
+ },
+ isFloat: function(str) {
+ return validators.isDecimal(str);
+ },
+ isDivisibleBy: function(str, n) {
+ return (parseFloat(str) % parseInt(n, 10)) === 0;
+ },
+ notNull: function(str) {
+ return str !== '';
+ },
+ isNull: function(str) {
+ return str === '';
+ },
+ notEmpty: function(str) {
+ return !str.match(/^[\s\t\r\n]*$/);
+ },
+ equals: function(a, b) {
+ return a == b;
+ },
+ contains: function(str, elem) {
+ return str.indexOf(elem) >= 0 && !!elem;
+ },
+ notContains: function(str, elem) {
+ return !validators.contains(str, elem);
+ },
+ regex: function(str, pattern, modifiers) {
+ str += '';
+ if (Object.prototype.toString.call(pattern).slice(8, -1) !== 'RegExp') {
+ pattern = new RegExp(pattern, modifiers);
+ }
+ return str.match(pattern);
+ },
+ is: function(str, pattern, modifiers) {
+ return validators.regex(str, pattern, modifiers);
+ },
+ notRegex: function(str, pattern, modifiers) {
+ return !validators.regex(str, pattern, modifiers);
+ },
+ not: function(str, pattern, modifiers) {
+ return validators.notRegex(str, pattern, modifiers);
+ },
+ len: function(str, min, max) {
+ return str.length >= min && (max === undefined || str.length <= max);
+ },
+ //Thanks to github.com/sreuter for the idea.
+ isUUID: function(str, version) {
+ var pattern;
+ if (version == 3 || version == 'v3') {
+ pattern = /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i;
+ } else if (version == 4 || version == 'v4') {
+ pattern = /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i;
+ } else if (version == 5 || version == 'v5') {
+ pattern = /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i;
+ } else {
+ pattern = /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i;
+ }
+ return pattern.test(str);
+ },
+ isUUIDv3: function(str) {
+ return validators.isUUID(str, 3);
+ },
+ isUUIDv4: function(str) {
+ return validators.isUUID(str, 4);
+ },
+ isUUIDv5: function(str) {
+ return validators.isUUID(str, 5);
+ },
+ isDate: function(str) {
+ var intDate = Date.parse(str);
+ return !isNaN(intDate);
+ },
+ isAfter: function(str, date) {
+ date = date || new Date();
+ var origDate = toDate(str);
+ var compDate = toDate(date);
+ return !(origDate && compDate && origDate <= compDate);
+ },
+ isBefore: function(str, date) {
+ date = date || new Date();
+ var origDate = toDate(str);
+ var compDate = toDate(date);
+ return !(origDate && compDate && origDate >= compDate);
+ },
+ isIn: function(str, options) {
+ if (!options || typeof options.indexOf !== 'function') {
+ return false;
+ }
+ if (Array.isArray(options)) {
+ options = options.map(String);
+ }
+ return options.indexOf(str) >= 0;
+ },
+ notIn: function(str, options) {
+ if (!options || typeof options.indexOf !== 'function') {
+ return false;
+ }
+ if (Array.isArray(options)) {
+ options = options.map(String);
+ }
+ return options.indexOf(str) < 0;
+ },
+ min: function(str, val) {
+ var number = parseFloat(str);
+ return isNaN(number) || number >= val;
+ },
+ max: function(str, val) {
+ var number = parseFloat(str);
+ return isNaN(number) || number <= val;
+ },
+ //Will work against Visa, MasterCard, American Express, Discover, Diners Club, and JCB card numbering formats
+ isCreditCard: function(str) {
+ //remove all dashes, spaces, etc.
+ var sanitized = str.replace(/[^0-9]+/g, '');
+ if (sanitized.match(/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/) === null) {
+ return null;
+ }
+ // Doing Luhn check
+ var sum = 0;
+ var digit;
+ var tmpNum;
+ var shouldDouble = false;
+ for (var i = sanitized.length - 1; i >= 0; i--) {
+ digit = sanitized.substring(i, (i + 1));
+ tmpNum = parseInt(digit, 10);
+ if (shouldDouble) {
+ tmpNum *= 2;
+ if (tmpNum >= 10) {
+ sum += ((tmpNum % 10) + 1);
+ }
+ else {
+ sum += tmpNum;
+ }
+ }
+ else {
+ sum += tmpNum;
+ }
+ if (shouldDouble) {
+ shouldDouble = false;
+ }
+ else {
+ shouldDouble = true;
+ }
+ }
+ if ((sum % 10) === 0) {
+ return sanitized;
+ } else {
+ return null;
+ }
+ }
+};
diff --git a/node_modules/resanitize/node_modules/validator/lib/xss.js b/node_modules/resanitize/node_modules/validator/lib/xss.js
new file mode 100755
index 00000000..4a2ed9e6
--- /dev/null
+++ b/node_modules/resanitize/node_modules/validator/lib/xss.js
@@ -0,0 +1,228 @@
+//This module is adapted from the CodeIgniter framework
+//The license is available at http://codeigniter.com/
+
+var html_entity_decode = require('./entities').decode;
+
+var never_allowed_str = {
+ 'document.cookie': '[removed]',
+ 'document.write': '[removed]',
+ '.parentNode': '[removed]',
+ '.innerHTML': '[removed]',
+ 'window.location': '[removed]',
+ '-moz-binding': '[removed]',
+ '': '-->',
+ '(': '<comment>'
+};
+
+var never_allowed_regex = {
+ 'javascript\\s*:': '[removed]',
+ 'expression\\s*(\\(|()': '[removed]',
+ 'vbscript\\s*:': '[removed]',
+ 'Redirect\\s+302': '[removed]',
+ "([\"'])?data\\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?": '[removed]'
+};
+
+var non_displayables = [
+ /%0[0-8bcef]/g, // url encoded 00-08, 11, 12, 14, 15
+ /%1[0-9a-f]/g, // url encoded 16-31
+ /[\x00-\x08]/g, // 00-08
+ /\x0b/g, /\x0c/g, // 11,12
+ /[\x0e-\x1f]/g // 14-31
+];
+
+var compact_words = [
+ 'javascript', 'expression', 'vbscript',
+ 'script', 'base64', 'applet', 'alert',
+ 'document', 'write', 'cookie', 'window'
+];
+
+exports.clean = function(str, is_image) {
+
+ //Remove invisible characters
+ str = remove_invisible_characters(str);
+
+ //Protect query string variables in URLs => 901119URL5918AMP18930PROTECT8198
+ var hash;
+ do {
+ // ensure str does not contain hash before inserting it
+ hash = xss_hash();
+ } while(str.indexOf(hash) >= 0)
+ str = str.replace(/\&([a-z\_0-9\-]+)\=([a-z\_0-9\-]+)/ig, hash + '$1=$2');
+
+ //Validate standard character entities. Add a semicolon if missing. We do this to enable
+ //the conversion of entities to ASCII later.
+ str = str.replace(/(?[0-9a-z]{2,})([\x00-\x20])*;?/ig, '$1;$2');
+
+ //Validate UTF16 two byte encoding (x00) - just as above, adds a semicolon if missing.
+ str = str.replace(/(?)([0-9A-F]+);?/ig, '$1$2;');
+
+ //Un-protect query string variables
+ str = str.replace(new RegExp(hash, 'g'), '&');
+
+ //Decode just in case stuff like this is submitted:
+ //Google
+ try{
+ str = decodeURIComponent(str);
+ }
+ catch(error){
+ // str was not actually URI-encoded
+ }
+
+ //Convert character entities to ASCII - this permits our tests below to work reliably.
+ //We only convert entities that are within tags since these are the ones that will pose security problems.
+ str = str.replace(/[a-z]+=([\'\"]).*?\1/gi, function(m, match) {
+ return m.replace(match, convert_attribute(match));
+ });
+ str = str.replace(/<\w+.*/gi, function(m) {
+ return m.replace(m, html_entity_decode(m));
+ });
+
+ //Remove invisible characters again
+ str = remove_invisible_characters(str);
+
+ //Convert tabs to spaces
+ str = str.replace('\t', ' ');
+
+ //Captured the converted string for later comparison
+ var converted_string = str;
+
+ //Remove strings that are never allowed
+ for (var i in never_allowed_str) {
+ str = str.replace(new RegExp(i, "gi"), never_allowed_str[i]);
+ }
+
+ //Remove regex patterns that are never allowed
+ for (var i in never_allowed_regex) {
+ str = str.replace(new RegExp(i, 'gi'), never_allowed_regex[i]);
+ }
+
+ //Compact any exploded words like: j a v a s c r i p t
+ // We only want to do this when it is followed by a non-word character
+ for (var i = 0, l = compact_words.length; i < l; i++) {
+ var spacified = compact_words[i].split('').join('\\s*')+'\\s*';
+
+ str = str.replace(new RegExp('('+spacified+')(\\W)', 'ig'), function(m, compat, after) {
+ return compat.replace(/\s+/g, '') + after;
+ });
+ }
+
+ //Remove disallowed Javascript in links or img tags
+ do {
+ var original = str;
+
+ if (str.match(/]*?)(>|$)/gi, function(m, attributes, end_tag) {
+ var filtered_attributes = filter_attributes(attributes.replace('<','').replace('>',''));
+ filtered_attributes = filtered_attributes.replace(/href=.*?(?:alert\(|alert(|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|