<commit_before>/*
Self note: when pasting code in github, remember to move the button onclick function to inside the window onload function. Move it outside on the onload function when using jsfiddle
*/
window.onload = function() {
  console.log("Started");
  var b = document.getElementById("b");
  //button
    //function called when Execute Program button is clicked
  b.onclick = function() {
    var program = document.getElementById("program");
    var output = document.getElementById("output");
    output.innerText = ""; //clearing output
    var input = document.getElementById("input");
    var programRunner = new Program(program.value+"", input.value+"");
    var programResult = programRunner.Main()[1];
    output.innerText = programResult;
    console.log("Output: " + programResult);
    console.log("---------------");
    console.log(stack+6);
  }

  var program = document.getElementById("program");
  var input = document.getElementById("input");

  //console.log(getQueryVariable("code") +":"+ getQueryVariable("input"));

  //t.textContent = getQueryVariable("code");
  //n.textContent = getQueryVariable("input");
};

var Program = function(_input, _args) {

  //IMPORTANT GLOBAL VARS
  this.stack = "";
  this.code = _input;
  this.args = _args;
  this.input = [];
	//var this.that;
  var that  = this;

  this.noCaretBuilding = true;
  this.caretMode = true;
  this.stackI = 0;
  this.stackF = 0.0;
  this.stackA = [];
  this.stackMode = ""; //"" = String; I = int; F = float; A = array
  this.operationMode = "";

  this.buildMode = "";
  this.stringBuilder = "";
  this.stringMode = "n"; //n is normal, e is escape
  this.intBuilder = "";
  this.floatBuilder = "";
  this.regexBuilder = "";
  this.regexMode = "n"; //n is normal, f is flag
  this.evalBuilder = "";

  this.caretFuncMode = "n"; //n for normal, e for escape

  privateVars = "stack,code,args,input,noCaretBuilding,caretMode,stackI,stackF,stackA,stackMode,operationMode,buildMode,stringBuilder,stringMode,intBuilder,floatBuilder,regexBuilder,regexMode,evalBuilder,caretFuncMode".split(",");

  var stack,code,args,input,noCaretBuilding,caretMode,stackI,stackF,stackA,stackMode,operationMode,buildMode,stringBuilder,stringMode,intBuilder,floatBuilder,regexBuilder,regexMode,evalBuilder,caretFuncMode;
//console.log("um"+that.stack);
  privateToPublic = function() {
    for(Var of privateVars) {
      //console.log(that.stack);
			//eval(`stack=this.stack+"a"`);
      eval(`${Var} = that.${Var}`);
    }
  }
console.log(this.stack);
  publicToPrivate = function() {
    for(Var of this.privateVars) {
      eval(`that.${Var} = ${Var}`);
    }
  }
privateToPublic();
  //parsing the c^rrot datetype
  caret = function(char) {
    console.log(char);
    if (caretFuncMode == "e") {
      caretFuncMode = "n";
      return char;
    }
    if (char === "^") return null;
    if (char === "#") return input.join("\n");
    if (char === "$") {
      return input.shift(-1);
    }
    if (char === "\\") {
      caretFuncMode = "e";
      return "";
    }
    return char;
  }

  //applying monad operations
  applyOperation = function(arg) {
    //what to do for each op
    switch (operationMode) {
      //TODO: add support for other stack types
      case "+":
        if (buildMode === "string") {
          if(stackMode === "A") {
            stackMode = "";
            stack = stackA.join(stringBuilder);
          }else{
            stackMode = "";
            stack += stringBuilder;
          }
          stringBuilder = "";
          buildMode="";
        } else if (buildMode === "float") {
          //v for converting int to float if necessary
          if (stackMode === "I" && /\./.test(floatBuilder)) {
            stackMode = "F";
            stackF = stackI + 0.0;
          }
          if(stackMode === "A") {
            for(var i=0; i<stackA.length; i++) {
              stackA[i] = parseFloat(stackA[i]+"")+parseFloat(floatBuilder);
            }
          }else{
            eval(`stack${stackMode} += parseFloat(${floatBuilder})`);
          }
          floatBuilder = "";
          buildMode="";
        } else if(arg === " ") {
          if(stackMode === "A") {
            stackMode = "F";
            stackF = 0.0;
            for(element of stackA) {
              stackF += parseFloat(element+"");
            }
          }
        }
        break;
      case "-":
        if (buildMode === "string") {
          stackMode = "";
          stack = stack.split(stringBuilder).join("");
          stringBuilder = "";
          buildMode="";
        } else if (buildMode === "float") {
          //v for converting int to float if necessary
          if (stackMode === "I" && /\./.test(floatBuilder)) {
            stackMode = "F";
            stackF = stackI + 0.0;
          }
          if(stackMode === "A") {
            for(var i=0; i<stackA.length; i++) {
              stackA[i] = parseFloat(stackA[i]+"")-parseFloat(floatBuilder);
            }
          }else{
            if(stackMode === "") {
              stack = stack.substring(parseInt(floatBuilder+""));
            }else {
              eval(`stack${stackMode} -= parseFloat(${floatBuilder})`);
            }
          }
          floatBuilder = "";
          buildMode="";
        }
        break;
      case "*":
        //TODO: add support for string stack type
        if (buildMode === "string") {/*
          stackMode = "";
          stack = stack.split(stringBuilder).join("");
          stringBuilder = "";*/
          buildMode="";
        } else if (buildMode === "float") {
          //v for converting int to float if necessary
          if (stackMode === "I" && /\./.test(floatBuilder)) {
            stackMode = "F";
            stackF = stackI + 0.0;
          }
          if(stackMode === "A") {
            for(var i=0; i<stackA.length; i++) {
              stackA[i] = parseFloat(stackA[i]+"")*parseFloat(floatBuilder);
            }
            break;
          }else{
            if(stackMode === "") {
              //TODO: fractional number support
              var originalStack = stack;
              for(var i=0;i<parseInt(floatBuilder+"");i++) {
                stack+=originalStack;
              }
            }else {
              eval(`stack${stackMode} *= parseFloat(${floatBuilder})`);
            }
          }
          floatBuilder = "";
          buildMode="";
        } else if(arg === " ") {
          if(stackMode === "A") {
            stackMode = "F";
            stackF = 1.0;
            for(element of stackA) {
              stackF *= parseFloat(element+"");
            }
          }
        }
        break;
      case "\/":
        //TODO: add support for string stack type
        if (buildMode === "regex") {
          regexMode = "n";
          stackMode = "A";
          eval(`stackA=stack.match(${regexBuilder})`);
					if(stackA === null){
						stackA = [];
					}
          regexBuilder = "";
          buildMode="";
        } else if (buildMode === "float") {
          //v for converting int to float if necessary
          if (stackMode === "I" && /\./.test(floatBuilder)) {
            stackMode = "F";
            stackF = stackI + 0.0;
          }
          if(stackMode === "A") {
            for(var i=0; i<stackA.length; i++) {
              stackA[i] = parseFloat(stackA[i]+"")/parseFloat(floatBuilder);
            }
            break;
          }else{
            if(stackMode === "") {
              var spliceIndex=parseInt(floatBuilder+"");
              stack=stack.slice(0,spliceIndex)+stack.slice(spliceIndex+1,stack.length);
            }else {
              eval(`stack${stackMode} /= parseFloat(${floatBuilder})`);
            }
          }
          floatBuilder = "";
          buildMode="";
        }
        break;
      case "S":
        if(buildMode === "string" || buildMode === "float") {
          if(stackMode === "A") {
						if(buildMode === "float"){
            	stack = stackA.join(floatBuilder+"");
							floatBuilder = "";
						}else if(buildMode === "string"){
							stack = stackA.join(stringBuilder+"");
							stringBuilder = "";
						}
            stackMode = "";
          }
          buildMode = "";
        }
        break;
    }
    operationMode = "";
  }

  //MAIN function
  this.Main = function() {

    //code = _input;
    input = args.split("\n");
    for (var i = 0; i < code.length; i++) {
      //note: in the builder, when I use continue; it means that I don't want to applyOperation since the building is incomplete. Otherwise, it could even be misinterpreted as an operator
      var onChar = code[i]; //this is the char we are processing
      if (caretMode) {
        var caretResult = caret(onChar);
        if (caretResult === null) caretMode = false;
        else stack += caretResult;
      } else {
        //building the string
        if (buildMode === "string") {
          if(stringMode == "e") {
            stringBuilder += onChar;
            stringMode = "n";
            continue;
          }
          switch (onChar) {
            case "\"":
              //stringBuilding = false;
              //buildMode = "";
              break;
            case "\\":
              stringMode = "e";
              continue;
            case "$":
              stringBuilder += (input.shift(-1));
              continue;
            case "#":
              stringBuilder += input.join("\n");
              continue;
            default:
              stringBuilder += onChar;
              console.log("stringBuilder=" + stringBuilder);
              continue;
          }
        }

        //for stringbuilding
        if (onChar == "\"" && stringBuilder == "" && buildMode === "") {
          //stringBuilding = true;
          buildMode="string";
          continue;
        }

        //float arg using "$"
        if(operationMode !== "" && buildMode === "" && /[$#]/.test(onChar)) {
          if(stackMode === "F" && onChar === "$") {
            floatBuilder = parseFloat(input.shift(-1)+"");
            applyOperation();
          }else if(stackMode === "") {
            stringBuilder = onChar==="$"?input.shift(-1):input.join("\n");
          }
        }

        //floatbuilding
        if (buildMode === "float") {
          if (/[0-9\.]/.test(onChar)) {
            floatBuilder += onChar;
            console.log("floatBuilder=" + floatBuilder);
            if (i === code.length - 1) {
              //floatBuilding = false;
              //buildMode = "";
            } else {
              continue;
            }
          } else {
            //floatBuilding = false;
            //buildMode = "";
            applyOperation();
          }
        }
        //floatbuilding
        if (/[0-9\.\-]/.test(onChar) && floatBuilder === "" && operationMode !== "" && buildMode === "") {
          //floatBuilding = true;
          buildMode="float";
          floatBuilder += onChar;
          console.log("floatBuilder=" + floatBuilder);
          if (i !== code.length - 1) continue;
        }

        //regexBuilding
        if (buildMode === "regex") {
          if (regexMode === "n") {
						if(onChar === "#"){
							regexBuilder += input.shift(-1);
						}else{
            	regexBuilder += onChar;
						}
            console.log("regexBuilder=" + regexBuilder);

            //start /flags?/                escaped slash
            if(onChar === "\/" && code[i-1] !== "\\") {
              if(i !== code.length - 1) {
                regexMode = "f";
                continue;
              }else{
                applyOperation();
              }
            }else {
              continue;
            }
          }else if(regexMode === "f") {
            //if it is a flag, append it to the regexBuilder
            if(/[gmif]/.test(onChar)) {
              regexBuilder += onChar;
              if(i === code.length-1) {
                console.log("regexBuilder=" + regexBuilder);
                applyOperation();
              }else{
                continue;
              }
            }else{
              //regexBuilding = false;
              //buildMode = "";
              console.log("regexBuilder=" + regexBuilder);
              applyOperation();
            }
          }
        }

        //regexBuilding
        if (onChar === "\/" && regexBuilder === "" && operationMode !== "" && buildMode === "") {
          //regexBuilding = true;
          buildMode="regex";
          regexBuilder += onChar;
          regexMode = "n";
          continue;
        }

        //evalBuilding
        /*
        if(buildMode === "eval") {
          if(onChar === ")") {
            //escaping `)`
            if(code[i] === "\\") {
              evalBuilder.replace(/.$/g,")");
            }else{
              //eval ends
              buildMode = "";
              var evaluatedExpression = Main(evalBuilder,input.join`\n`);
              eval(`${evaluatedExpression[0]}Builder="${evaluatedExpression[1]}"`);
            }
          }else{
            evalBuilder += onChar;
          }
        }

        //eval building
        if (onChar === "(" && evalBuilder === "" && operationMode !== "" && buildMode === "") {
          buildMode = "eval";
          //stringBuilder = "hi";
          console.log("toEval");
        }*/

        //normal operations
        if (operationMode == "") {
          switch (onChar) {
            case '^':
              caretMode = true;
              break;
            case 'I':
              //v for handling case of empty stack
              eval(`stackI = parseFloat((stack${stackMode}+"").length>0?stack${stackMode}+"":"0")`);
              stackMode = "I";
              break;
            case 'F':
              eval(`stackF = parseFloat((stack${stackMode}+"").length>0?stack${stackMode}+"":"0")`);
              stackMode = "F";
              break;
            case 'S':
              if(stackMode === "A") {
              //S accepts an argument that tells it what comes in join(what)
                operationMode = "S";
              }else{
                eval(`stack = stack${stackMode}+""`);
                stackMode = "";
              }
              break;
            case 'A':
              //monad it takes the next char as arg
              eval(`stackA = (stack${stackMode}+"").split(\`${code[i+1]+""}\`)`);
              stackMode = "A";
              i++;
              break;
            default:
              operationMode = onChar;
          }
          continue;
        } else {
          console.log(operationMode);
          applyOperation(onChar);
          continue;
        }
      }
    }

    var returnValue = eval(`stack${stackMode}`);
    return [stackMode, returnValue];
  }
}
/**
 * TODO:
 * =====
 *
 * intBuilder            [ ]
 * convert to string     [x]
 * fix bug with 1^I+4S+4 [x]
 * - for stack (string)  [x]
 * negative numbers      [x]
 * * op for fraction and negatives for stack [ ]
 * / op for stack with regex arg             [x]
 * / op for stack with int arg               [x]
 * operation support for arrays              [ ]
 * - stringMode string arg substitution      [ ]
 **/

/**
 * DOCS (will update as I complete commands)
 * (is incomplete as I am adding everything to GitHub)
 * =========================================
 *
 * stack^operations^... (goes on endlessly)
 *  - stack  = string
 *  - stackI = int (also float maybe)
 *
 *  (int is inaccurately used to describe any number)
 *
 * data types:
 *  there are 4 types of constants in carrot: c^rrot, string, number, array, regex
 *
 *  c^rrot: data
 *  string: "data" (has escape characters)
 *  num: 123.456
 *  regex: /^(\d)u+$/gmi
 *  space: ` `
 *
 * operators:
 *  ops are defined by their type (monad or nilad), the arguments they take
 *  (int or string or regex) and the stackMode (string, float, int, array, ...)
 *
 *  nilads (takes in 0 arguments):
 *   I F S
 *  monads:
 *   + - * / A
 *
 * list of operators
 *  I = converts to intMode
 *  F = converts to floatMode
 *  S = converts to stringMode
 *  A = converts to arrayMode by splitting at next char
 *  + = string concatenation/int addition
 *  - = string (remove_all_instancesOf/substring)/int subtraction
 *  * = string (/multi-duplication)/int multiplication
 *  / = string (regex_match/remove_char_at_index)/int division
 **/
<commit_msg>removed stack error in console<commit_after>/*
Self note: when pasting code in github, remember to move the button onclick function to inside the window onload function. Move it outside on the onload function when using jsfiddle
*/
window.onload = function() {
  console.log("Started");
  var b = document.getElementById("b");
  //button
    //function called when Execute Program button is clicked
  b.onclick = function() {
    var program = document.getElementById("program");
    var output = document.getElementById("output");
    output.innerText = ""; //clearing output
    var input = document.getElementById("input");
    var programRunner = new Program(program.value+"", input.value+"");
    var programResult = programRunner.Main()[1];
    output.innerText = programResult;
    console.log("Output: " + programResult);
    console.log("---------------");
  }

  var program = document.getElementById("program");
  var input = document.getElementById("input");

  //console.log(getQueryVariable("code") +":"+ getQueryVariable("input"));

  //t.textContent = getQueryVariable("code");
  //n.textContent = getQueryVariable("input");
};

var Program = function(_input, _args) {

  //IMPORTANT GLOBAL VARS
  this.stack = "";
  this.code = _input;
  this.args = _args;
  this.input = [];
	//var this.that;
  var that  = this;

  this.noCaretBuilding = true;
  this.caretMode = true;
  this.stackI = 0;
  this.stackF = 0.0;
  this.stackA = [];
  this.stackMode = ""; //"" = String; I = int; F = float; A = array
  this.operationMode = "";

  this.buildMode = "";
  this.stringBuilder = "";
  this.stringMode = "n"; //n is normal, e is escape
  this.intBuilder = "";
  this.floatBuilder = "";
  this.regexBuilder = "";
  this.regexMode = "n"; //n is normal, f is flag
  this.evalBuilder = "";

  this.caretFuncMode = "n"; //n for normal, e for escape

  privateVars = "stack,code,args,input,noCaretBuilding,caretMode,stackI,stackF,stackA,stackMode,operationMode,buildMode,stringBuilder,stringMode,intBuilder,floatBuilder,regexBuilder,regexMode,evalBuilder,caretFuncMode".split(",");

  var stack,code,args,input,noCaretBuilding,caretMode,stackI,stackF,stackA,stackMode,operationMode,buildMode,stringBuilder,stringMode,intBuilder,floatBuilder,regexBuilder,regexMode,evalBuilder,caretFuncMode;
//console.log("um"+that.stack);
  privateToPublic = function() {
    for(Var of privateVars) {
      //console.log(that.stack);
			//eval(`stack=this.stack+"a"`);
      eval(`${Var} = that.${Var}`);
    }
  }
console.log(this.stack);
  publicToPrivate = function() {
    for(Var of this.privateVars) {
      eval(`that.${Var} = ${Var}`);
    }
  }
privateToPublic();
  //parsing the c^rrot datetype
  caret = function(char) {
    console.log(char);
    if (caretFuncMode == "e") {
      caretFuncMode = "n";
      return char;
    }
    if (char === "^") return null;
    if (char === "#") return input.join("\n");
    if (char === "$") {
      return input.shift(-1);
    }
    if (char === "\\") {
      caretFuncMode = "e";
      return "";
    }
    return char;
  }

  //applying monad operations
  applyOperation = function(arg) {
    //what to do for each op
    switch (operationMode) {
      //TODO: add support for other stack types
      case "+":
        if (buildMode === "string") {
          if(stackMode === "A") {
            stackMode = "";
            stack = stackA.join(stringBuilder);
          }else{
            stackMode = "";
            stack += stringBuilder;
          }
          stringBuilder = "";
          buildMode="";
        } else if (buildMode === "float") {
          //v for converting int to float if necessary
          if (stackMode === "I" && /\./.test(floatBuilder)) {
            stackMode = "F";
            stackF = stackI + 0.0;
          }
          if(stackMode === "A") {
            for(var i=0; i<stackA.length; i++) {
              stackA[i] = parseFloat(stackA[i]+"")+parseFloat(floatBuilder);
            }
          }else{
            eval(`stack${stackMode} += parseFloat(${floatBuilder})`);
          }
          floatBuilder = "";
          buildMode="";
        } else if(arg === " ") {
          if(stackMode === "A") {
            stackMode = "F";
            stackF = 0.0;
            for(element of stackA) {
              stackF += parseFloat(element+"");
            }
          }
        }
        break;
      case "-":
        if (buildMode === "string") {
          stackMode = "";
          stack = stack.split(stringBuilder).join("");
          stringBuilder = "";
          buildMode="";
        } else if (buildMode === "float") {
          //v for converting int to float if necessary
          if (stackMode === "I" && /\./.test(floatBuilder)) {
            stackMode = "F";
            stackF = stackI + 0.0;
          }
          if(stackMode === "A") {
            for(var i=0; i<stackA.length; i++) {
              stackA[i] = parseFloat(stackA[i]+"")-parseFloat(floatBuilder);
            }
          }else{
            if(stackMode === "") {
              stack = stack.substring(parseInt(floatBuilder+""));
            }else {
              eval(`stack${stackMode} -= parseFloat(${floatBuilder})`);
            }
          }
          floatBuilder = "";
          buildMode="";
        }
        break;
      case "*":
        //TODO: add support for string stack type
        if (buildMode === "string") {/*
          stackMode = "";
          stack = stack.split(stringBuilder).join("");
          stringBuilder = "";*/
          buildMode="";
        } else if (buildMode === "float") {
          //v for converting int to float if necessary
          if (stackMode === "I" && /\./.test(floatBuilder)) {
            stackMode = "F";
            stackF = stackI + 0.0;
          }
          if(stackMode === "A") {
            for(var i=0; i<stackA.length; i++) {
              stackA[i] = parseFloat(stackA[i]+"")*parseFloat(floatBuilder);
            }
            break;
          }else{
            if(stackMode === "") {
              //TODO: fractional number support
              var originalStack = stack;
              for(var i=0;i<parseInt(floatBuilder+"");i++) {
                stack+=originalStack;
              }
            }else {
              eval(`stack${stackMode} *= parseFloat(${floatBuilder})`);
            }
          }
          floatBuilder = "";
          buildMode="";
        } else if(arg === " ") {
          if(stackMode === "A") {
            stackMode = "F";
            stackF = 1.0;
            for(element of stackA) {
              stackF *= parseFloat(element+"");
            }
          }
        }
        break;
      case "\/":
        //TODO: add support for string stack type
        if (buildMode === "regex") {
          regexMode = "n";
          stackMode = "A";
          eval(`stackA=stack.match(${regexBuilder})`);
					if(stackA === null){
						stackA = [];
					}
          regexBuilder = "";
          buildMode="";
        } else if (buildMode === "float") {
          //v for converting int to float if necessary
          if (stackMode === "I" && /\./.test(floatBuilder)) {
            stackMode = "F";
            stackF = stackI + 0.0;
          }
          if(stackMode === "A") {
            for(var i=0; i<stackA.length; i++) {
              stackA[i] = parseFloat(stackA[i]+"")/parseFloat(floatBuilder);
            }
            break;
          }else{
            if(stackMode === "") {
              var spliceIndex=parseInt(floatBuilder+"");
              stack=stack.slice(0,spliceIndex)+stack.slice(spliceIndex+1,stack.length);
            }else {
              eval(`stack${stackMode} /= parseFloat(${floatBuilder})`);
            }
          }
          floatBuilder = "";
          buildMode="";
        }
        break;
      case "S":
        if(buildMode === "string" || buildMode === "float") {
          if(stackMode === "A") {
						if(buildMode === "float"){
            	stack = stackA.join(floatBuilder+"");
							floatBuilder = "";
						}else if(buildMode === "string"){
							stack = stackA.join(stringBuilder+"");
							stringBuilder = "";
						}
            stackMode = "";
          }
          buildMode = "";
        }
        break;
    }
    operationMode = "";
  }

  //MAIN function
  this.Main = function() {

    //code = _input;
    input = args.split("\n");
    for (var i = 0; i < code.length; i++) {
      //note: in the builder, when I use continue; it means that I don't want to applyOperation since the building is incomplete. Otherwise, it could even be misinterpreted as an operator
      var onChar = code[i]; //this is the char we are processing
      if (caretMode) {
        var caretResult = caret(onChar);
        if (caretResult === null) caretMode = false;
        else stack += caretResult;
      } else {
        //building the string
        if (buildMode === "string") {
          if(stringMode == "e") {
            stringBuilder += onChar;
            stringMode = "n";
            continue;
          }
          switch (onChar) {
            case "\"":
              //stringBuilding = false;
              //buildMode = "";
              break;
            case "\\":
              stringMode = "e";
              continue;
            case "$":
              stringBuilder += (input.shift(-1));
              continue;
            case "#":
              stringBuilder += input.join("\n");
              continue;
            default:
              stringBuilder += onChar;
              console.log("stringBuilder=" + stringBuilder);
              continue;
          }
        }

        //for stringbuilding
        if (onChar == "\"" && stringBuilder == "" && buildMode === "") {
          //stringBuilding = true;
          buildMode="string";
          continue;
        }

        //float arg using "$"
        if(operationMode !== "" && buildMode === "" && /[$#]/.test(onChar)) {
          if(stackMode === "F" && onChar === "$") {
            floatBuilder = parseFloat(input.shift(-1)+"");
            applyOperation();
          }else if(stackMode === "") {
            stringBuilder = onChar==="$"?input.shift(-1):input.join("\n");
          }
        }

        //floatbuilding
        if (buildMode === "float") {
          if (/[0-9\.]/.test(onChar)) {
            floatBuilder += onChar;
            console.log("floatBuilder=" + floatBuilder);
            if (i === code.length - 1) {
              //floatBuilding = false;
              //buildMode = "";
            } else {
              continue;
            }
          } else {
            //floatBuilding = false;
            //buildMode = "";
            applyOperation();
          }
        }
        //floatbuilding
        if (/[0-9\.\-]/.test(onChar) && floatBuilder === "" && operationMode !== "" && buildMode === "") {
          //floatBuilding = true;
          buildMode="float";
          floatBuilder += onChar;
          console.log("floatBuilder=" + floatBuilder);
          if (i !== code.length - 1) continue;
        }

        //regexBuilding
        if (buildMode === "regex") {
          if (regexMode === "n") {
						if(onChar === "#"){
							regexBuilder += input.shift(-1);
						}else{
            	regexBuilder += onChar;
						}
            console.log("regexBuilder=" + regexBuilder);

            //start /flags?/                escaped slash
            if(onChar === "\/" && code[i-1] !== "\\") {
              if(i !== code.length - 1) {
                regexMode = "f";
                continue;
              }else{
                applyOperation();
              }
            }else {
              continue;
            }
          }else if(regexMode === "f") {
            //if it is a flag, append it to the regexBuilder
            if(/[gmif]/.test(onChar)) {
              regexBuilder += onChar;
              if(i === code.length-1) {
                console.log("regexBuilder=" + regexBuilder);
                applyOperation();
              }else{
                continue;
              }
            }else{
              //regexBuilding = false;
              //buildMode = "";
              console.log("regexBuilder=" + regexBuilder);
              applyOperation();
            }
          }
        }

        //regexBuilding
        if (onChar === "\/" && regexBuilder === "" && operationMode !== "" && buildMode === "") {
          //regexBuilding = true;
          buildMode="regex";
          regexBuilder += onChar;
          regexMode = "n";
          continue;
        }

        //evalBuilding
        /*
        if(buildMode === "eval") {
          if(onChar === ")") {
            //escaping `)`
            if(code[i] === "\\") {
              evalBuilder.replace(/.$/g,")");
            }else{
              //eval ends
              buildMode = "";
              var evaluatedExpression = Main(evalBuilder,input.join`\n`);
              eval(`${evaluatedExpression[0]}Builder="${evaluatedExpression[1]}"`);
            }
          }else{
            evalBuilder += onChar;
          }
        }

        //eval building
        if (onChar === "(" && evalBuilder === "" && operationMode !== "" && buildMode === "") {
          buildMode = "eval";
          //stringBuilder = "hi";
          console.log("toEval");
        }*/

        //normal operations
        if (operationMode == "") {
          switch (onChar) {
            case '^':
              caretMode = true;
              break;
            case 'I':
              //v for handling case of empty stack
              eval(`stackI = parseFloat((stack${stackMode}+"").length>0?stack${stackMode}+"":"0")`);
              stackMode = "I";
              break;
            case 'F':
              eval(`stackF = parseFloat((stack${stackMode}+"").length>0?stack${stackMode}+"":"0")`);
              stackMode = "F";
              break;
            case 'S':
              if(stackMode === "A") {
              //S accepts an argument that tells it what comes in join(what)
                operationMode = "S";
              }else{
                eval(`stack = stack${stackMode}+""`);
                stackMode = "";
              }
              break;
            case 'A':
              //monad it takes the next char as arg
              eval(`stackA = (stack${stackMode}+"").split(\`${code[i+1]+""}\`)`);
              stackMode = "A";
              i++;
              break;
            default:
              operationMode = onChar;
          }
          continue;
        } else {
          console.log(operationMode);
          applyOperation(onChar);
          continue;
        }
      }
    }

    var returnValue = eval(`stack${stackMode}`);
    return [stackMode, returnValue];
  }
}
/**
 * TODO:
 * =====
 *
 * intBuilder            [ ]
 * convert to string     [x]
 * fix bug with 1^I+4S+4 [x]
 * - for stack (string)  [x]
 * negative numbers      [x]
 * * op for fraction and negatives for stack [ ]
 * / op for stack with regex arg             [x]
 * / op for stack with int arg               [x]
 * operation support for arrays              [ ]
 * - stringMode string arg substitution      [ ]
 **/

/**
 * DOCS (will update as I complete commands)
 * (is incomplete as I am adding everything to GitHub)
 * =========================================
 *
 * stack^operations^... (goes on endlessly)
 *  - stack  = string
 *  - stackI = int (also float maybe)
 *
 *  (int is inaccurately used to describe any number)
 *
 * data types:
 *  there are 4 types of constants in carrot: c^rrot, string, number, array, regex
 *
 *  c^rrot: data
 *  string: "data" (has escape characters)
 *  num: 123.456
 *  regex: /^(\d)u+$/gmi
 *  space: ` `
 *
 * operators:
 *  ops are defined by their type (monad or nilad), the arguments they take
 *  (int or string or regex) and the stackMode (string, float, int, array, ...)
 *
 *  nilads (takes in 0 arguments):
 *   I F S
 *  monads:
 *   + - * / A
 *
 * list of operators
 *  I = converts to intMode
 *  F = converts to floatMode
 *  S = converts to stringMode
 *  A = converts to arrayMode by splitting at next char
 *  + = string concatenation/int addition
 *  - = string (remove_all_instancesOf/substring)/int subtraction
 *  * = string (/multi-duplication)/int multiplication
 *  / = string (regex_match/remove_char_at_index)/int division
 **/
<|endoftext|>


<commit_before>$('body').scrollspy({ target: '#side-menu' , offset : 10});
var disableEvent = function(e) {
    e.preventDefault();
};
$('a[href="#"]').click(disableEvent);
$('button[type="submit"]').click(disableEvent);

// Initialize popovers
$('[data-toggle="popover"]').popover();
$('#openGithub').click(function(e){
    e.preventDefault();
    window.location = "https://github.com/caneruguz/osf-style";
});
<commit_msg>Fix commit mistake with tooltip<commit_after>$('body').scrollspy({ target: '#side-menu' , offset : 10});
var disableEvent = function(e) {
    e.preventDefault();
};
$('a[href="#"]').click(disableEvent);
$('button[type="submit"]').click(disableEvent);

// Initialize popovers
$('[data-toggle="popover"]').popover();
$('#openGithub').click(function(e){
    e.preventDefault();
    window.location = "https://github.com/caneruguz/osf-style";
});
$('[data-toggle="tooltip"]').tooltip();
<|endoftext|>


<commit_before>weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

init();

function init() {
  var fieldsets = document.getElementsByTagName('fieldset');
  for (var i = fieldsets.length - 1; i >= 0; i--) {
    fieldsets[i].onmouseover = function() {
      this.getElementsByTagName('legend')[0].style.borderColor = 'threedhighlight';
      this.style.borderColor = 'threedhighlight'
    };
    fieldsets[i].onmouseout = function() {
      this.getElementsByTagName('legend')[0].style.borderColor = 'threedface';
      this.style.borderColor = 'threedface'
    };
  }
  var output = document.getElementById('output');
  output.onmouseover = function() {
    this.style.borderColor = 'threedhighlight'
  };
  output.onmouseout = function() {
    this.style.borderColor = 'threedface'
  };
  today();
  var formInputs = document.getElementsByTagName('form')[0].getElementsByTagName('input');
  for (var i = formInputs.length - 1; i >= 0; i--) {
    formInputs[i].onmouseover = function() {
      this.style.backgroundColor = '#eee'
    };
    formInputs[i].onmouseout = function() {
      this.style.backgroundColor = '#ddd'
    };
  }
  var textAreas = document.getElementsByTagName('form')[0].getElementsByTagName('textarea');
  for (var i = textAreas.length - 1; i >= 0; i--) {
    textAreas[i].onmouseover = function() {
      this.style.backgroundColor = '#eee'
    };
    textAreas[i].onmouseout = function() {
      this.style.backgroundColor = '#ddd'
    };
  }
}

function today() {
  var digestDate = document.getElementById('digestDate');
  var todayDate = new Date(Date.now());
  digestDate.value = constructDateString(todayDate, "-", false);
}

function nextSunday() {
  var digestDate = document.getElementById('digestDate');
  today();
  var d = new Date(digestDate.value);
  while (d.getUTCDay() != 0) {
    d.setUTCDate(d.getUTCDate() + 1);
  }
  digestDate.value = constructDateString(d, "-", true);
}

function render() {
  document.getElementById('genButton').innerHTML = 'REGEN ADVISED';
  document.getElementById('genButton').style.color = '#f00';
  document.getElementById('output').style.backgroundColor = '#fdd';
  document.getElementById('outputText').style.backgroundColor = '#fdd';
  var renderWindow = window.open();
  renderWindow.document.write(document.getElementById('outputText').value);
}

function gen() {
  document.getElementById('genButton').innerHTML = 'GENERATE';
  document.getElementById('genButton').style.color = '#000';
  document.getElementById('renderButton').disabled = false;
  document.getElementById('output').style.backgroundColor = '#fff';
  document.getElementById('outputText').style.backgroundColor = '#fff';
  var output = "";
  output += genHeader();
  if (document.getElementById('enableMessage').checked)
    output += genMessage();
  output += genFooter();
  document.getElementById('outputText').value = output;
}

function genHeader() {
  var digestDate = new Date(document.getElementById('digestDate').value);
  var dateString = constructDateString(digestDate, ".", true);
  var digestCSS = document.getElementById("digestCSS").value;
  var mdyString = constructMDYString(digestDate, true);
  var headerStr =
      '<!doctype HTML>\n'
    + '\n'
    + '  \n'
    + '    <title>EZ Digest - ' + dateString + '</title>\n'
    + '    <link rel="stylesheet" type="text/css" href="' + digestCSS + '">\n'
    + '  \n'
    + '  \n'
    + '
\n'
    + '
\n'
    + '        \n'
    + '          EZ Digest - ' + mdyString + '\n'
    + '        	\n'
    + '
\n';
  return headerStr;
}

function genMessage() {
  var messageBody = document.getElementById("messageBody").value;
  var signoff1 = document.getElementById("signoff1").value;
  var signoff2 = document.getElementById("signoff2").value;
  var messageStr =
      '
\n'
    + '        \n'
    + '          ' + messageBody.replace(/\r?\n/g, '
') + '\n'
    + '        	\n'
    + '
\n'
    + '
\n'
    + '        \n'
    + '          ' + signoff1 + '
' + signoff2 + '\n'
    + '        	\n'
    + '
\n';
  return messageStr;
}

function genFooter() {
  var footerStr =
      '
\n'
    + '        \n'
    + '
The goal of the weekly EZ Digest is to provide a concise summary of recent and upcoming chapter activities.
\n'
    + '
Unlike eggs, digests are always better when they\'re Over EZ.
\n'
    + '
Feel free to reply to this message with any comments or suggestions!
\n'
    + '
Alpha Phi Omega - Epsilon Zeta Chapter - Rensselaer Polytechnic Institute
\n'
    + '        	\n'
    + '
\n'
    + '
\n'
    + '  \n'
    + '\n';
  return footerStr;
}

function pad(num, size) {
    var s = num+"";
    while (s.length < size) s = "0" + s;
    return s;
}

function constructDateString(date, separator, utc) {
  return utc
    ? date.getUTCFullYear() + separator + pad(date.getUTCMonth() + 1, 2) + separator + pad(date.getUTCDate(), 2)
    : date.getFullYear() + separator + pad(date.getMonth() + 1, 2) + separator + pad(date.getDate(), 2);
}

function constructMDYString(date, utc) {
  return utc
    ? months[date.getUTCMonth()] + " " + date.getUTCDate() + ", " + date.getUTCFullYear()
    : months[date.getMonth()] + " "  + date.getDate() + ", " + date.getFullYear();

}
<commit_msg>Modify render button instead of generate button when prompting regen<commit_after>weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

init();

function init() {
  var fieldsets = document.getElementsByTagName('fieldset');
  for (var i = fieldsets.length - 1; i >= 0; i--) {
    fieldsets[i].onmouseover = function() {
      this.getElementsByTagName('legend')[0].style.borderColor = 'threedhighlight';
      this.style.borderColor = 'threedhighlight'
    };
    fieldsets[i].onmouseout = function() {
      this.getElementsByTagName('legend')[0].style.borderColor = 'threedface';
      this.style.borderColor = 'threedface'
    };
  }
  var output = document.getElementById('output');
  output.onmouseover = function() {
    this.style.borderColor = 'threedhighlight'
  };
  output.onmouseout = function() {
    this.style.borderColor = 'threedface'
  };
  today();
  var formInputs = document.getElementsByTagName('form')[0].getElementsByTagName('input');
  for (var i = formInputs.length - 1; i >= 0; i--) {
    formInputs[i].onmouseover = function() {
      this.style.backgroundColor = '#eee'
    };
    formInputs[i].onmouseout = function() {
      this.style.backgroundColor = '#ddd'
    };
  }
  var textAreas = document.getElementsByTagName('form')[0].getElementsByTagName('textarea');
  for (var i = textAreas.length - 1; i >= 0; i--) {
    textAreas[i].onmouseover = function() {
      this.style.backgroundColor = '#eee'
    };
    textAreas[i].onmouseout = function() {
      this.style.backgroundColor = '#ddd'
    };
  }
}

function today() {
  var digestDate = document.getElementById('digestDate');
  var todayDate = new Date(Date.now());
  digestDate.value = constructDateString(todayDate, "-", false);
}

function nextSunday() {
  var digestDate = document.getElementById('digestDate');
  today();
  var d = new Date(digestDate.value);
  while (d.getUTCDay() != 0) {
    d.setUTCDate(d.getUTCDate() + 1);
  }
  digestDate.value = constructDateString(d, "-", true);
}

function render() {
  document.getElementById('renderButton').innerHTML = 'REGEN ADVISED';
  document.getElementById('renderButton').style.color = '#f00';
  document.getElementById('output').style.backgroundColor = '#fdd';
  document.getElementById('outputText').style.backgroundColor = '#fdd';
  var renderWindow = window.open();
  renderWindow.document.write(document.getElementById('outputText').value);
}

function gen() {
  document.getElementById('renderButton').innerHTML = 'RENDER';
  document.getElementById('renderButton').style.color = '#000';
  document.getElementById('renderButton').disabled = false;
  document.getElementById('output').style.backgroundColor = '#fff';
  document.getElementById('outputText').style.backgroundColor = '#fff';
  var output = "";
  output += genHeader();
  if (document.getElementById('enableMessage').checked)
    output += genMessage();
  output += genFooter();
  document.getElementById('outputText').value = output;
}

function genHeader() {
  var digestDate = new Date(document.getElementById('digestDate').value);
  var dateString = constructDateString(digestDate, ".", true);
  var digestCSS = document.getElementById("digestCSS").value;
  var mdyString = constructMDYString(digestDate, true);
  var headerStr =
      '<!doctype HTML>\n'
    + '\n'
    + '  \n'
    + '    <title>EZ Digest - ' + dateString + '</title>\n'
    + '    <link rel="stylesheet" type="text/css" href="' + digestCSS + '">\n'
    + '  \n'
    + '  \n'
    + '
\n'
    + '
\n'
    + '        \n'
    + '          EZ Digest - ' + mdyString + '\n'
    + '        	\n'
    + '
\n';
  return headerStr;
}

function genMessage() {
  var messageBody = document.getElementById("messageBody").value;
  var signoff1 = document.getElementById("signoff1").value;
  var signoff2 = document.getElementById("signoff2").value;
  var messageStr =
      '
\n'
    + '        \n'
    + '          ' + messageBody.replace(/\r?\n/g, '
') + '\n'
    + '        	\n'
    + '
\n'
    + '
\n'
    + '        \n'
    + '          ' + signoff1 + '
' + signoff2 + '\n'
    + '        	\n'
    + '
\n';
  return messageStr;
}

function genFooter() {
  var footerStr =
      '
\n'
    + '        \n'
    + '
The goal of the weekly EZ Digest is to provide a concise summary of recent and upcoming chapter activities.
\n'
    + '
Unlike eggs, digests are always better when they\'re Over EZ.
\n'
    + '
Feel free to reply to this message with any comments or suggestions!
\n'
    + '
Alpha Phi Omega - Epsilon Zeta Chapter - Rensselaer Polytechnic Institute
\n'
    + '        	\n'
    + '
\n'
    + '
\n'
    + '  \n'
    + '\n';
  return footerStr;
}

function pad(num, size) {
    var s = num+"";
    while (s.length < size) s = "0" + s;
    return s;
}

function constructDateString(date, separator, utc) {
  return utc
    ? date.getUTCFullYear() + separator + pad(date.getUTCMonth() + 1, 2) + separator + pad(date.getUTCDate(), 2)
    : date.getFullYear() + separator + pad(date.getMonth() + 1, 2) + separator + pad(date.getDate(), 2);
}

function constructMDYString(date, utc) {
  return utc
    ? months[date.getUTCMonth()] + " " + date.getUTCDate() + ", " + date.getUTCFullYear()
    : months[date.getMonth()] + " "  + date.getDate() + ", " + date.getFullYear();

}
<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const Script = require('smooch-bot').Script;
var pg = require('pg');
var Q = require("q");
var request = require("request");
var newUser = require("./db");
var logConversation = require("./conversation");
var nlp = require("./nlp");
var pushConv = require("./push");

const scriptRules = require('./script.json');

var msgLog = {
    smoochId: '',
    received: '',
    usermessage: '',
    role: '',
    message_id: '',
    sourcetype: '',
    receivedtime: '',
    responsemessage: '',
    responsetype: '',
    responsetime: ''
  };

function wait(ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}

module.exports = new Script({
    processing: {
        //prompt: (bot) => bot.say('Beep boop...'),
        receive: () => 'processing'
    },

    start: {
        receive: (bot) => {
            return bot.say('Hello!  I\'m A16, the Agile2016 EventBot Concierge.  To find out what I can help you with type MENU for options or KEY for Keywords.')
                .then(() => 'speak');
        }
    },

    speak: {
        receive: (bot, message) => {
            console.log("===bot user ");
            console.log("===receive step 1",message);
            let upperText = message.text.trim().toUpperCase();

            //var receivedtime = new new Date( new Date().getTime() -6 * 3600 * 1000).toUTCString().replace( / GMT$/, "" );

            msgLog.smoochId = bot.userId;
            msgLog.received = message.received;
            msgLog.usermessage = message.text;
            msgLog.role = message.role;
            msgLog.message_id = message.message_id;
            msgLog.sourcetype = message.source.type;
            msgLog.receivedtime = new Date();

            var botUser = bot.userId;
            var authUsers = ['a30fa820d0a0f0216fa26070'];

            //Undone - currently only creates new user
            //needs to create conversation record and update it throughout the prcoess
            //console.log("===before db");
            newUser(bot)
            //  .then (console.log("===after db", bot.userId))

              //undone - test for userid and or message
              //look at stacking messages in DB and looking them up
              //end conversation and do not send a response to the user that kicked things off
              //
              if (authUsers.indexOf(bot.userId) !== -1) {
                if (message.text.substr(0,5) == '/msg ') {
                  upperText = upperText.substr(0,4);
                  pushConv(bot, message.text.substr(5));
                  console.log("****after push msg:  ",message.text);
                }
              }

            function updateSilent() {
                switch (upperText) {
                    case "CONNECT ME":
                        return bot.setProp("silent", true);
                    case "/SUPPORT":
                        return bot.setProp("silent", true);
                    case "DISCONNECT":
                        return bot.setProp("silent", false);
                    case "/A16":
                        console.log("*** ",upperText," ***");
                        processMessage(false);
                        return bot.setProp("silent", false);
                    default:
                        return Promise.resolve();
                }
            }

            console.log("===receive step 2",upperText);

            function getSilent() {
                return bot.getProp("silent");
            }

            function processMessage(isSilent) {
                if (isSilent) {
                    return Promise.resolve("speak");
                }

                var promises = [];
                var source;
                var fulfillmentSpeech;
                var simplified;
                promises.push(nlp(upperText, bot.userId));

                Q.all(promises).then(function(responses) {
                    // response is the JSON from API.ai
                    responses.forEach(function(response) {
                        //console.log("===in Q.all");
                        console.log("===received result from API.ai",response);
                        source = response.result.source;
                        if (source && source !== 'agent')
                        {
                            fulfillmentSpeech = response.result.fulfillment.speech;
                            simplified = response.result.parameters.simplified;
                        }
                        console.log("source: ", source);
                        console.log("fulfillmentSpeech: ", fulfillmentSpeech);
                        console.log("simplified: ", simplified);

                        respondMessage(source, fulfillmentSpeech, simplified);
                    });
                }, function(error) {
                    console.log("===Q all error ", error);
                });
                //return next();

            }

            function respondMessage(source, fulfillmentSpeech, simplified)
            {
                console.log("source: ", source);
                console.log("fulfillmentSpeech: ", fulfillmentSpeech);
                console.log("simplified: ", simplified);
                console.log("===receive step 3",upperText);

              if (source != 'agent')
                {
                    console.log("===source is ", source);
                    if (fulfillmentSpeech)
                    {
                        console.log("fulfillmentSpeech is: ", fulfillmentSpeech);
                        if (fulfillmentSpeech !== "hello") {
                            return bot.say(fulfillmentSpeech).then(() => 'speak');
                        }
                        upperText = simplified.trim().toUpperCase();
                    }
                    else if (simplified)
                    {
                        console.log("simplified is: ", simplified);
                        upperText = simplified.toUpperCase();
                    }
                }
                if (!_.has(scriptRules, upperText)) {
                    console.log("===no rule", upperText);
                    return bot.say(`I'm sorry that is not something I know.  Type MENU or KEY for a list of things I can help you with.`).then(() => 'speak');
                }

                console.log("===receive step 3",upperText);

                var response = scriptRules[upperText];
                var lines = response.split('\n');

                bot.userId = botUser;

                var p = Promise.resolve();
                _.each(lines, function(line) {
                    line = line.trim();
                    p = p.then(function() {
                        console.log("=== p line",line);
                        return wait(5).then(function() {
                            msgLog.responsemessage = line;
                            //var senttime = new new Date( new Date().getTime() -6 * 3600 * 1000).toUTCString().replace( / GMT$/, "" );
                            msgLog.responsetime = new Date;
                            msgLog.responsetype = 'JSON';
                            console.log("=== msgLog  obj",msgLog);
                            logConversation(msgLog);
                            return bot.say(line);
                        });
                    });
                });
                return p.then(() => 'speak');
            }

            return updateSilent()
                .then(getSilent)
                .then(processMessage);
        }
    }
});
<commit_msg>rolling ahead 1<commit_after>'use strict';

const _ = require('lodash');
const Script = require('smooch-bot').Script;
var pg = require('pg');
var Q = require("q");
var request = require("request");
var newUser = require("./db");
var logConversation = require("./conversation");
var nlp = require("./nlp");
var pushConv = require("./push");

const scriptRules = require('./script.json');

var msgLog = {
    smoochId: '',
    received: '',
    usermessage: '',
    role: '',
    message_id: '',
    sourcetype: '',
    receivedtime: '',
    responsemessage: '',
    responsetype: '',
    responsetime: ''
  };

function wait(ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}

module.exports = new Script({
    processing: {
        //prompt: (bot) => bot.say('Beep boop...'),
        receive: () => 'processing'
    },

    start: {
        receive: (bot) => {
            return bot.say('Hello!  I\'m A16, the Agile2016 EventBot Concierge.  To find out what I can help you with type MENU for options or KEY for Keywords.')
                .then(() => 'speak');
        }
    },

    speak: {
        receive: (bot, message) => {
            console.log("===bot user ");
            console.log("===receive step 1",message);
            let upperText = message.text.trim().toUpperCase();

            //var receivedtime = new new Date( new Date().getTime() -6 * 3600 * 1000).toUTCString().replace( / GMT$/, "" );

            msgLog.smoochId = bot.userId;
            msgLog.received = message.received;
            msgLog.usermessage = message.text;
            msgLog.role = message.role;
            msgLog.message_id = message.message_id;
            msgLog.sourcetype = message.source.type;
            msgLog.receivedtime = new Date();

            var botUser = bot.userId;
            var authUsers = ['a30fa820d0a0f0216fa26070'];

            //Undone - currently only creates new user
            //needs to create conversation record and update it throughout the prcoess
            //console.log("===before db");
            newUser(bot)
            //  .then (console.log("===after db", bot.userId))

              //undone - test for userid and or message
              //look at stacking messages in DB and looking them up
              //end conversation and do not send a response to the user that kicked things off
              //
              if (authUsers.indexOf(bot.userId) !== -1) {
                if (message.text.substr(0,5) == '/msg ') {
                  upperText = upperText.substr(0,4);
                  pushConv(bot, message.text.substr(5));
                  console.log("****after push msg:  ",message.text);
                }
              }

            function updateSilent() {
                switch (upperText) {
                    case "CONNECT ME":
                        return bot.setProp("silent", true);
                    case "/SUPPORT":
                        return bot.setProp("silent", true);
                    case "DISCONNECT":
                        return bot.setProp("silent", false);
                    case "/A16":
                        console.log("*** ",upperText," ***");
                        processMessage(false);
                        return bot.setProp("silent", false);
                    default:
                        return Promise.resolve();
                }
            }

            console.log("===receive step 2",upperText);

            function getSilent() {
                return bot.getProp("silent");
            }

            function processMessage(isSilent) {
                if (isSilent) {
                    return Promise.resolve("speak");
                }

                var promises = [];
                var source;
                var fulfillmentSpeech;
                var simplified;
                promises.push(nlp(upperText, bot.userId));

                Q.all(promises).then(function(responses) {
                    // response is the JSON from API.ai
                    responses.forEach(function(response) {
                        //console.log("===in Q.all");
                        console.log("===received result from API.ai",response);
                        source = response.result.source;
                        if (source && source !== 'agent')
                        {
                            fulfillmentSpeech = response.result.fulfillment.speech;
                            simplified = response.result.parameters.simplified;
                        }
                        console.log("source: ", source);
                        console.log("fulfillmentSpeech: ", fulfillmentSpeech);
                        console.log("simplified: ", simplified);

                        respondMessage(source, fulfillmentSpeech, simplified);
                    });
                }, function(error) {
                    console.log("===Q all error ", error);
                });
                //return next();

            }

            function respondMessage(source, fulfillmentSpeech, simplified)
            {
                console.log("source: ", source);
                console.log("fulfillmentSpeech: ", fulfillmentSpeech);
                console.log("simplified: ", simplified);
                console.log("===receive step 3",upperText);

              if (source != 'agent')
                {
                    console.log("===source is ", source);
                    if (fulfillmentSpeech)
                    {
                        console.log("fulfillmentSpeech is: ", fulfillmentSpeech);
                        if (simplified !== "hello") {
                            return bot.say(fulfillmentSpeech).then(() => 'speak');
                        }
                        upperText = simplified.trim().toUpperCase();
                    }
                    else if (simplified)
                    {
                        console.log("simplified is: ", simplified);
                        upperText = simplified.toUpperCase();
                    }
                }
                if (!_.has(scriptRules, upperText)) {
                    console.log("===no rule", upperText);
                    return bot.say(`I'm sorry that is not something I know.  Type MENU or KEY for a list of things I can help you with.`).then(() => 'speak');
                }

                console.log("===receive step 3",upperText);

                var response = scriptRules[upperText];
                var lines = response.split('\n');

                bot.userId = botUser;

                var p = Promise.resolve();
                _.each(lines, function(line) {
                    line = line.trim();
                    p = p.then(function() {
                        console.log("=== p line",line);
                        return wait(5).then(function() {
                            msgLog.responsemessage = line;
                            //var senttime = new new Date( new Date().getTime() -6 * 3600 * 1000).toUTCString().replace( / GMT$/, "" );
                            msgLog.responsetime = new Date;
                            msgLog.responsetype = 'JSON';
                            console.log("=== msgLog  obj",msgLog);
                            logConversation(msgLog);
                            return bot.say(line);
                        });
                    });
                });
                return p.then(() => 'speak');
            }

            return updateSilent()
                .then(getSilent)
                .then(processMessage);
        }
    }
});
<|endoftext|>


<commit_before>/*
 *  Shout.js
 *
 *  A quick and dirty pub/sub thingamajig
 *
 *  Author: Stephen Murray
 *  Shout.js may be freely distributed under the MIT license.
 */

;(function(){

  "use strict";

  // Event string delimiter (spaces)
  var _delim = /\s+/,
  // Convenience for slice cuz, yknow laziness
    slice = Array.prototype.slice;

  // Utility functions

  function getArgs( args ) {
    return slice.call(args, 0);
  }

  function splitEvents( events ) {
    return events.split(_delim);
  }

  // Primary constructor
  function Shout() {
    this._cache = {};
  }

  // Instance methods
  Shout.prototype = {

    /**
     * Bind event(s) to handlers
     * @return {self}
     */
    on: function(){
      var args = getArgs(arguments),
        events = splitEvents(args[0]),
        handlers = slice.call(args, 1),
        ev;

      while ( ev = events.shift() ) {
        this._cache[ev] = this._cache[ev] || [];
        for ( var i = 0; i < handlers.length; i++ ) {
          this._cache[ev].push(handlers[i]);
        }
      }

      return this;
    },

    /**
     * Unbind event(s)
     * @return {self}
     */
    off: function(){
      var args = getArgs(arguments),
        events = splitEvents(args[0]),
        ev,
        retains = [],
        handlers = slice.call(args, 1);

      while ( ev = events.shift() ) {
        this._cache[ev] = this._cache[ev] || [];
        if ( handlers.length ) {
          for ( var i = 0; i < this._cache[ev].length; i++ ) {
            for ( var j = 0; j < handlers.length; j++ ) {
              if ( this._cache[ev][i] !== handlers[j] ) {
                retains.push(this._cache[ev][i]);
              }
            }
          }
          this._cache[ev] = retains;
        } else {
          delete this._cache[ev];
        }
      }

      return this;
    },

    /**
     * Emit/trigger events
     * @return {self}
     */
    emit: function(){
      var args = getArgs(arguments),
        events = splitEvents(args[0]),
        argsToPass = slice.call(args, 1),
        ev,
        handlers;

      while ( ev = events.shift() ) {
        handlers = this._cache[ev] || [];
        for ( var i = 0; i < handlers.length; i++ ) {
          handlers[i].apply(null, argsToPass);
        }
      }

      return this;
    }

  };

  this.Shout = Shout;

}).call(this);
<commit_msg>update Shout.js<commit_after>/*
 *  Shout.js
 *
 *  A quick and dirty pub/sub thingamajig
 *
 *  Author: Stephen Murray
 *  Shout.js may be freely distributed under the MIT license.
 */

;(function(){

  "use strict";

  // Event string delimiter (spaces)
  var _delim = /\s+/,
  // Convenience for slice cuz, yknow laziness
    slice = Array.prototype.slice;

  // Utility functions

  function getArgs( args ) {
    return slice.call(args, 0);
  }

  function splitEvents( events ) {
    return events.split(_delim);
  }

  // Primary constructor
  function Shout() {
    this._cache = {};
  }

  // Instance methods
  Shout.prototype = {

    /**
     * Bind event(s) to handlers
     * @return {self}
     */
    on: function(){
      var args = getArgs(arguments),
        events = splitEvents(args[0]),
        handlers = slice.call(args, 1),
        ev;

      while ( ev = events.shift() ) {
        this._cache[ev] = this._cache[ev] || [];
        for ( var i = 0; i < handlers.length; i++ ) {
          this._cache[ev].push(handlers[i]);
        }
      }

      return this;
    },

    /**
     * Unbind event(s)
     * @return {self}
     */
    off: function(){
      var args = getArgs(arguments),
        events = splitEvents(args[0]),
        ev,
        retains = [],
        handlers = slice.call(args, 1);

      while ( ev = events.shift() ) {
        this._cache[ev] = this._cache[ev] || [];
        if ( handlers.length ) {
          for ( var i = 0; i < this._cache[ev].length; i++ ) {
            for ( var j = 0; j < handlers.length; j++ ) {
              if ( this._cache[ev][i] !== handlers[j] ) {
                retains.push(this._cache[ev][i]);
              }
            }
          }
          this._cache[ev] = retains;
        } else {
          delete this._cache[ev];
        }
      }

      return this;
    },

    /**
     * Emit/trigger events
     * @return {self}
     */
    emit: function(){
      var args = getArgs(arguments),
        events = splitEvents(args[0]),
        argsToPass = slice.call(args, 1),
        ev,
        handlers;

      while ( ev = events.shift() ) {
        handlers = this._cache[ev] || [];
        for ( var i = 0; i < handlers.length; i++ ) {
          switch ( argsToPass.length ) {
                case 0:
                    handlers[i]();
                    break;
                case 1:
                    handlers[i](argsToPass[0]);
                    break;
                case 2:
                    handlers[i](argsToPass[0], argsToPass[1]);
                    break;
                case 3:
                    handlers[i](argsToPass[0], argsToPass[1], argsToPass[2]);
                    break;
                default:
                    handlers[i].apply(null, argsToPass);
                    break;
          }
        }
      }

      return this;
    }

  };

  this.Shout = Shout;

}).call(this);
<|endoftext|>


<commit_before>/* Parse Expression
 * Type "value" is a string or number
 * Type "word" is an identifier
 * Type "apply" is an application
 */
function parseExpression(program) {
  program = skipSpace(program);
  var match, expr;
  /* Disable JSHint warning about assignment in conditional expression */
  /* jshint -W084 */
  if (match = /^"([^"]*)"/.exec(program))
    expr = {type: "value", value: match[1]};
  else if (match = /^\d+\b/.exec(program))
    expr = {type: "value", value: Number(match[0])};
  else if (match = /^[^\s(),"]+/.exec(program))
    expr = {type: "word", name: match[0]};
  else
    throw new SyntaxError("Unexpected syntax: " + program);
  /* jshint +W084 */

  return parseApply(expr, program.slice(match[0].length));
}

/* Parse the application. E.g. +(1, 2) */
function parseApply(expr, program) {
  program = skipSpace(program);
  if (program[0] != "(")
    return {expr: expr, rest: program};

  program = skipSpace(program.slice(1));
  expr = {type: "apply", operator: expr, args: []};
  while (program[0] != ")") {
    var arg = parseExpression(program);
    expr.args.push(arg.expr);
    program = skipSpace(arg.rest);
    if (program[0] == ",")
      program = skipSpace(program.slice(1));
    else if (program[0] != ")")
      throw new SyntaxError("Expected ',' or ')'");
  }
  return parseApply(expr, program.slice(1));
}

/* Remove leading space */
function skipSpace(string) {
  var first = string.search(/\S/);
  if (first == -1) return "";
  return string.slice(first);
}

/* Parses the program and returns an object containing expressions
   and statements */
function parse(program) {
  var result = parseExpression(program);
  if (skipSpace(result.rest).length > 0)
    throw new SyntaxError("Unexpected text after program");
  return result.expr;
}
<commit_msg>Added evaluator.<commit_after>/* Parse Expression
 * Type "value" is a string or number
 * Type "word" is an identifier
 * Type "apply" is an application
 */
function parseExpression(program) {
  program = skipSpace(program);
  var match, expr;
  /* Disable JSHint warning about assignment in conditional expression */
  /* jshint -W084 */
  if (match = /^"([^"]*)"/.exec(program))
    expr = {type: "value", value: match[1]};
  else if (match = /^\d+\b/.exec(program))
    expr = {type: "value", value: Number(match[0])};
  else if (match = /^[^\s(),"]+/.exec(program))
    expr = {type: "word", name: match[0]};
  else
    throw new SyntaxError("Unexpected syntax: " + program);
  /* jshint +W084 */

  return parseApply(expr, program.slice(match[0].length));
}

/* Parse the application. E.g. +(1, 2) */
function parseApply(expr, program) {
  program = skipSpace(program);
  if (program[0] != "(")
    return {expr: expr, rest: program};

  program = skipSpace(program.slice(1));
  expr = {type: "apply", operator: expr, args: []};
  while (program[0] != ")") {
    var arg = parseExpression(program);
    expr.args.push(arg.expr);
    program = skipSpace(arg.rest);
    if (program[0] == ",")
      program = skipSpace(program.slice(1));
    else if (program[0] != ")")
      throw new SyntaxError("Expected ',' or ')'");
  }
  return parseApply(expr, program.slice(1));
}

/* Remove leading space */
function skipSpace(string) {
  var first = string.search(/\S/);
  if (first == -1) return "";
  return string.slice(first);
}

/* Parses the program and returns an object containing expressions
   and statements */
function parse(program) {
  var result = parseExpression(program);
  if (skipSpace(result.rest).length > 0)
    throw new SyntaxError("Unexpected text after program");
  return result.expr;
}

/* Evaluate an expression */
function evaluate(expr, env) {
  switch(expr.type) {
    case "value":
      return expr.value;

    case "word":
      if (expr.name in env)
        return env[expr.name];
      else
      /* jshint -W086 */
        throw new ReferenceError("Undefined variable: " +
                                 expr.name);
    case "apply":
      /* jshint +W086 */
      if (expr.operator.type === "word" &&
          expr.operator.name in specialForms)
        return specialForms[expr.operator.name](expr.args,
                                                env);
      var op = evaluate(expr.operator, env);
      if (typeof op != "function")
        throw new TypeError("Applying a non-function.");
      return op.apply(null, expr.args.map(function(arg) {
        return evaluate(arg, env);
      }));
  }
}

var specialForms = Object.create(null);
<|endoftext|>


<commit_before>// Structure of items.
//var items = [{
//    id: "",
//    entityType: "",
//    rarity: 1,
//    type: "",
//    subType: "",
//    ts: 0,
//    plus: 0,
//    ageLevel: 0,
//    handed: 1,
//    stats: {
//        hp: 0,
//        attackMin: 0,
//        attackMax: 0,
//        defense: 0,
//        overkill: 0,
//        heal: 0,
//        hpBonus: 0
//    },
//    name: "", // {Helmet, Armor, Gloves, Leggings, Boots, Sword, 2-handed Sword, Bow, Crossbow, Wand, Staff, Shield}
//
//}];

/** ============= Battle INF Script to End All Scripts ==========
 * Authored by Sam 'Halithor' Marquart.
 * Licenced under MIT (https://opensource.org/licenses/MIT).
 *
 * Features:
 * - Sells items below a threshold.
 * - Crafts new items into equipped items and, optionally, inventory items.
 *      + Inventory items is useful for getting higher rarity gear to be more powerful than current gear
 * - Equips items that are better than currently equipped items.
 * - Sells items that have reached maximum Plus, and aren't better than the currently equipped item of that type.
 * - Dips all of your items into the fountain, automagically, to increase the age modifiers.
 * - Removes duplicate items of a given type, leaving only the strongest.
 *
 * Notes:
 * - Only works on items you have gained after you include the script. It will not fix your inventory.
 * -
 */
(function () {
    // Settings for the game. Edit these to fit your character better.
    var settings = {
        version: 1.0,
        openInvSlots: 2, // The number of spots that will ALWAYS be held open in your inventory. I recommend setting this to the drops you get per cycle.
        sellBelow: 2, // The threshold below which we automatically sell (exclusive), without combining. Increase this as you like.
        keepAbove: 4, // The rarity above which we DO NOT ever auto sell (exclusive).
        craftInventory: true, // Whether or not to craft items in your inventory.
        sellDuplicateInventory: true, // If we should sell any duplicates of a given item type in the inventory, and only keep the strongest item.
        forceEquipHighestStrength: false // If you always want to equip the highest rarity, no matter the stats. I recommend this to be true if you don't craft the inventory.
    };

    var _rarities = ["None", "Gray", "Green", "Blue", "Red", "Orange", "Purple", "Teal"];
    var _ages = ["Worn", "Fine", "Refined", "Aged", "Exotic", "Famous", "Master", "Heroic", "Ancient", "Fabled", "Ascended", "Legendary", "Eternal"];

    function postMessage(text) {
        console.log(text);
        API.notifications.create("" + text, 10);
    }

    // String that displays most info about an item.
    function getItemString(item) {
        return '' + item.rarity + "." + item.mod + " " + _ages[item.ageLevel] + " " + item.name + " +" + item.plus;
    }

    // Calculates the relative starting strength of an item.
    function getItemStrength(item) {
        return (item.mod * 2) + item.rarity;
    }

    function isItemAtMaxPlus(item) {
        return item.plus >= (5 + item.rarity * 5);
    }

    function isSameItemType(first, second) {
        return first.type == second.type && first.subType == second.subType
    }

    // Returns if two items are compatible for crafting.
    function canCraftItems(first, second) {
        return isSameItemType(first, second)
            && first.rarity == second.rarity
            && first.mod == second.mod;
    }

    // Tries to find a good item from items for secondary to combined into. Picks the first one found.
    function findPrimaryCraft(items, secondary) {
        for (var j = 0; j < items.length; j++) {
            var candidate = items[j];
            if (canCraftItems(candidate, secondary) && !isItemAtMaxPlus(candidate)) {
                return candidate;
            }
        }
        return undefined;
    }

    // Less picky than find primary craft. Used for inventory duplicates.
    function findDuplicateTypes(items, item) {
        var duplicates = [];
        for (var j = 0; j < items.length; j++) {
            var candidate = items[j];
            if (isSameItemType(item, candidate)) {
                duplicates.push(candidate);
            }
        }
        if (duplicates.length > 0) {
            return duplicates;
        }
        return undefined;
    }

    // Will the additional items push the inventory to full?
    function getInventoryFull(itemsLeft) {
        if (!itemsLeft)
            itemsLeft = settings.openInvSlots;
        return (ScriptAPI.$user.inventory.items.length + itemsLeft >= ScriptAPI.$user.upgrades.inventoryMax.value);
    }

    function getSortedInventory() {
        return ScriptAPI.$user.inventory.items.sort(function (a, b) {
            if (getItemStrength(a) == getItemStrength(b)) {
                if (a.plus == b.plus) {
                    if (a.ageLevel == b.ageLevel) {
                        return 0;
                    } else {
                        return a.ageLevel > b.ageLevel ? -1 : 1;
                    }
                } else {
                    return a.plus > b.plus ? -1 : 1;
                }
            } else {
                return getItemStrength(a) > getItemStrength(b) ? -1 : 1;
            }
        });
    }

    // Find the item equipped matching the given item.
    function findEquipped(primary) {
        var equipment = ScriptAPI.$user.character.equipment;
        for (var i in equipment) {
            var candidate = equipment[i];
            if (isSameItemType(primary, candidate)) {
                return candidate;
            }
        }
        return undefined;
    }

    // Returns true if first is better than second. False otherwise.
    // Does not care about rarity, age, etc. Purely based on stats.
    // This is one of the things you can change to value items differently!
    function isItemBetter(first, second) {
        if (!isSameItemType(first, second)) {
            return false; // Don't even compare if not the same.
        }

        if (first.type == "weapon") {
            // Damage consideration
            var damageFirst = (first.stats.attackMax + first.stats.attackMin) / 2;
            var damageSecond = (second.stats.attackMax + second.stats.attackMin) / 2;
            var damageRatio = damageSecond > 0 ? damageFirst / damageSecond : 1.0;
            // Overkill consideration
            var overkillDiff = first.stats.overkill - second.stats.overkill;
            // Healing ratio.
            var healRatio = second.stats.heal > 0 ? (first.stats.heal / second.stats.heal) : 1.0;
            var armorRatio = second.stats.defense > 0 ? first.stats.defense / second.stats.defense : 1.0;
            var hpBonusDiff = (first.hpBonus - second.hpBonus);
            return damageRatio + healRatio + armorRatio + (overkillDiff / 15) + (hpBonusDiff / 50) > 3.0;
        } else {
            // Everything that's not a weapon is armor.
            var armorRatio = first.stats.defense / second.stats.defense;
            var hpBonusDiff = (first.hpBonus - second.hpBonus);
            return armorRatio + (hpBonusDiff / 50) > 1.0;
        }
    }

    function isItemStronger(first, second) {
        return getItemStrength(first) > getItemStrength(second);
    }

    // Sells an item, without any safety checks.
    function sellItem(item) {
        postMessage("Selling " + getItemString(item));
        API.inventory.sell(item);
    }

    // Sells an item if it's at the max level.
    function sellIfMax(item) {
        if (isItemAtMaxPlus(item) && item.rarity <= settings.keepAbove) {
            sellItem(item);
            return true;
        }
        return false;
    }

    // Sells the inventory item in the last occupied location, sorted by strength.
    function sellLastInventoryItem() {
        var item = getSortedInventory()[ScriptAPI.$user.upgrades.inventoryMax.value - 1];
        if (item) {
            sellItem(item);
        }
    }

    // Similar to above, but forces the spots open based on the setting
    function openLastInventorySpots() {
        var inv = getSortedInventory();
        for (var i = 0; i < settings.openInvSlots; i++) {
            var item = inv[ScriptAPI.$user.upgrades.inventoryMax.value - 1 - i];
            if (item) {
                sellItem(item);
            }
        }
    }

    // Tries to equip the item if better. Returns the unequiped item if it works, false otherwise.
    function equipIfBetter(item, callback) {
        var equip = findEquipped(item);

        if (equip && (isItemBetter(item, equip) || (settings.forceEquipHighestStrength && isItemStronger(item, equip)) )) {
            postMessage("Changed equipped " + equip.type + ":" + equip.subType);
            API.inventory.unequip(equip, function () {
                API.inventory.equip(item, function () {
                    if (callback) {
                        callback(equip);
                    }
                });
            });
            return equip;
        }
        if (callback) {
            callback(false);
        }
        return false;
    }
    // Get a sorted list of the inventory items.
    var inventory = getSortedInventory();

    // Get the equipment
    var equipment = ScriptAPI.$user.character.equipment;

    var itemsLeft = items.length;
    items.forEach(function (item) {

        if (item.rarity < settings.sellBelow && !(item.rarity > settings.keepAbove)) {
            itemsLeft--;
            sellItem(item);
            return;
        }

        // Now we want to combine the item with the inventory into more powerful items. First, we want to upgrade your currently
        // equipped items fully. Then we want to try to combine between items in your inventory. After combining, we look to see
        // if any of the items we have are better than equipped, if so, switch them. Finally, if anything has
        // reached the max Plus value that it can, and it isn't equipped,  sell it.

        // Find a good candidate to craft this item into.
        var isEquipped = false;
        var primary = findPrimaryCraft(equipment, item);
        if (primary) isEquipped = true;

        if (!primary && settings.craftInventory) {
            primary = findPrimaryCraft(inventory, item);
        }

        if (primary) {
            // We can craft this into something.
            itemsLeft--;
            postMessage("Crafting " + getItemString(primary) + (isEquipped ? " [equipped]" : " [inventory]") + " with " + getItemString(item));
            API.inventory.craft(primary, item); // TODO bind this callback

            // Primary has now been upgraded. Compare it to the currently equipped piece of equipment, and if it wins, equip it.
            if (!isEquipped) {
                var unequippedItem = equipIfBetter(primary);
                // If we changed equipment, try to sell the un-equipped item.
                if (unequippedItem) {
                    if (sellIfMax(unequippedItem)) {
                        itemsLeft--;
                    }
                } else {
                    if (sellIfMax(primary)) {
                        itemsLeft--;
                    }
                }
            }
        } else {
            // Deal with the new item.

            // First try to equip it.
            var unequippedItem = equipIfBetter(item);
            if (unequippedItem) {
                if (sellIfMax(unequippedItem)) {
                    itemsLeft--;
                    return;
                } else {
                    if (getInventoryFull(itemsLeft)) {
                        sellLastInventoryItem();
                        itemsLeft--;
                    }
                }
            }
            // Now look at the inventory and remove any duplicates.
            if (settings.sellDuplicateInventory) {
                // Use the unequipped item for the duplicates.
                if (unequippedItem) {
                    item = unequippedItem;
                }
                // Remove all duplicates in the inventory.
                var duplicates = findDuplicateTypes(inventory, item);
                var tempItem = item;
                if (duplicates) {
                    for (var i = 0; i < duplicates.length; i++) {
                        var dup = duplicates[i];
                        if (isItemBetter(item, dup)) {
                            sellItem(dup);
                        } else {
                            sellItem(tempItem);
                            tempItem = dup;
                        }
                        itemsLeft--;
                    }
                }
            }

        }
    });

    // Given our current inventory, sell stuff to keep some slots open. Hopefully, these slots will
    if (getInventoryFull()) {
        openLastInventorySpots();
    }

    // Age up everything that we own, if possible.
    inventory.forEach(ScriptAPI.$craftingService.ageUpItem);
    equipment.forEach(ScriptAPI.$craftingService.ageUpItem);

}());<commit_msg>Changed duplicates to evaluate on the Strength, not stats<commit_after>// Structure of items.
//var items = [{
//    id: "",
//    entityType: "",
//    rarity: 1,
//    type: "",
//    subType: "",
//    ts: 0,
//    plus: 0,
//    ageLevel: 0,
//    handed: 1,
//    stats: {
//        hp: 0,
//        attackMin: 0,
//        attackMax: 0,
//        defense: 0,
//        overkill: 0,
//        heal: 0,
//        hpBonus: 0
//    },
//    name: "", // {Helmet, Armor, Gloves, Leggings, Boots, Sword, 2-handed Sword, Bow, Crossbow, Wand, Staff, Shield}
//
//}];

/** ============= Battle INF Script to End All Scripts ==========
 * Authored by Sam 'Halithor' Marquart.
 * Licenced under MIT (https://opensource.org/licenses/MIT).
 *
 * Features:
 * - Sells items below a threshold.
 * - Crafts new items into equipped items and, optionally, inventory items.
 *      + Inventory items is useful for getting higher rarity gear to be more powerful than current gear
 * - Equips items that are better than currently equipped items.
 * - Sells items that have reached maximum Plus, and aren't better than the currently equipped item of that type.
 * - Dips all of your items into the fountain, automagically, to increase the age modifiers.
 * - Removes duplicate items of a given type, leaving only the strongest.
 *
 * Notes:
 * - Only works on items you have gained after you include the script. It will not fix your inventory.
 * -
 */
(function () {
    // Settings for the game. Edit these to fit your character better.
    var settings = {
        version: 1.0,
        openInvSlots: 2, // The number of spots that will ALWAYS be held open in your inventory. I recommend setting this to the drops you get per cycle.
        sellBelow: 2, // The threshold below which we automatically sell (exclusive), without combining. Increase this as you like.
        keepAbove: 4, // The rarity above which we DO NOT ever auto sell (exclusive).
        craftInventory: true, // Whether or not to craft items in your inventory.
        sellDuplicateInventory: true, // If we should sell any duplicates of a given item type in the inventory, and only keep the strongest item.
        forceEquipHighestStrength: false // If you always want to equip the highest rarity, no matter the stats. I recommend this to be true if you don't craft the inventory.
    };

    var _rarities = ["None", "Gray", "Green", "Blue", "Red", "Orange", "Purple", "Teal"];
    var _ages = ["Worn", "Fine", "Refined", "Aged", "Exotic", "Famous", "Master", "Heroic", "Ancient", "Fabled", "Ascended", "Legendary", "Eternal"];

    function postMessage(text) {
        console.log(text);
        API.notifications.create("" + text, 10);
    }

    // String that displays most info about an item.
    function getItemString(item) {
        return '' + item.rarity + "." + item.mod + " " + _ages[item.ageLevel] + " " + item.name + " +" + item.plus;
    }

    // Calculates the relative starting strength of an item.
    function getItemStrength(item) {
        return (item.mod * 2) + item.rarity;
    }

    function isItemAtMaxPlus(item) {
        return item.plus >= (5 + item.rarity * 5);
    }

    function isSameItemType(first, second) {
        return first.type == second.type && first.subType == second.subType
    }

    // Returns if two items are compatible for crafting.
    function canCraftItems(first, second) {
        return isSameItemType(first, second)
            && first.rarity == second.rarity
            && first.mod == second.mod;
    }

    // Tries to find a good item from items for secondary to combined into. Picks the first one found.
    function findPrimaryCraft(items, secondary) {
        for (var j = 0; j < items.length; j++) {
            var candidate = items[j];
            if (canCraftItems(candidate, secondary) && !isItemAtMaxPlus(candidate)) {
                return candidate;
            }
        }
        return undefined;
    }

    // Less picky than find primary craft. Used for inventory duplicates.
    function findDuplicateTypes(items, item) {
        var duplicates = [];
        for (var j = 0; j < items.length; j++) {
            var candidate = items[j];
            if (isSameItemType(item, candidate)) {
                duplicates.push(candidate);
            }
        }
        if (duplicates.length > 0) {
            return duplicates;
        }
        return undefined;
    }

    // Will the additional items push the inventory to full?
    function getInventoryFull(itemsLeft) {
        if (!itemsLeft)
            itemsLeft = settings.openInvSlots;
        return (ScriptAPI.$user.inventory.items.length + itemsLeft >= ScriptAPI.$user.upgrades.inventoryMax.value);
    }

    function getSortedInventory() {
        return ScriptAPI.$user.inventory.items.sort(function (a, b) {
            if (getItemStrength(a) == getItemStrength(b)) {
                if (a.plus == b.plus) {
                    if (a.ageLevel == b.ageLevel) {
                        return 0;
                    } else {
                        return a.ageLevel > b.ageLevel ? -1 : 1;
                    }
                } else {
                    return a.plus > b.plus ? -1 : 1;
                }
            } else {
                return getItemStrength(a) > getItemStrength(b) ? -1 : 1;
            }
        });
    }

    // Find the item equipped matching the given item.
    function findEquipped(primary) {
        var equipment = ScriptAPI.$user.character.equipment;
        for (var i in equipment) {
            var candidate = equipment[i];
            if (isSameItemType(primary, candidate)) {
                return candidate;
            }
        }
        return undefined;
    }

    // Returns true if first is better than second. False otherwise.
    // Does not care about rarity, age, etc. Purely based on stats.
    // This is one of the things you can change to value items differently!
    function isItemBetter(first, second) {
        if (!isSameItemType(first, second)) {
            return false; // Don't even compare if not the same.
        }

        if (first.type == "weapon") {
            // Damage consideration
            var damageFirst = (first.stats.attackMax + first.stats.attackMin) / 2;
            var damageSecond = (second.stats.attackMax + second.stats.attackMin) / 2;
            var damageRatio = damageSecond > 0 ? damageFirst / damageSecond : 1.0;
            // Overkill consideration
            var overkillDiff = first.stats.overkill - second.stats.overkill;
            // Healing ratio.
            var healRatio = second.stats.heal > 0 ? (first.stats.heal / second.stats.heal) : 1.0;
            var armorRatio = second.stats.defense > 0 ? first.stats.defense / second.stats.defense : 1.0;
            var hpBonusDiff = (first.hpBonus - second.hpBonus);
            return damageRatio + healRatio + armorRatio + (overkillDiff / 15) + (hpBonusDiff / 50) > 3.0;
        } else {
            // Everything that's not a weapon is armor.
            var armorRatio = first.stats.defense / second.stats.defense;
            var hpBonusDiff = (first.hpBonus - second.hpBonus);
            return armorRatio + (hpBonusDiff / 50) > 1.0;
        }
    }

    function isItemStronger(first, second) {
        return getItemStrength(first) > getItemStrength(second);
    }

    // Sells an item, without any safety checks.
    function sellItem(item) {
        postMessage("Selling " + getItemString(item));
        API.inventory.sell(item);
    }

    // Sells an item if it's at the max level.
    function sellIfMax(item) {
        if (isItemAtMaxPlus(item) && item.rarity <= settings.keepAbove) {
            sellItem(item);
            return true;
        }
        return false;
    }

    // Sells the inventory item in the last occupied location, sorted by strength.
    function sellLastInventoryItem() {
        var item = getSortedInventory()[ScriptAPI.$user.upgrades.inventoryMax.value - 1];
        if (item) {
            sellItem(item);
        }
    }

    // Similar to above, but forces the spots open based on the setting
    function openLastInventorySpots() {
        var inv = getSortedInventory();
        for (var i = 0; i < settings.openInvSlots; i++) {
            var item = inv[ScriptAPI.$user.upgrades.inventoryMax.value - 1 - i];
            if (item) {
                sellItem(item);
            }
        }
    }

    // Tries to equip the item if better. Returns the unequiped item if it works, false otherwise.
    function equipIfBetter(item, callback) {
        var equip = findEquipped(item);

        if (equip && (isItemBetter(item, equip) || (settings.forceEquipHighestStrength && isItemStronger(item, equip)) )) {
            postMessage("Changed equipped " + equip.type + ":" + equip.subType);
            API.inventory.unequip(equip, function () {
                API.inventory.equip(item, function () {
                    if (callback) {
                        callback(equip);
                    }
                });
            });
            return equip;
        }
        if (callback) {
            callback(false);
        }
        return false;
    }
    // Get a sorted list of the inventory items.
    var inventory = getSortedInventory();

    // Get the equipment
    var equipment = ScriptAPI.$user.character.equipment;

    var itemsLeft = items.length;
    items.forEach(function (item) {

        if (item.rarity < settings.sellBelow && !(item.rarity > settings.keepAbove)) {
            itemsLeft--;
            sellItem(item);
            return;
        }

        // Now we want to combine the item with the inventory into more powerful items. First, we want to upgrade your currently
        // equipped items fully. Then we want to try to combine between items in your inventory. After combining, we look to see
        // if any of the items we have are better than equipped, if so, switch them. Finally, if anything has
        // reached the max Plus value that it can, and it isn't equipped,  sell it.

        // Find a good candidate to craft this item into.
        var isEquipped = false;
        var primary = findPrimaryCraft(equipment, item);
        if (primary) isEquipped = true;

        if (!primary && settings.craftInventory) {
            primary = findPrimaryCraft(inventory, item);
        }

        if (primary) {
            // We can craft this into something.
            itemsLeft--;
            postMessage("Crafting " + getItemString(primary) + (isEquipped ? " [equipped]" : " [inventory]") + " with " + getItemString(item));
            API.inventory.craft(primary, item); // TODO bind this callback

            // Primary has now been upgraded. Compare it to the currently equipped piece of equipment, and if it wins, equip it.
            if (!isEquipped) {
                var unequippedItem = equipIfBetter(primary);
                // If we changed equipment, try to sell the un-equipped item.
                if (unequippedItem) {
                    if (sellIfMax(unequippedItem)) {
                        itemsLeft--;
                    }
                } else {
                    if (sellIfMax(primary)) {
                        itemsLeft--;
                    }
                }
            }
        } else {
            // Deal with the new item.

            // First try to equip it.
            var unequippedItem = equipIfBetter(item);
            if (unequippedItem) {
                if (sellIfMax(unequippedItem)) {
                    itemsLeft--;
                    return;
                } else {
                    if (getInventoryFull(itemsLeft)) {
                        sellLastInventoryItem();
                        itemsLeft--;
                    }
                }
            }
            // Now look at the inventory and remove any duplicates.
            if (settings.sellDuplicateInventory) {
                // Use the unequipped item for the duplicates.
                if (unequippedItem) {
                    item = unequippedItem;
                }
                // Remove all duplicates in the inventory.
                var duplicates = findDuplicateTypes(inventory, item);
                var tempItem = item;
                if (duplicates) {
                    for (var i = 0; i < duplicates.length; i++) {
                        var dup = duplicates[i];
                        if (isItemStronger(item, dup)) {
                            sellItem(dup);
                        } else {
                            sellItem(tempItem);
                            tempItem = dup;
                        }
                        itemsLeft--;
                    }
                }
            }

        }
    });

    // Given our current inventory, sell stuff to keep some slots open. Hopefully, these slots will
    if (getInventoryFull()) {
        openLastInventorySpots();
    }

    // Age up everything that we own, if possible.
    inventory.forEach(ScriptAPI.$craftingService.ageUpItem);
    equipment.forEach(ScriptAPI.$craftingService.ageUpItem);

}());<|endoftext|>