not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

535 lines
22 KiB

  1. var storedVariableValues = {};
  2. var botAttributes = {};
  3. var lastWildCardValue = '';
  4. var wildCardArray = [];
  5. var domArray = [];
  6. var domIndex = 0;
  7. var isAIMLFileLoadingStarted = false;
  8. var isAIMLFileLoaded = false;
  9. var previousAnswer = '';
  10. var previousThinkTag = false;
  11. //botAttributes contain things like name, age, master, gender...
  12. var AIMLInterpreter = function(botAttributesParam){
  13. var self = this;
  14. botAttributes = botAttributesParam;
  15. this.loadAIMLFilesIntoArray = function(fileArray){
  16. isAIMLFileLoadingStarted = true;
  17. var fileIndex = 0;
  18. var readAIMLFile = function(filename){
  19. fileIndex++;
  20. //console.log(document.getElementById('botdata').import);
  21. var datastring;
  22. $(document).ready(function() {
  23. $.ajax({
  24. url : filename,
  25. dataType: "text",
  26. success : function (data) {
  27. datastring = data;
  28. //console.log(datastring);
  29. new DomJS().parse(datastring, function(err, dom) {
  30. if(dom == null){
  31. dom = "<aiml></aiml>";
  32. }
  33. var topCategories, topics;
  34. if (err) {
  35. // return cb(err);
  36. }
  37. if (dom.name === !'aiml') {
  38. // return cb('Unsupported file');
  39. }
  40. domArray[domIndex] = dom;
  41. domIndex++;
  42. if(fileIndex < fileArray.length){
  43. readAIMLFile(fileArray[fileIndex]);
  44. }
  45. else{
  46. console.log('AIML file is loaded!');
  47. isAIMLFileLoaded = true;
  48. }
  49. });
  50. }
  51. });
  52. });
  53. }
  54. readAIMLFile(fileArray[fileIndex]);
  55. };
  56. this.findAnswerInLoadedAIMLFiles = function(clientInput, cb){
  57. //check if all AIML files have been loaded. If not, call this method again after a delay
  58. if(isAIMLFileLoaded){
  59. wildCardArray = [];
  60. var result = '';
  61. for(var i = 0; i < domArray.length; i++){
  62. cleanDom(domArray[i].children);
  63. result = findCorrectCategory(clientInput, domArray[i].children);
  64. if(result){
  65. break;
  66. }
  67. }
  68. if(result){
  69. result = cleanStringFormatCharacters(result);
  70. previousAnswer = result;
  71. }
  72. cb(result, wildCardArray, clientInput);
  73. }
  74. else{
  75. var findAnswerInLoadedAIMLFilesWrapper = function(clientInput, cb){
  76. return function(){
  77. self.findAnswerInLoadedAIMLFiles(clientInput, cb);
  78. };
  79. };
  80. setTimeout(findAnswerInLoadedAIMLFilesWrapper(clientInput, cb), 1000);
  81. }
  82. };
  83. //restart the DOM in order to load a new AIML File
  84. this.restartDom = function(){
  85. domArray=[];
  86. domIndex=0;
  87. };
  88. };
  89. // remove string control characters (like line-breaks '\r\n', leading / trailing spaces etc.)
  90. var cleanStringFormatCharacters = function(str){
  91. var cleanedStr = str.replace(/\r\n/gi, '');
  92. cleanedStr = cleanedStr.replace(/^\s*/, '');
  93. cleanedStr = cleanedStr.replace(/\s*$/,'');
  94. return cleanedStr;
  95. }
  96. var cleanDom = function(childNodes){
  97. for(var i = 0; i < childNodes.length; i++){
  98. if(childNodes[i].hasOwnProperty('text') & typeof(childNodes[i].text) === 'string'){
  99. // remove all nodes of type 'text' when they just contain '\r\n'. This indicates line break in the AIML file
  100. if(childNodes[i].text.match(/^\s*\r\n\s*$/gi)){
  101. childNodes.splice(i, 1);
  102. }
  103. }
  104. }
  105. // traverse through whole tree by recursive calls
  106. for(var j = 0; j < childNodes.length; j++){
  107. if(childNodes[j].hasOwnProperty('children')){
  108. cleanDom(childNodes[j].children);
  109. }
  110. }
  111. };
  112. var findCorrectCategory = function(clientInput, domCategories){
  113. //indexOfSetTagAmountWithWildCard indicates how many sets with wildcard occur so that those sets store the correct wildcard value
  114. var indexOfSetTagAmountWithWildCard = 0;
  115. var travereseThroughDomToFindMatchingPattern= function(categories){
  116. for(var i = categories.length-1; i >= 0; i--){
  117. if(categories[i].name === 'category'){
  118. //traverse through the dom
  119. //text gets the value of the current pattern node
  120. var text = travereseThroughDomToFindMatchingPattern(categories[i].children);
  121. //check if the input of the user matches the pattern text
  122. var matches = checkIfMessageMatchesPattern(clientInput, text);
  123. if(matches){
  124. //check if a 'that' tag is existing. If yes, check if the text of the that tag matches the previous given answer.
  125. //If it does not match, continue the traversion through the AIML file
  126. var isMatchingThat = checkForThatMatching(categories[i].children);
  127. if(isMatchingThat){
  128. var text = findFinalTextInTemplateNode(categories[i].children);
  129. if(text){
  130. return text;
  131. }
  132. break;
  133. }
  134. }
  135. }
  136. else if(categories[i].name === 'pattern'){
  137. var text = resolveChildNodesInPatternNode(categories[i].children);
  138. return text;
  139. }
  140. }
  141. }
  142. var checkForThatMatching = function(categoryChildNodes){
  143. for(var i = 0; i < categoryChildNodes.length; i++){
  144. if(categoryChildNodes[i].name === 'that'){
  145. //if the previous answer of the bot does not match the that-tag text, then return undefined!
  146. if(categoryChildNodes[i].children[0].text != previousAnswer){
  147. return false;
  148. }
  149. else{
  150. return true;
  151. }
  152. }
  153. }
  154. //if no that tag was found, everything 'fits'
  155. return true;
  156. }
  157. var resolveChildNodesInPatternNode = function(patternChildNodes){
  158. var text = '';
  159. for(var i = 0; i < patternChildNodes.length; i++){
  160. if(patternChildNodes[i].name === 'bot'){
  161. text = text + botAttributes[patternChildNodes[i].attributes.name];
  162. }
  163. else if(patternChildNodes[i].name === 'get'){
  164. text = text + storedVariableValues[patternChildNodes[i].attributes.name];
  165. }
  166. else if(patternChildNodes[i].name === 'set'){
  167. text = text + patternChildNodes[i].children[0].text;
  168. }
  169. else{
  170. text = text + patternChildNodes[i].text;
  171. }
  172. }
  173. return text;
  174. }
  175. var findFinalTextInTemplateNode = function(childNodesOfTemplate){
  176. var text = '';
  177. //traverse through template nodes until final text is found
  178. //return it then to very beginning
  179. for(var i = 0; i < childNodesOfTemplate.length; i++){
  180. if(childNodesOfTemplate[i].name === 'template'){
  181. //traverse as long through the dom until final text was found
  182. //final text -> text after special nodes (bot, get, set,...) were resolved
  183. return findFinalTextInTemplateNode(childNodesOfTemplate[i].children);
  184. }
  185. else if(childNodesOfTemplate[i].name === 'condition'){
  186. return resolveSpecialNodes(childNodesOfTemplate);
  187. }
  188. else if(childNodesOfTemplate[i].name === 'random'){
  189. //if random node was found, its children are 'li' nodes.
  190. return resolveSpecialNodes(childNodesOfTemplate);
  191. }
  192. else if(childNodesOfTemplate[i].name === 'srai'){
  193. //take pattern text of srai node to get answer of another category
  194. var sraiText = '' + findFinalTextInTemplateNode(childNodesOfTemplate[i].children);
  195. sraiText = sraiText.toUpperCase();
  196. var referredPatternText = sraiText;
  197. //call findCorrectCategory again to find the category that belongs to the srai node
  198. var text = findCorrectCategory(referredPatternText, domCategories);
  199. return text;
  200. }
  201. else if(childNodesOfTemplate[i].name === 'li'){
  202. return findFinalTextInTemplateNode(childNodesOfTemplate[i].children);
  203. }
  204. else if(childNodesOfTemplate[i].name === 'br'){
  205. //br elements are used for putting '\n' into the text
  206. return resolveSpecialNodes(childNodesOfTemplate);
  207. }
  208. else if(childNodesOfTemplate[i].name === 'pattern'){
  209. //(here it is already checked that this is the right pattern that matches the user input)
  210. //make use of the functions of the special nodes - bot, set, get...
  211. resolveSpecialNodes(childNodesOfTemplate[i].children);
  212. continue;
  213. }
  214. else if(childNodesOfTemplate[i].name === 'think'){
  215. text = resolveSpecialNodes(childNodesOfTemplate);
  216. return text;
  217. }
  218. else if(childNodesOfTemplate[i].name === 'bot'){
  219. text = resolveSpecialNodes(childNodesOfTemplate);
  220. return text;
  221. }
  222. else if(childNodesOfTemplate[i].name === 'set'){
  223. text = resolveSpecialNodes(childNodesOfTemplate);
  224. return text;
  225. }
  226. else if(childNodesOfTemplate[i].name === 'get'){
  227. text = resolveSpecialNodes(childNodesOfTemplate);
  228. return text;
  229. }
  230. else if(childNodesOfTemplate[i].name === 'sr'){
  231. text = resolveSpecialNodes(childNodesOfTemplate);
  232. return text;
  233. }
  234. else if(childNodesOfTemplate[i].name === 'star'){
  235. text = resolveSpecialNodes(childNodesOfTemplate);
  236. return text;
  237. }
  238. else if(childNodesOfTemplate[i].name === 'that'){
  239. }
  240. else{
  241. //this is the text of template node
  242. //after all special functions (bot, get, set,...) were resolved
  243. //return that text
  244. text = resolveSpecialNodes(childNodesOfTemplate);
  245. if((text.match('[\\n|\\t]*[^A-Z|^a-z|^!|^?]*')[0] === '') && (text.indexOf('function ()') === -1)){
  246. return (text);
  247. }
  248. }
  249. }
  250. };
  251. var resolveSpecialNodes = function(innerNodes){
  252. var text = '';
  253. //concatenate string of all node children - normal text, bot tags, get tags, set tags...
  254. for(var i = 0; i < innerNodes.length; i++){
  255. if(innerNodes[i].name === 'bot'){
  256. //replace bot tags by the belonging bot attribute value
  257. text = text + botAttributes[innerNodes[i].attributes.name];
  258. }
  259. else if(innerNodes[i].name === 'get'){
  260. //replace get tag by belonging variable value
  261. var getAux = storedVariableValues[innerNodes[i].attributes.name];
  262. if(getAux === undefined){
  263. text = text + '';
  264. }else{
  265. text = text + getAux;
  266. }
  267. }
  268. else if(innerNodes[i].name === 'set'){
  269. //store value of set tag text into variable (variable name = attribute of set tag)
  270. //replace than set tag by the text value
  271. var aux='';
  272. if(innerNodes[i].children[0].name === 'star'){
  273. aux = resolveSpecialNodes(innerNodes[i].children);
  274. storedVariableValues[innerNodes[i].attributes.name] = aux;
  275. if(!previousThinkTag){
  276. text = text + aux;
  277. }
  278. }
  279. else if(innerNodes[i].children[0].text === '*'){
  280. //the first set-Tag with wildCard gets the first wildCardValue, the second set-Tag with wildCard gets the second wildCardValue etc.
  281. storedVariableValues[innerNodes[i].attributes.name] = wildCardArray[indexOfSetTagAmountWithWildCard];
  282. indexOfSetTagAmountWithWildCard++;
  283. }else{
  284. storedVariableValues[innerNodes[i].attributes.name] = innerNodes[i].children[0].text;
  285. }
  286. //If this set tag is a think tag's child
  287. if(previousThinkTag){
  288. previousThinkTag=false;
  289. text= text + '';
  290. }else{
  291. text = text + resolveSpecialNodes(innerNodes[i].children);
  292. }
  293. }
  294. else if(innerNodes[i].name === 'br'){
  295. text = text + '\n';
  296. }
  297. else if(innerNodes[i].name === 'think'){
  298. previousThinkTag=true;
  299. text = text + resolveSpecialNodes(innerNodes[i].children);
  300. }
  301. else if(innerNodes[i].name === 'sr'){
  302. var result;
  303. //for-loop to go through all loaded AIML files
  304. for(var j = 0; j < domArray.length; j++){
  305. result = findCorrectCategory(lastWildCardValue, domArray[j].children);
  306. //if in one of the dom trees a matching pattern was found, exit this inner loop
  307. if(result){
  308. text = text + result;
  309. break;
  310. }
  311. }
  312. }
  313. else if(innerNodes[i].name === 'random'){
  314. //Get a random number and find the li tag chosen
  315. var randomNumber = Math.floor(Math.random() * (innerNodes[i].children.length));
  316. text = text + findFinalTextInTemplateNode([innerNodes[i].children[randomNumber]]);
  317. ;
  318. }
  319. else if(innerNodes[i].name === 'star'){
  320. text = text + lastWildCardValue;
  321. }
  322. else if(innerNodes[i].name === 'srai'){
  323. //take pattern text of srai node to get answer of another category
  324. var sraiText = '' + findFinalTextInTemplateNode(innerNodes[i].children);
  325. sraiText = sraiText.toUpperCase();
  326. var referredPatternText = sraiText;
  327. //call findCorrectCategory again to find the category that belongs to the srai node
  328. text = text + findCorrectCategory(referredPatternText, domCategories);
  329. }
  330. else if(innerNodes[i].name === 'condition') {
  331. // condition tag specification: list condition tag
  332. if(innerNodes[i].attributes.name === undefined){
  333. if(innerNodes[i].children === undefined){
  334. return undefined;
  335. }
  336. var child;
  337. for(var c in innerNodes[i].children){
  338. child = innerNodes[i].children[c];
  339. if(child.name === 'li'){
  340. if(child.attributes.value == undefined
  341. || storedVariableValues[child.attributes.name] === child.attributes.value.toUpperCase()){
  342. return findFinalTextInTemplateNode(child.children);
  343. }
  344. }
  345. }
  346. }
  347. // condition tag specification: multi condition tag
  348. else if(innerNodes[i].attributes.value !== undefined){
  349. if (storedVariableValues[innerNodes[i].attributes.name] === innerNodes[i].attributes.value.toUpperCase()) {
  350. text = text + resolveSpecialNodes(innerNodes[i].children);
  351. }
  352. }
  353. // condition tag specification: single name list condition tags
  354. else if(innerNodes[i].children !== undefined){
  355. var child;
  356. for(var c in innerNodes[i].children){
  357. child = innerNodes[i].children[c];
  358. if(child.name === 'li'){
  359. if(child.attributes.value === undefined
  360. || storedVariableValues[innerNodes[i].attributes.name] === child.attributes.value.toUpperCase()){
  361. return resolveSpecialNodes(child.children);
  362. }
  363. }
  364. }
  365. return undefined;
  366. }
  367. }
  368. else if(innerNodes[i].name === undefined){
  369. //normal text (no special tag)
  370. text = text + innerNodes[i].text;
  371. }
  372. }
  373. text = cleanStringFormatCharacters(text);
  374. return text;
  375. }
  376. return travereseThroughDomToFindMatchingPattern(domCategories);
  377. }
  378. var checkIfMessageMatchesPattern = function(userInput, patternText){
  379. //convert wildcards in of the pattern node into a regex that matches every char
  380. var regexPattern = convertWildcardToRegex(patternText);
  381. //add one with the text in function 'convertWildcardToRegex' here a space is added before and after the user input
  382. //to prevent false matching
  383. if(userInput.charAt(0) != " "){
  384. userInput = " " + userInput;
  385. }
  386. var lastCharacterPosition = userInput.length - 1;
  387. var lastCharacter = userInput.charAt(lastCharacterPosition);
  388. if(lastCharacter != " "){
  389. userInput = userInput + " ";
  390. }
  391. //match userInput with the regex pattern
  392. //if it matches, matchedString is defined
  393. var matchedString = userInput.toUpperCase().match(regexPattern);
  394. if(matchedString){
  395. //the matched pattern must be at least as long as the user input or must contain the regex
  396. if(matchedString[0].length >= userInput.length || regexPattern.indexOf('[A-Z|0-9|\\s]*[A-Z|0-9|-]*[A-Z|0-9]*[!|.|?|\\s]*') > -1){
  397. //if patternText contained a wild card, get the user input that were put into this wild card
  398. //use original patternText (* is not replaced by regex!)
  399. var information = getWildCardValue(userInput, patternText);
  400. return true;
  401. }
  402. }
  403. else{
  404. return false;
  405. }
  406. }
  407. var convertWildcardToRegex = function(text){
  408. var firstCharacter = text.charAt(0);
  409. //add a space before and after the pattern text (THIS IS LATER ALSO DONE FOR THE USER INPUT)
  410. //prevents false matchings
  411. //e.g. (HI as regex also matches HIM or HISTORY, but <space>HI</space> does only match <space>HI</space>)
  412. if(firstCharacter != "*"){
  413. var text = " " + text;
  414. }
  415. var lastCharacterPosition = text.length - 1;
  416. var lastCharacter = text.charAt(lastCharacterPosition);
  417. //replace space before wildcard
  418. var modifiedText = text.replace(' *', '*');
  419. //replace wildcard (*) by regex
  420. modifiedText = modifiedText.replace(/\*/g, '[A-Z|0-9|\\s]*[A-Z|0-9|\*|-]*[A-Z|0-9]*[!|.|?|\\s]*');
  421. if(lastCharacter != "*"){
  422. // text = text + " ";
  423. //pattern should also match when user inputs ends with a space, ?, ! or .
  424. modifiedText = modifiedText + '[\\s|?|!|.]*';
  425. }
  426. return modifiedText;
  427. }
  428. var getWildCardValue = function(userInput, patternText){
  429. //get all strings of the pattern that are divided by a *
  430. //e.g. WHAT IS THE RELATION BETWEEN * AND * -> [WHAT IS THE RELATION BETWEEN , AND ]
  431. var replaceArray = patternText.split('*');
  432. var wildCardInput = userInput;
  433. if(replaceArray.length > 1){
  434. //replace the string of the userInput which is fixed by the pattern
  435. for(var i = 0; i < replaceArray.length; i++){
  436. wildCardInput = wildCardInput.replace(new RegExp(replaceArray[i], 'i'), '|');
  437. }
  438. //split the wildCardInput string by | to differentiate multiple * inputs
  439. //e.g. userInput = WHAT IS THE RELATION BETWEEN TIM AND STRUPPI?
  440. //-> | TIM | STRUPPI
  441. //-> [TIM, STRUPPI]
  442. wildCardInput = wildCardInput.split('|');
  443. //split function can create an array which also includes spaces etc. -> e.g. [TIM, " ", "", STRUPPI, " "]
  444. //we just want the information
  445. var wildCardArrayIndex = 0;
  446. for(var i = 0; i < wildCardInput.length; i++){
  447. if(wildCardInput[i] != '' && wildCardInput[i] != ' ' && wildCardInput != undefined){
  448. var wildCard = wildCardInput[i];
  449. var wildCardLastCharIndex = wildCard.length - 1;
  450. var firstCharOfWildCard = wildCard.charAt(0);
  451. var lastCharOfWildCard = wildCard.charAt(wildCardLastCharIndex);
  452. try{
  453. //harmonize the wildcard string
  454. //remove first char if it is a space.
  455. //calculate the last index again since the length of the string changed
  456. if(firstCharOfWildCard === ' '){
  457. wildCard = wildCard.splice(0);
  458. wildCardLastCharIndex = wildCard.length - 1;
  459. lastCharOfWildCard = wildCard.charAt(wildCardLastCharIndex);
  460. }
  461. //if the last char is a space, remove it
  462. //calculate the last index again since the length of the string changed
  463. if(lastCharOfWildCard === ' '){
  464. wildCard = wildCard.substr(0, wildCardLastCharIndex);
  465. wildCardLastCharIndex = wildCard.length - 1;
  466. lastCharOfWildCard = wildCard.charAt(wildCardLastCharIndex);
  467. }
  468. if(lastCharOfWildCard === '?'){
  469. wildCard = wildCard.substr(0, wildCardLastCharIndex);
  470. }
  471. }
  472. catch(e){
  473. }
  474. wildCardArray[wildCardArrayIndex] = wildCard;
  475. wildCardArrayIndex++;
  476. }
  477. }
  478. }
  479. if(wildCardArray.length - 1 >= 0){
  480. lastWildCardValue = wildCardArray[wildCardArray.length - 1];
  481. }
  482. return wildCardArray;
  483. }