raethlein / AIML.js

AIML Interpreter written in node.js
69 stars 32 forks source link

Why the contents on npm are different from here? #9

Open peter0749 opened 7 years ago

peter0749 commented 7 years ago

A little mess. Sorry for that... package.json on npm:

{
  "READ ME": "README.md",
  "_args": [
    [
      {
        "raw": "aimlinterpreter",
        "scope": null,
        "escapedName": "aimlinterpreter",
        "name": "aimlinterpreter",
        "rawSpec": "",
        "spec": "latest",
        "type": "tag"
      },
      "/Users/kuang-yujeng/testtesttest"
    ]
  ],
  "_from": "aimlinterpreter@latest",
  "_id": "aimlinterpreter@0.1.7",
  "_inCache": true,
  "_location": "/aimlinterpreter",
  "_npmUser": {
    "name": "b3nra",
    "email": "bennyra92@gmail.com"
  },
  "_npmVersion": "1.4.28",
  "_phantomChildren": {},
  "_requested": {
    "raw": "aimlinterpreter",
    "scope": null,
    "escapedName": "aimlinterpreter",
    "name": "aimlinterpreter",
    "rawSpec": "",
    "spec": "latest",
    "type": "tag"
  },
  "_requiredBy": [
    "#USER",
    "/"
  ],
  "_resolved": "https://registry.npmjs.org/aimlinterpreter/-/aimlinterpreter-0.1.7.tgz",
  "_shasum": "988c1b6b135a8099b3d1144832ad89691d1b7cf6",
  "_shrinkwrap": null,
  "_spec": "aimlinterpreter",
  "_where": "/Users/kuang-yujeng/testtesttest",
  "author": {
    "name": "B3nRa"
  },
  "bugs": {
    "url": "https://github.com/B3nRa/AIML.js/issues"
  },
  "dependencies": {
    "dom-js": "~0.0.9"
  },
  "description": "Module to interpret AIML files in node.js",
  "devDependencies": {},
  "directories": {},
  "dist": {
    "shasum": "988c1b6b135a8099b3d1144832ad89691d1b7cf6",
    "tarball": "https://registry.npmjs.org/aimlinterpreter/-/aimlinterpreter-0.1.7.tgz"
  },
  "gitHead": "1497b828dfa61cfc8555b935ca40f9dad60c0125",
  "homepage": "https://github.com/B3nRa/AIML.js",
  "keywords": [
    "aiml",
    "ai",
    "interpreter",
    "bot"
  ],
  "license": "BSD",
  "main": "AIMLInterpreter.js",
  "maintainers": [
    {
      "name": "b3nra",
      "email": "bennyra92@gmail.com"
    }
  ],
  "name": "aimlinterpreter",
  "optionalDependencies": {},
  "readme": "ERROR: No README data found!",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/B3nRa/AIML.js.git"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "version": "0.1.7"
}

diff AIMLInterpreter.js(npm) AIMLInterpreter.js(GitHub)

1,477c1,538
< DomJS = require("dom-js").DomJS;
< fs = require('fs');
< 
< var storedVariableValues = {};
< var botAttributes = {};
< 
< var lastWildCardValue = '';
< var wildCardArray = [];
< 
< var domArray = [];
< var domIndex = 0;
< 
< var isAIMLFileLoadingStarted = false;
< var isAIMLFileLoaded = false;
< 
< var previousAnswer = '';
< 
< //botAttributes contain things like name, age, master, gender...
< var AIMLInterpreter = function(botAttributesParam){
<     var self = this;
<     botAttributes = botAttributesParam;
< 
<     this.loadAIMLFilesIntoArray = function(fileArray){
<         isAIMLFileLoadingStarted = true;
<         var fileIndex = 0;
<         var readAIMLFile = function(file){
<             fs.readFile(file, 'utf8', function (err,data) {
<                 if (err) {
<                     return console.log(err);
<                 }
< 
<                 fileIndex++;
< 
<                 new DomJS().parse(data, function(err, dom) {
<                     var topCategories, topics;
<                     if (err) {
<                         //            return cb(err);
<                     }
<                     if (dom.name === !'aiml') {
<                         //            return cb('Unsupported file');
<                     }
<                     domArray[domIndex] = dom;
<                     domIndex++;
<                     if(fileIndex < fileArray.length){
<                         readAIMLFile(fileArray[fileIndex]);
<                     }
<                     else{
<                         console.log('AIML file is loaded!');
<                         isAIMLFileLoaded = true;
<                     }
<                 });
<             });
<         }
<         readAIMLFile(fileArray[fileIndex]);
<     };
< 
<     this.findAnswerInLoadedAIMLFiles = function(clientInput, cb){
<         //check if all AIML files have been loaded. If not, call this method again after a delay
<         if(isAIMLFileLoaded){
<             clientInput = clientInput.toUpperCase();
<             wildCardArray = [];
<             var result = '';
<             for(var i = 0; i < domArray.length; i++){
<                 cleanDom(domArray[i].children);
<                 result = findCorrectCategory(clientInput, domArray[i].children);
<                 if(result){
<                     break;
<                 }
<             }
< 
<             if(result){
<                 result = cleanStringFormatCharacters(result);
<                 previousAnswer = result;
<             }
<             cb(result, wildCardArray, clientInput);
<         }
<         else{
<             var findAnswerInLoadedAIMLFilesWrapper = function(clientInput, cb){
<                 return function(){
<                     self.findAnswerInLoadedAIMLFiles(clientInput, cb);
<                 };
<             };
< 
<             setTimeout(findAnswerInLoadedAIMLFilesWrapper(clientInput, cb), 1000);
<         }
<     };
< };
< 
< // remove string control characters (like line-breaks '\r\n', leading / trailing spaces etc.)
< var cleanStringFormatCharacters = function(str){
<     var cleanedStr = str.replace(/\r\n/gi, '');
<     cleanedStr = cleanedStr.replace(/^\s*/, '');
<     cleanedStr = cleanedStr.replace(/\s*$/,'');
< 
<     return cleanedStr;
< }
< 
< var cleanDom = function(childNodes){
<     for(var i = 0; i < childNodes.length; i++){
<         if(childNodes[i].hasOwnProperty('text') & typeof(childNodes[i].text) === 'string'){
< 
<             // remove all nodes of type 'text' when they just contain '\r\n'. This indicates line break in the AIML file
<             if(childNodes[i].text.match(/^\s*\r\n\s*$/gi)){
<                 childNodes.splice(i, 1);
<             }
<         }
<     }
< 
< 
<     // traverse through whole tree by recursive calls
<     for(var j = 0; j < childNodes.length; j++){
<         if(childNodes[j].hasOwnProperty('children')){
<             cleanDom(childNodes[j].children);
<         }
<     }
< };
< 
< var findCorrectCategory = function(clientInput, domCategories){
<     //indexOfSetTagAmountWithWildCard indicates how many sets with wildcard occur so that those sets store the correct wildcard value
<     var indexOfSetTagAmountWithWildCard = 0;
< 
<     var  travereseThroughDomToFindMatchingPattern= function(categories){
<         for(var i = 0; i < categories.length; i++){
<             if(categories[i].name === 'category'){
<                 //traverse through the dom
<                 //text gets the value of the current pattern node
<                 var text = travereseThroughDomToFindMatchingPattern(categories[i].children);
<                 //check if the input of the user matches the pattern text
<                 var matches = checkIfMessageMatchesPattern(clientInput, text);
<                 if(matches){
<                     //check if a 'that' tag is existing. If yes, check if the text of the that tag matches the previous given answer.
<                     //If it does not match, continue the traversion through the AIML file
<                     var isMatchingThat = checkForThatMatching(categories[i].children);
<                     if(isMatchingThat){
<                         var text = findFinalTextInTemplateNode(categories[i].children);
<                         if(text){
<                             return text;
<                         }
<                         break;
<                     }
<                 }
<             }
<             else if(categories[i].name === 'pattern'){
<                 var text = resolveChildNodesInPatternNode(categories[i].children);
<                 return text;
<             }
<         }
<     }
< 
<     var checkForThatMatching = function(categoryChildNodes){
<         for(var i = 0; i < categoryChildNodes.length; i++){
<             if(categoryChildNodes[i].name === 'that'){
<                 //if the previous answer of the bot does not match the that-tag text, then return undefined!
<                 if(categoryChildNodes[i].children[0].text != previousAnswer){
<                     return false;
<                 }
<                 else{
<                     return true;
<                 }
<             }
<         }
<         //if no that tag was found, everything 'fits'
<         return true;
<     }
< 
<     var resolveChildNodesInPatternNode = function(patternChildNodes){
<         var text = '';
< 
<         for(var i = 0; i < patternChildNodes.length; i++){
<             if(patternChildNodes[i].name === 'bot'){
<                 text = text + botAttributes[patternChildNodes[i].attributes.name];
<             }
<             else if(patternChildNodes[i].name === 'get'){
<                 text = text + storedVariableValues[patternChildNodes[i].attributes.name];
<             }
<             else if(patternChildNodes[i].name === 'set'){
<                 text = text + patternChildNodes[i].children[0].text;
<             }
<             else{
<                 text = text + patternChildNodes[i].text;
<             }
<         }
< 
<         return text;
<     }
< 
<     var findFinalTextInTemplateNode = function(childNodesOfTemplate){
<         var text = '';
< 
<         //traverse through template nodes until final text is found
<         //return it then to very beginning
<         for(var i = 0; i < childNodesOfTemplate.length; i++){
<             if(childNodesOfTemplate[i].name === 'template'){
<                 //traverse as long through the dom until final text was found
<                 //final text -> text after special nodes (bot, get, set,...) were resolved
<                 return findFinalTextInTemplateNode(childNodesOfTemplate[i].children);
<             }
<             else if(childNodesOfTemplate[i].name === 'condition'){
<                 return resolveSpecialNodes(childNodesOfTemplate);
<             }
<             else if(childNodesOfTemplate[i].name === 'random'){
<                 //if random node was found, it's children are 'li' nodes.
<                 //pick one li node by random and continue dom traversion until final text is found
<                 var randomNumber = Math.floor(Math.random() * (childNodesOfTemplate[i].children.length));
<                 return findFinalTextInTemplateNode([childNodesOfTemplate[i].children[randomNumber]]);
<             }
<             else if(childNodesOfTemplate[i].name === 'srai'){
<                 //take pattern text of srai node to get answer of another category
<                 var sraiText = '' + findFinalTextInTemplateNode(childNodesOfTemplate[i].children);
<                 sraiText = sraiText.toUpperCase();
<                 var referredPatternText = sraiText;
<                 //call findCorrectCategory again to find the category that belongs to the srai node
<                 var text = findCorrectCategory(referredPatternText, domCategories);
<                 return text;
<             }
<             else if(childNodesOfTemplate[i].name === 'li'){
<                 return findFinalTextInTemplateNode(childNodesOfTemplate[i].children);
<             }
<             else if(childNodesOfTemplate[i].name === 'pattern'){
<                 //(here it is already checked that this is the right pattern that matches the user input)
<                 //make use of the functions of the special nodes - bot, set, get...
<                 resolveSpecialNodes(childNodesOfTemplate[i].children);
<                 continue;
<             }
<             else if(childNodesOfTemplate[i].name === 'bot'){
<                 text = resolveSpecialNodes(childNodesOfTemplate);
<                 return text;
<             }
<             else if(childNodesOfTemplate[i].name === 'set'){
<                 text = resolveSpecialNodes(childNodesOfTemplate);
<                 return text;
<             }
<             else if(childNodesOfTemplate[i].name === 'get'){
<                 text = resolveSpecialNodes(childNodesOfTemplate);
<                 return text;
<             }
<             else if(childNodesOfTemplate[i].name === 'sr'){
<                 text = resolveSpecialNodes(childNodesOfTemplate);
<                 return text;
<             }
<             else if(childNodesOfTemplate[i].name === 'star'){
<                 text = resolveSpecialNodes(childNodesOfTemplate);
<                 return text;
<             }
<             else if(childNodesOfTemplate[i].name === 'that'){
< 
<             }
<             else{
<                 //this is the text of template node
<                 //after all special functions (bot, get, set,...) were resolved
<                 //return that text
<                 text = resolveSpecialNodes(childNodesOfTemplate);
<                 if((text.match('[\\n|\\t]*[^A-Z|^a-z|^!|^?]*')[0] === '') && (text.indexOf('function ()') === -1)){
<                     return (text);
<                 }
<             }
<         }
<     };
< 
<     var resolveSpecialNodes = function(innerNodes){
<         var text = '';
<         //concatenate string of all node children - normal text, bot tags, get tags, set tags...
<         for(var i = 0; i < innerNodes.length; i++){
< 
<             if(innerNodes[i].name === 'bot'){
<                 //replace bot tags by the belonging bot attribute value
<                 text = text + botAttributes[innerNodes[i].attributes.name];
<             }
<             else if(innerNodes[i].name === 'get'){
<                 //replace get tag by belonging variable value
<                 text = text + storedVariableValues[innerNodes[i].attributes.name];
<             }
<             else if(innerNodes[i].name === 'set'){
<                 //store value of set tag text into variable (variable name = attribute of set tag)
<                 //replace than set tag by the text value
< 
<                 if(innerNodes[i].children[0].text === '*'){
<                     //the first set-Tag with wildCard gets the first wildCardValue, the second set-Tag with wildCard gets the second wildCardValue etc.
<                     storedVariableValues[innerNodes[i].attributes.name] = wildCardArray[indexOfSetTagAmountWithWildCard];
<                     indexOfSetTagAmountWithWildCard++;
<                 }else{
<                     storedVariableValues[innerNodes[i].attributes.name] = innerNodes[i].children[0].text;
<                 }
< //                text = text + innerNodes[i].children[0].text;
<                 text = text + resolveSpecialNodes(innerNodes[i].children);
<             }
<             else if(innerNodes[i].name === 'sr'){
<                 var result;
< 
<                 //for-loop to go through all loaded AIML files
<                 for(var j = 0; j < domArray.length; j++){
<                     result = findCorrectCategory(lastWildCardValue, domArray[j].children);
<                     //if in one of the dom trees a matching pattern was found, exit this inner loop
<                     if(result){
<                         text = text + result;
<                         break;
<                     }
<                 }
<             }
<             else if(innerNodes[i].name === 'star'){
<                 text = text + lastWildCardValue;
<             }
<             else if(innerNodes[i].name === 'condition') {                
<                 // condition tag specification: list condition tag
<                 if(innerNodes[i].attributes.name === undefined){
<                     if(innerNodes[i].children === undefined){
<                         return undefined;
<                     }
<                     var child;
<                     for(var c in innerNodes[i].children){
<                         child = innerNodes[i].children[c];
<                         if(child.name === 'li'){
<                             if(child.attributes.value == undefined 
<                                 || storedVariableValues[child.attributes.name] === child.attributes.value.toUpperCase()){
<                                 return findFinalTextInTemplateNode(child.children);
<                             }
<                         }
<                     }
<                 } 
<                 // condition tag specification: multi condition tag
<                 else if(innerNodes[i].attributes.value !== undefined){         
<                     if (storedVariableValues[innerNodes[i].attributes.name] === innerNodes[i].attributes.value.toUpperCase()) {
<                         text = text + resolveSpecialNodes(innerNodes[i].children);
<                     }
<                 }
<                 // condition tag specification: single name list condition tags
<                 else if(innerNodes[i].children !== undefined){
<                     var child;
<                     for(var c in innerNodes[i].children){
<                         child = innerNodes[i].children[c];
<                         if(child.name === 'li'){
<                             if(child.attributes.value === undefined 
<                                 || storedVariableValues[innerNodes[i].attributes.name] === child.attributes.value.toUpperCase()){
<                                 return findFinalTextInTemplateNode(child.children);
<                             }
<                         }
<                     }
< 
<                     return undefined;
<                 }
<             }
<             else{
<                 //normal text (no special tag)
<                 text = text + innerNodes[i].text;
<             }
<         }
< 
<         text = cleanStringFormatCharacters(text);
<         return text;
<     }
< 
<     return travereseThroughDomToFindMatchingPattern(domCategories);
< }
< 
< var checkIfMessageMatchesPattern = function(userInput, patternText){
<     //convert wildcards in of the pattern node into a regex that matches every char
<     var regexPattern = convertWildcardToRegex(patternText);
< 
<     //add one with the text in function 'convertWildcardToRegex' here a space is added before and after the user input
<     //to prevent false matching
<     if(userInput.charAt(0) != " "){
<         userInput = " " + userInput;
<     }
< 
<     var lastCharacterPosition  = userInput.length - 1;
<     var lastCharacter = userInput.charAt(lastCharacterPosition);
<     if(lastCharacter != " "){
<         userInput = userInput + " ";
<     }
< 
<     //match userInput with the regex pattern
<     //if it matches, matchedString is defined
<     var matchedString = userInput.match(regexPattern);
< 
<     if(matchedString){
<         //the matched pattern must be at least as long as the user input or must contain the regex
<         if(matchedString[0].length >= userInput.length || regexPattern.indexOf('[A-Z|0-9|\\s]*[A-Z|0-9|-]*[A-Z|0-9]*[!|.|?|\\s]*') > -1){
<             //if patternText contained a wild card, get the user input that were put into this wild card
<             //use original patternText (* is not replaced by regex!)
<             var information = getWildCardValue(userInput, patternText);
< 
<             return true;
<         }
<     }
<     else{
<         return false;
<     }
< }
< 
< var convertWildcardToRegex = function(text){
<     var firstCharacter = text.charAt(0);
<     //add a space before and after the pattern text (THIS IS LATER ALSO DONE FOR THE USER INPUT)
<     //prevents false matchings
<     //e.g. (HI as regex also matches HIM or HISTORY, but <space>HI</space> does only match <space>HI</space>)
<     if(firstCharacter != "*"){
<         var text = " " + text;
<     }
<     var lastCharacterPosition = text.length - 1;
<     var lastCharacter = text.charAt(lastCharacterPosition);
< 
<     //replace space before wildcard
<     var modifiedText = text.replace(' *', '*');
<     //replace wildcard (*) by regex
<     modifiedText = modifiedText.replace(/\*/g, '[A-Z|0-9|\\s]*[A-Z|0-9|\*|-]*[A-Z|0-9]*[!|.|?|\\s]*');
< 
<     if(lastCharacter != "*"){
< //        text = text + " ";
<         //pattern should also match when user inputs ends with a space, ?, ! or .
<         modifiedText = modifiedText + '[\\s|?|!|.]*';
<     }
< 
<     return modifiedText;
< }
< 
< var getWildCardValue = function(userInput, patternText){
<     //get all strings of the pattern that are divided by a *
<     //e.g. WHAT IS THE RELATION BETWEEN * AND * -> [WHAT IS THE RELATION BETWEEN , AND ]
<     var replaceArray = patternText.split('*');
<     var wildCardInput = userInput;
< 
<     if(replaceArray.length > 1){
<         //replace the string of the userInput which is fixed by the pattern
<         for(var i = 0; i < replaceArray.length; i++){
<             wildCardInput = wildCardInput.replace(replaceArray[i], '|');
<         }
<         //split the wildCardInput string by | to differentiate multiple * inputs
<         //e.g. userInput = WHAT IS THE RELATION BETWEEN TIM AND STRUPPI?
<         //-> | TIM | STRUPPI
<         //-> [TIM, STRUPPI]
<         wildCardInput = wildCardInput.split('|');
<         //split function can create an array which also includes spaces etc. -> e.g. [TIM, " ", "", STRUPPI, " "]
<         //we just want the information
<         var wildCardArrayIndex = 0;
<         for(var i = 0; i < wildCardInput.length; i++){
<             if(wildCardInput[i] != '' && wildCardInput[i] != ' ' && wildCardInput != undefined){
<                 var wildCard = wildCardInput[i];
<                 var wildCardLastCharIndex = wildCard.length - 1;
<                 var firstCharOfWildCard = wildCard.charAt(0);
<                 var lastCharOfWildCard = wildCard.charAt(wildCardLastCharIndex);
< 
<                 try{
<                     //harmonize the wildcard string
<                     //remove first char if it is a space.
<                     //calculate the last index again since the length of the string changed
<                     if(firstCharOfWildCard === ' '){
<                         wildCard = wildCard.splice(0);
<                         wildCardLastCharIndex = wildCard.length - 1;
<                         lastCharOfWildCard = wildCard.charAt(wildCardLastCharIndex);
<                     }
<                     //if the last char is a space, remove it
<                     //calculate the last index again since the length of the string changed
<                     if(lastCharOfWildCard === ' '){
<                         wildCard = wildCard.substr(0, wildCardLastCharIndex);
<                         wildCardLastCharIndex = wildCard.length - 1;
<                         lastCharOfWildCard = wildCard.charAt(wildCardLastCharIndex);
<                     }
<                     if(lastCharOfWildCard === '?'){
<                         wildCard = wildCard.substr(0, wildCardLastCharIndex);
<                     }
<                 }
<                 catch(e){
< 
<                 }
<                 wildCardArray[wildCardArrayIndex] = wildCard;
<                 wildCardArrayIndex++;
<             }
<         }
<     }
<     if(wildCardArray.length - 1 >= 0){
<         lastWildCardValue = wildCardArray[wildCardArray.length - 1];
<     }
< 
<     return wildCardArray;
< }
< 
< module.exports = AIMLInterpreter;
< 
---
> DomJS = require("dom-js").DomJS;
> fs = require('fs');
> 
> var storedVariableValues = {};
> var botAttributes = {};
> 
> var lastWildCardValue = '';
> var wildCardArray = [];
> 
> var domArray = [];
> var domIndex = 0;
> 
> var isAIMLFileLoadingStarted = false;
> var isAIMLFileLoaded = false;
> 
> var previousAnswer = '';
> var previousThinkTag = false;
> 
> //botAttributes contain things like name, age, master, gender...
> var AIMLInterpreter = function(botAttributesParam){
>     var self = this;
>     botAttributes = botAttributesParam;
> 
>     this.loadAIMLFilesIntoArray = function(fileArray){
>         isAIMLFileLoadingStarted = true;
>         var fileIndex = 0;
>         var readAIMLFile = function(file){
>             fs.readFile(file, 'utf8', function (err,data) {
>                 if (err) {
>                     return console.log(err);
>                 }
> 
>                 fileIndex++;
> 
>                 data = data.replace(/>\r*\n+\s*</gi,'><');
> 
>                 new DomJS().parse(data, function(err, dom) {
>                     var topCategories, topics;
>                     if (err) {
>                         //            return cb(err);
>                     }
>                     if (dom.name === !'aiml') {
>                         //            return cb('Unsupported file');
>                     }
>                     domArray[domIndex] = dom;
>                     domIndex++;
>                     if(fileIndex < fileArray.length){
>                         readAIMLFile(fileArray[fileIndex]);
>                     }
>                     else{
>                         console.log('AIML file is loaded!');
>                         isAIMLFileLoaded = true;
>                     }
>                 });
>             });
>         }
>         readAIMLFile(fileArray[fileIndex]);
>     };
> 
>     this.findAnswerInLoadedAIMLFiles = function(clientInput, cb){
>         //check if all AIML files have been loaded. If not, call this method again after a delay
>         if(isAIMLFileLoaded){
>             wildCardArray = [];
>             lastWildCardValue = '';
>             var result = '';
>             for(var i = 0; i < domArray.length; i++){
>                 cleanDom(domArray[i].children);
>                 result = findCorrectCategory(clientInput, domArray[i].children);
>                 if(result){
>                     break;
>                 }
>             }
> 
>             if(result){
>                 result = cleanStringFormatCharacters(result);
>                 previousAnswer = result;
>             }
>             cb(result, wildCardArray, clientInput);
>         }
>         else{
>             var findAnswerInLoadedAIMLFilesWrapper = function(clientInput, cb){
>                 return function(){
>                     self.findAnswerInLoadedAIMLFiles(clientInput, cb);
>                 };
>             };
> 
>             setTimeout(findAnswerInLoadedAIMLFilesWrapper(clientInput, cb), 1000);
>         }
>     };
>     //restart the DOM in order to load a new AIML File
>     this.restartDom = function(){
>         domArray=[];
>         domIndex=0;
>     };
> };
> 
> 
> 
> 
> // remove string control characters (like line-breaks '\r\n', leading / trailing spaces etc.)
> var cleanStringFormatCharacters = function(str){
>     var cleanedStr = str.replace(/\r\n/gi, '');
>     cleanedStr = cleanedStr.replace(/^\s*/, '');
>     cleanedStr = cleanedStr.replace(/\s*$/,'');
> 
>     return cleanedStr;
> }
> 
> var cleanDom = function(childNodes){
>     for(var i = 0; i < childNodes.length; i++){
>         if(childNodes[i].hasOwnProperty('text') & typeof(childNodes[i].text) === 'string'){
> 
>             // remove all nodes of type 'text' when they just contain '\r\n'. This indicates line break in the AIML file
>             if(childNodes[i].text.match(/^\s*\r\n\s*$/gi)){
>                 childNodes.splice(i, 1);
>             }
>         }
>     }
> 
> 
>     // traverse through whole tree by recursive calls
>     for(var j = 0; j < childNodes.length; j++){
>         if(childNodes[j].hasOwnProperty('children')){
>             cleanDom(childNodes[j].children);
>         }
>     }
> };
> 
> var findCorrectCategory = function(clientInput, domCategories){
>     //indexOfSetTagAmountWithWildCard indicates how many sets with wildcard occur so that those sets store the correct wildcard value
>     var indexOfSetTagAmountWithWildCard = 0;
> 
>     var  travereseThroughDomToFindMatchingPattern= function(categories){
>         for(var i = 0; i < categories.length; i++){
>             if(categories[i].name === 'category'){
>                 //traverse through the dom
>                 //text gets the value of the current pattern node
>                 var text = travereseThroughDomToFindMatchingPattern(categories[i].children);
>                 //check if the input of the user matches the pattern text
>                 var matches = checkIfMessageMatchesPattern(clientInput, text);
>                 if(matches){
>                     //check if a 'that' tag is existing. If yes, check if the text of the that tag matches the previous given answer.
>                     //If it does not match, continue the traversion through the AIML file
>                     var isMatchingThat = checkForThatMatching(categories[i].children);
>                     if(isMatchingThat){
>                         var text = findFinalTextInTemplateNode(categories[i].children);
>                         if(text){
>                             return text;
>                         }
>                         break;
>                     }
>                 }
>             }
>             else if(categories[i].name === 'pattern'){
>                 var text = resolveChildNodesInPatternNode(categories[i].children);
>                 return text;
>             }
>         }
>     }
> 
>     var checkForThatMatching = function(categoryChildNodes){
>         for(var i = 0; i < categoryChildNodes.length; i++){
>             if(categoryChildNodes[i].name === 'that'){
>                 //if the previous answer of the bot does not match the that-tag text, then return undefined!
>                 if(categoryChildNodes[i].children[0].text != previousAnswer){
>                     return false;
>                 }
>                 else{
>                     return true;
>                 }
>             }
>         }
>         //if no that tag was found, everything 'fits'
>         return true;
>     }
> 
>     var resolveChildNodesInPatternNode = function(patternChildNodes){
>         var text = '';
> 
>         for(var i = 0; i < patternChildNodes.length; i++){
>             if(patternChildNodes[i].name === 'bot'){
>                 text = text + botAttributes[patternChildNodes[i].attributes.name];
>             }
>             else if(patternChildNodes[i].name === 'get'){
>                 text = text + storedVariableValues[patternChildNodes[i].attributes.name];
>             }
>             else if(patternChildNodes[i].name === 'set'){
>                 text = text + patternChildNodes[i].children[0].text;
>             }
>             else{
>                 text = text + patternChildNodes[i].text;
>             }
>         }
> 
>         return text;
>     }
> 
>     var findFinalTextInTemplateNode = function(childNodesOfTemplate){
>         var text = '';
> 
>         //traverse through template nodes until final text is found
>         //return it then to very beginning
> 
>         for(var i = 0; i < childNodesOfTemplate.length; i++){
>             if(childNodesOfTemplate[i].name === 'template'){
>                 //traverse as long through the dom until final text was found
>                 //final text -> text after special nodes (bot, get, set,...) were resolved
>                 return findFinalTextInTemplateNode(childNodesOfTemplate[i].children);
>             }
>             else if(childNodesOfTemplate[i].name === 'condition'){
>                 return resolveSpecialNodes(childNodesOfTemplate);
>             }
>             else if(childNodesOfTemplate[i].name === 'random'){
>                 //if random node was found, its children are 'li' nodes.
>                 return resolveSpecialNodes(childNodesOfTemplate);
>             }
>             else if(childNodesOfTemplate[i].name === 'srai'){
>                 //take pattern text of srai node to get answer of another category
>                 var sraiText = '' + findFinalTextInTemplateNode(childNodesOfTemplate[i].children);
>                 sraiText = sraiText.toUpperCase();
>                 var referredPatternText = sraiText;
>                 //call findCorrectCategory again to find the category that belongs to the srai node
>                 var text = findCorrectCategory(referredPatternText, domCategories);
>                 return text;
>             }
>             else if(childNodesOfTemplate[i].name === 'li'){
>                 return findFinalTextInTemplateNode(childNodesOfTemplate[i].children);
>             }
>             else if(childNodesOfTemplate[i].name === 'br'){
>                 //br elements are used for putting '\n' into the text
>                 return resolveSpecialNodes(childNodesOfTemplate);
>             }
>             else if(childNodesOfTemplate[i].name === 'pattern'){
>                 //(here it is already checked that this is the right pattern that matches the user input)
>                 //make use of the functions of the special nodes - bot, set, get...
>                 resolveSpecialNodes(childNodesOfTemplate[i].children);
>                 continue;
>             }
>             else if(childNodesOfTemplate[i].name === 'think'){
>                 text = resolveSpecialNodes(childNodesOfTemplate);
>                 return text;
>             }
>             else if(childNodesOfTemplate[i].name === 'bot'){
>                 text = resolveSpecialNodes(childNodesOfTemplate);
>                 return text;
>             }
>             else if(childNodesOfTemplate[i].name === 'set'){
>                 text = resolveSpecialNodes(childNodesOfTemplate);
>                 return text;
>             }
>             else if(childNodesOfTemplate[i].name === 'get'){
>                 text = resolveSpecialNodes(childNodesOfTemplate);
>                 return text;
>             }
>             else if(childNodesOfTemplate[i].name === 'sr'){
>                 text = resolveSpecialNodes(childNodesOfTemplate);
>                 return text;
>             }
>             else if(childNodesOfTemplate[i].name === 'star'){
>                 text = resolveSpecialNodes(childNodesOfTemplate);
>                 return text;
>             }
>             else if(childNodesOfTemplate[i].name === 'that'){
> 
>             }
>             else{
>                 //this is the text of template node
>                 //after all special functions (bot, get, set,...) were resolved
>                 //return that text
>                 text = resolveSpecialNodes(childNodesOfTemplate);
>                 if((text.match('[\\n|\\t]*[^A-Z|^a-z|^1-9|^!|^?]*')[0] === '') && (text.indexOf('function ()') === -1)){
>                     return (text);
>                 }
>             }
>         }
>     };
> 
>     var resolveSpecialNodes = function(innerNodes){
>         var text = '';
>         //concatenate string of all node children - normal text, bot tags, get tags, set tags...
>         for(var i = 0; i < innerNodes.length; i++){
> 
>             if(innerNodes[i].name === 'bot'){
>                 //replace bot tags by the belonging bot attribute value
>                 text = text + botAttributes[innerNodes[i].attributes.name];
>             }
>             else if(innerNodes[i].name === 'get'){
>                 //replace get tag by belonging variable value
>                 var getAux = storedVariableValues[innerNodes[i].attributes.name];
>                 if(getAux === undefined){
>                     text = text + '';
>                 }else{
>                     text = text + getAux;
>                 }
>             }
>             else if(innerNodes[i].name === 'set'){
>                 //store value of set tag text into variable (variable name = attribute of set tag)
>                 //replace than set tag by the text value
>                 var aux='';
>                 if(innerNodes[i].children[0].name === 'star'){
>                     aux = resolveSpecialNodes(innerNodes[i].children);
>                     storedVariableValues[innerNodes[i].attributes.name] = aux;
>                     if(!previousThinkTag){
>                         text = text + aux;
>                     }
>                 }
>                 else if(innerNodes[i].children[0].text === '*'){
>                     //the first set-Tag with wildCard gets the first wildCardValue, the second set-Tag with wildCard gets the second wildCardValue etc.
>                     storedVariableValues[innerNodes[i].attributes.name] = wildCardArray[indexOfSetTagAmountWithWildCard];
>                     indexOfSetTagAmountWithWildCard++;
>                 }else{
>                     storedVariableValues[innerNodes[i].attributes.name] = innerNodes[i].children[0].text;
>                 }
> 
>                 //If this set tag is a think tag's child
>                 if(previousThinkTag){
>                     previousThinkTag=false;
>                     text= text + '';
>                 }else{
>                     text = text + resolveSpecialNodes(innerNodes[i].children);
>                 }
>             }
>             else if(innerNodes[i].name === 'br'){
>                 text = text + '\n';
>             }
>             else if(innerNodes[i].name === 'think'){
>                 previousThinkTag=true;
>                 text = text + resolveSpecialNodes(innerNodes[i].children);
>             }
>             else if(innerNodes[i].name === 'sr'){
>                 var result;
> 
>                 //for-loop to go through all loaded AIML files
>                 for(var j = 0; j < domArray.length; j++){
>                     result = findCorrectCategory(lastWildCardValue, domArray[j].children);
>                     //if in one of the dom trees a matching pattern was found, exit this inner loop
>                     if(result){
>                         text = text + result;
>                         break;
>                     }
>                 }
>             }
>             else if(innerNodes[i].name === 'random'){
>                 //Get a random number and find the li tag chosen
>                 var randomNumber = Math.floor(Math.random() * (innerNodes[i].children.length));
>                 text = text + findFinalTextInTemplateNode([innerNodes[i].children[randomNumber]]);
>             ;
>             }
>             else if(innerNodes[i].name === 'star'){
>               if(innerNodes[i].attributes && innerNodes[i].attributes.index && innerNodes[i].attributes.index <= wildCardArray.length){
>                 text = text + wildCardArray[innerNodes[i].attributes.index-1];
>               }
>               else {
>                 text = text + wildCardArray[0];
>               }
>             }
>             else if(innerNodes[i].name === 'srai'){
>                 //take pattern text of srai node to get answer of another category
>                 var sraiText = '' + findFinalTextInTemplateNode(innerNodes[i].children);
>                 sraiText = sraiText.toUpperCase();
>                 var referredPatternText = sraiText;
>                 //call findCorrectCategory again to find the category that belongs to the srai node
>                 text = text + findCorrectCategory(referredPatternText, domCategories);
>             }
>             else if(innerNodes[i].name === 'condition') {
>                 // condition tag specification: list condition tag
>                 if(innerNodes[i].attributes.name === undefined){
>                     if(innerNodes[i].children === undefined){
>                         return undefined;
>                     }
>                     var child;
>                     for(var c in innerNodes[i].children){
>                         child = innerNodes[i].children[c];
>                         if(child.name === 'li'){
>                             if(child.attributes.value == undefined
>                                 || storedVariableValues[child.attributes.name] === child.attributes.value.toUpperCase()){
>                                 return findFinalTextInTemplateNode(child.children);
>                             }
>                         }
>                     }
>                 }
>                 // condition tag specification: multi condition tag
>                 else if(innerNodes[i].attributes.value !== undefined){
>                     if (storedVariableValues[innerNodes[i].attributes.name] === innerNodes[i].attributes.value.toUpperCase()) {
>                         text = text + resolveSpecialNodes(innerNodes[i].children);
>                     }
>                 }
>                 // condition tag specification: single name list condition tags
>                 else if(innerNodes[i].children !== undefined){
>                     var child;
>                     for(var c in innerNodes[i].children){
>                         child = innerNodes[i].children[c];
>                         if(child.name === 'li'){
>                             if(child.attributes.value === undefined
>                                 || storedVariableValues[innerNodes[i].attributes.name] === child.attributes.value.toUpperCase()){
>                                 return resolveSpecialNodes(child.children);
>                             }
>                         }
>                     }
> 
>                     return undefined;
>                 }
>             }
>             else if(innerNodes[i].name === undefined){
>                 //normal text (no special tag)
>                 text = text + innerNodes[i].text;
>             }
>         }
> 
>         text = cleanStringFormatCharacters(text);
>         return text;
>     }
> 
>     return travereseThroughDomToFindMatchingPattern(domCategories);
> }
> 
> var checkIfMessageMatchesPattern = function(userInput, patternText){
>     //convert wildcards in of the pattern node into a regex that matches every char
>     var regexPattern = convertWildcardToRegex(patternText);
> 
>     //add one with the text in function 'convertWildcardToRegex' here a space is added before and after the user input
>     //to prevent false matching
>     if(userInput.charAt(0) != " "){
>         userInput = " " + userInput;
>     }
> 
>     var lastCharacterPosition  = userInput.length - 1;
>     var lastCharacter = userInput.charAt(lastCharacterPosition);
>     if(lastCharacter != " "){
>         userInput = userInput + " ";
>     }
> 
>     //match userInput with the regex pattern
>     //if it matches, matchedString is defined
>     var matchedString = userInput.toUpperCase().match(regexPattern);
> 
>     if(matchedString){
>         //the matched pattern must be at least as long as the user input or must contain the regex
>         if(matchedString[0].length >= userInput.length || regexPattern.indexOf('[A-Z|0-9|\\s]*[A-Z|0-9|-]*[A-Z|0-9]*[!|.|?|\\s]*') > -1){
>             //if patternText contained a wild card, get the user input that were put into this wild card
>             //use original patternText (* is not replaced by regex!)
>             var information = getWildCardValue(userInput, patternText);
> 
>             return true;
>         }
>     }
>     else{
>         return false;
>     }
> }
> 
> var convertWildcardToRegex = function(text){
>     var firstCharacter = text.charAt(0);
>     //add a space before and after the pattern text (THIS IS LATER ALSO DONE FOR THE USER INPUT)
>     //prevents false matchings
>     //e.g. (HI as regex also matches HIM or HISTORY, but <space>HI</space> does only match <space>HI</space>)
>     if(firstCharacter != "*"){
>         var text = " " + text;
>     }
>     var lastCharacterPosition = text.length - 1;
>     var lastCharacter = text.charAt(lastCharacterPosition);
> 
>     //replace space before wildcard
>     var modifiedText = text.replace(' *', '*');
>     //replace wildcard (*) by regex
>     modifiedText = modifiedText.replace(/\*/g, '[A-Z|0-9|\\s]*[A-Z|0-9|\*|-]*[A-Z|0-9]*[!|.|?|\\s]*');
> 
>     if(lastCharacter != "*"){
> //        text = text + " ";
>         //pattern should also match when user inputs ends with a space, ?, ! or .
>         modifiedText = modifiedText + '[\\s|?|!|.]*';
>     }
> 
>     return modifiedText;
> }
> 
> var getWildCardValue = function(userInput, patternText){
>     //get all strings of the pattern that are divided by a *
>     //e.g. WHAT IS THE RELATION BETWEEN * AND * -> [WHAT IS THE RELATION BETWEEN , AND ]
>     var replaceArray = patternText.split('*');
>     var wildCardInput = userInput;
> 
>     if(replaceArray.length > 1){
>         //replace the string of the userInput which is fixed by the pattern
>         for(var i = 0; i < replaceArray.length; i++){
>             wildCardInput = wildCardInput.replace(new RegExp(replaceArray[i], 'i'), '|');
>         }
>         //split the wildCardInput string by | to differentiate multiple * inputs
>         //e.g. userInput = WHAT IS THE RELATION BETWEEN TIM AND STRUPPI?
>         //-> | TIM | STRUPPI
>         //-> [TIM, STRUPPI]
>         wildCardInput = wildCardInput.split('|');
>         //split function can create an array which also includes spaces etc. -> e.g. [TIM, " ", "", STRUPPI, " "]
>         //we just want the information
>         var wildCardArrayIndex = 0;
>         for(var i = 0; i < wildCardInput.length; i++){
>             if(wildCardInput[i] != '' && wildCardInput[i] != ' ' && wildCardInput != undefined){
>                 var wildCard = wildCardInput[i];
>                 var wildCardLastCharIndex = wildCard.length - 1;
>                 var firstCharOfWildCard = wildCard.charAt(0);
>                 var lastCharOfWildCard = wildCard.charAt(wildCardLastCharIndex);
> 
>                 try{
>                     //harmonize the wildcard string
>                     //remove first char if it is a space.
>                     //calculate the last index again since the length of the string changed
>                     if(firstCharOfWildCard === ' '){
>                         wildCard = wildCard.splice(0);
>                         wildCardLastCharIndex = wildCard.length - 1;
>                         lastCharOfWildCard = wildCard.charAt(wildCardLastCharIndex);
>                     }
>                     //if the last char is a space, remove it
>                     //calculate the last index again since the length of the string changed
>                     if(lastCharOfWildCard === ' '){
>                         wildCard = wildCard.substr(0, wildCardLastCharIndex);
>                         wildCardLastCharIndex = wildCard.length - 1;
>                         lastCharOfWildCard = wildCard.charAt(wildCardLastCharIndex);
>                     }
>                     if(lastCharOfWildCard === '?'){
>                         wildCard = wildCard.substr(0, wildCardLastCharIndex);
>                     }
>                 }
>                 catch(e){
> 
>                 }
>                 wildCardArray[wildCardArrayIndex] = wildCard;
>                 wildCardArrayIndex++;
>             }
>         }
>     }
>     if(wildCardArray.length - 1 >= 0){
>         lastWildCardValue = wildCardArray[wildCardArray.length - 1];
>     }
> 
>     return wildCardArray;
> }
> 
> module.exports = AIMLInterpreter;
\ No newline at end of file