// --------------------------------------------------------------------------------------------------------------------
//
// coupon-code.js : An implementation of Perl's Algorithm::CouponCode for NodeJS.
//
// Author           : Andrew Chilton
// Web              : http://www.chilts.org/blog/
// Email            : <andychilton@gmail.com>
//
// Copyright (c)    : AppsAttic Ltd 2011
// Web              : http://www.appsattic.com/
// License          : http://opensource.org/licenses/MIT
//
// Copyright (c)    : Andrew Chilton 2013
// Web              : http://chilts.org/
// License          : http://opensource.org/licenses/MIT
//
// --------------------------------------------------------------------------------------------------------------------

// npm
var xtend = require('xtend');

// --------------------------------------------------------------------------------------------------------------------
// constants

var symbolsStr = '0123456789ABCDEFGHJKLMNPQRTUVWXY';
var symbolsArr = symbolsStr.split('');
var symbolsObj = {};
symbolsArr.forEach(function(c, i) {
    symbolsObj[c] = i;
});

var defaults = {
    parts   : 3,
    partLen : 4,
};

// --------------------------------------------------------------------------------------------------------------------
// exports

module.exports.generate = function(opts) {
    opts = xtend({}, defaults, opts);

    var parts = [];
    var part;
    var i;
    var j;

    // if we have a plaintext, generate a code from that
    if ( opts.plaintext ) {
        // not yet implemented
        return '';
    }
    else {
        // default to a random code
        for( i = 0; i < opts.parts; i++ ) {
            part = '';
            for ( j = 0; j < opts.partLen - 1; j++ ) {
                part += randomSymbol();
            }
            part = part + checkDigitAlg1(part, i+1);
            parts.push(part);
        }
    }

    return parts.join('-');
};

module.exports.validate = function(code, opts) {
    if ( !code ) {
        throw new Error("Provide a code to be validated");
    }
    opts = xtend({}, defaults, opts);

    // uppercase the code, take out any random chars and replace OIZS with 0125
    code = code.toUpperCase()
        .replace(/[^0-9A-Z]+/g, '')
        .replace(/O/g, '0')
        .replace(/I/g, '1')
        .replace(/Z/g, '2')
        .replace(/S/g, '5');

    // split in the different parts
    var parts = [];
    var tmp = code;
    while( tmp.length > 0 ) {
        parts.push( tmp.substr(0, opts.partLen) );
        tmp = tmp.substr(opts.partLen);
    }

    // make sure we have been given the same number of parts as we are expecting
    if ( parts.length !== opts.parts ) {
        return '';
    }

    // validate each part
    var part, str, check;
    for ( var i = 0; i < parts.length; i++ ) {
        part = parts[i];
        // check this part has 4 chars
        if ( part.length !== opts.partLen ) {
            return '';
        }

        // split out the data and the check
        data = part.substr(0, opts.partLen-1);
        check = part.substr(opts.partLen-1, 1);

        if ( check !== checkDigitAlg1(data, i+1) ) {
            return '';
        }
    }

    // everything looked ok with this code
    return parts.join('-');
};

// --------------------------------------------------------------------------------------------------------------------
// internal helpers

function randomSymbol() {
    return symbolsArr[parseInt(Math.random() * symbolsArr.length, 10)];
}

// returns the checksum character for this (data/part) combination
function checkDigitAlg1(data, check) {
    // check's initial value is the part number (e.g. 3 or above)

    // loop through the data chars
    data.split('').forEach(function(v) {
        var k = symbolsObj[v];
        check = check * 19 + k;
    });

    return symbolsArr[ check % 31 ];
}

// --------------------------------------------------------------------------------------------------------------------

@@ -921,16 +921,331 @@
 nstants%0A
+var badWordsList = 'SHPX PHAG JNAX JNAT CVFF PBPX FUVG GJNG GVGF SNEG URYY ZHSS QVPX XABO ' +%0A                   'NEFR FUNT GBFF FYHG GHEQ FYNT PENC CBBC OHGG SRPX OBBO WVFZ WVMM CUNG'%0A                   .replace(/%5Ba-zA-Z%5D/g,function(c)%7Breturn String.fromCharCode((c%3C=%22Z%22?90:122)%3E=(c=c.charCodeAt(0)+13)?c:c-26);%7D);
 %0Avar sym
@@ -1964,32 +1964,53 @@
 .parts; i++ ) %7B%0A
+            do %7B%0A    
             part
@@ -2024,24 +2024,28 @@
             
+  
+  
 for ( j = 0;
@@ -2087,24 +2087,28 @@
             
+    
 part += rand
@@ -2127,26 +2127,32 @@
             
-%7D%0A
+    %7D%0A  
             
@@ -2143,32 +2143,34 @@
 %7D%0A              
+  
 part = part + ch
@@ -2190,24 +2190,78 @@
 part, i+1);%0A
+            %7D while (badWordsList.indexOf(part) %3E -1)%0A
             

Add a rude word check