I decided to try and package up the complicated GUI functions in FCR into a neat little bundle that feels like a mix of p5.js drawing functions and CSS/HTML styling. I decided to make the GUI elements into a component-based system so that they are easily expandable and mixable. For now, I have simple boxes, text boxes, images, and input text boxes, but I plan on adding more stuff at a later point. I will also add scrollable containers at some point.

Demo: https://studio.code.org/projects/gamelab/rOXQsmd4_OVOOWKzsdPAQegNHSwSzXd-St4PMW3aaNg/view

Version 1.0

var elementList = [];
var interactiveList = [];
var activeInput = false;

var defaultStyle = {
  padx:5,
  pady:5,
  fill:128,
  stroke:0,
  strokeWeight:1,
  textFill:0,
  textStroke:false,
  textStrokeWeight:1,
  textSize:12,
  horizAlign:"left",
  vertAlign:"top",
  textmax:true,
  font:"heveltica",
  bevel:0,
  imageFit:"none",
  imageHorizAlign:"center",
  imageVertAlign:"center",
  cursor:0,
  get:function(e) {
    return defaultStyle[e];
  }
};
function createStyle(parent) {
  var obj = {};
  obj.parent = parent || defaultStyle;
  obj.get = function(e) {
    if (obj[e] === undefined) return obj.parent.get(e);
    return obj[e];
  }
  obj.use = function() {
    if (obj.get("fill") === false) noFill();
    else fill(obj.get("fill"));
    if (obj.get("stroke") === false) noStroke();
    else stroke(obj.get("stroke"));
    strokeWeight(obj.get("strokeWeight"));
  };
  obj.usetext = function() {
    if (obj.get("textFill") === false) noFill();
    else fill(obj.get("textFill"));
    if (obj.get("textStroke") === false) noStroke();
    else stroke(obj.get("textStroke"));
    strokeWeight(obj.get("textStrokeWeight"));
    textAlign(obj.get("horizAlign"),obj.get("vertAlign"));
    textSize(obj.get("textSize"));
    textFont(obj.get("font"));
  };
  return obj;
}

function createElement(x,y,w,h) {
  var obj = {};
  obj.x = x || 0;
  obj.y = y || 0;
  obj.width = w === undefined ? 50 : w;
  obj.height = h === undefined ? 50 : h;
  obj.style = createStyle();
  obj.visible = true;
  obj._cache = {};
  obj.testhover = function() {
    return mouseRect(obj.x,obj.y,obj.width,obj.height);
  };
  obj.box = function(s) {
    s.use();
    rect(obj.x,obj.y,obj.width,obj.height,s.get("bevel"));
  };
  obj.draw = function() {
    var s = obj.style;
    if (obj.hover) {
      if (mouseDown("left")) s = obj.clickStyle;
      else s = obj.hoverStyle;
    }
    if (activeInput == obj) s = obj.activeStyle;
    obj.box(s);
    if (obj.image) obj.image(s);
    if (obj.text) obj.text(s);
    if (obj.cursor) obj.cursor(s);
  };
  obj.addComponent = function(e,a,b,c) {
    Components[e](obj,a,b,c);
  };
  elementList.push(obj);
  return obj;
}
var Components = {};
Components.text = function(obj,txt) {
  obj.value = txt||"";
  obj.text = function(s) {
    s.usetext();
    var max, tx = s.get("padx"), ty = s.get("pady");
    var ax = s.get("horizAlign");
    var ay = s.get("vertAlign");
    var tm = s.get("textmax");
    if (tm) {
      max = obj.width - tx;
      if (ax == "center" && !obj.cursor) max = obj.width - tx * 2;
    } else {
      if (ax == "right" && !obj.cursor) tx = obj.width - tx;
      else if (ax == "center" && !obj.cursor) tx = obj.width/2;
    }
    if (ay == "bottom" && !obj.cursor) ty = obj.height - ty;
    else if (ay == "center" && !obj.cursor) ty = obj.height/2;
    if (tm == 'number') max = tm;
    obj._cache.max = max;
    obj._cache.tx = tx;
    obj._cache.ty = ty;
    text(obj.value,obj.x+tx,obj.y+ty,max);
  };
}
Components.button = function(obj,txt) {
  Components.text(obj,txt);
  obj.hoverStyle = createStyle(obj.style);
  obj.clickStyle = createStyle(obj.hoverStyle);
  obj.hover = false;
  obj.onclick = function(){};
  interactiveList.push(obj);
};
Components.image = function(obj,url,callback,failure) {
  if (typeof url == 'string') obj.img = loadImage(url,function(img) {
    if (obj.width === 0) obj.width = img.width;
    if (obj.height === 0) obj.height = img.height;
  },failure);
  else obj.img = url;
  obj.image = function(s) {
    var f = s.get("imageFit");
    var ax = s.get("imageHorizAlign");
    var ay = s.get("imageVertAlign");
    if (f == "inside") {
      var bb = centerRect(obj.width,obj.height,obj.img.width,obj.img.height);
      if (ax == "left") bb.x = 0;
      if (ax == "right") bb.x = obj.width-bb.w;
      if (ay == "top") bb.y = 0;
      if (ay == "bottom") bb.y = obj.height-bb.h;
      image(obj.img,obj.x+bb.x,obj.y+bb.y,bb.w,bb.h);
    }
    else if (f == "outside") {
      var bb = centerRect(obj.img.width,obj.img.height,obj.width,obj.height);
      if (ax == "left") bb.x = 0;
      if (ax == "right") bb.x = obj.img.width-bb.w;
      if (ay == "top") bb.y = 0;
      if (ay == "bottom") bb.y = obj.img.height-bb.h;
      image(obj.img,bb.x,bb.y,bb.w,bb.h,obj.x,obj.y,obj.width,obj.height);
    }
    else image(obj.img,obj.x,obj.y,obj.width,obj.height);
  };
};
Components.input = function(obj,txt) {
  Components.text(obj,txt);
  Components.button(obj);
  obj.activeStyle = createStyle(obj.clickStyle);
  obj.cursorPos = obj.value.length;
  obj.cursor = function(s) {
    if (frameCount % 30 < 15 && activeInput == obj) return;
    noStroke();
    fill(s.get("cursor"));
    text("|",obj.x+obj._cache.cursorX,obj.y+obj._cache.cursorY+0.5);
  };
  obj.calculateCursorPos = function() {
    var s = obj.style, c = obj._cache;
    c.cursorY = (obj.beflines.length-1)*(s.get("textSize")+2.5)+c.ty;
    c.cursorX = textWidth(obj.beflines[obj.beflines.length-1])+c.tx;
  };
  obj.onclick = function() {
    var s = obj.style, c = obj._cache;
    activeInput = obj;
    var lines = getLines(obj.value,c.max);
    var dx = mouseX - obj.x - c.tx;
    var dy = mouseY - obj.y - c.ty;
    var li = constrain(round(dy/(s.get("textSize")-1)),0,lines.length)-1;
    var e = textWidth("_");
    var last = lines[li]||"";
    var cpos = min(round(dx/e),last.length);
    while (textWidth(last.substring(0,cpos))>dx) cpos--;
    while (textWidth(last.substring(0,cpos))<dx && cpos < last.length) cpos++;
    for (var i = 0; i < li; i++) {
      cpos += lines[i].length;
    }
    cpos += (obj.value.substring(0,cpos).match(/\n/g)||[]).length;
    obj.cursorPos = cpos;
    obj.update();
  };
  obj.update = function() {
    var str = obj.value.substring(0,obj.cursorPos);
    obj.beflines = getLines(str,obj._cache.max);
    obj.lines = getLines(obj.value,obj._cache.max);
    obj.calculateCursorPos();
    obj.onchange();
  }
  obj.onchange = function() {};
  setTimeout(function(){
    obj.text(obj.style);
    obj.update();
  },0);
};

function drawElements() {
  cursor(ARROW);
  var done = false;
  for (var i = interactiveList.length-1; i >= 0; i--) {
    var obj = interactiveList[i];
    if (!obj.visible) continue;
    var h = !done && obj.testhover();
    obj.hover = h;
    if (obj.hover && obj.onclick) {
      if (obj.cursor) cursor(TEXT);
      else cursor(HAND);
    }
    if (h) done = true;
  }
  for (var i = 0; i < elementList.length; i++) {
    if (!elementList[i].visible) continue;
    elementList[i].draw();
  }
}
function mousePressedElements() {
  activeInput = false;
  var done = false;
  for (var i = interactiveList.length-1; i >= 0; i--) {
    if (!interactiveList[i].visible) continue;
    var h = !done && interactiveList[i].testhover();
    if (h) {
      interactiveList[i].onclick();
      done = true;
    }
  }
}
function keyTypedElements() {
  if (!activeInput) return;
  if (key == "\r") return;
  var oldkey = key;
  onHold(keyCode,key,function(){
    activeInput.value = activeInput.value.substring(0, activeInput.cursorPos) + oldkey + activeInput.value.substring(activeInput.cursorPos, activeInput.value.length);
    activeInput.cursorPos++;
    activeInput.update();
  });
}
function keyPressedElements() {
  if (!activeInput) return;
  onHold(BACKSPACE,"backspace",function() {
    activeInput.value = activeInput.value.substring(0, activeInput.cursorPos-1) + activeInput.value.substring(activeInput.cursorPos, activeInput.value.length);
    activeInput.cursorPos--;
    activeInput.cursorPos = max(0,activeInput.cursorPos);
    activeInput.update();
  });
  onHold(LEFT_ARROW,"left",function() {
    activeInput.cursorPos = max(activeInput.cursorPos-1,0);
    activeInput.update();
  });
  onHold(RIGHT_ARROW,"right",function() {
    activeInput.cursorPos = min(activeInput.cursorPos+1,activeInput.value.length);
    activeInput.update();
  });
  if (keyCode == ENTER) {
    activeInput.value = activeInput.value.substring(0, activeInput.cursorPos) + "\n" + activeInput.value.substring(activeInput.cursorPos, activeInput.value.length);
    activeInput.cursorPos++;
    activeInput.update();
    return;
  }
  var lines, cursorLine, cursorOffset;
  var updateLines = function() {
    activeInput.update();
    lines = activeInput.lines;
    cursorLine = activeInput.beflines.length-1;
    cursorOffset = activeInput.cursorPos;
    for (var i = 0; i < cursorLine; i++) {
      cursorOffset -= lines[i].length+1;
    }
  }
  onHold(UP_ARROW,"up",function() {
    updateLines();
    if (cursorLine <= 0) return;
    var newPos = 0;
    for (var i = 0; i < cursorLine-1; i++) {
      newPos += lines[i].length+1;
    }
    activeInput.cursorPos = newPos + min(cursorOffset,lines[cursorLine-1].length);
  });
  onHold(DOWN_ARROW,"down",function() {
    updateLines();
    if (cursorLine >= lines.length-1) return;
    var newPos = 0;
    for (var i = 0; i < cursorLine+1; i++) {
      newPos += lines[i].length+1;
    }
    activeInput.cursorPos = newPos + min(cursorOffset,lines[cursorLine+1].length);
  });
  if (keyCode == 86 && keyDown(CONTROL)) {
    var pasted = prompt("Paste here:");
    if (!pasted) return;
    activeInput.value = activeInput.value.substring(0,activeInput.cursorPos) + pasted + activeInput.value.substring(activeInput.cursorPos,activeInput.value.length);
    activeInput.cursorPos += pasted.length;
    activeInput.update();
  }
}
function mousePressed() {
  mousePressedElements();
}
function keyTyped() {
  keyTypedElements();
}
function keyPressed() {
  keyPressedElements();
}

function mouseRect(x,y,width,height) {
  return (
    mouseX > x &&
    mouseX < x+width &&
    mouseY > y && 
    mouseY < y+height
  );
}
function centerRect(w1,h1,w2,h2) {
  var fatness1 = w1 / h1;
  var fatness2 = w2 / h2;

  var scale;
  if (fatness2 >= fatness1) scale = w1 / w2;
  else scale = h1 / h2;
  var w = w2 * scale;
  var h = h2 * scale;

  var xCenter = 0 + (w1 / 2);
  var yCenter = 0 + (h1 / 2);

  var x = xCenter - (w / 2);
  var y = yCenter - (h / 2);

  return { x:x, y:y, w:w, h:h };
}
function getLines(string,width) {
  if (typeof string != 'string') return [];
  var lines = string.split("\n");
  for (var i = 0; i < lines.length; i++) {
    if (textWidth(lines[i]) <= width) continue;
    var off = "";
    while (textWidth(lines[i]) > width || (!lines[i].match(/[\s-_]$/) && lines[i].match(/[\s-_]/))) {
      off = lines[i].charAt(lines[i].length-1)+off;
      lines[i] = lines[i].substr(0,lines[i].length-1);
    }
    lines.splice(i+1,0,off);
  }
  return lines;
}
function onHold(kc,k,funct) {
  if (keyCode != kc && key != k) return;
  funct();
  setTimeout(function() {
    if (!keyDown(k)) return;
    funct();
    var int = setInterval(function() {
      if (!keyDown(k)) {
        clearInterval(int);
        return;
      }
      funct();
    },100);
  },600);
}

// Main

var test = createElement(10,10,50,50);
test.addComponent("button","this is a test of the text");
test.style.fill = "red";
test.hoverStyle.fill = "pink";
test.clickStyle.fill = "white";
test.style.bevel = 5;
test.onclick = function() {
  console.log(1);
};

var test = createElement(20,20,60,50);
test.addComponent("button","wow! it's a test of the text");
test.style.fill = "blue";
test.hoverStyle.fill = "cyan";
test.clickStyle.fill = "white";
test.style.bevel = 5;
test.style.stroke = "blue";
test.onclick = function() {
  console.log(2);
};

var test = createElement(50,100,300,200);
test.addComponent("image","https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg");
test.addComponent("text","wow!");
test.style.imageFit = "inside";
test.style.textFill = "white";
test.style.horizAlign = "center";
test.style.vertAlign = "center";

var test = createElement(20,310,360,80);
test.addComponent("input","Wow!\nWhat a so very nice day to spend lots of time with friends and family.\n Tons of cheer and spirit to go round. Oh yes, it is that time of year again. Christmas bells ring in the distance.");
test.onchange = function() {console.log(key)};
test.activeStyle.stroke = "white";
test.hoverStyle.stroke = "grey";

var test = createElement(70,10,100,40);
test.addComponent("input","Other Text Box");
test.style.fill = "green";
test.hoverStyle.fill = "lime";
test.style.bevel = 5;
test.style.stroke = "white";

function draw() {
  background(220);
  drawElements();
}

If you have any suggestions, or you find any bugs, please comment them below. Also, upvote this post if you found it useful.

Awards

Updated version 1.1 with scrollable divs

// FGUI Fire Graphics User Interface
// By: DragonFireGames
// Version: 1.1
// Description: A port/mix of HTML, CSS, 
//   and p5.js to make a component based
//   graphics interface. Styles are used
//   and can be inherited and set.

var testgraphics = createGraphics(400,400);
background(255);
testgraphics.fill(0);
testgraphics.noStroke();
testgraphics.rect(0,0,200,200);
image(testgraphics,0,0,200,200);
var halfGraphics = get(150,150)[0] == 0;
delete testgraphics;

var elementList = [];
var activeInput = false;

var defaultStyle = {
  padx:5,
  pady:5,
  fill:128,
  stroke:0,
  strokeWeight:1,
  textFill:0,
  textStroke:false,
  textStrokeWeight:1,
  textSize:12,
  horizAlign:"left",
  vertAlign:"top",
  textmax:true,
  font:"heveltica",
  bevel:0,
  imageFit:"none",
  imageHorizAlign:"center",
  imageVertAlign:"center",
  cursor:0,
  get:function(e) {
    return defaultStyle[e];
  }
};
function createStyle(parent) {
  var obj = {};
  obj.parent = parent || defaultStyle;
  obj.get = function(e) {
    if (obj[e] === undefined) return obj.parent.get(e);
    return obj[e];
  }
  obj.use = function(b) {
    if (obj.get("fill") === false) b.noFill();
    else b.fill(obj.get("fill"));
    if (obj.get("stroke") === false) b.noStroke();
    else b.stroke(obj.get("stroke"));
    b.strokeWeight(obj.get("strokeWeight"));
  };
  obj.usetext = function(b) {
    if (obj.get("textFill") === false) b.noFill();
    else b.fill(obj.get("textFill"));
    if (obj.get("textStroke") === false) b.noStroke();
    else b.stroke(obj.get("textStroke"));
    b.strokeWeight(obj.get("textStrokeWeight"));
    b.textAlign(obj.get("horizAlign"),obj.get("vertAlign"));
    b.textSize(obj.get("textSize"));
    b.textFont(obj.get("font"));
  };
  return obj;
}

function createElement(x,y,w,h) {
  var obj = {};
  obj.x = x || 0;
  obj.y = y || 0;
  obj.width = w === undefined ? 50 : w;
  obj.height = h === undefined ? 50 : h;
  obj.style = createStyle();
  obj.visible = true;
  obj._cache = {};
  obj.testhover = function() {
    return mouseRect(obj.x,obj.y,obj.width,obj.height);
  };
  obj.getmax = function() {
    return {x:obj.x+obj.w,y:obj.y+obj.h};
  };
  obj.box = function(s,b) {
    s.use(b);
    b.rect(obj.x,obj.y,obj.width,obj.height,s.get("bevel"));
  };
  obj.draw = function(b) {
    if (!obj.visible) return;
    var s = obj.style;
    b = b || window;
    if (obj.hover) {
      if (mouseDown("left") && obj.onclick) s = obj.clickStyle;
      else s = obj.hoverStyle;
    }
    if (activeInput == obj && obj.cursor) s = obj.activeStyle;
    obj.box(s,b);
    if (obj.image) obj.image(s,b);
    if (obj.text) obj.text(s,b);
    if (obj.cursor) obj.cursor(s,b);
    if (obj.div) obj.div(s,b);
  };
  obj.addComponent = function(e,a,b,c) {
    Components[e](obj,a,b,c);
  };
  elementList.push(obj);
  return obj;
}
var Components = {};
Components.text = function(obj,txt) {
  obj.value = txt||"";
  obj.text = function(s,b) {
    var c = obj._cache;
    s.usetext(b);
    var max, tx = s.get("padx"), ty = s.get("pady");
    var ax = s.get("horizAlign");
    var ay = s.get("vertAlign");
    var tm = s.get("textmax");
    if (tm) {
      max = obj.width - tx;
      if (ax == "center" && !obj.cursor) max = obj.width - tx * 2;
    } else {
      if (ax == "right" && !obj.cursor) tx = obj.width - tx;
      else if (ax == "center" && !obj.cursor) tx = obj.width/2;
    }
    if (ay == "bottom" && !obj.cursor) ty = obj.height - ty;
    else if (ay == "center" && !obj.cursor) ty = obj.height/2;
    if (tm == 'number') max = tm;
    c.max = max; c.tx = tx; c.ty = ty;
    b.text(obj.value,obj.x+tx,obj.y+ty,max);
    if (ax == "left" && c.value !== obj.value) {
      c.lines = getLines(obj.value,max);
      obj.textHeight = c.lines.length*(s.get("textSize")+2.5)+ty;
      obj.textWidth = textWidth(c.lines[c.lines.length-1])+tx;
      c.value = obj.value;
      if (obj.onchange) obj.onchange();
    }
  };
  obj.getmax = function() {
    var c = obj._cache;
    var m1x = obj.x+obj.w, m1y = obj.y+obj.h;
    return {x:max(m1x,obj.textWidth||0),y:max(m1y,obj.textHeight||0)}
  };
};
Components.button = function(obj,txt) {
  obj.hoverStyle = createStyle(obj.style);
  obj.clickStyle = createStyle(obj.hoverStyle);
  obj.hover = false;
  obj.onclick = function(){};
  obj.interactive = true;
};
Components.image = function(obj,url,callback,failure) {
  if (typeof url == 'string') obj.img = loadImage(url,function(img) {
    if (obj.width === 0) obj.width = img.width;
    if (obj.height === 0) obj.height = img.height;
  },failure);
  else obj.img = url;
  obj.image = function(s,b) {
    var f = s.get("imageFit");
    var ax = s.get("imageHorizAlign");
    var ay = s.get("imageVertAlign");
    if (f == "inside") {
      var bb = centerRect(obj.width,obj.height,obj.img.width,obj.img.height);
      if (ax == "left") bb.x = 0;
      if (ax == "right") bb.x = obj.width-bb.w;
      if (ay == "top") bb.y = 0;
      if (ay == "bottom") bb.y = obj.height-bb.h;
      b.image(obj.img,obj.x+bb.x,obj.y+bb.y,bb.w,bb.h);
    }
    else if (f == "outside") {
      var bb = centerRect(obj.img.width,obj.img.height,obj.width,obj.height);
      if (ax == "left") bb.x = 0;
      if (ax == "right") bb.x = obj.img.width-bb.w;
      if (ay == "top") bb.y = 0;
      if (ay == "bottom") bb.y = obj.img.height-bb.h;
      b.image(obj.img,bb.x,bb.y,bb.w,bb.h,obj.x,obj.y,obj.width,obj.height);
    }
    else b.image(obj.img,obj.x,obj.y,obj.width,obj.height);
  };
};
Components.input = function(obj,txt) {
  Components.text(obj,txt);
  Components.button(obj);
  obj.activeStyle = createStyle(obj.clickStyle);
  obj.cursorPos = obj.value.length;
  obj.cursor = function(s,b) {
    if (frameCount % 30 < 15 && activeInput == obj) return;
    b.noStroke();
    b.fill(s.get("cursor"));
    b.text("|",obj.x+obj._cache.cursorX,obj.y+obj._cache.cursorY+0.5);
  };
  obj.calculateCursorPos = function() {
    var s = obj.style, c = obj._cache;
    c.cursorY = (obj.beflines.length-1)*(s.get("textSize")+2.5)+c.ty;
    c.cursorX = textWidth(obj.beflines[obj.beflines.length-1])+c.tx;
  };
  obj.onclick = function() {
    var s = obj.style, c = obj._cache;
    activeInput = obj;
    var lines = obj.lines;
    var dx = mouseX - obj.x - c.tx;
    var dy = mouseY - obj.y - c.ty;
    var li = constrain(round(dy/(s.get("textSize")-1)),0,lines.length)-1;
    var e = textWidth("_");
    var last = lines[li]||"";
    var cpos = min(round(dx/e),last.length);
    while (textWidth(last.substring(0,cpos))>dx) cpos--;
    while (textWidth(last.substring(0,cpos))<dx && cpos < last.length) cpos++;
    for (var i = 0; i < li; i++) {
      cpos += lines[i].length;
    }
    cpos += (obj.value.substring(0,cpos).match(/\n/g)||[]).length;
    obj.cursorPos = cpos;
    obj.update();
  };
  obj.update = function() {
    obj.lines = getLines(obj.value,obj._cache.max);
    var bef = [];
    var c = obj.cursorPos;
    for (var i = 0; i < obj.lines.length; i++) {
      bef.push(obj.lines[i]);
      c-=obj.lines[i].length;
      if (c < 0) break;
    }
    bef[bef.length-1] = bef[bef.length-1].substring(0,bef[bef.length-1].length+c);
    obj.beflines = bef;
    obj.calculateCursorPos();
    obj.onchange();
  }
  obj.onchange = function() {};
  setTimeout(function(){
    obj.text(obj.style,window);
    obj.update();
  },0);
};
Components.div = function(obj) {
  obj.buff = createGraphics(obj.width,obj.height);
  if (halfGraphics) obj.buff.scale(0.5,0.5);
  obj._cache.width = obj.width;
  obj._cache.height = obj.height;
  obj.children = [];
  obj.appendChild = function(elem) {
    elem.parent = obj;
    obj.children.push(elem);
  };
  obj.prependChild = function(elem) {
    elem.parent = obj;
    obj.children.shift(elem);
  };
  obj.removeChild = function(elem) {
    var i = obj.children.indexOf(elem);
    delete elem.parent;
    obj.children.splice(i,1);
  };
  obj.removeChildAtIndex = function(i) {
    delete obj.children[i].parent;
    obj.children.splice(i,1);
  };
  obj.div = function(s,b) {
    obj.buff.clear();
    obj.buff.translate(-obj.scrollX,-obj.scrollY);
    if (obj._cache.width != obj.width || obj._cache.width != obj.width) {
      obj.buff = createGraphics(obj.width,obj.height);
      if (halfGraphics) obj.buff.scale(0.5,0.5);
    }
    for (var i = 0; i < obj.children.length; i++) {
      obj.children[i].draw(obj.buff);
    }
    obj.buff.translate(obj.scrollX,obj.scrollY);
    b.image(obj.buff,obj.x,obj.y,obj.width,obj.height);
  };
  obj.scrollX = 0;
  obj.scrollY = 0;
  obj.minScrollX = 0;
  obj.minScrollY = 0;
  obj.maxScrollX = 0;
  obj.maxScrollY = 0;
  obj.scroll = function(x,y) {
    obj.scrollX = constrain(obj.scrollX+x,obj.minScrollX,obj.maxScrollX);
    obj.scrollY = constrain(obj.scrollY+y,obj.minScrollY,obj.maxScrollY);
  };
};

function drawElements() {
  cursor(ARROW);
  clickerElement(function(obj) {
    if (obj.onclick) {
      if (obj.cursor) cursor(TEXT);
      else cursor(HAND);
    }
    if (!obj.div) return;
    var sx = 0, sy = 0;
    if (mouseDown("left")) {
      sx += pmouseX-mouseX;
      sy += pmouseY-mouseY;
    }
    if (keyDown("left")) sx -= 5;
    if (keyDown("right")) sx += 5;
    if (keyDown("up")) sy -= 5;
    if (keyDown("down")) sy += 5;
    obj.scroll(sx,sy);
  });
  for (var i = 0; i < elementList.length; i++) {
    if (elementList[i].parent) continue;
    elementList[i].draw();
  }
}
function clickerElement(callback) {
  var done = false;
  var hov = function(obj) {
    if (!obj.visible || (!obj.interactive && !obj.div)) return;
    var h = !done && obj.testhover();
    if (obj.div) {
      if (!h) return;
      mouseX -= obj.x - obj.scrollX;
      mouseY -= obj.y - obj.scrollY;
      for (var j = obj.children.length-1; j >= 0; j--) {
        hov(obj.children[j]);
      }
      mouseX += obj.x - obj.scrollX;
      mouseY += obj.y - obj.scrollY;
    } else {
      obj.hover = h;
      if (h) done = true;
    }
    if (h) callback(obj);
  };
  for (var i = elementList.length-1; i >= 0; i--) {
    var obj = elementList[i];
    if (obj.parent) continue;
    hov(obj);
  }
}
function mousePressedElements() {
  activeInput = false;
  clickerElement(function(obj) {
    if (!obj.onclick) return;
    activeInput = obj;
    obj.onclick();
  });
}
function keyTypedElements() {
  if (!activeInput) return;
  if (key == "\r") return;
  var oldkey = key;
  onHold(keyCode,key,function(){
    activeInput.value = activeInput.value.substring(0, activeInput.cursorPos) + oldkey + activeInput.value.substring(activeInput.cursorPos, activeInput.value.length);
    activeInput.cursorPos++;
    activeInput.update();
  });
}
function keyPressedElements() {
  if (!activeInput || !activeInput.cursor) return;
  onHold(BACKSPACE,"backspace",function() {
    activeInput.value = activeInput.value.substring(0, activeInput.cursorPos-1) + activeInput.value.substring(activeInput.cursorPos, activeInput.value.length);
    activeInput.cursorPos--;
    activeInput.cursorPos = max(0,activeInput.cursorPos);
    activeInput.update();
  });
  onHold(LEFT_ARROW,"left",function() {
    activeInput.cursorPos = max(activeInput.cursorPos-1,0);
    activeInput.update();
  });
  onHold(RIGHT_ARROW,"right",function() {
    activeInput.cursorPos = min(activeInput.cursorPos+1,activeInput.value.length);
    activeInput.update();
  });
  if (keyCode == ENTER) {
    activeInput.value = activeInput.value.substring(0, activeInput.cursorPos) + "\n" + activeInput.value.substring(activeInput.cursorPos, activeInput.value.length);
    activeInput.cursorPos++;
    activeInput.update();
    return;
  }
  var lines, cursorLine, cursorOffset;
  var updateLines = function() {
    activeInput.update();
    lines = activeInput.lines;
    cursorLine = activeInput.beflines.length-1;
    cursorOffset = activeInput.cursorPos;
    for (var i = 0; i < cursorLine; i++) {
      cursorOffset -= lines[i].length+1;
    }
  }
  onHold(UP_ARROW,"up",function() {
    updateLines();
    if (cursorLine <= 0) return;
    var newPos = 0;
    for (var i = 0; i < cursorLine-1; i++) {
      newPos += lines[i].length+1;
    }
    activeInput.cursorPos = newPos + min(cursorOffset,lines[cursorLine-1].length);
  });
  onHold(DOWN_ARROW,"down",function() {
    updateLines();
    if (cursorLine >= lines.length-1) return;
    var newPos = 0;
    for (var i = 0; i < cursorLine+1; i++) {
      newPos += lines[i].length+1;
    }
    activeInput.cursorPos = newPos + min(cursorOffset,lines[cursorLine+1].length);
  });
  if (keyCode == 86 && keyDown(CONTROL)) {
    var pasted = prompt("Paste here:");
    if (!pasted) return;
    activeInput.value = activeInput.value.substring(0,activeInput.cursorPos) + pasted + activeInput.value.substring(activeInput.cursorPos,activeInput.value.length);
    activeInput.cursorPos += pasted.length;
    activeInput.update();
  }
}
function mousePressed() {
  mousePressedElements();
}
function keyTyped() {
  keyTypedElements();
}
function keyPressed() {
  keyPressedElements();
}

function mouseRect(x,y,width,height) {
  return (
    mouseX > x &&
    mouseX < x+width &&
    mouseY > y && 
    mouseY < y+height
  );
}
function centerRect(w1,h1,w2,h2) {
  var fatness1 = w1 / h1;
  var fatness2 = w2 / h2;

  var scale;
  if (fatness2 >= fatness1) scale = w1 / w2;
  else scale = h1 / h2;
  var w = w2 * scale;
  var h = h2 * scale;

  var xCenter = 0 + (w1 / 2);
  var yCenter = 0 + (h1 / 2);

  var x = xCenter - (w / 2);
  var y = yCenter - (h / 2);

  return { x:x, y:y, w:w, h:h };
}
function getLines(string,width) {
  if (typeof string != 'string') return [];
  var lines = string.split("\n");
  for (var i = 0; i < lines.length; i++) {
    if (textWidth(lines[i]) <= width) continue;
    var off = "";
    while (textWidth(lines[i]) > width || (!lines[i].match(/[\s-_]$/) && lines[i].match(/[\s-_]/))) {
      off = lines[i].charAt(lines[i].length-1)+off;
      lines[i] = lines[i].substr(0,lines[i].length-1);
    }
    lines.splice(i+1,0,off);
  }
  return lines;
}
function onHold(kc,k,funct) {
  if (keyCode != kc && key != k) return;
  funct();
  setTimeout(function() {
    if (!keyDown(k)) return;
    funct();
    var int = setInterval(function() {
      if (!keyDown(k)) {
        clearInterval(int);
        return;
      }
      funct();
    },100);
  },600);
}

// ------------
// Testing Area
// ------------

var test = createElement(10,10,50,50);
test.addComponent("text","this is a test of the text");
test.addComponent("button");
test.style.fill = "red";
test.hoverStyle.fill = "pink";
test.clickStyle.fill = "white";
test.style.bevel = 5;
test.onclick = function() {
  console.log("clicked #1");
};
var test2 = test;

var test = createElement(20,20,60,50);
test.addComponent("text","wow! it's a test of the text");
test.addComponent("button");
test.style.fill = "blue";
test.hoverStyle.fill = "cyan";
test.clickStyle.fill = "white";
test.style.bevel = 5;
test.style.stroke = "blue";
test.onclick = function() {
  console.log("clicked #2");
};

var test = createElement(50,100,300,200);
test.addComponent("image","https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg");
test.addComponent("text","wow!");
test.style.imageFit = "inside";
test.style.textFill = "white";
test.style.horizAlign = "center";
test.style.vertAlign = "center";

var test = createElement(20,310,360,80);
test.addComponent("input","Wow!\nWhat a so very nice day to spend lots of time with friends and family.\n Tons of cheer and spirit to go round. Oh yes, it is that time of year again. Christmas bells ring in the distance.");
test.onchange = function() {console.log(key)};
test.activeStyle.stroke = "white";
test.hoverStyle.stroke = "grey";

var test = createElement(70,10,100,40);
test.addComponent("input","Other Text Box\nWhee!");
test.style.fill = "green";
test.hoverStyle.fill = "lime";
test.style.bevel = 5;
test.style.stroke = "white";
test.onchange = function() {
  this.height = this.textHeight;
};
var t1 = test;

var div = createElement(250,10,120,60);
div.addComponent("div");
div.style.bevel = 5;

var test = createElement(0,15,120,0);
test.addComponent("input","I have vivid dreams in the night every night. They are strange and wonderful and mysterious and wild. My deepest desires and worst fears are revealed to me.");
test.style.fill = false;
test.style.bevel = 0;
test.style.stroke = false;
div.appendChild(test);
test.onchange = function() {
  this.height = this.textHeight+15;
  div.maxScrollY = max(this.height-div.height,0);
};

var test = createElement(5,5,20,10);
test.addComponent("button");
test.style.fill = "green";
test.hoverStyle.fill = "lime";
test.style.bevel = 5;
test.style.stroke = "white";
div.appendChild(test);
test.onclick = function() {
  console.log("clicked #3");
};

function draw() {
  background(220);
  drawElements();
}

1.2


// FGUI Fire Graphics User Interface
// By: DragonFireGames
// Version: 1.2
// Description: A port/mix of HTML, CSS, 
//   and p5.js to make a component based
//   graphics interface. Styles are used
//   and can be inherited and set.

var testgraphics = createGraphics(400,400);
background(255);
testgraphics.fill(0);
testgraphics.noStroke();
testgraphics.rect(0,0,200,200);
image(testgraphics,0,0,200,200);
var halfGraphics = get(150,150)[0] == 0;
delete testgraphics;

var elementList = [];
var activeElement = false;

var defaultStyle = {
  padx:5,
  pady:5,
  fill:128,
  stroke:0,
  strokeWeight:1,
  textFill:0,
  textStroke:false,
  textStrokeWeight:1,
  textSize:12,
  horizAlign:"left",
  vertAlign:"top",
  textmax:true,
  font:"heveltica",
  bevel:0,
  imageFit:"none",
  imageHorizAlign:"center",
  imageVertAlign:"center",
  cursor:0,
  get:function(e) {
    return defaultStyle[e];
  }
};
function createStyle(parent) {
  var obj = {};
  obj.parent = parent || defaultStyle;
  obj.get = function(e) {
    if (obj[e] === undefined) return obj.parent.get(e);
    return obj[e];
  }
  obj.use = function(b) {
    if (obj.get("fill") === false) b.noFill();
    else b.fill(obj.get("fill"));
    if (obj.get("stroke") === false) b.noStroke();
    else b.stroke(obj.get("stroke"));
    b.strokeWeight(obj.get("strokeWeight"));
  };
  obj.usetext = function(b) {
    if (obj.get("textFill") === false) b.noFill();
    else b.fill(obj.get("textFill"));
    if (obj.get("textStroke") === false) b.noStroke();
    else b.stroke(obj.get("textStroke"));
    b.strokeWeight(obj.get("textStrokeWeight"));
    b.textAlign(obj.get("horizAlign"),obj.get("vertAlign"));
    b.textSize(obj.get("textSize"));
    b.textFont(obj.get("font"));
  };
  return obj;
}

function createElement(x,y,w,h) {
  var obj = {};
  obj.x = x || 0;
  obj.y = y || 0;
  obj.width = w === undefined ? 50 : w;
  obj.height = h === undefined ? 50 : h;
  obj.style = createStyle();
  obj.visible = true;
  obj._cache = {};
  obj.testhover = function() {
    return mouseRect(obj.x,obj.y,obj.width,obj.height);
  };
  obj.getmax = function() {
    return {x:obj.x+obj.w,y:obj.y+obj.h};
  };
  obj.body = function(s,b) {
    s.use(b);
    b.rect(obj.x,obj.y,obj.width,obj.height,s.get("bevel"));
  };
  obj.show = function(b) {
    if (!obj.visible) return;
    if (obj.drag) obj.drag(s,b);
    var s = obj.style;
    b = b || window;
    if (obj.hover) {
      if (mouseDown("left") && obj.onclick) s = obj.clickStyle;
      else s = obj.hoverStyle;
    }
    if (activeElement == obj) {
      if (obj.cursor) s = obj.activeStyle;
      else if (obj.drag) s = obj.clickStyle;
    }
    obj.body(s,b);
    if (obj.image) obj.image(s,b);
    if (obj.text) obj.text(s,b);
    if (obj.cursor) obj.cursor(s,b);
    if (obj.canvas) obj.canvas(s,b);
  };
  obj.addComponent = function(e,a,b,c) {
    Components[e](obj,a,b,c);
  };
  elementList.push(obj);
  return obj;
}
var Components = {};
Components.text = function(obj,txt) {
  obj.value = txt||"";
  obj.text = function(s,b) {
    var c = obj._cache;
    s.usetext(b);
    var max, tx = s.get("padx"), ty = s.get("pady");
    var ax = s.get("horizAlign");
    var ay = s.get("vertAlign");
    var tm = s.get("textmax");
    if (obj.cursor) ax = "left", ay = "top";
    if (tm) {
      max = obj.width - tx;
      if (ax == "center") max = obj.width - tx * 2;
    } else {
      if (ax == "right") tx = obj.width - tx;
      else if (ax == "center") tx = obj.width/2;
    }
    if (ay == "bottom") ty = obj.height - ty;
    else if (ay == "center") ty = obj.height/2;
    if (tm == 'number') max = tm;
    c.max = max; c.tx = tx; c.ty = ty;
    b.text(obj.value,obj.x+tx,obj.y+ty,max);
    if (ax == "left" && c.value !== obj.value) {
      c.lines = getLines(obj.value,max);
      obj.textHeight = c.lines.length*(s.get("textSize")+2.5)+ty;
      obj.textWidth = textWidth(c.lines[c.lines.length-1])+tx;
      c.value = obj.value;
      if (obj.onchange) obj.onchange();
    }
  };
  obj.getmax = function() {
    var c = obj._cache;
    var m1x = obj.x+obj.w, m1y = obj.y+obj.h;
    return {x:max(m1x,obj.textWidth||0),y:max(m1y,obj.textHeight||0)}
  };
};
Components.button = function(obj) {
  obj.hoverStyle = createStyle(obj.style);
  obj.clickStyle = createStyle(obj.hoverStyle);
  obj.hover = false;
  obj.onclick = function(){};
  obj.onrelease = function(){};
  obj.interactive = true;
};
Components.image = function(obj,url,callback,failure) {
  if (typeof url == 'string') obj.img = loadImage(url,function(img) {
    if (obj.width === 0) obj.width = img.width;
    if (obj.height === 0) obj.height = img.height;
  },failure);
  else obj.img = url;
  obj.image = function(s,b) {
    var f = s.get("imageFit");
    var ax = s.get("imageHorizAlign");
    var ay = s.get("imageVertAlign");
    if (f == "inside") {
      var bb = centerRect(obj.width,obj.height,obj.img.width,obj.img.height);
      if (ax == "left") bb.x = 0;
      if (ax == "right") bb.x = obj.width-bb.w;
      if (ay == "top") bb.y = 0;
      if (ay == "bottom") bb.y = obj.height-bb.h;
      b.image(obj.img,obj.x+bb.x,obj.y+bb.y,bb.w,bb.h);
    }
    else if (f == "outside") {
      var bb = centerRect(obj.img.width,obj.img.height,obj.width,obj.height);
      if (ax == "left") bb.x = 0;
      if (ax == "right") bb.x = obj.img.width-bb.w;
      if (ay == "top") bb.y = 0;
      if (ay == "bottom") bb.y = obj.img.height-bb.h;
      b.image(obj.img,bb.x,bb.y,bb.w,bb.h,obj.x,obj.y,obj.width,obj.height);
    }
    else b.image(obj.img,obj.x,obj.y,obj.width,obj.height);
  };
};
Components.input = function(obj,txt) {
  Components.text(obj,txt);
  Components.button(obj);
  obj.activeStyle = createStyle(obj.clickStyle);
  obj.cursorPos = obj.value.length;
  obj.cursor = function(s,b) {
    var c = obj._cache;
    if (frameCount % 30 < 15 && activeElement == obj) return;
    b.noStroke();
    b.fill(s.get("cursor"));
    b.text("|",obj.x+c.cursorX,obj.y+c.cursorY+0.5);
  };
  obj.calculateCursorPos = function() {
    var s = obj.style, c = obj._cache;
    c.cursorY = (obj.beflines.length-1)*(s.get("textSize")+2.5)+c.ty;
    c.cursorX = textWidth(obj.beflines[obj.beflines.length-1])+c.tx;
  };
  obj.onclick = function() {
    var s = obj.style, c = obj._cache;
    activeElement = obj;
    var lines = obj.lines;
    var dx = mouseX - obj.x - c.tx;
    var dy = mouseY - obj.y - c.ty;
    var li = constrain(ceil(dy/(s.get("textSize")+2.5)),0,lines.length)-1;
    var e = textWidth("_");
    var last = lines[li]||"";
    var cpos = min(round(dx/e),last.length);
    while (textWidth(last.substring(0,cpos))>dx) cpos--;
    while (textWidth(last.substring(0,cpos))<dx-e && cpos < last.length) cpos++;
    for (var i = 0; i < li; i++) {
      cpos += lines[i].length;
    }
    cpos += (obj.value.substring(0,cpos).match(/\n/g)||[]).length;
    obj.cursorPos = cpos;
    obj.update();
  };
  obj.update = function() {
    obj.lines = getLines(obj.value,obj._cache.max);
    var bef = [];
    var c = obj.cursorPos;
    for (var i = 0; i < obj.lines.length; i++) {
      bef.push(obj.lines[i]);
      c-=obj.lines[i].length;
      if (c < 0) break;
    }
    bef[bef.length-1] = bef[bef.length-1].substring(0,bef[bef.length-1].length+c);
    obj.beflines = bef;
    obj.calculateCursorPos();
    obj.onchange();
  }
  obj.onchange = function() {};
  setTimeout(function(){
    obj.text(obj.style,window);
    obj.update();
  },0);
};
Components.canvas = function(obj) {
  obj.buff = createGraphics(obj.width,obj.height);
  if (halfGraphics) obj.buff.scale(0.5,0.5);
  obj._cache.width = obj.width;
  obj._cache.height = obj.height;
  obj.children = [];
  obj.canvas = function(s,b) {
    obj.buff.clear();
    if (obj._cache.width != obj.width || obj._cache.width != obj.width) {
      obj.buff = createGraphics(obj.width,obj.height);
      if (halfGraphics) obj.buff.scale(0.5,0.5);
    }
    obj.draw(obj.buff,s);
    b.image(obj.buff,obj.x,obj.y,obj.width,obj.height);
  };
};
Components.div = function(obj) {
  Components.canvas(obj);
  obj.children = [];
  obj.appendChild = function(elem) {
    elem.parent = obj;
    obj.children.push(elem);
  };
  obj.prependChild = function(elem) {
    elem.parent = obj;
    obj.children.shift(elem);
  };
  obj.removeChild = function(elem) {
    var i = obj.children.indexOf(elem);
    delete elem.parent;
    obj.children.splice(i,1);
  };
  obj.removeChildAtIndex = function(i) {
    delete obj.children[i].parent;
    obj.children.splice(i,1);
  };
  obj.draw = function(b,s) {
    for (var i = 0; i < obj.children.length; i++) {
      obj.children[i].show(b);
    }
  };
  obj.scrollX = 0;
  obj.scrollY = 0;
  obj.minScrollX = 0;
  obj.minScrollY = 0;
  obj.maxScrollX = 0;
  obj.maxScrollY = 0;
  obj.onscroll = function(){};
  obj.scroll = function(x,y) {
    var px = obj.scrollX, py = obj.scrollY;
    obj.scrollX = constrain(px+x,obj.minScrollX,obj.maxScrollX);
    obj.scrollY = constrain(py+y,obj.minScrollY,obj.maxScrollY);
    px -= obj.scrollX;
    py -= obj.scrollY;
    if (px != 0 || py != 0) obj.onscroll(px,py);
  };
  obj.div = true;
};
Components.draggable = function(obj) {
  Components.button(obj);
  obj.drag = function(s,b) {
    if (activeElement == obj) {
      obj.x += mouseX-pmouseX;
      obj.y += mouseY-pmouseY;
    }
  }
  obj.ongrab = function() {};
  obj.onletgo = function(){};
  obj.onclick = function() {
    activeElement = obj;
    obj.ongrab();
  };
  obj.onrelease = function(){
    activeElement = false;
    obj.onletgo();
  };
};

function drawElements() {
  cursor(ARROW);
  clickerElement(function(obj) {
    if (obj.onclick) {
      if (obj.cursor) cursor(TEXT);
      else cursor(HAND);
    }
    if (!obj.div) return;
    var sx = 0, sy = 0;
    if (mouseDown("left") && !activeElement.drag) {
      sx += pmouseX-mouseX;
      sy += pmouseY-mouseY;
    }
    if (keyDown("left")) sx -= 5;
    if (keyDown("right")) sx += 5;
    if (keyDown("up")) sy -= 5;
    if (keyDown("down")) sy += 5;
    obj.scroll(sx,sy);
  });
  for (var i = 0; i < elementList.length; i++) {
    if (elementList[i].parent) continue;
    elementList[i].show();
  }
}
function clickerElement(callback) {
  var done = false;
  var hov = function(obj) {
    if (!obj.visible || (!obj.interactive && !obj.div)) return;
    var h = !done && obj.testhover();
    if (obj.div) {
      if (!h) return;
      mouseX -= obj.x - obj.scrollX;
      mouseY -= obj.y - obj.scrollY;
      for (var j = obj.children.length-1; j >= 0; j--) {
        hov(obj.children[j]);
      }
      mouseX += obj.x - obj.scrollX;
      mouseY += obj.y - obj.scrollY;
    } else {
      obj.hover = h;
      if (h) done = true;
    }
    if (h) callback(obj);
  };
  for (var i = elementList.length-1; i >= 0; i--) {
    var obj = elementList[i];
    if (obj.parent) continue;
    hov(obj);
  }
}
function mousePressedElements() {
  activeElement = false;
  clickerElement(function(obj) {
    if (!obj.onclick) return;
    obj.onclick();
  });
}
function mouseReleasedElements() {
  clickerElement(function(obj) {
    if (!obj.onrelease) return;
    obj.onrelease();
  });
}
function keyTypedElements() {
  if (!activeElement) return;
  if (key == "\r") return;
  var oldkey = key;
  onHold(keyCode,key,function(){
    activeElement.value = activeElement.value.substring(0, activeElement.cursorPos) + oldkey + activeElement.value.substring(activeElement.cursorPos, activeElement.value.length);
    activeElement.cursorPos++;
    activeElement.update();
  });
}
function keyPressedElements() {
  if (!activeElement || !activeElement.cursor) return;
  onHold(BACKSPACE,"backspace",function() {
    activeElement.value = activeElement.value.substring(0, activeElement.cursorPos-1) + activeElement.value.substring(activeElement.cursorPos, activeElement.value.length);
    activeElement.cursorPos--;
    activeElement.cursorPos = max(0,activeElement.cursorPos);
    activeElement.update();
  });
  onHold(LEFT_ARROW,"left",function() {
    activeElement.cursorPos = max(activeElement.cursorPos-1,0);
    activeElement.update();
  });
  onHold(RIGHT_ARROW,"right",function() {
    activeElement.cursorPos = min(activeElement.cursorPos+1,activeElement.value.length);
    activeElement.update();
  });
  if (keyCode == ENTER) {
    activeElement.value = activeElement.value.substring(0, activeElement.cursorPos) + "\n" + activeElement.value.substring(activeElement.cursorPos, activeElement.value.length);
    activeElement.cursorPos++;
    activeElement.update();
    return;
  }
  var lines, cursorLine, cursorOffset;
  var updateLines = function() {
    activeElement.update();
    lines = activeElement.lines;
    cursorLine = activeElement.beflines.length-1;
    cursorOffset = activeElement.cursorPos;
    for (var i = 0; i < cursorLine; i++) {
      cursorOffset -= lines[i].length+1;
    }
  }
  onHold(UP_ARROW,"up",function() {
    updateLines();
    if (cursorLine <= 0) return;
    var newPos = 0;
    for (var i = 0; i < cursorLine-1; i++) {
      newPos += lines[i].length+1;
    }
    activeElement.cursorPos = newPos + min(cursorOffset,lines[cursorLine-1].length);
  });
  onHold(DOWN_ARROW,"down",function() {
    updateLines();
    if (cursorLine >= lines.length-1) return;
    var newPos = 0;
    for (var i = 0; i < cursorLine+1; i++) {
      newPos += lines[i].length+1;
    }
    activeElement.cursorPos = newPos + min(cursorOffset,lines[cursorLine+1].length);
  });
  if (keyCode == 86 && keyDown(CONTROL)) {
    var pasted = prompt("Paste here:");
    if (!pasted) return;
    activeElement.value = activeElement.value.substring(0,activeElement.cursorPos) + pasted + activeElement.value.substring(activeElement.cursorPos,activeElement.value.length);
    activeElement.cursorPos += pasted.length;
    activeElement.update();
  }
}
function mousePressed() {
  mousePressedElements();
}
function mouseReleased() {
  mouseReleasedElements();
}
function keyTyped() {
  keyTypedElements();
}
function keyPressed() {
  keyPressedElements();
}

function mouseRect(x,y,width,height) {
  return (
    mouseX > x &&
    mouseX < x+width &&
    mouseY > y && 
    mouseY < y+height
  );
}
function centerRect(w1,h1,w2,h2) {
  var fatness1 = w1 / h1;
  var fatness2 = w2 / h2;

  var scale;
  if (fatness2 >= fatness1) scale = w1 / w2;
  else scale = h1 / h2;
  var w = w2 * scale;
  var h = h2 * scale;

  var xCenter = 0 + (w1 / 2);
  var yCenter = 0 + (h1 / 2);

  var x = xCenter - (w / 2);
  var y = yCenter - (h / 2);

  return { x:x, y:y, w:w, h:h };
}
function getLines(string,width) {
  if (typeof string != 'string') return [];
  var lines = string.split("\n");
  for (var i = 0; i < lines.length; i++) {
    if (textWidth(lines[i]) <= width) continue;
    var off = "";
    while (textWidth(lines[i]) > width || (!lines[i].match(/[\s-_]$/) && lines[i].match(/[\s-_]/))) {
      off = lines[i].charAt(lines[i].length-1)+off;
      lines[i] = lines[i].substr(0,lines[i].length-1);
    }
    lines.splice(i+1,0,off);
  }
  return lines;
}
function onHold(kc,k,funct) {
  if (keyCode != kc && key != k) return;
  funct();
  setTimeout(function() {
    if (!keyDown(k)) return;
    funct();
    var int = setInterval(function() {
      if (!keyDown(k)) {
        clearInterval(int);
        return;
      }
      funct();
    },100);
  },600);
}

// ------------
// Testing Area
// ------------

var test = createElement(10,10,50,50);
test.addComponent("text","this is a test of the text");
test.addComponent("button");
test.style.fill = "red";
test.hoverStyle.fill = "pink";
test.clickStyle.fill = "white";
test.style.bevel = 5;
test.onclick = function() {
  console.log("clicked #1");
};

var test = createElement(20,20,60,50);
test.addComponent("text","wow! it's a test of the grab");
test.addComponent("draggable");
test.style.fill = "blue";
test.hoverStyle.fill = "cyan";
test.clickStyle.fill = "white";
test.style.bevel = 5;
test.style.stroke = "blue";
test.ongrab = function() {
  console.log("grabbed #2");
};

var test = createElement(50,100,300,200);
test.addComponent("image","https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg");
test.addComponent("text","wow!");
test.style.imageFit = "inside";
test.style.textFill = "white";
test.style.horizAlign = "center";
test.style.vertAlign = "center";

var test = createElement(20,310,360,80);
test.addComponent("input","Wow!\nWhat a so very nice day to spend lots of time with friends and family.\n Tons of cheer and spirit to go round. Oh yes, it is that time of year again. Christmas bells ring in the distance.");
test.onchange = function() {console.log(key)};
test.activeStyle.stroke = "white";
test.hoverStyle.stroke = "grey";

var test = createElement(70,10,100,40);
test.addComponent("input","Other Text Box\nWhee!");
test.style.fill = "green";
test.hoverStyle.fill = "lime";
test.style.bevel = 5;
test.style.stroke = "white";
test.onchange = function() {
  this.height = this.textHeight;
};

var div = createElement(250,10,120,60);
div.addComponent("div");
div.style.bevel = 5;

var test = createElement(0,15,120,0);
test.addComponent("input","I have vivid dreams in the night every night. They are strange and wonderful and mysterious and wild. My deepest desires and worst fears are revealed to me.");
test.style.fill = false;
test.style.bevel = 0;
test.style.stroke = false;
div.appendChild(test);
test.onchange = function() {
  this.height = this.textHeight+15;
  div.maxScrollY = max(this.height-div.height,0);
};

var test = createElement(5,5,20,10);
test.addComponent("button");
test.style.fill = "green";
test.hoverStyle.fill = "lime";
test.style.bevel = 5;
test.style.stroke = "white";
div.appendChild(test);
test.onclick = function() {
  console.log("clicked #3");
};

function draw() {
  background(220);
  drawElements();
}
6 days later

FGUI 1.4 - Pages

// FGUI Fire Graphics User Interface
// By: DragonFireGames
// Version: 1.4
// Description: A port/mix of HTML, CSS, 
//   and p5.js to make a component based
//   graphics interface. Styles are used
//   and can be inherited and set.

var isMobile = _isSafari();
showMobileControls(true,true,true,true);

var testgraphics = createGraphics(400,400);
background(255);
testgraphics.fill(0);
testgraphics.noStroke();
testgraphics.rect(0,0,200,200);
image(testgraphics,0,0,200,200);
var doubleGraphics = get(150,150)[0] == 0;
delete testgraphics;
window._createGraphics = window.createGraphics;
window.createGraphics = function(w,h,gl) {
  if (doubleGraphics) w *= 2, h*= 2;
  var buf = window._createGraphics(w,h,gl);
  buf.format = function(f,s,sw,ts,tax,tay) {
    return format(f,s,sw,ts,tax,tay,buf);
  };
  buf.typewrite = function(str,t,x,y,mw,mh){
    return typewrite(str,t,x,y,mw,mh,buf);
  };
  buf.beveledImage = function(img,sx,sy,sw,sh,x,y,w,h,bev) {
    return beveledImage(img,sx,sy,sw,sh,x,y,w,h,bev,buf);
  }
  return buf;
};

var elementList = [];
var pageMap1 = {};
var pageMap2 = {};
var pageMap3 = {};
var display = "default";
var activeElement = false;

var defaultStyle = {
  padx:5,
  pady:5,
  fill:128,
  stroke:0,
  strokeWeight:1,
  textFill:0,
  textStroke:false,
  textStrokeWeight:1,
  textSize:12,
  placeholderTextFill:100,
  placeholderStroke:false,
  horizAlign:"left",
  vertAlign:"top",
  textmax:true,
  font:"heveltica",
  bevel:0,
  imageBevel:0,
  imageFit:"none",
  imageHorizAlign:"center",
  imageVertAlign:"center",
  cursor:0,
  get:function(e) {
    return defaultStyle[e];
  }
};
// Create a style
function createStyle(parent) {
  var obj = {};
  obj.parent = parent || defaultStyle;
  obj.get = function(e) {
    if (obj[e] === undefined) return obj.parent.get(e);
    return obj[e];
  };
  return obj;
}
// Create an element
function createElement(x,y,w,h) {
  var obj = {};
  obj.x = x || 0;
  obj.y = y || 0;
  obj.width = w === undefined ? 50 : w;
  obj.height = h === undefined ? 50 : h;
  obj.style = createStyle();
  obj.visible = true;
  obj._cache = {};
  obj.page = "default";
  obj.testhover = function() {
    return mouseRect(obj.x,obj.y,obj.width,obj.height);
  };
  obj.getmax = function() {
    return {x:obj.x+obj.w,y:obj.y+obj.h};
  };
  obj.body = function(s,b) {
    b.format(s.get("fill"),s.get("stroke"),s.get("strokeWeight"));
    b.rect(obj.x,obj.y,obj.width,obj.height,s.get("bevel"));
  };
  obj.show = function(b) {
    if (obj.page != display) return;
    if (!obj.visible) return;
    if (obj.drag) obj.drag(s,b);
    var s = obj.style;
    b = b || window;
    if (obj.hover) {
      if (mouseDown("left") && obj.onclick) s = obj.clickStyle;
      else s = obj.hoverStyle;
    }
    if (activeElement == obj) {
      if (obj.cursor) s = obj.activeStyle;
      else if (obj.drag) s = obj.clickStyle;
    }
    obj.body(s,b);
    if (obj.image) obj.image(s,b);
    if (obj.text) obj.text(s,b);
    if (obj.cursor) obj.cursor(s,b);
    if (obj.canvas) obj.canvas(s,b);
    if (obj.onshow) obj.onshow(s,b);
  };
  obj.addComponent = function(e,a,b,c) {
    Components[e](obj,a,b,c);
  };
  elementList.push(obj);
  obj.zindex = 0;
  elementList = elementList.sort(function(a,b){return a.zindex - b.zindex;});
  return obj;
}
// Create a page
function createPage(name,bef,aft,onopen) {
  pageMap1[name] = bef || function(){};
  pageMap2[name] = aft || function(){};
  pageMap3[name] = onopen || function(){};
}
function setPage(name) {
  display = name;
  pageMap3[name]();
}
var Components = {};
Components.text = function(obj,txt) {
  obj.value = txt||"";
  obj.placeholder = "";
  obj.activeStyle = createStyle(obj.clickStyle||obj.style);
  obj.text = function(s,b) {
    var c = obj._cache;
    var ax = s.get("horizAlign");
    var ay = s.get("vertAlign");
    b.format(s.get("textFill"),s.get("textStroke"),s.get("textStrokeWeight"),s.get("textSize"),ax,ay);
    b.textFont(s.get("font"));
    var str = obj.value;
    if (!obj.value && obj.placeholder) {
      b.format(s.get("placeholderTextFill"),s.get("placeholderTextStroke"),s.get("textStrokeWeight"),s.get("textSize"),ax,ay);
      str = obj.placeholder;
    }
    var max, tx = s.get("padx"), ty = s.get("pady");
    var tm = s.get("textmax");
    if (obj.cursor) ax = "left", ay = "top";
    if (tm) {
      max = obj.width - tx;
      if (ax == "center") max = obj.width - tx * 2;
    } else {
      if (ax == "right") tx = obj.width - tx;
      else if (ax == "center") tx = obj.width/2;
    }
    if (ay == "bottom") ty = obj.height - ty;
    else if (ay == "center") ty = obj.height/2;
    if (tm == 'number') max = tm;
    c.max = max; c.tx = tx; c.ty = ty;
    b.text(str,obj.x+tx,obj.y+ty,max);
    if (ax == "left" && c.value !== obj.value) {
      c.lines = getLines(obj.value,max);
      obj.textHeight = c.lines.length*(s.get("textSize")*1.25)+ty;
      obj.textWidth = textWidth(c.lines[c.lines.length-1])+tx;
      c.value = obj.value;
    }
  };
  obj.getmax = function() {
    var c = obj._cache;
    var m1x = obj.x+obj.w, m1y = obj.y+obj.h;
    return {x:max(m1x,obj.textWidth||0),y:max(m1y,obj.textHeight||0)}
  };
};
Components.button = function(obj) {
  obj.hoverStyle = createStyle(obj.style);
  obj.clickStyle = createStyle(obj.hoverStyle);
  obj.hover = false;
  obj.onclick = function(){};
  obj.onunclick = function(){};
  obj.interactive = true;
};
Components.image = function(obj,url,callback,failure) {
  if (typeof url == 'string') obj.img = loadImage(url,function(img) {
    if (obj.width === 0) obj.width = img.width;
    if (obj.height === 0) obj.height = img.height;
  },failure);
  else obj.img = url;
  obj.image = function(s,b) {
    var f = s.get("imageFit");
    var ax = s.get("imageHorizAlign");
    var ay = s.get("imageVertAlign");
    var bev = s.get("imageBevel");
    if (f == "inside") {
      var bb = centerRect(obj.width,obj.height,obj.img.width,obj.img.height);
      if (ax == "left") bb.x = 0;
      if (ax == "right") bb.x = obj.width-bb.w;
      if (ay == "top") bb.y = 0;
      if (ay == "bottom") bb.y = obj.height-bb.h;
      beveledImage(obj.img,obj.x+bb.x,obj.y+bb.y,bb.w,bb.h,bev);
    }
    else if (f == "outside") {
      var bb = centerRect(obj.img.width,obj.img.height,obj.width,obj.height);
      if (ax == "left") bb.x = 0;
      if (ax == "right") bb.x = obj.img.width-bb.w;
      if (ay == "top") bb.y = 0;
      if (ay == "bottom") bb.y = obj.img.height-bb.h;
      beveledImage(obj.img,bb.x,bb.y,bb.w,bb.h,obj.x,obj.y,obj.width,obj.height,bev);
    }
    else beveledImage(obj.img,obj.x,obj.y,obj.width,obj.height,bev);
  };
};
Components.input = function(obj,txt) {
  Components.text(obj,txt);
  Components.button(obj);
  obj.activeStyle = createStyle(obj.clickStyle);
  obj.cursorPos = obj.value.length;
  obj.cursor = function(s,b) {
    if (isMobile) return;
    var c = obj._cache;
    if (frameCount % 30 < 15 && activeElement == obj) return;
    b.noStroke();
    b.fill(s.get("cursor"));
    b.text("|",obj.x+c.cursorX,obj.y+c.cursorY+0.5);
  };
  obj.calculateCursorPos = function() {
    var s = obj.style, c = obj._cache;
    c.cursorY = (obj.beflines.length-1)*(s.get("textSize")*1.25)+c.ty;
    c.cursorX = textWidth(obj.beflines[obj.beflines.length-1])+c.tx;
  };
  obj.onclick = function() {
    if (isMobile) {
      var str = JSON.stringify(obj.value);
      str = str.substring(1,str.length-1);
      str = prompt(obj.placeholder||"Enter text:",str);
      if (str === null) return;
      str = str.replace(/"/g,"\\\"");
      obj.value = JSON.parse("\""+str+"\"");
      obj.update();
      obj.oninput();
      obj.onchange();
      obj.onunfocus();
      return;
    }
    var s = obj.style, c = obj._cache;
    activeElement = obj;
    var lines = obj.lines;
    var dx = mouseX - obj.x - c.tx;
    var dy = mouseY - obj.y - c.ty;
    var li = constrain(ceil(dy/(s.get("textSize")*1.25)),0,lines.length)-1;
    var e = textWidth("_");
    var last = lines[li]||"";
    var cpos = min(round(dx/e),last.length);
    while (textWidth(last.substring(0,cpos))>dx) cpos--;
    while (textWidth(last.substring(0,cpos))<dx-e && cpos < last.length) cpos++;
    for (var i = 0; i < li; i++) {
      cpos += lines[i].length;
    }
    cpos += (obj.value.substring(0,cpos).match(/\n/g)||[]).length;
    obj.cursorPos = cpos;
    obj.update();
    obj._cache.val = obj.value;
    obj.onfocus();
  };
  obj.update = function() {
    obj.text(obj.style,window);
    obj.lines = getLines(obj.value,obj._cache.max);
    var bef = [];
    var c = obj.cursorPos;
    for (var i = 0; i < obj.lines.length; i++) {
      bef.push(obj.lines[i]);
      c-=obj.lines[i].length;
      if (c < 0) break;
    }
    bef[bef.length-1] = bef[bef.length-1].substring(0,bef[bef.length-1].length+c);
    obj.beflines = bef;
    obj.calculateCursorPos();
    obj.oninput();
  };
  obj.oninput = function() {};
  obj.onfocus = function() {};
  obj.onunfocus = function() {};
  obj.onchange = function() {};
  setTimeout(function(){
    obj.update();
  },0);
};
Components.canvas = function(obj,gl) {
  obj.buff = createGraphics(obj.width,obj.height,gl);
  obj._cache.width = obj.width;
  obj._cache.height = obj.height;
  obj.children = [];
  obj.canvas = function(s,b) {
    obj.buff.clear();
    if (obj._cache.width != obj.width || obj._cache.width != obj.width) {
      obj.buff = createGraphics(obj.width,obj.height);
    }
    obj.draw(obj.buff,s);
    b.image(obj.buff,obj.x,obj.y,obj.width,obj.height);
  };
};
Components.div = function(obj) {
  Components.canvas(obj);
  obj.children = [];
  obj.appendChild = function(elem) {
    elem.parent = obj;
    obj.children.push(elem);
    obj.children = obj.children.sort(function(a,b){return a.zindex - b.zindex;});
  };
  obj.prependChild = function(elem) {
    elem.parent = obj;
    obj.children.shift(elem);
    obj.children = obj.children.sort(function(a,b){return a.zindex - b.zindex;});
  };
  obj.removeChild = function(elem) {
    var i = obj.children.indexOf(elem);
    delete elem.parent;
    obj.children.splice(i,1);
  };
  obj.removeChildAtIndex = function(i) {
    delete obj.children[i].parent;
    obj.children.splice(i,1);
  };
  obj.draw = function(b,s) {
    b.translate(-obj.scrollX,-obj.scrollY);
    for (var i = 0; i < obj.children.length; i++) {
      obj.children[i].show(b);
    }
    b.translate(obj.scrollX,obj.scrollY);
  };
  obj.scrollX = 0;
  obj.scrollY = 0;
  obj.minScrollX = 0;
  obj.minScrollY = 0;
  obj.maxScrollX = 0;
  obj.maxScrollY = 0;
  obj.onscroll = function(){};
  obj.scroll = function(x,y) {
    if (x == 0 && y == 0) return;
    var px = obj.scrollX, py = obj.scrollY;
    obj.scrollX = constrain(px+x,obj.minScrollX,obj.maxScrollX);
    obj.scrollY = constrain(py+y,obj.minScrollY,obj.maxScrollY);
    px -= obj.scrollX;
    py -= obj.scrollY;
    if (px != 0 || py != 0) obj.onscroll(px,py);
  };
  obj.div = true;
};
Components.draggable = function(obj) {
  Components.button(obj);
  obj.drag = function(s,b) {
    if (activeElement == obj) {
      obj.x += mouseX-pmouseX;
      obj.y += mouseY-pmouseY;
    }
  }
  obj.ongrab = function() {};
  obj.onrelease = function(){};
  obj.onclick = function() {
    activeElement = obj;
    obj.ongrab();
  };
  obj.onunclick = function(){
    activeElement = false;
    obj.onrelease();
  };
};

// Draw elements
function drawElements() {
  cursor(ARROW);
  pageMap1[display]();
  clickerElement(function(obj) {
    if (obj.onclick) {
      if (obj.cursor) cursor(TEXT);
      else cursor(HAND);
    }
    if (!obj.div) return;
    var sx = 0, sy = 0;
    if (mouseDown("left") && !activeElement.drag) {
      sx += pmouseX-mouseX;
      sy += pmouseY-mouseY;
    }
    if (keyDown("left")) sx -= 5;
    if (keyDown("right")) sx += 5;
    if (keyDown("up")) sy -= 5;
    if (keyDown("down")) sy += 5;
    obj.scroll(sx,sy);
  });
  for (var i = 0; i < elementList.length; i++) {
    if (elementList[i].parent) continue;
    elementList[i].show();
  }
  pageMap2[display]();
}
// Run When Mouse Pressed
function mousePressedElements() {
  var celem = activeElement;
  activeElement = false;
  clickerElement(function(obj) {
    if (!obj.onclick) return;
    obj.onclick();
  });
  if (celem != activeElement && celem.cursor) {
    if (celem._cache.val !== celem.value) celem.onchange(celem.value);
    celem.onunfocus();
  }
}
// Run When Mouse Released
function mouseReleasedElements() {
  clickerElement(function(obj) {
    if (!obj.onunclick) return;
    obj.onunclick();
  });
}
// Run Key Typed
function keyTypedElements() {
  if (!activeElement) return;
  if (key == "\r") return;
  var oldkey = key;
  onHold(keyCode,key,function(){
    activeElement.value = activeElement.value.substring(0, activeElement.cursorPos) + oldkey + activeElement.value.substring(activeElement.cursorPos, activeElement.value.length);
    activeElement.cursorPos++;
    activeElement.update();
    activeElement.oninput(key);
  });
}
// Run Key Pressed
function keyPressedElements() {
  if (!activeElement || !activeElement.cursor) return;
  onHold(BACKSPACE,"backspace",function() {
    activeElement.value = activeElement.value.substring(0, activeElement.cursorPos-1) + activeElement.value.substring(activeElement.cursorPos, activeElement.value.length);
    activeElement.cursorPos--;
    activeElement.cursorPos = max(0,activeElement.cursorPos);
    activeElement.update();
    activeElement.oninput("backspace");
  });
  onHold(LEFT_ARROW,"left",function() {
    activeElement.cursorPos = max(activeElement.cursorPos-1,0);
    activeElement.update();
    activeElement.oninput("left");
  });
  onHold(RIGHT_ARROW,"right",function() {
    activeElement.cursorPos = min(activeElement.cursorPos+1,activeElement.value.length);
    activeElement.update();
    activeElement.oninput("right");
  });
  if (keyCode == ENTER) {
    activeElement.value = activeElement.value.substring(0, activeElement.cursorPos) + "\n" + activeElement.value.substring(activeElement.cursorPos, activeElement.value.length);
    activeElement.cursorPos++;
    activeElement.update();
    activeElement.oninput("\n");
    return;
  }
  var lines, cursorLine, cursorOffset;
  var updateLines = function() {
    activeElement.update();
    lines = activeElement.lines;
    cursorLine = activeElement.beflines.length-1;
    cursorOffset = activeElement.cursorPos;
    for (var i = 0; i < cursorLine; i++) {
      cursorOffset -= lines[i].length+1;
    }
  }
  onHold(UP_ARROW,"up",function() {
    updateLines();
    if (cursorLine <= 0) return;
    var newPos = 0;
    for (var i = 0; i < cursorLine-1; i++) {
      newPos += lines[i].length+1;
    }
    activeElement.cursorPos = newPos + min(cursorOffset,lines[cursorLine-1].length);
  });
  onHold(DOWN_ARROW,"down",function() {
    updateLines();
    if (cursorLine >= lines.length-1) return;
    var newPos = 0;
    for (var i = 0; i < cursorLine+1; i++) {
      newPos += lines[i].length+1;
    }
    activeElement.cursorPos = newPos + min(cursorOffset,lines[cursorLine+1].length);
  });
  if (keyCode == 86 && keyDown(CONTROL)) {
    var pasted = prompt("Paste here:");
    if (!pasted) return;
    activeElement.value = activeElement.value.substring(0,activeElement.cursorPos) + pasted + activeElement.value.substring(activeElement.cursorPos,activeElement.value.length);
    activeElement.cursorPos += pasted.length;
    activeElement.update();
    activeElement.oninput(pasted);
  }
}
// Run Mouse Wheel
function mouseWheelElements(e) {
  /*
  if (activeElement && activeElement.div && activeElement.hover) {
    var ty = activeElement.y + activeElement.height/2;
    var s = 0;
    if (mouseY > ty) {
      activeElement.scroll(0,-5);
    }
  }
  */
}

// Check if cursor in rect
function mouseRect(x,y,width,height) {
  return (
    mouseX > x &&
    mouseX < x+width &&
    mouseY > y && 
    mouseY < y+height
  );
}
// Center rectange in another
function centerRect(w1,h1,w2,h2) {
  var fatness1 = w1 / h1;
  var fatness2 = w2 / h2;

  var scale;
  if (fatness2 >= fatness1) scale = w1 / w2;
  else scale = h1 / h2;
  var w = w2 * scale;
  var h = h2 * scale;

  var xCenter = 0 + (w1 / 2);
  var yCenter = 0 + (h1 / 2);

  var x = xCenter - (w / 2);
  var y = yCenter - (h / 2);

  return { x:x, y:y, w:w, h:h };
}
// Get lines of text
function getLines(string,width) {
  if (typeof string !== 'string') return [];
  var lines = string.split("\n");
  for (var i = 0; i < lines.length; i++) {
    if (textWidth(lines[i]) <= width) continue;
    if (!lines[i].match(/[\s-_]/)) continue;
    var off = "";
    while (textWidth(lines[i]) > width || (!lines[i].match(/[\s-_]$/) && lines[i].match(/[\s-_]/))) {
      off = lines[i].charAt(lines[i].length-1)+off;
      lines[i] = lines[i].substr(0,lines[i].length-1);
    }
    lines.splice(i+1,0,off);
  }
  return lines;
}
function clickerElement(callback) {
  var done = false;
  var hov = function(obj) {
    if (!obj.visible || (!obj.interactive && !obj.div)) return;
    var h = !done && obj.testhover();
    if (obj.div) {
      if (!h) return;
      mouseX -= obj.x - obj.scrollX;
      mouseY -= obj.y - obj.scrollY;
      for (var j = obj.children.length-1; j >= 0; j--) {
        hov(obj.children[j]);
      }
      mouseX += obj.x - obj.scrollX;
      mouseY += obj.y - obj.scrollY;
    } else {
      obj.hover = h;
      if (h) done = true;
    }
    if (h) callback(obj);
  };
  for (var i = elementList.length-1; i >= 0; i--) {
    var obj = elementList[i];
    if (obj.page != display) continue;
    if (obj.parent) continue;
    hov(obj);
  }
}
function onHold(kc,k,funct) {
  if (keyCode != kc && key != k) return;
  funct();
  setTimeout(function() {
    if (!keyDown(k)) return;
    funct();
    var int = setInterval(function() {
      if (!keyDown(k)) {
        clearInterval(int);
        return;
      }
      funct();
    },100);
  },600);
}
function beveledImage(img,sx,sy,sw,sh,x,y,w,h,bev,b) {
  b = b || window;
  if (!y) {
    bev = x;
    x = sx;
    y = sy;
    w = sw;
    h = sh;
    sx = 0;
    sy = 0;
    sw = img.width;
    sh = img.height;
  }
  if (img.width != 0 && bev) {
    var n = "bev"+[sx,sy,sw,sh,w,h,bev].join(",");
    if (!img[n]) {
      var buf = window._createGraphics(w,h);
      buf.image(img,sx,sy,sw,sh,0,0,w,h);
      var get1 = buf.get();
      buf.clear();
      buf.noStroke();
      buf.rect(0,0,w,h,bev);
      var get2 = buf.get();
      get1.mask(get2);
      img[n] = get1;
    }
    b.image(img[n],x,y,w,h);
  } else b.image(img,sx,sy,sw,sh,x,y,w,h);
}

createPage("default");

FANIM 1.1 - Sequences



// FANIM Fire Animations
// By: DragonFireGames
// Version: 1.1
// Description: Animation functions

var Sequences = [];
// Creates an animation sequence
function createSequence() {
  var seq = {};
  // Animation Handlers
  seq.animations = [];
  // Creates Animations
  seq.createAnimation = function(start,end,drawer,interp) {
    var len = end-start;
    interp = interp || function(t){return t};
    seq.animations.push(function(time){
      if (time > end || time < start) return;
      drawer(interp((time-start)/len));
    });
  };
  // Draws Animations
  seq.draw = function() {
    if (!seq.startTime) return;
    for (var i = 0; i < seq.animations.length; i++) {
      seq.animations[i](Date.now()-seq.startTime);
    }
  };
  seq.start = function() {
    seq.startTime = Date.now();
  };
  seq.end = function() {
    delete seq.startTime;
  };
  Sequences.push(seq);
  return seq;
}
// Draw animation sequences
function drawSequences() {
  for (var i = 0; i < Sequences.length; i++) {
    Sequences[i].draw();
  }
}

//easings from: https://easings.net
var Ease = {
  in: {
    line: function(t) {
      return t;
    },
    sine: function(t) {
      return 1-Math.cos((t*Math.PI)/2);
    },
    quad: function(t) {
      return t*t;
    },
    cubic: function(t) {
      return t*t*t;
    },
    quart: function(t) {
      return t*t*t*t;
    },
    quint: function(t) {
      return t*t*t*t*t;
    },
    expo: function(t) {
      if (t === 0) return 0;
      return Math.pow(2,10*t-10);
    },
    circ: function(t) {
      return 1-Math.sqrt(1-t*t);
    },
    back: function(t) {
      var c1 = 1.70158;
      var c3 = c1 + 1;  
      return c3*t*t*t-c1*t*t;
    },
    elastic: function(t) {
      if (t === 0 || t === 1) return t;
      var c4 = (2 * Math.PI) / 3;
      return -Math.pow(2,10*t-10)*Math.sin((t*10-10.75)*c4);
    },
    bounce: function(t) {
      return 1-Ease.out.bounce(1-t);
    },
  },
  out: {
    line: function(t) {
      return 1-t;
    },
    sine: function(t) {
      return Math.sin((t*Math.PI)/2);
    },
    quad: function(t) {
      t = 1-t;
      return 1-t*t;
    },
    cubic: function(t) {
      t = 1-t;
      return 1-t*t*t;
    },
    quart: function(t) {
      t = 1-t;
      return 1-t*t*t*t;
    },
    quint: function(t) {
      t = 1-t;
      return 1-t*t*t*t*t;
    },
    expo: function(t) {
      if (t === 1) return 1;
      return 1-Math.pow(2,-10*t);
    },
    circ: function(t) {
      t = 1-t;
      return Math.sqrt(1-t*t);
    },
    back: function(t) {
      var c1 = 1.70158;
      var c3 = c1 + 1;
      t = t-1;
      return 1+c3*t*t*t-c1*t*t;
    },
    elastic: function(t) {
      if (t === 0 || t === 1) return t;
      var c4 = (2 * Math.PI) / 3;
      return -Math.pow(2,10*t-10)*Math.sin((t*10-10.75)*c4);
    },
    bounce: function(t) {
      var n1 = 7.5625;
      var d1 = 2.75;
      if (t < 1/d1) {
        return n1*t*t;
      } else if (t < 2/d1) {
        return n1*(t-=1.5/d1)*t+0.75;
      } else if (t < 2.5/d1) {
        return n1*(t-=2.25/d1)*t+0.9375;
      } else {
        return n1*(t-=2.625/d1)*t+0.984375;
      }
    },
  },
  inout: {
    line: function(t) {
      return t;
    },
    sine: function(t) {
      return -(Math.cos(Math.PI*t)-1)/2;
    },
    quad: function(t) {
      if (t < 0.5) return 2*t*t;
      var x = -2*t+2;
      return 1-x*x/2;
    },
    cubic: function(t) {
      if (t < 0.5) return 4*t*t*t;
      var x = -2*t+2;
      return 1-x*x*x/2;
    },
    quart: function(t) {
      if (t < 0.5) return 8*t*t*t*t;
      var x = -2*t+2;
      return 1-x*x*x*x/2;
    },
    quint: function(t) {
      if (t < 0.5) return 16*t*t*t*t*t;
      var x = -2*t+2;
      return 1-x*x*x*x*x/2;
    },
    expo: function(t) {
      if (t === 0 || t === 1) return t;
      if (t < 0.5) return Math.pow(2,20*t-10)/2;
      else return (2-Math.pow(2,-20*t+10))/2;
    },
    circ: function(t) {
      if (t < 0.5) return (1-Math.sqrt(1-4*t*t))/2
      var x = -2*x+2;
      return (Math.sqrt(1-x*x)+1)/2;
    },
    back: function(t) {
      var c1 = 1.70158;
      var c2 = c1 * 1.525;
      if (t < 0.5) return (4*t*t*((c2+1)*2*t-c2))/2;
      var x = -2*x+2;
      return (x*x*((c2+1)*(t*2-2)+c2)+2)/2;
    },
    elastic: function(t) {
      if (t === 0 || t === 1) return t;
      var c5 = (2 * Math.PI) / 4.5;
      if (t < 0.5) return -(Math.pow(2,20*t-10)*Math.sin((20*t-11.125)*c5))/2;
      return (Math.pow(2,-20*t+10)*Math.sin((20*t-11.125)*c5))/2+1;
    },
    bounce: function(t) {
      if (t < 0.5) return 1-Ease.out.bounce(1-2*t)/2;
      return 1+Ease.out.bounce(2*t-1)/2;
    }
  },
  outin: {
    line: function(t) {
      return 1-t;
    },
    sine: function(t) {
      return 1-Ease.inout.sine(1-t);
    },
    quad: function(t) {
      return 1-Ease.inout.quad(1-t);
    },
    cubic: function(t) {
      return 1-Ease.inout.cubic(1-t);
    },
    quart: function(t) {
      return 1-Ease.inout.quart(1-t);
    },
    quint: function(t) {
      return 1-Ease.inout.quint(1-t);
    },
    expo: function(t) {
      return 1-Ease.inout.expo(1-t);
    },
    circ: function(t) {
      return 1-Ease.inout.circ(1-t);
    },
    back: function(t) {
      return 1-Ease.inout.back(1-t);
    },
    elastic: function(t) {
      return 1-Ease.inout.elastic(1-t);
    },
    bounce: function(t) {
      return 1-Ease.inout.bounce(1-t);
    }
  },
}
// https://cubic-bezier.com/
/*function cubicBezier(x1,y1,x2,y2) {
  
}*/

// Misc
function format(f,s,sw,ts,tax,tay,b) {
  b = b || window;
  if (!f && f !== 0) b.noFill();
  else b.fill(f);
  if (!s && s !== 0) b.noStroke(s);
  else b.stroke(s);
  b.strokeWeight(sw||1);
  b.textSize(ts||12);
  b.textAlign(tax||CENTER,tay||CENTER);
}
function typewrite(str,t,x,y,mw,mh,b){
  b = b || window;
  b.text(str.substring(0,Math.round(str.length*t)),x,y,mw,mh);
}
function alpha(c,a){
  if (typeof a != 'number') return c;
  a = constrain(a,0,1);
  var col = color(c);
  col._array[3] = a;
  return col;
}
4 days later

1.5
Merge of FGUI & FANIM, I'm honestly surprised this hasn't taken off yet:

// FGUI Fire Graphics User Interface
// By: DragonFireGames
// Version: 1.5
// Description: A port/mix of HTML, CSS, 
//   and p5.js to make a component based
//   graphics interface. Styles are used
//   and can be inherited and set.

window.isMobile = _isSafari();
showMobileControls(true,true,true,true);

var testgraphics = createGraphics(400,400);
background(255);
testgraphics.fill(0);
testgraphics.noStroke();
testgraphics.rect(0,0,200,200);
image(testgraphics,0,0,200,200);
window.doubleGraphics = get(150,150)[0] == 0;
delete testgraphics;
background(242);
window._createGraphics = window.createGraphics;
window.createGraphics = function(w,h,gl) {
  if (doubleGraphics) w *= 2, h*= 2;
  var buf = window._createGraphics(w,h,gl);
  buf.format = function(f,s,sw,ts,tax,tay) {
    return format(f,s,sw,ts,tax,tay,buf);
  };
  buf.typewrite = function(str,t,x,y,mw,mh){
    return typewrite(str,t,x,y,mw,mh,buf);
  };
  buf.beveledImage = function(img,sx,sy,sw,sh,x,y,w,h,bev) {
    return beveledImage(img,sx,sy,sw,sh,x,y,w,h,bev,buf);
  }
  return buf;
};

window.elementList = [];
window.pageMap = {};
window.display = "default";
window.activeElement = false;
window.activePage = false;

// Create a style
window.defaultStyle = {
  padx:5,
  pady:5,
  fill:128,
  stroke:0,
  strokeWeight:1,
  textFill:0,
  textStroke:false,
  textStrokeWeight:1,
  textSize:12,
  placeholderTextFill:100,
  placeholderStroke:false,
  horizAlign:"left",
  vertAlign:"top",
  textmax:true,
  font:"heveltica",
  bevel:0,
  imageBevel:0,
  imageFit:"none",
  imageHorizAlign:"center",
  imageVertAlign:"center",
  cursor:0,
  get:function(e) {
    return defaultStyle[e];
  }
};
window.createStyle = function(parent) {
  var obj = {};
  obj.parent = parent || defaultStyle;
  obj.get = function(e) {
    if (obj[e] === undefined) return obj.parent.get(e);
    return obj[e];
  };
  obj.set = function(o) {
    for (var i in o) {
      obj[i] = o[i];
    }
  }
  return obj;
}
// Create a page
window.createPage = function(name,bef,aft,onopen) {
  var obj = {};
  obj.name = name;
  obj.before = bef || function(){};
  obj.after = aft || function(){};
  obj.onopen = onopen || function(){};
  obj.elementList = [];
  obj.addElement = function(elem) {
    obj.elementList.push(elem);
    obj.elementList = obj.elementList.sort(function(a,b){return a.zindex - b.zindex;});
  };
  obj.removeElement = function(elem) {
    var i = obj.elementList.indexOf(elem);
    if (i == -1) return;
    obj.elementList.splice(i,1);
  };
  pageMap[name] = obj;
  return obj;
}
window.setPage = function(name) {
  display = name;
  activePage = pageMap[name];
  pageMap[name].onopen();
}
window.defaultPage = createPage("default");
setPage("default");
// Create an element
window.createElement = function(x,y,w,h,page) {
  var obj = {};
  obj.x = x || 0;
  obj.y = y || 0;
  obj.width = w === undefined ? 50 : w;
  obj.height = h === undefined ? 50 : h;
  obj.style = createStyle();
  obj.visible = true;
  obj._cache = {};
  obj.testhover = function() {
    return mouseRect(obj.x,obj.y,obj.width,obj.height);
  };
  obj.getmax = function() {
    return {x:obj.x+obj.w,y:obj.y+obj.h};
  };
  obj.body = function(s,b) {
    b.format(s.get("fill"),s.get("stroke"),s.get("strokeWeight"));
    b.rect(obj.x,obj.y,obj.width,obj.height,s.get("bevel"));
  };
  obj.show = function(b) {
    if (!obj.visible) return;
    if (obj.drag) obj.drag(s,b);
    var s = obj.style;
    b = b || window;
    if (obj.hover) {
      if (mouseDown("left")) s = obj.clickStyle;
      else if (obj.hoverStyle) s = obj.hoverStyle;
    }
    if (activeElement == obj) {
      if (obj.cursor) s = obj.activeStyle;
      else if (obj.drag) s = obj.clickStyle;
    }
    obj.body(s,b);
    if (obj.image) obj.image(s,b);
    if (obj.text) obj.text(s,b);
    if (obj.cursor) obj.cursor(s,b);
    if (obj.canvas) obj.canvas(s,b);
    if (obj.onshow) obj.onshow(s,b);
  };
  obj.addComponent = function(e,a,b,c) {
    Components[e](obj,a,b,c);
  };
  obj.addToPage = function(name) {
    pageMap[name].addElement(obj);
  }
  obj.removeFromPage = function(name) {
    pageMap[name].removeElement(obj);
  }
  obj.zindex = 0;
  elementList.push(obj);
  pageMap[page||"default"].addElement(obj);
  return obj;
}
// Component List
var Components = {};
Components.text = function(obj,txt) {
  obj.value = txt||"";
  obj.placeholder = "";
  obj.activeStyle = createStyle(obj.clickStyle||obj.style);
  obj.text = function(s,b) {
    var c = obj._cache;
    var ax = s.get("horizAlign");
    var ay = s.get("vertAlign");
    b.format(s.get("textFill"),s.get("textStroke"),s.get("textStrokeWeight"),s.get("textSize"),ax,ay);
    b.textFont(s.get("font"));
    var str = obj.value;
    if (!obj.value && obj.placeholder) {
      b.format(s.get("placeholderTextFill"),s.get("placeholderTextStroke"),s.get("textStrokeWeight"),s.get("textSize"),ax,ay);
      str = obj.placeholder;
    }
    var max, tx = s.get("padx"), ty = s.get("pady");
    var tm = s.get("textmax");
    if (obj.cursor) ax = "left", ay = "top";
    if (tm) {
      max = obj.width - tx;
      if (ax == "center") max = obj.width - tx * 2;
    } else {
      if (ax == "right") tx = obj.width - tx;
      else if (ax == "center") tx = obj.width/2;
    }
    if (ay == "bottom") ty = obj.height - ty;
    else if (ay == "center") ty = obj.height/2;
    if (tm == 'number') max = tm;
    c.max = max; c.tx = tx; c.ty = ty;
    b.text(str,obj.x+tx,obj.y+ty,max);
    if (ax == "left" && c.value !== obj.value) {
      c.lines = getLines(obj.value,max);
      obj.textHeight = c.lines.length*(s.get("textSize")*1.25)+ty;
      obj.textWidth = textWidth(c.lines[c.lines.length-1])+tx;
      c.value = obj.value;
    }
  };
  obj.getmax = function() {
    var c = obj._cache;
    var m1x = obj.x+obj.w, m1y = obj.y+obj.h;
    return {x:max(m1x,obj.textWidth||0),y:max(m1y,obj.textHeight||0)}
  };
};
Components.button = function(obj) {
  obj.hoverStyle = createStyle(obj.style);
  obj.clickStyle = createStyle(obj.hoverStyle);
  obj.hover = false;
  obj.onclick = function(){};
  obj.onunclick = function(){};
  obj.interactive = true;
};
Components.image = function(obj,url,callback,failure) {
  if (typeof url == 'string') obj.img = loadImage(url,function(img) {
    if (obj.width === 0) obj.width = img.width;
    if (obj.height === 0) obj.height = img.height;
  },failure);
  else obj.img = url;
  obj.image = function(s,b) {
    var f = s.get("imageFit");
    var ax = s.get("imageHorizAlign");
    var ay = s.get("imageVertAlign");
    var bev = s.get("imageBevel");
    if (f == "inside") {
      var bb = centerRect(obj.width,obj.height,obj.img.width,obj.img.height);
      if (ax == "left") bb.x = 0;
      if (ax == "right") bb.x = obj.width-bb.w;
      if (ay == "top") bb.y = 0;
      if (ay == "bottom") bb.y = obj.height-bb.h;
      beveledImage(obj.img,obj.x+bb.x,obj.y+bb.y,bb.w,bb.h,bev);
    }
    else if (f == "outside") {
      var bb = centerRect(obj.img.width,obj.img.height,obj.width,obj.height);
      if (ax == "left") bb.x = 0;
      if (ax == "right") bb.x = obj.img.width-bb.w;
      if (ay == "top") bb.y = 0;
      if (ay == "bottom") bb.y = obj.img.height-bb.h;
      beveledImage(obj.img,bb.x,bb.y,bb.w,bb.h,obj.x,obj.y,obj.width,obj.height,bev);
    }
    else beveledImage(obj.img,obj.x,obj.y,obj.width,obj.height,bev);
  };
};
Components.input = function(obj,txt) {
  Components.text(obj,txt);
  Components.button(obj);
  obj.activeStyle = createStyle(obj.clickStyle);
  obj.cursorPos = obj.value.length;
  obj.cursor = function(s,b) {
    if (isMobile) return;
    var c = obj._cache;
    if (frameCount % 30 < 15 && activeElement == obj) return;
    b.noStroke();
    b.fill(s.get("cursor"));
    b.text("|",obj.x+c.cursorX,obj.y+c.cursorY+0.5);
  };
  obj.calculateCursorPos = function() {
    var s = obj.style, c = obj._cache;
    c.cursorY = (obj.beflines.length-1)*(s.get("textSize")*1.25)+c.ty;
    c.cursorX = textWidth(obj.beflines[obj.beflines.length-1])+c.tx;
  };
  obj.onclick = function() {
    if (isMobile) {
      var str = JSON.stringify(obj.value);
      str = str.substring(1,str.length-1);
      str = prompt(obj.placeholder||"Enter text:",str);
      if (str === null) return;
      str = str.replace(/"/g,"\\\"");
      obj.value = JSON.parse("\""+str+"\"");
      obj.update();
      obj.oninput();
      obj.onchange();
      obj.onunfocus();
      return;
    }
    var s = obj.style, c = obj._cache;
    activeElement = obj;
    var lines = obj.lines;
    var dx = mouseX - obj.x - c.tx;
    var dy = mouseY - obj.y - c.ty;
    var li = constrain(ceil(dy/(s.get("textSize")*1.25)),0,lines.length)-1;
    var e = textWidth("_");
    var last = lines[li]||"";
    var cpos = min(round(dx/e),last.length);
    while (textWidth(last.substring(0,cpos))>dx) cpos--;
    while (textWidth(last.substring(0,cpos))<dx-e && cpos < last.length) cpos++;
    for (var i = 0; i < li; i++) {
      cpos += lines[i].length;
    }
    cpos += (obj.value.substring(0,cpos).match(/\n/g)||[]).length;
    obj.cursorPos = cpos;
    obj.update();
    obj._cache.val = obj.value;
    obj.onfocus();
  };
  obj.update = function() {
    obj.text(obj.style,window);
    obj.lines = getLines(obj.value,obj._cache.max);
    var bef = [];
    var c = obj.cursorPos;
    for (var i = 0; i < obj.lines.length; i++) {
      bef.push(obj.lines[i]);
      c-=obj.lines[i].length;
      if (c < 0) break;
    }
    bef[bef.length-1] = bef[bef.length-1].substring(0,bef[bef.length-1].length+c);
    obj.beflines = bef;
    obj.calculateCursorPos();
    obj.oninput();
  };
  obj.oninput = function() {};
  obj.onfocus = function() {};
  obj.onunfocus = function() {};
  obj.onchange = function() {};
  setTimeout(function(){
    obj.update();
  },0);
};
Components.canvas = function(obj,gl) {
  obj.buff = createGraphics(obj.width,obj.height,gl);
  obj._cache.width = obj.width;
  obj._cache.height = obj.height;
  obj.children = [];
  obj.canvas = function(s,b) {
    obj.buff.clear();
    if (obj._cache.width != obj.width || obj._cache.width != obj.width) {
      obj.buff = createGraphics(obj.width,obj.height);
    }
    obj.draw(obj.buff,s);
    b.image(obj.buff,obj.x,obj.y,obj.width,obj.height);
  };
};
Components.div = function(obj) {
  Components.canvas(obj);
  obj.children = [];
  obj.appendChild = function(elem) {
    elem.parent = obj;
    obj.children.push(elem);
    obj.children = obj.children.sort(function(a,b){return a.zindex - b.zindex;});
  };
  obj.prependChild = function(elem) {
    elem.parent = obj;
    obj.children.shift(elem);
    obj.children = obj.children.sort(function(a,b){return a.zindex - b.zindex;});
  };
  obj.removeChild = function(elem) {
    var i = obj.children.indexOf(elem);
    if (i == -1) return;
    delete elem.parent;
    obj.children.splice(i,1);
  };
  obj.removeChildAtIndex = function(i) {
    if (!obj.children[i]) return;
    delete obj.children[i].parent;
    obj.children.splice(i,1);
  };
  obj.draw = function(b,s) {
    b.translate(-obj.scrollX,-obj.scrollY);
    for (var i = 0; i < obj.children.length; i++) {
      obj.children[i].show(b);
    }
    b.translate(obj.scrollX,obj.scrollY);
  };
  obj.scrollX = 0;
  obj.scrollY = 0;
  obj.minScrollX = 0;
  obj.minScrollY = 0;
  obj.maxScrollX = 0;
  obj.maxScrollY = 0;
  obj.onscroll = function(){};
  obj.scroll = function(x,y) {
    if (x == 0 && y == 0) return;
    var px = obj.scrollX, py = obj.scrollY;
    obj.scrollX = constrain(px+x,obj.minScrollX,obj.maxScrollX);
    obj.scrollY = constrain(py+y,obj.minScrollY,obj.maxScrollY);
    px -= obj.scrollX;
    py -= obj.scrollY;
    if (px != 0 || py != 0) obj.onscroll(px,py);
  };
  obj.div = true;
};
Components.draggable = function(obj) {
  Components.button(obj);
  obj.drag = function(s,b) {
    if (activeElement == obj) {
      obj.x += mouseX-pmouseX;
      obj.y += mouseY-pmouseY;
    }
  }
  obj.ongrab = function() {};
  obj.onrelease = function(){};
  obj.onclick = function() {
    activeElement = obj;
    obj.ongrab();
  };
  obj.onunclick = function(){
    activeElement = false;
    obj.onrelease();
  };
};

// Draw elements
window.drawElements = function() {
  cursor(ARROW);
  activePage.before();
  clickerElement(function(obj) {
    if (obj.onclick) {
      if (obj.cursor) cursor(TEXT);
      else cursor(HAND);
    }
    if (!obj.div) return;
    var sx = 0, sy = 0;
    if (mouseDown("left") && !activeElement.drag) {
      sx += pmouseX-mouseX;
      sy += pmouseY-mouseY;
    }
    if (keyDown("left")) sx -= 5;
    if (keyDown("right")) sx += 5;
    if (keyDown("up")) sy -= 5;
    if (keyDown("down")) sy += 5;
    obj.scroll(sx,sy);
  });
  for (var i = 0; i < activePage.elementList.length; i++) {
    var obj = activePage.elementList[i];
    if (obj.parent) continue;
    obj.show();
  }
  activePage.after();
}
function mousePressedElements() {
  var celem = activeElement;
  activeElement = false;
  clickerElement(function(obj) {
    if (!obj.onclick) return;
    obj.onclick();
  });
  if (celem != activeElement && celem.cursor) {
    if (celem._cache.val !== celem.value) celem.onchange(celem.value);
    celem.onunfocus();
  }
}
function mouseReleasedElements() {
  clickerElement(function(obj) {
    if (!obj.onunclick) return;
    obj.onunclick();
  });
}
function keyTypedElements() {
  if (!activeElement) return;
  if (key == "\r") return;
  var oldkey = key;
  onHold(keyCode,key,function(){
    activeElement.value = activeElement.value.substring(0, activeElement.cursorPos) + oldkey + activeElement.value.substring(activeElement.cursorPos, activeElement.value.length);
    activeElement.cursorPos++;
    activeElement.update();
    activeElement.oninput(key);
  });
}
function keyPressedElements() {
  if (!activeElement || !activeElement.cursor) return;
  onHold(BACKSPACE,"backspace",function() {
    activeElement.value = activeElement.value.substring(0, activeElement.cursorPos-1) + activeElement.value.substring(activeElement.cursorPos, activeElement.value.length);
    activeElement.cursorPos--;
    activeElement.cursorPos = max(0,activeElement.cursorPos);
    activeElement.update();
    activeElement.oninput("backspace");
  });
  onHold(LEFT_ARROW,"left",function() {
    activeElement.cursorPos = max(activeElement.cursorPos-1,0);
    activeElement.update();
    activeElement.oninput("left");
  });
  onHold(RIGHT_ARROW,"right",function() {
    activeElement.cursorPos = min(activeElement.cursorPos+1,activeElement.value.length);
    activeElement.update();
    activeElement.oninput("right");
  });
  if (keyCode == ENTER) {
    activeElement.value = activeElement.value.substring(0, activeElement.cursorPos) + "\n" + activeElement.value.substring(activeElement.cursorPos, activeElement.value.length);
    activeElement.cursorPos++;
    activeElement.update();
    activeElement.oninput("\n");
    return;
  }
  var lines, cursorLine, cursorOffset;
  var updateLines = function() {
    activeElement.update();
    lines = activeElement.lines;
    cursorLine = activeElement.beflines.length-1;
    cursorOffset = activeElement.cursorPos;
    for (var i = 0; i < cursorLine; i++) {
      cursorOffset -= lines[i].length+1;
    }
  }
  onHold(UP_ARROW,"up",function() {
    updateLines();
    if (cursorLine <= 0) return;
    var newPos = 0;
    for (var i = 0; i < cursorLine-1; i++) {
      newPos += lines[i].length+1;
    }
    activeElement.cursorPos = newPos + min(cursorOffset,lines[cursorLine-1].length);
  });
  onHold(DOWN_ARROW,"down",function() {
    updateLines();
    if (cursorLine >= lines.length-1) return;
    var newPos = 0;
    for (var i = 0; i < cursorLine+1; i++) {
      newPos += lines[i].length+1;
    }
    activeElement.cursorPos = newPos + min(cursorOffset,lines[cursorLine+1].length);
  });
  if (keyCode == 86 && keyDown(CONTROL)) {
    var pasted = prompt("Paste here:");
    if (!pasted) return;
    activeElement.value = activeElement.value.substring(0,activeElement.cursorPos) + pasted + activeElement.value.substring(activeElement.cursorPos,activeElement.value.length);
    activeElement.cursorPos += pasted.length;
    activeElement.update();
    activeElement.oninput(pasted);
  }
}
function mouseWheelElements(e) {
  /*if (activeElement && activeElement.div && activeElement.hover) {
    var ty = activeElement.y + activeElement.height/2;
    if (mouseY > ty) {
      activeElement.scroll(0,-5);
    }
  }*/
}
function clickerElement(callback) {
  var done = false;
  var hov = function(obj) {
    if (!obj.visible || (!obj.interactive && !obj.div)) return;
    var h = !done && obj.testhover();
    if (obj.div) {
      if (!h) return;
      mouseX -= obj.x - obj.scrollX;
      mouseY -= obj.y - obj.scrollY;
      for (var j = obj.children.length-1; j >= 0; j--) {
        hov(obj.children[j]);
      }
      mouseX += obj.x - obj.scrollX;
      mouseY += obj.y - obj.scrollY;
    } else {
      obj.hover = h;
      if (h) done = true;
    }
    if (h) callback(obj);
  };
  for (var i = activePage.elementList.length-1; i >= 0; i--) {
    var obj = activePage.elementList[i];
    if (obj.parent) continue;
    hov(obj);
  }
}
function onHold(kc,k,funct) {
  if (keyCode != kc && key != k) return;
  funct();
  setTimeout(function() {
    if (!keyDown(k)) return;
    funct();
    var int = setInterval(function() {
      if (!keyDown(k)) {
        clearInterval(int);
        return;
      }
      funct();
    },100);
  },600);
}

// Misc
window.mouseRect = function(x,y,width,height) {
  return (
    mouseX > x &&
    mouseX < x+width &&
    mouseY > y && 
    mouseY < y+height
  );
}
window.centerRect = function(w1,h1,w2,h2) {
  var fatness1 = w1 / h1;
  var fatness2 = w2 / h2;

  var scale;
  if (fatness2 >= fatness1) scale = w1 / w2;
  else scale = h1 / h2;
  var w = w2 * scale;
  var h = h2 * scale;

  var xCenter = 0 + (w1 / 2);
  var yCenter = 0 + (h1 / 2);

  var x = xCenter - (w / 2);
  var y = yCenter - (h / 2);

  return { x:x, y:y, w:w, h:h };
}
window.getLines = function(string,width) {
  if (typeof string !== 'string') return [];
  var lines = string.split("\n");
  for (var i = 0; i < lines.length; i++) {
    if (textWidth(lines[i]) <= width) continue;
    if (!lines[i].match(/[\s-_]/)) continue;
    var off = "";
    while (textWidth(lines[i]) > width || (!lines[i].match(/[\s-_]$/) && lines[i].match(/[\s-_]/))) {
      off = lines[i].charAt(lines[i].length-1)+off;
      lines[i] = lines[i].substr(0,lines[i].length-1);
    }
    lines.splice(i+1,0,off);
  }
  return lines;
}
window.beveledImage = function(img,sx,sy,sw,sh,x,y,w,h,bev,b) {
  b = b || window;
  if (!y) {
    bev = x;
    x = sx;
    y = sy;
    w = sw;
    h = sh;
    sx = 0;
    sy = 0;
    sw = img.width;
    sh = img.height;
  }
  if (img.width != 0 && bev) {
    var n = "bev"+[sx,sy,sw,sh,w,h,bev].join(",");
    if (!img[n]) {
      var buf = window._createGraphics(w,h);
      buf.image(img,sx,sy,sw,sh,0,0,w,h);
      var get1 = buf.get();
      buf.clear();
      buf.noStroke();
      buf.rect(0,0,w,h,bev);
      var get2 = buf.get();
      get1.mask(get2);
      img[n] = get1;
    }
    b.image(img[n],x,y,w,h);
  } else b.image(img,sx,sy,sw,sh,x,y,w,h);
}

// FANIM Fire Animations
// By: DragonFireGames
// Version: 1.2
// Description: Animation functions

window.SequenceList = [];
// Creates an animation sequence
window.createSequence = function(length) {
  var seq = {};
  // Animation Handlers
  seq.animations = [];
  // Creates Animations
  seq.createAnimation = function(start,end,drawer,interp) {
    var len = end-start;
    interp = interp || function(t){return t};
    seq.animations.push(function(time){
      if (time > end || time < start) return;
      drawer(interp((time-start)/len));
    });
  };
  //
  seq.length = length; // in ms
  // Callbacks
  seq.before = function() {};
  seq.after = function() {};
  seq.onstart = function() {};
  seq.onend = function() {};
  // Draws Animations
  seq.draw = function() {
    if (!seq.startTime) return;
    var time = Date.now()-seq.startTime;
    seq.before(time);
    for (var i = 0; i < seq.animations.length; i++) {
      seq.animations[i](time);
    }
    seq.after(time);
    if (seq.length && time >= seq.length) seq.end();
  };
  seq.start = function() {
    seq.onstart();
    seq.startTime = Date.now();
  };
  seq.end = function() {
    seq.onend();
    delete seq.startTime;
  };
  SequenceList.push(seq);
  return seq;
}
// Draw animation sequences
window.drawSequences = function() {
  for (var i = 0; i < SequenceList.length; i++) {
    SequenceList[i].draw();
  }
}

//easings from: https://easings.net
window.Ease = {
  in: {
    line: function(t) {
      return t;
    },
    sine: function(t) {
      return 1-Math.cos((t*Math.PI)/2);
    },
    quad: function(t) {
      return t*t;
    },
    cubic: function(t) {
      return t*t*t;
    },
    quart: function(t) {
      return t*t*t*t;
    },
    quint: function(t) {
      return t*t*t*t*t;
    },
    expo: function(t) {
      if (t === 0) return 0;
      return Math.pow(2,10*t-10);
    },
    circ: function(t) {
      return 1-Math.sqrt(1-t*t);
    },
    back: function(t) {
      var c1 = 1.70158;
      var c3 = c1 + 1;  
      return c3*t*t*t-c1*t*t;
    },
    elastic: function(t) {
      if (t === 0 || t === 1) return t;
      var c4 = (2 * Math.PI) / 3;
      return -Math.pow(2,10*t-10)*Math.sin((t*10-10.75)*c4);
    },
    bounce: function(t) {
      return 1-Ease.out.bounce(1-t);
    },
  },
  out: {
    line: function(t) {
      return 1-t;
    },
    sine: function(t) {
      return Math.sin((t*Math.PI)/2);
    },
    quad: function(t) {
      t = 1-t;
      return 1-t*t;
    },
    cubic: function(t) {
      t = 1-t;
      return 1-t*t*t;
    },
    quart: function(t) {
      t = 1-t;
      return 1-t*t*t*t;
    },
    quint: function(t) {
      t = 1-t;
      return 1-t*t*t*t*t;
    },
    expo: function(t) {
      if (t === 1) return 1;
      return 1-Math.pow(2,-10*t);
    },
    circ: function(t) {
      t = 1-t;
      return Math.sqrt(1-t*t);
    },
    back: function(t) {
      var c1 = 1.70158;
      var c3 = c1 + 1;
      t = t-1;
      return 1+c3*t*t*t-c1*t*t;
    },
    elastic: function(t) {
      if (t === 0 || t === 1) return t;
      var c4 = (2 * Math.PI) / 3;
      return -Math.pow(2,10*t-10)*Math.sin((t*10-10.75)*c4);
    },
    bounce: function(t) {
      var n1 = 7.5625;
      var d1 = 2.75;
      if (t < 1/d1) {
        return n1*t*t;
      } else if (t < 2/d1) {
        return n1*(t-=1.5/d1)*t+0.75;
      } else if (t < 2.5/d1) {
        return n1*(t-=2.25/d1)*t+0.9375;
      } else {
        return n1*(t-=2.625/d1)*t+0.984375;
      }
    },
  },
  inout: {
    line: function(t) {
      return t;
    },
    sine: function(t) {
      return -(Math.cos(Math.PI*t)-1)/2;
    },
    quad: function(t) {
      if (t < 0.5) return 2*t*t;
      var x = -2*t+2;
      return 1-x*x/2;
    },
    cubic: function(t) {
      if (t < 0.5) return 4*t*t*t;
      var x = -2*t+2;
      return 1-x*x*x/2;
    },
    quart: function(t) {
      if (t < 0.5) return 8*t*t*t*t;
      var x = -2*t+2;
      return 1-x*x*x*x/2;
    },
    quint: function(t) {
      if (t < 0.5) return 16*t*t*t*t*t;
      var x = -2*t+2;
      return 1-x*x*x*x*x/2;
    },
    expo: function(t) {
      if (t === 0 || t === 1) return t;
      if (t < 0.5) return Math.pow(2,20*t-10)/2;
      else return (2-Math.pow(2,-20*t+10))/2;
    },
    circ: function(t) {
      if (t < 0.5) return (1-Math.sqrt(1-4*t*t))/2
      var x = -2*x+2;
      return (Math.sqrt(1-x*x)+1)/2;
    },
    back: function(t) {
      var c1 = 1.70158;
      var c2 = c1 * 1.525;
      if (t < 0.5) return (4*t*t*((c2+1)*2*t-c2))/2;
      var x = -2*x+2;
      return (x*x*((c2+1)*(t*2-2)+c2)+2)/2;
    },
    elastic: function(t) {
      if (t === 0 || t === 1) return t;
      var c5 = (2 * Math.PI) / 4.5;
      if (t < 0.5) return -(Math.pow(2,20*t-10)*Math.sin((20*t-11.125)*c5))/2;
      return (Math.pow(2,-20*t+10)*Math.sin((20*t-11.125)*c5))/2+1;
    },
    bounce: function(t) {
      if (t < 0.5) return 1-Ease.out.bounce(1-2*t)/2;
      return 1+Ease.out.bounce(2*t-1)/2;
    }
  },
  outin: {
    line: function(t) {
      return 1-t;
    },
    sine: function(t) {
      return 1-Ease.inout.sine(1-t);
    },
    quad: function(t) {
      return 1-Ease.inout.quad(1-t);
    },
    cubic: function(t) {
      return 1-Ease.inout.cubic(1-t);
    },
    quart: function(t) {
      return 1-Ease.inout.quart(1-t);
    },
    quint: function(t) {
      return 1-Ease.inout.quint(1-t);
    },
    expo: function(t) {
      return 1-Ease.inout.expo(1-t);
    },
    circ: function(t) {
      return 1-Ease.inout.circ(1-t);
    },
    back: function(t) {
      return 1-Ease.inout.back(1-t);
    },
    elastic: function(t) {
      return 1-Ease.inout.elastic(1-t);
    },
    bounce: function(t) {
      return 1-Ease.inout.bounce(1-t);
    }
  },
}
// https://cubic-bezier.com/
/*window.cubicBezier = function(x1,y1,x2,y2) {
  
}*/

// Misc
// Format Anything
window.format = function(f,s,sw,ts,tax,tay,b) {
  b = b || window;
  if (!f && f !== 0) b.noFill();
  else b.fill(f);
  if (!s && s !== 0) b.noStroke(s);
  else b.stroke(s);
  b.strokeWeight(sw||1);
  b.textSize(ts||12);
  b.textAlign(tax||CENTER,tay||CENTER);
}
window.typewrite = function(str,t,x,y,mw,mh,b){
  b = b || window;
  b.text(str.substring(0,Math.round(str.length*t)),x,y,mw,mh);
}
window.alpha = function(c,a){
  if (typeof a != 'number') return c;
  a = constrain(a,0,1);
  var col = color(c);
  col._array[3] = a;
  return col;
}

// ---------
// Defaults:
// ---------

// Default Draw
function draw() {
  drawElements();
  drawSequences();
}
// Default Mouse Pressed
function mousePressed() {
  mousePressedElements();
}
// Default Mouse Released
function mouseReleased() {
  mouseReleasedElements();
}
// Default Key Typed
function keyTyped() {
  keyTypedElements();
}
// Default Key Pressed
function keyPressed() {
  keyPressedElements();
}
// Default Mouse Wheel
function mouseWheel(e) {
  mouseWheelElements(e);
}

You can also use this as a library now.

a potato. Not too hot, not too cold. Noice

could you kinda explain it to me, also i am usually on 2h a day cuz the doctor said i should cut being on screens for like 15 hours a day cuz my eyesight is really bad. I can't even see a stop sign without my glasses.

    1.6 - FGUI
    Added constraints to draggable elements
    Added link component
    Added ellipse component

    1.4 - FANIM
    Finally added cubic bezier easing

    // FGUI Fire Graphics User Interface
    // By: DragonFireGames
    // Version: 1.6
    // Description: A port/mix of HTML, CSS, 
    //   and p5.js to make a component based
    //   graphics interface. Styles are used
    //   and can be inherited and set.
    
    window.isMobile = _isSafari();
    showMobileControls(true,true,true,true);
    
    var testgraphics = createGraphics(400,400);
    background(255);
    testgraphics.fill(0);
    testgraphics.noStroke();
    testgraphics.rect(0,0,200,200);
    image(testgraphics,0,0,200,200);
    window.doubleGraphics = get(150,150)[0] == 0;
    delete testgraphics;
    background(242);
    window._createGraphics = window.createGraphics;
    window.createGraphics = function(w,h,gl) {
      if (doubleGraphics) w *= 2, h*= 2;
      var buf = window._createGraphics(w,h,gl);
      buf.format = function(f,s,sw,ts,tax,tay) {
        return format(f,s,sw,ts,tax,tay,buf);
      };
      buf.typewrite = function(str,t,x,y,mw,mh){
        return typewrite(str,t,x,y,mw,mh,buf);
      };
      buf.beveledImage = function(img,sx,sy,sw,sh,x,y,w,h,bev) {
        return beveledImage(img,sx,sy,sw,sh,x,y,w,h,bev,buf);
      }
      return buf;
    };
    
    window.elementList = [];
    window.pageMap = {};
    window.display = "default";
    window.activeElement = false;
    window.activePage = false;
    
    // Create a style
    window.defaultStyle = {
      padx:5,
      pady:5,
      fill:128,
      stroke:0,
      strokeWeight:1,
      textFill:0,
      textStroke:false,
      textStrokeWeight:1,
      textSize:12,
      placeholderTextFill:100,
      placeholderStroke:false,
      horizAlign:"left",
      vertAlign:"top",
      textmax:true,
      font:"heveltica",
      bevel:0,
      imageBevel:0,
      imageFit:"none",
      imageHorizAlign:"center",
      imageVertAlign:"center",
      cursor:0,
      get:function(e) {
        return defaultStyle[e];
      }
    };
    window.createStyle = function(parent) {
      var obj = {};
      obj.parent = parent || defaultStyle;
      obj.get = function(e) {
        if (obj[e] === undefined) return obj.parent.get(e);
        return obj[e];
      };
      obj.set = function(o) {
        for (var i in o) {
          obj[i] = o[i];
        }
      }
      return obj;
    }
    // Create a page
    window.createPage = function(name,bef,aft,onopen) {
      var obj = {};
      obj.name = name;
      obj.before = bef || function(){};
      obj.after = aft || function(){};
      obj.onopen = onopen || function(){};
      obj.elementList = [];
      obj.addElement = function(elem) {
        obj.elementList.push(elem);
        obj.elementList = obj.elementList.sort(function(a,b){return a.zindex - b.zindex;});
      };
      obj.removeElement = function(elem) {
        var i = obj.elementList.indexOf(elem);
        if (i == -1) return;
        obj.elementList.splice(i,1);
      };
      pageMap[name] = obj;
      return obj;
    }
    window.setPage = function(name) {
      display = name;
      activePage = pageMap[name];
      pageMap[name].onopen();
    }
    window.defaultPage = createPage("default");
    setPage("default");
    // Create an element
    window.createElement = function(x,y,w,h,page) {
      var obj = {};
      obj.x = x || 0;
      obj.y = y || 0;
      obj.width = w === undefined ? 50 : w;
      obj.height = h === undefined ? 50 : h;
      obj.style = createStyle();
      obj.visible = true;
      obj._cache = {};
      obj.testhover = function() {
        return mouseRect(obj.x,obj.y,obj.width,obj.height);
      };
      obj.getmax = function() {
        return {x:obj.x+obj.w,y:obj.y+obj.h};
      };
      obj.body = function(s,b) {
        b.format(s.get("fill"),s.get("stroke"),s.get("strokeWeight"));
        b.rect(obj.x,obj.y,obj.width,obj.height,s.get("bevel"));
      };
      obj.show = function(b) {
        if (!obj.visible) return;
        if (obj.drag) obj.drag(s,b);
        var s = obj.style;
        b = b || window;
        if (obj.hover) {
          if (mouseDown("left")) s = obj.clickStyle;
          else if (obj.hoverStyle) s = obj.hoverStyle;
        }
        if (activeElement == obj) {
          if (obj.cursor) s = obj.activeStyle;
          else if (obj.drag) s = obj.clickStyle;
        }
        obj.body(s,b);
        if (obj.image) obj.image(s,b);
        if (obj.text) obj.text(s,b);
        if (obj.cursor) obj.cursor(s,b);
        if (obj.canvas) obj.canvas(s,b);
        if (obj.onshow) obj.onshow(s,b);
      };
      obj.addComponent = function(e,a,b,c) {
        Components[e](obj,a,b,c);
      };
      obj.addToPage = function(name) {
        pageMap[name].addElement(obj);
      }
      obj.removeFromPage = function(name) {
        pageMap[name].removeElement(obj);
      }
      obj.zindex = 0;
      elementList.push(obj);
      pageMap[page||"default"].addElement(obj);
      return obj;
    }
    // Component List
    var Components = {};
    Components.ellipse = function(obj) {
      obj.body = function(s,b) {
        b.format(s.get("fill"),s.get("stroke"),s.get("strokeWeight"));
        b.ellipse(obj.x+obj.width/2,obj.y+obj.height/2,obj.width,obj.height);
      };
    };
    Components.text = function(obj,txt) {
      obj.value = txt||"";
      obj.placeholder = "";
      obj.activeStyle = createStyle(obj.clickStyle||obj.style);
      obj.text = function(s,b) {
        var c = obj._cache;
        var ax = s.get("horizAlign");
        var ay = s.get("vertAlign");
        b.format(s.get("textFill"),s.get("textStroke"),s.get("textStrokeWeight"),s.get("textSize"),ax,ay);
        b.textFont(s.get("font"));
        var str = obj.value;
        if (!obj.value && obj.placeholder) {
          b.format(s.get("placeholderTextFill"),s.get("placeholderTextStroke"),s.get("textStrokeWeight"),s.get("textSize"),ax,ay);
          str = obj.placeholder;
        }
        var max, tx = s.get("padx"), ty = s.get("pady");
        var tm = s.get("textmax");
        if (obj.cursor) ax = "left", ay = "top";
        if (tm) {
          max = obj.width - tx;
          if (ax == "center") max = obj.width - tx * 2;
        } else {
          if (ax == "right") tx = obj.width - tx;
          else if (ax == "center") tx = obj.width/2;
        }
        if (ay == "bottom") ty = obj.height - ty;
        else if (ay == "center") ty = obj.height/2;
        if (tm == 'number') max = tm;
        c.max = max; c.tx = tx; c.ty = ty;
        b.text(str,obj.x+tx,obj.y+ty,max);
        if (ax == "left" && c.value !== obj.value) {
          c.lines = getLines(obj.value,max);
          obj.textHeight = c.lines.length*(s.get("textSize")*1.25)+ty;
          obj.textWidth = textWidth(c.lines[c.lines.length-1])+tx;
          c.value = obj.value;
        }
      };
      obj.getmax = function() {
        var c = obj._cache;
        var m1x = obj.x+obj.w, m1y = obj.y+obj.h;
        return {x:max(m1x,obj.textWidth||0),y:max(m1y,obj.textHeight||0)}
      };
    };
    Components.button = function(obj) {
      obj.hoverStyle = createStyle(obj.style);
      obj.clickStyle = createStyle(obj.hoverStyle);
      obj.hover = false;
      obj.onclick = function(){};
      obj.onunclick = function(){};
      obj.interactive = true;
    };
    Components.image = function(obj,url,callback,failure) {
      if (typeof url == 'string') obj.img = loadImage(url,function(img) {
        if (obj.width === 0) obj.width = img.width;
        if (obj.height === 0) obj.height = img.height;
      },failure);
      else obj.img = url;
      obj.image = function(s,b) {
        var f = s.get("imageFit");
        var ax = s.get("imageHorizAlign");
        var ay = s.get("imageVertAlign");
        var bev = s.get("imageBevel");
        if (f == "inside") {
          var bb = centerRect(obj.width,obj.height,obj.img.width,obj.img.height);
          if (ax == "left") bb.x = 0;
          if (ax == "right") bb.x = obj.width-bb.w;
          if (ay == "top") bb.y = 0;
          if (ay == "bottom") bb.y = obj.height-bb.h;
          beveledImage(obj.img,obj.x+bb.x,obj.y+bb.y,bb.w,bb.h,bev);
        }
        else if (f == "outside") {
          var bb = centerRect(obj.img.width,obj.img.height,obj.width,obj.height);
          if (ax == "left") bb.x = 0;
          if (ax == "right") bb.x = obj.img.width-bb.w;
          if (ay == "top") bb.y = 0;
          if (ay == "bottom") bb.y = obj.img.height-bb.h;
          beveledImage(obj.img,bb.x,bb.y,bb.w,bb.h,obj.x,obj.y,obj.width,obj.height,bev);
        }
        else beveledImage(obj.img,obj.x,obj.y,obj.width,obj.height,bev);
      };
    };
    Components.input = function(obj,txt) {
      Components.text(obj,txt);
      Components.button(obj);
      obj.activeStyle = createStyle(obj.clickStyle);
      obj.cursorPos = obj.value.length;
      obj.cursor = function(s,b) {
        if (isMobile) return;
        var c = obj._cache;
        if (frameCount % 30 < 15 && activeElement == obj) return;
        b.noStroke();
        b.fill(s.get("cursor"));
        b.text("|",obj.x+c.cursorX,obj.y+c.cursorY+0.5);
      };
      obj.calculateCursorPos = function() {
        var s = obj.style, c = obj._cache;
        c.cursorY = (obj.beflines.length-1)*(s.get("textSize")*1.25)+c.ty;
        c.cursorX = textWidth(obj.beflines[obj.beflines.length-1])+c.tx;
      };
      obj.onclick = function() {
        if (isMobile) {
          var str = JSON.stringify(obj.value);
          str = str.substring(1,str.length-1);
          str = prompt(obj.placeholder||"Enter text:",str);
          if (str === null) return;
          str = str.replace(/"/g,"\\\"");
          obj.value = JSON.parse("\""+str+"\"");
          obj.update();
          obj.oninput();
          obj.onchange();
          obj.onunfocus();
          return;
        }
        var s = obj.style, c = obj._cache;
        activeElement = obj;
        var lines = obj.lines;
        var dx = mouseX - obj.x - c.tx;
        var dy = mouseY - obj.y - c.ty;
        var li = constrain(ceil(dy/(s.get("textSize")*1.25)),0,lines.length)-1;
        var e = textWidth("_");
        var last = lines[li]||"";
        var cpos = min(round(dx/e),last.length);
        while (textWidth(last.substring(0,cpos))>dx) cpos--;
        while (textWidth(last.substring(0,cpos))<dx-e && cpos < last.length) cpos++;
        for (var i = 0; i < li; i++) {
          cpos += lines[i].length;
        }
        cpos += (obj.value.substring(0,cpos).match(/\n/g)||[]).length;
        obj.cursorPos = cpos;
        obj.update();
        obj._cache.val = obj.value;
        obj.onfocus();
      };
      obj.update = function() {
        obj.text(obj.style,window);
        obj.lines = getLines(obj.value,obj._cache.max);
        var bef = [];
        var c = obj.cursorPos;
        for (var i = 0; i < obj.lines.length; i++) {
          bef.push(obj.lines[i]);
          c-=obj.lines[i].length;
          if (c < 0) break;
        }
        bef[bef.length-1] = bef[bef.length-1].substring(0,bef[bef.length-1].length+c);
        obj.beflines = bef;
        obj.calculateCursorPos();
        obj.oninput();
      };
      obj.oninput = function() {};
      obj.onfocus = function() {};
      obj.onunfocus = function() {};
      obj.onchange = function() {};
      setTimeout(function(){
        obj.update();
      },0);
    };
    Components.canvas = function(obj,gl) {
      obj.buff = createGraphics(obj.width,obj.height,gl);
      obj._cache.width = obj.width;
      obj._cache.height = obj.height;
      obj.children = [];
      obj.canvas = function(s,b) {
        obj.buff.clear();
        if (obj._cache.width != obj.width || obj._cache.width != obj.width) {
          obj.buff = createGraphics(obj.width,obj.height);
        }
        obj.draw(obj.buff,s);
        b.image(obj.buff,obj.x,obj.y,obj.width,obj.height);
      };
    };
    Components.div = function(obj) {
      Components.canvas(obj);
      obj.children = [];
      obj.appendChild = function(elem) {
        elem.parent = obj;
        obj.children.push(elem);
        obj.children = obj.children.sort(function(a,b){return a.zindex - b.zindex;});
      };
      obj.prependChild = function(elem) {
        elem.parent = obj;
        obj.children.shift(elem);
        obj.children = obj.children.sort(function(a,b){return a.zindex - b.zindex;});
      };
      obj.removeChild = function(elem) {
        var i = obj.children.indexOf(elem);
        if (i == -1) return;
        delete elem.parent;
        obj.children.splice(i,1);
      };
      obj.removeChildAtIndex = function(i) {
        if (!obj.children[i]) return;
        delete obj.children[i].parent;
        obj.children.splice(i,1);
      };
      obj.draw = function(b,s) {
        b.translate(-obj.scrollX,-obj.scrollY);
        for (var i = 0; i < obj.children.length; i++) {
          obj.children[i].show(b);
        }
        b.translate(obj.scrollX,obj.scrollY);
      };
      obj.scrollX = 0;
      obj.scrollY = 0;
      obj.minScrollX = 0;
      obj.minScrollY = 0;
      obj.maxScrollX = 0;
      obj.maxScrollY = 0;
      obj.onscroll = function(){};
      obj.scroll = function(x,y) {
        if (x == 0 && y == 0) return;
        var px = obj.scrollX, py = obj.scrollY;
        obj.scrollX = constrain(px+x,obj.minScrollX,obj.maxScrollX);
        obj.scrollY = constrain(py+y,obj.minScrollY,obj.maxScrollY);
        px -= obj.scrollX;
        py -= obj.scrollY;
        if (px != 0 || py != 0) obj.onscroll(px,py);
      };
      obj.div = true;
    };
    Components.draggable = function(obj) {
      Components.button(obj);
      obj.dragX = true;
      obj.dragY = true;
      obj.minX = -Infinity;
      obj.maxX = Infinity;
      obj.minY = -Infinity;
      obj.maxY = Infinity;
      obj.drag = function(s,b) {
        if (activeElement != obj) return;
        if (obj.dragX) {
          obj.x += mouseX-pmouseX;
          obj.x = constrain(obj.x,obj.minX,obj.maxX);
        }
        if (obj.dragY) {
          obj.y += mouseY-pmouseY;
          obj.y = constrain(obj.y,obj.minY,obj.maxY);
        }
      }
      obj.ongrab = function() {};
      obj.onrelease = function(){};
      obj.onclick = function() {
        activeElement = obj;
        obj.ongrab();
      };
      obj.onunclick = function(){
        activeElement = false;
        obj.onrelease();
      };
    };
    Components.link = function(obj,url,pmt) {
      obj.url = url;
      obj.prompt = pmt || "Paste this URL in new tab:";
      obj.icon = loadIcon(url);
      obj.hoverExtra = 5;
      obj.body = function(s,b) {
        if (obj.hover) {
          var s = obj.hoverExtra;
          b.image(obj.icon,obj.x-s/2,obj.y-s/2,obj.width+s,obj.height+s);
        } else {
          b.image(obj.icon,obj.x,obj.y,obj.width,obj.height);
        }
      };
      obj.hover = false;
      obj.onclick = function() {
        prompt(obj.prompt, obj.url);
        obj.onopen();
      };
      obj.onopen = function() {};
      obj.onunclick = function(){};
      obj.interactive = true;
      delete obj.style;
    };
    
    // Links
    var linkIcon = loadImage("https://cdn-icons-png.flaticon.com/512/3214/3214746.png");
    var linkIcons = {
      "discord." : loadImage("https://cdn3.iconfinder.com/data/icons/popular-services-brands-vol-2/512/discord-512.png"),
      "youtube.com" : loadImage("https://www.iconpacks.net/icons/2/free-youtube-logo-icon-2431-thumb.png"),
      "youtu.be" : loadImage("https://www.iconpacks.net/icons/2/free-youtube-logo-icon-2431-thumb.png"),
      "spotify.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/8/84/Spotify_icon.svg/1982px-Spotify_icon.svg.png"),
      "code.org" : loadImage("https://studio.code.org/assets/logo-2acd4ebc69c447786b866b98034bb3c0777b5f67cd8bd7955e97bba0b16f2bd1.svg"),
      "pumpkin-smasher" : loadImage("https://pumpkin-smasher-2.dragonfiregames.repl.co/assets/pumpkins/objective.png"),
      "sites.google.com/view/dragonfiregames" : loadImage("https://i.ibb.co/PGFTDGy/ship.png"),
      /*
      "google.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/1200px-Google_%22G%22_Logo.svg.png"),
      "reddit.com" : loadImage("https://upload.wikimedia.org/wikipedia/en/b/bd/Reddit_Logo_Icon.svg"),
      "twitch.tv" : loadImage("https://cdn-icons-png.flaticon.com/512/5968/5968819.png"),
      "twitter.com" : loadImage("https://cdn-icons-png.flaticon.com/512/3536/3536424.png"),
      "facebook.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/2021_Facebook_icon.svg/2048px-2021_Facebook_icon.svg.png"),
      "instagram.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Instagram_logo_2022.svg/1200px-Instagram_logo_2022.svg.png"),
      "snapchat.com" : loadImage("https://play-lh.googleusercontent.com/KxeSAjPTKliCErbivNiXrd6cTwfbqUJcbSRPe_IBVK_YmwckfMRS1VIHz-5cgT09yMo"),
      "tiktok.com" : loadImage("https://play-lh.googleusercontent.com/Ui_-OW6UJI147ySDX9guWWDiCPSq1vtxoC-xG17BU2FpU0Fi6qkWwuLdpddmT9fqrA=w240-h480-rw"),
      "outlook.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/d/df/Microsoft_Office_Outlook_%282018%E2%80%93present%29.svg/2203px-Microsoft_Office_Outlook_%282018%E2%80%93present%29.svg.png"),
      "gmail.com" : loadImage("https://freelogopng.com/images/all_img/1657906383gmail-icon-png.png"),
      "roblox.com" : loadImage("https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/c3207cf8-6638-4073-979c-092861231689/de2fzyg-29a1af41-1242-46f4-a33b-6f8a6ae2e1ef.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7InBhdGgiOiJcL2ZcL2MzMjA3Y2Y4LTY2MzgtNDA3My05NzljLTA5Mjg2MTIzMTY4OVwvZGUyZnp5Zy0yOWExYWY0MS0xMjQyLTQ2ZjQtYTMzYi02ZjhhNmFlMmUxZWYucG5nIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmZpbGUuZG93bmxvYWQiXX0.1XioYcpOV0zu8MOTbrQBWT4qFjx1l0lt91tlis6hvkc"),
      "microsoft.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/4/44/Microsoft_logo.svg/2048px-Microsoft_logo.svg.png"),
      "prodigygame.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Prodigy_icon_2020.svg/1200px-Prodigy_icon_2020.svg.png"),
      */
    };
    var cachedIcons = {};
    window.loadIcon = function(url) {
      for (var i in linkIcons) {
        if (url.includes(i)) return linkIcons[i];
      }
      var domain = url.match(/^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n?]+)/img);
      if (cachedIcons[domain]) return cachedIcons[domain];
      cachedIcons[domain] = linkIcon;
      cachedIcons[domain] = loadImage("https://www.google.com/s2/favicons?sz=64&domain="+domain,function(img){
        cachedIcons[domain] = img;
      });
      return linkIcon;
    }
    
    // Draw elements
    window.drawElements = function() {
      cursor(ARROW);
      activePage.before();
      clickerElement(function(obj) {
        if (obj.onclick) {
          if (obj.cursor) cursor(TEXT);
          else cursor(HAND);
        }
        if (!obj.div) return;
        var sx = 0, sy = 0;
        if (mouseDown("left") && !activeElement.drag) {
          sx += pmouseX-mouseX;
          sy += pmouseY-mouseY;
        }
        if (keyDown("left")) sx -= 5;
        if (keyDown("right")) sx += 5;
        if (keyDown("up")) sy -= 5;
        if (keyDown("down")) sy += 5;
        obj.scroll(sx,sy);
      });
      for (var i = 0; i < activePage.elementList.length; i++) {
        var obj = activePage.elementList[i];
        if (obj.parent) continue;
        obj.show();
      }
      activePage.after();
    }
    function mousePressedElements() {
      var celem = activeElement;
      activeElement = false;
      clickerElement(function(obj) {
        if (!obj.onclick) return;
        obj.onclick();
      });
      if (celem != activeElement && celem.cursor) {
        if (celem._cache.val !== celem.value) celem.onchange(celem.value);
        celem.onunfocus();
      }
    }
    function mouseReleasedElements() {
      clickerElement(function(obj) {
        if (!obj.onunclick) return;
        obj.onunclick();
      });
    }
    function keyTypedElements() {
      if (!activeElement) return;
      if (key == "\r") return;
      var oldkey = key;
      onHold(keyCode,key,function(){
        activeElement.value = activeElement.value.substring(0, activeElement.cursorPos) + oldkey + activeElement.value.substring(activeElement.cursorPos, activeElement.value.length);
        activeElement.cursorPos++;
        activeElement.update();
        activeElement.oninput(key);
      });
    }
    function keyPressedElements() {
      if (!activeElement || !activeElement.cursor) return;
      onHold(BACKSPACE,"backspace",function() {
        activeElement.value = activeElement.value.substring(0, activeElement.cursorPos-1) + activeElement.value.substring(activeElement.cursorPos, activeElement.value.length);
        activeElement.cursorPos--;
        activeElement.cursorPos = max(0,activeElement.cursorPos);
        activeElement.update();
        activeElement.oninput("backspace");
      });
      onHold(LEFT_ARROW,"left",function() {
        activeElement.cursorPos = max(activeElement.cursorPos-1,0);
        activeElement.update();
        activeElement.oninput("left");
      });
      onHold(RIGHT_ARROW,"right",function() {
        activeElement.cursorPos = min(activeElement.cursorPos+1,activeElement.value.length);
        activeElement.update();
        activeElement.oninput("right");
      });
      if (keyCode == ENTER) {
        activeElement.value = activeElement.value.substring(0, activeElement.cursorPos) + "\n" + activeElement.value.substring(activeElement.cursorPos, activeElement.value.length);
        activeElement.cursorPos++;
        activeElement.update();
        activeElement.oninput("\n");
        return;
      }
      var lines, cursorLine, cursorOffset;
      var updateLines = function() {
        activeElement.update();
        lines = activeElement.lines;
        cursorLine = activeElement.beflines.length-1;
        cursorOffset = activeElement.cursorPos;
        for (var i = 0; i < cursorLine; i++) {
          cursorOffset -= lines[i].length+1;
        }
      }
      onHold(UP_ARROW,"up",function() {
        updateLines();
        if (cursorLine <= 0) return;
        var newPos = 0;
        for (var i = 0; i < cursorLine-1; i++) {
          newPos += lines[i].length+1;
        }
        activeElement.cursorPos = newPos + min(cursorOffset,lines[cursorLine-1].length);
      });
      onHold(DOWN_ARROW,"down",function() {
        updateLines();
        if (cursorLine >= lines.length-1) return;
        var newPos = 0;
        for (var i = 0; i < cursorLine+1; i++) {
          newPos += lines[i].length+1;
        }
        activeElement.cursorPos = newPos + min(cursorOffset,lines[cursorLine+1].length);
      });
      if (keyCode == 86 && keyDown(CONTROL)) {
        var pasted = prompt("Paste here:");
        if (!pasted) return;
        activeElement.value = activeElement.value.substring(0,activeElement.cursorPos) + pasted + activeElement.value.substring(activeElement.cursorPos,activeElement.value.length);
        activeElement.cursorPos += pasted.length;
        activeElement.update();
        activeElement.oninput(pasted);
      }
    }
    function mouseWheelElements(e) {
      /*if (activeElement && activeElement.div && activeElement.hover) {
        var ty = activeElement.y + activeElement.height/2;
        if (mouseY > ty) {
          activeElement.scroll(0,-5);
        }
      }*/
    }
    function clickerElement(callback) {
      var done = false;
      var hov = function(obj) {
        if (!obj.visible || (!obj.interactive && !obj.div)) return;
        var h = !done && obj.testhover();
        if (obj.div) {
          if (!h) return;
          mouseX -= obj.x - obj.scrollX;
          mouseY -= obj.y - obj.scrollY;
          for (var j = obj.children.length-1; j >= 0; j--) {
            hov(obj.children[j]);
          }
          mouseX += obj.x - obj.scrollX;
          mouseY += obj.y - obj.scrollY;
        } else {
          obj.hover = h;
          if (h) done = true;
        }
        if (h) callback(obj);
      };
      for (var i = activePage.elementList.length-1; i >= 0; i--) {
        var obj = activePage.elementList[i];
        if (obj.parent) continue;
        hov(obj);
      }
    }
    function onHold(kc,k,funct) {
      if (keyCode != kc && key != k) return;
      funct();
      setTimeout(function() {
        if (!keyDown(k)) return;
        funct();
        var int = setInterval(function() {
          if (!keyDown(k)) {
            clearInterval(int);
            return;
          }
          funct();
        },100);
      },600);
    }
    
    // Misc
    window.mouseRect = function(x,y,width,height) {
      return (
        mouseX > x &&
        mouseX < x+width &&
        mouseY > y && 
        mouseY < y+height
      );
    }
    window.centerRect = function(w1,h1,w2,h2) {
      var fatness1 = w1 / h1;
      var fatness2 = w2 / h2;
    
      var scale;
      if (fatness2 >= fatness1) scale = w1 / w2;
      else scale = h1 / h2;
      var w = w2 * scale;
      var h = h2 * scale;
    
      var xCenter = 0 + (w1 / 2);
      var yCenter = 0 + (h1 / 2);
    
      var x = xCenter - (w / 2);
      var y = yCenter - (h / 2);
    
      return { x:x, y:y, w:w, h:h };
    }
    window.getLines = function(string,width) {
      if (typeof string !== 'string') return [];
      var lines = string.split("\n");
      for (var i = 0; i < lines.length; i++) {
        if (textWidth(lines[i]) <= width) continue;
        if (!lines[i].match(/[\s-_]/)) continue;
        var off = "";
        while (textWidth(lines[i]) > width || (!lines[i].match(/[\s-_]$/) && lines[i].match(/[\s-_]/))) {
          off = lines[i].charAt(lines[i].length-1)+off;
          lines[i] = lines[i].substr(0,lines[i].length-1);
        }
        lines.splice(i+1,0,off);
      }
      return lines;
    }
    window.beveledImage = function(img,sx,sy,sw,sh,x,y,w,h,bev,b) {
      b = b || window;
      if (!y) {
        bev = x;
        x = sx;
        y = sy;
        w = sw;
        h = sh;
        sx = 0;
        sy = 0;
        sw = img.width;
        sh = img.height;
      }
      if (img.width != 0 && bev) {
        var n = "bev"+[sx,sy,sw,sh,w,h,bev].join(",");
        if (!img[n]) {
          var buf = window._createGraphics(w,h);
          buf.image(img,sx,sy,sw,sh,0,0,w,h);
          var get1 = buf.get();
          buf.clear();
          buf.noStroke();
          buf.rect(0,0,w,h,bev);
          var get2 = buf.get();
          get1.mask(get2);
          img[n] = get1;
        }
        b.image(img[n],x,y,w,h);
      } else b.image(img,sx,sy,sw,sh,x,y,w,h);
    }
    
    // FANIM Fire Animations
    // By: DragonFireGames
    // Version: 1.3
    // Description: Animation functions
    
    window.SequenceList = [];
    // Creates an animation sequence
    window.createSequence = function(length) {
      var seq = {};
      // Animation Handlers
      seq.animations = [];
      // Creates Animations
      seq.createAnimation = function(start,end,drawer,interp) {
        var len = end-start;
        interp = interp || function(t){return t};
        seq.animations.push(function(time){
          if (time > end || time < start) return;
          drawer(interp((time-start)/len));
        });
      };
      //
      seq.length = length; // in ms
      // Callbacks
      seq.before = function() {};
      seq.after = function() {};
      seq.onstart = function() {};
      seq.onend = function() {};
      // Draws Animations
      seq.draw = function() {
        if (!seq.startTime) return;
        var time = Date.now()-seq.startTime;
        seq.before(time);
        for (var i = 0; i < seq.animations.length; i++) {
          seq.animations[i](time);
        }
        seq.after(time);
        if (seq.length && time >= seq.length) seq.end();
      };
      seq.start = function() {
        seq.onstart();
        seq.startTime = Date.now();
      };
      seq.end = function() {
        seq.onend();
        delete seq.startTime;
      };
      SequenceList.push(seq);
      return seq;
    }
    // Draw animation sequences
    window.drawSequences = function() {
      for (var i = 0; i < SequenceList.length; i++) {
        SequenceList[i].draw();
      }
    }
    
    //easings from: https://easings.net
    window.Ease = {
      in: {
        line: function(t) {
          return t;
        },
        sine: function(t) {
          return 1-Math.cos((t*Math.PI)/2);
        },
        quad: function(t) {
          return t*t;
        },
        cubic: function(t) {
          return t*t*t;
        },
        quart: function(t) {
          return t*t*t*t;
        },
        quint: function(t) {
          return t*t*t*t*t;
        },
        expo: function(t) {
          if (t === 0) return 0;
          return Math.pow(2,10*t-10);
        },
        circ: function(t) {
          return 1-Math.sqrt(1-t*t);
        },
        back: function(t) {
          var c1 = 1.70158;
          var c3 = c1 + 1;  
          return c3*t*t*t-c1*t*t;
        },
        elastic: function(t) {
          if (t === 0 || t === 1) return t;
          var c4 = (2 * Math.PI) / 3;
          return -Math.pow(2,10*t-10)*Math.sin((t*10-10.75)*c4);
        },
        bounce: function(t) {
          return 1-Ease.out.bounce(1-t);
        },
      },
      out: {
        line: function(t) {
          return 1-t;
        },
        sine: function(t) {
          return Math.sin((t*Math.PI)/2);
        },
        quad: function(t) {
          t = 1-t;
          return 1-t*t;
        },
        cubic: function(t) {
          t = 1-t;
          return 1-t*t*t;
        },
        quart: function(t) {
          t = 1-t;
          return 1-t*t*t*t;
        },
        quint: function(t) {
          t = 1-t;
          return 1-t*t*t*t*t;
        },
        expo: function(t) {
          if (t === 1) return 1;
          return 1-Math.pow(2,-10*t);
        },
        circ: function(t) {
          t = 1-t;
          return Math.sqrt(1-t*t);
        },
        back: function(t) {
          var c1 = 1.70158;
          var c3 = c1 + 1;
          t = t-1;
          return 1+c3*t*t*t-c1*t*t;
        },
        elastic: function(t) {
          if (t === 0 || t === 1) return t;
          var c4 = (2 * Math.PI) / 3;
          return -Math.pow(2,10*t-10)*Math.sin((t*10-10.75)*c4);
        },
        bounce: function(t) {
          var n1 = 7.5625;
          var d1 = 2.75;
          if (t < 1/d1) {
            return n1*t*t;
          } else if (t < 2/d1) {
            return n1*(t-=1.5/d1)*t+0.75;
          } else if (t < 2.5/d1) {
            return n1*(t-=2.25/d1)*t+0.9375;
          } else {
            return n1*(t-=2.625/d1)*t+0.984375;
          }
        },
      },
      inout: {
        line: function(t) {
          return t;
        },
        sine: function(t) {
          return -(Math.cos(Math.PI*t)-1)/2;
        },
        quad: function(t) {
          if (t < 0.5) return 2*t*t;
          var x = -2*t+2;
          return 1-x*x/2;
        },
        cubic: function(t) {
          if (t < 0.5) return 4*t*t*t;
          var x = -2*t+2;
          return 1-x*x*x/2;
        },
        quart: function(t) {
          if (t < 0.5) return 8*t*t*t*t;
          var x = -2*t+2;
          return 1-x*x*x*x/2;
        },
        quint: function(t) {
          if (t < 0.5) return 16*t*t*t*t*t;
          var x = -2*t+2;
          return 1-x*x*x*x*x/2;
        },
        expo: function(t) {
          if (t === 0 || t === 1) return t;
          if (t < 0.5) return Math.pow(2,20*t-10)/2;
          else return (2-Math.pow(2,-20*t+10))/2;
        },
        circ: function(t) {
          if (t < 0.5) return (1-Math.sqrt(1-4*t*t))/2
          var x = -2*x+2;
          return (Math.sqrt(1-x*x)+1)/2;
        },
        back: function(t) {
          var c1 = 1.70158;
          var c2 = c1 * 1.525;
          if (t < 0.5) return (4*t*t*((c2+1)*2*t-c2))/2;
          var x = -2*x+2;
          return (x*x*((c2+1)*(t*2-2)+c2)+2)/2;
        },
        elastic: function(t) {
          if (t === 0 || t === 1) return t;
          var c5 = (2 * Math.PI) / 4.5;
          if (t < 0.5) return -(Math.pow(2,20*t-10)*Math.sin((20*t-11.125)*c5))/2;
          return (Math.pow(2,-20*t+10)*Math.sin((20*t-11.125)*c5))/2+1;
        },
        bounce: function(t) {
          if (t < 0.5) return 1-Ease.out.bounce(1-2*t)/2;
          return 1+Ease.out.bounce(2*t-1)/2;
        }
      },
      outin: {
        line: function(t) {
          return 1-t;
        },
        sine: function(t) {
          return 1-Ease.inout.sine(1-t);
        },
        quad: function(t) {
          return 1-Ease.inout.quad(1-t);
        },
        cubic: function(t) {
          return 1-Ease.inout.cubic(1-t);
        },
        quart: function(t) {
          return 1-Ease.inout.quart(1-t);
        },
        quint: function(t) {
          return 1-Ease.inout.quint(1-t);
        },
        expo: function(t) {
          return 1-Ease.inout.expo(1-t);
        },
        circ: function(t) {
          return 1-Ease.inout.circ(1-t);
        },
        back: function(t) {
          return 1-Ease.inout.back(1-t);
        },
        elastic: function(t) {
          return 1-Ease.inout.elastic(1-t);
        },
        bounce: function(t) {
          return 1-Ease.inout.bounce(1-t);
        }
      },
    }
    // https://cubic-bezier.com/
    window.cubicBezier = function(p1x,p1y,p2x,p2y) {
      //https://probablymarcus.com/blocks/2015/02/26/using-bezier-curves-as-easing-functions.html
      // Horner's method:
      var cx = 3*p1x;
      var bx = 3*(p2x-p1x)-cx;
      var ax = 1-cx-bx;
      var Bx = function(t) {
        return ((ax*t+bx)*t+cx)*t;
      };
      var BxPrime = function(t) {
        return 3*ax*t*t + 2*bx*t + cx;
      };
      var cy = 3*p1y;
      var by = 3*(p2y-p1y)-cy;
      var ay = 1-cy-by;
      var By = function(t) {
        return ((ay*t+by)*t+cy)*t;
      };
      var invBx = function(t) {
        // Newton method
        var x0 = t;
        for (var i = 0; i < 8; i++) {
          var y0 = Bx(x0)-t;
          if (abs(y0) <= 0.001) return x0;
          x0 -= y0/BxPrime(x0);
        }
        // Otherwise do bisection:
        var a = 0;
        var b = 1;
        x0 = 0.5;
        var step = 0.25;
        for (var i = 0; i < 16; i++) {
          y0 = Bx(x0)-t;
          if (abs(y0) <= 0.001) return x0;
          if (sign(y0) == sign(Bx(a)-t)) a = x0;
          else b = x0;
          x0 = (a+b)/2;
        }
        return x0;
      };
      return function(t) {
        return By(invBx(t));
      }
    }
    
    // Misc
    // Format Anything
    window.format = function(f,s,sw,ts,tax,tay,b) {
      b = b || window;
      if (!f && f !== 0) b.noFill();
      else b.fill(f);
      if (!s && s !== 0) b.noStroke(s);
      else b.stroke(s);
      b.strokeWeight(sw||1);
      b.textSize(ts||12);
      b.textAlign(tax||CENTER,tay||CENTER);
    }
    window.typewrite = function(str,t,x,y,mw,mh,b){
      b = b || window;
      b.text(str.substring(0,Math.round(str.length*t)),x,y,mw,mh);
    }
    window.alpha = function(c,a){
      if (typeof a != 'number') return c;
      a = constrain(a,0,1);
      var col = color(c);
      col._array[3] = a;
      return col;
    }
    Math.sign = function(x) {
      if (x == 0) return 0;
      if (x > 0) return 1;
      return -1;
    } // Why does this not exist on code.org
    window.sign = Math.sign;
    
    // ---------
    // Defaults:
    // ---------
    
    // Default Draw
    function draw() {
      drawElements();
      drawSequences();
    }
    // Default Mouse Pressed
    function mousePressed() {
      mousePressedElements();
    }
    // Default Mouse Released
    function mouseReleased() {
      mouseReleasedElements();
    }
    // Default Key Typed
    function keyTyped() {
      keyTypedElements();
    }
    // Default Key Pressed
    function keyPressed() {
      keyPressedElements();
    }
    // Default Mouse Wheel
    function mouseWheel(e) {
      mouseWheelElements(e);
    }

      kid It's a little bit complicated, but the demo should have enough in there for you to pick it apart and mess around to figure out how it works. I may eventually write documentation.

      5 days later

      text box is bugged if you hit enter multiple times, click out, click in, and then delete stuff

      20 days later

      1.7 FGUI - added a lot & changed way too much
      Library ID: rOXQsmd4_OVOOWKzsdPAQf2FqnDqmjPWTu570Zp4DGA

      // See the forum post on it: https://gamelab.freeflarum.com/d/1548-gui-module/
      
      // FGUI Fire Graphics User Interface
      // By: DragonFireGames
      // Version: 1.7
      // Description: A port/mix of HTML, CSS, 
      //   and p5.js to make a component based
      //   graphics interface. Styles are used
      //   and can be inherited and set.
      
      window.isMobile = _isSafari();
      showMobileControls(true,true,true,true);
      window.debug = createSprite(0,0,0,0);
      window.SQRT2 = Math.sqrt(2);
      
      background(242);
      window._boundGraphicsMethods = {};
      window._bindGraphicsMethod = function(name,funct) {
        window[name] = funct.bind(window);
        window._boundGraphicsMethods[name] = funct;
      }
      window._createGraphics = window.createGraphics;
      window.createGraphics = function(w,h,gl) {
        var buf = window._createGraphics(w,h,gl);
        buf.twidth = w;
        buf.theight = h;
        buf.width *= buf._pixelDensity;
        buf.height *= buf._pixelDensity;
        for (var i in window._boundGraphicsMethods) {
          buf[i] = window._boundGraphicsMethods[i].bind(buf);
        }
        return buf;
      };
      
      // Event Handling
      var eventHandlers = [];
      var watchedEvents = {
        mouseMoved:["movementX","movementY"],
        mouseDragged:["movementX","movementY"],
        mousePressed:["offsetX","offsetY"],
        mouseReleased:["offsetX","offsetY"],
        mouseClicked:["offsetX","offsetY"],
        mouseWheel:["deltaX","deltaY"],
        keyPressed:["keyCode"],
        keyReleased:["keyCode"],
        keyTyped:["key"],
      };
      window.createEventHandler = function() {
        var obj = {};
        for (var i in watchedEvents) {
          obj[i] = function(){};
        };
        obj.fileDropped = function(){};
        obj.fileLoaded = function(){};
        obj.start = function() {
          eventHandlers.push(obj);
          return obj;
        };
        obj.stop = function() {
          var i = eventHandlers.indexOf(obj);
          if (i === -1) return;
          eventHandlers.splice(i,1);
          return obj;
        };
        obj.on = function(name,callback) {
          obj[name] = callback;
        }
        return obj;
      }
      for (var i in watchedEvents) (function(i){
        _renderer._pInst[i] = function(e) {
          var a = watchedEvents[i].map(function(l){
            return e[l];
          });
          a.push(e);
          for (var j = 0; j < eventHandlers.length; j++) {
            var h = eventHandlers[j];
            h[i].apply(h,a);
          }
        };
      })(i);
      _renderer.drop(function(e){
        console.log("File Loaded");
        prompt("If you see this, notify dragonfiregames!","Discord: dragonfire7z")
        for (var j = 0; j < eventHandlers.length; j++) {
          var h = eventHandlers[j];
          h.fileLoaded.apply(h,[e]);
        }
      },function(e){
        for (var j = 0; j < eventHandlers.length; j++) {
          var h = eventHandlers[j];
          h.fileDropped.apply(h,[e]);
        }
      });
      window.Event = createEventHandler().start();
      
      // Globals
      window.elementList = [];
      window.pageMap = {};
      window.activeElement = false;
      window.activePage = false;
      
      // Create a style
      window.defaultStyle = {
        padx:5,
        pady:5,
        fill:128,
        stroke:0,
        strokeWeight:1,
        shadow:false,
        shadowBlur:0,
        shadowStrength:1,
        shadowOffX:0,
        shadowOffY:0,
        textFill:0,
        textStroke:false,
        textStrokeWeight:1,
        textSize:12,
        textShadow:false,
        textShadowBlur:0,
        textShadowStrength:1,
        textShadowOffX:0,
        textShadowOffY:0,
        placeholderTextFill:100,
        placeholderStroke:false,
        horizAlign:"left",
        vertAlign:"top",
        font:"sans-serif",
        bevel:0,
        imageBevel:0,
        imageFit:"none",
        imageHorizAlign:"center",
        imageVertAlign:"center",
        cursor:0,
      };
      window.createStyle = function(parent,mod) {
        var obj = {};
        obj.parent = parent || defaultStyle;
        obj.data = {};
        for (var i in defaultStyle) (function(i) {
          Object.defineProperty(obj,i,{
            get: function() {
              if (obj.data[i] === undefined) return obj.parent[i];
              return obj.data[i];
            },
            set: function(e) {
              obj.data[i] = e;
            }
          });
        })(i);
        obj.set = function(o) {
          for (var i in o) {
            obj.data[i] = o[i];
          }
        }
        if (mod) obj.set(mod);
        return obj;
      };
      // Create a page
      var displayPage = "default";
      window.createPage = function(name,before,after,onopen,onclose) {
        var obj = {};
        obj.name = name;
        obj.before = before || function(){};
        obj.after = after || function(){};
        obj.onopen = onopen || function(){};
        obj.onclose = onclose || function(){};
        obj.elementList = [];
        obj.addElement = function(elem) {
          obj.elementList.push(elem);
          obj.elementList = obj.elementList.sort(function(a,b){return a.zindex - b.zindex;});
        };
        obj.removeElement = function(elem) {
          var i = obj.elementList.indexOf(elem);
          if (i == -1) return;
          obj.elementList.splice(i,1);
        };
        obj.Event = createEventHandler();
        pageMap[name] = obj;
        return obj;
      };
      window.setPage = function(name) {
        pageMap[displayPage].Event.stop();
        pageMap[displayPage].onclose();
        displayPage = name;
        activePage = pageMap[name];
        activePage.Event.start();
        pageMap[name].onopen();
      };
      window.defaultPage = createPage("default");
      setPage("default");
      // Create an element
      window.createElement = function(x,y,w,h,page) {
        var obj = {};
        obj.x = x || 0;
        obj.y = y || 0;
        obj.width = w === undefined ? 50 : w;
        obj.height = h === undefined ? 50 : h;
        obj.style = createStyle();
        obj.styles = ["style"];
        obj.setStyles = function(styler,mod) {
          for (var i = 0; i < obj.styles.length; i++) {
            var s = obj.styles[i];
            if (styler[s]) obj[s] = createStyle(styler[s],mod);
          }
        }
        obj.modifyStyles = function(mod) {
          for (var i = 0; i < obj.styles.length; i++) {
            var s = obj.styles[i];
            for (var j in mod) {
              obj[s][j] = mod[j];
            }
          }
        }
        obj.visible = true;
        obj._cache = {};
        obj.testhover = function() {
          return mouseRect(obj.x,obj.y,obj.width,obj.height);
        };
        obj.getmax = function() {
          return {x:obj.x+obj.w,y:obj.y+obj.h};
        };
        obj.body = function(s,b) {
          b.format(s.fill,s.stroke,s.strokeWeight);
          b.shadow(s.shadow,s.shadowBlur,s.shadowOffX,s.shadowOffY);
          for (var i = 0; i < Math.max(1,s.shadowStrength); i++) {
            b.rect(obj.x,obj.y,obj.width,obj.height,s.bevel);
          }
        };
        obj.debug = function(b) {
          b.format(false,"lime",1);
          b.rect(obj.x,obj.y,obj.width,obj.height);
        };
        obj.drawers = ["body"];
        obj.getStyle = function() {
          return obj.style;
        }
        obj.show = function(b) {
          if (!obj.visible) return;
          if (obj.drag) obj.drag(s,b);
          var s = obj.getStyle();
          b = b || window;
          for (var i = 0; i < obj.drawers.length; i++) {
            obj[obj.drawers[i]](s,b); 
          }
          if (obj.onshow) obj.onshow(s,b);
          if (debug.debug) obj.debug(b);
        };
        obj.addComponent = function(name) {
          var args = arguments.slice(1);
          args.unshift(obj);
          Components[name].apply(obj,args);
        };
        obj.addToPage = function(name) {
          pageMap[name].addElement(obj);
        }
        obj.removeFromPage = function(name) {
          pageMap[name].removeElement(obj);
        }
        obj.click = function() {
          if (obj.onclick) obj.onclick();
        };
        obj.zindex = 0;
        elementList.push(obj);
        pageMap[page||"default"].addElement(obj);
        return obj;
      };
      // Component List
      var Components = {};
      Components.ellipse = function(obj) {
        obj.testhover = function() {
          return mouseEllipse(obj.x+obj.width/2,obj.y+obj.height/2,obj.width,obj.height);
        };
        obj.body = function(s,b) {
          b.format(s.fill,s.stroke,s.strokeWeight);
          b.ellipseMode(CORNER);
          b.ellipse(obj.x,obj.y,obj.width,obj.height);
        };
      };
      Components.text = function(obj,txt) {
        obj._value = txt||"";
        Object.defineProperty(obj,"value",{
          get: function() {
            return obj._value;
          },
          set: function(e) {
            obj._value = e;
            obj.update(e);
          }
        });
        obj.placeholder = "";
        obj.password = false;
        obj.text = function(s,b) {
          var c = obj._cache;
          var ax = s.horizAlign;
          var ay = s.vertAlign;
          b.format(s.textFill,s.textStroke,s.textStrokeWeight,s.textSize,ax,"top");
          b.shadow(s.textShadow,s.textShadowBlur,s.textShadowOffX,s.textShadowOffY);
          b.textFont(s.font);
          var str = obj._value;
          if (obj.password) str = str.replace(/[^\n\s]/g,"*");
          if (!obj._value && obj.placeholder) {
            b.format(s.placeholderTextFill,s.placeholderTextStroke,s.textStrokeWeight,s.textSize,ax,ay);
            str = obj.placeholder;
          }
          var max, tx = s.padx, ty = s.pady;
          max = obj.width - tx;
          if (ax == "center") max -= tx;
          if (typeof tm == 'number') max = tm;
          c.max = max; c.tx = tx; c.ty = ty;
          for (var i = 0; i < Math.max(1,s.textShadowStrength); i++) {
            b.text(str,obj.x+tx,obj.y+ty+c.ydiff,max);
          }
        };
        obj.update = function() {
          obj.text(obj.style,window);
          obj.updateSizes();
        }
        obj.updateSizes = function() {
          var s = obj.style;
          var c = obj._cache;
          obj.lines = getLines(obj._value,c.max);
          obj.textHeight = obj.lines.length*(s.textSize*1.25);
          obj.textWidth = textWidth(obj.lines[obj.lines.length-1]);
          
          var ay = s.vertAlign;
          c.ydiff = 0;
          if (ay != "top") {
            c.ydiff = obj.height - obj.textHeight;
            if (ay == "center") c.ydiff /= 2;
          }
        }
        obj.drawers.push("text");
        obj.getmax = function() {
          var c = obj._cache;
          var m1x = obj.x+obj.w, m1y = obj.y+obj.h;
          return {x:max(m1x,obj.textWidth||0),y:max(m1y,obj.textHeight||0)};
        };
        setTimeout(function(){
          obj.update();
        },0);
      };
      Components.button = function(obj) {
        obj.hoverStyle = createStyle(obj.style);
        obj.styles.push("hoverStyle");
        obj.clickStyle = createStyle(obj.hoverStyle);
        obj.styles.push("clickStyle");
        obj.getStyle = function() {
          var s = obj.style;
          if (obj.hover) {
            if (mouseDown("left")) s = obj.clickStyle;
            else s = obj.hoverStyle;
          }
          return s;
        }
        obj.hover = false;
        obj.onclick = function(){};
        obj.onunclick = function(){};
        obj.interactive = true;
        obj.changecursor = true;
      };
      Components.toggle = function(obj,toggle) {
        Components.button(obj);
        obj.styleOn = createStyle(obj.style);
        obj.styles.push("styleOn");
        obj.hoverStyleOn = createStyle(obj.hoverStyle);
        obj.styles.push("hoverStyleOn");
        obj.clickStyleOn = createStyle(obj.clickStyle);
        obj.styles.push("clickStyleOn");
        obj.getStyle = function() {
          if (obj.toggle) {
            var s = obj.styleOn;
            if (obj.hover) {
              if (mouseDown("left")) s = obj.clickStyleOn;
              else if (obj.hoverStyle) s = obj.hoverStyleOn;
            }
          } else {
            var s = obj.style;
            if (obj.hover) {
              if (mouseDown("left")) s = obj.clickStyle;
              else if (obj.hoverStyle) s = obj.hoverStyle;
            }
          }
          return s;
        }
        obj.toggle = toggle || false;
        obj.click = function() {
          obj.toggle = !obj.toggle;
          obj.onclick(obj.toggle);
        };
      };
      Components.image = function(obj,url,callback,failure) {
        if (typeof url == 'string') obj.img = loadImage(url,function(img) {
          if (obj.width === 0) obj.width = img.width;
          if (obj.height === 0) obj.height = img.height;
        },failure);
        else obj.img = url;
        obj.image = function(s,b) {
          var f = s.imageFit;
          var ax = s.imageHorizAlign;
          var ay = s.imageVertAlign;
          var bev = s.imageBevel;
          if (f == "inside") {
            var bb = centerRect(obj.width,obj.height,obj.img.width,obj.img.height);
            if (ax == "left") bb.x = 0;
            if (ax == "right") bb.x = obj.width-bb.w;
            if (ay == "top") bb.y = 0;
            if (ay == "bottom") bb.y = obj.height-bb.h;
            b.beveledImage(obj.img,obj.x+bb.x,obj.y+bb.y,bb.w,bb.h,bev);
          }
          else if (f == "outside") {
            var bb = centerRect(obj.img.width,obj.img.height,obj.width,obj.height);
            if (ax == "left") bb.x = 0;
            if (ax == "right") bb.x = obj.img.width-bb.w;
            if (ay == "top") bb.y = 0;
            if (ay == "bottom") bb.y = obj.img.height-bb.h;
            b.beveledImage(obj.img,bb.x,bb.y,bb.w,bb.h,obj.x,obj.y,obj.width,obj.height,bev);
          }
          else b.beveledImage(obj.img,obj.x,obj.y,obj.width,obj.height,bev);
        };
        obj.drawers.push("image");
      };
      Components.input = function(obj,txt) {
        Components.text(obj,txt);
        Components.button(obj);
        obj.activeStyle = createStyle(obj.clickStyle);
        obj.styles.push("activeStyle");
        obj.getStyle = function() {
          var s = obj.style;
          if (obj.hover) {
            if (mouseDown("left")) s = obj.clickStyle;
            else if (obj.hoverStyle) s = obj.hoverStyle;
          }
          if (activeElement == obj) s = obj.activeStyle;
          return s;
        }
        obj.cursor = function(s,b) {
          if (isMobile) return;
          var c = obj._cache;
          if ((Date.now()-_startTime) % 1000 < 500 && activeElement == obj) return;
          b.noStroke();
          b.fill(s.cursor);
          b.text("|",obj.x+c.cursorX,obj.y+c.cursorY-0.5);
        };
        obj.drawers.push("cursor");
        obj.calculateCursorPos = function() {
          var s = obj.style, c = obj._cache;
          c.cursorY = (obj.beflines.length-1)*(s.textSize*1.25)+c.ty;
          c.cursorX = textWidth(obj.beflines[obj.beflines.length-1])+c.tx;
          
          var ax = s.horizAlign;
          c.xdiff = 0;
          if (ax != "left") {
            var total = textWidth(obj.lines[obj.beflines.length-1]);
            c.xdiff = c.max - total;
            if (ax == "center") c.xdiff /= 2;
          }
          c.cursorX += c.xdiff;
          c.cursorY += c.ydiff;
        };
        obj.cursorPos = obj._value.length;
        obj.click = function() {
          obj.onclick();
          if (isMobile) {
            var str = JSON.stringify(obj._value);
            str = str.substring(1,str.length-1);
            str = prompt(obj.placeholder||"Enter text:",str);
            if (str === null) return;
            str = str.replace(/"/g,"\\\"");
            obj._value = JSON.parse("\""+str+"\"");
            obj.update();
            obj.onchange();
            obj.onunfocus();
            return;
          }
          var s = obj.style, c = obj._cache;
          activeElement = obj;
          var lines = obj.lines;
          var dy = mouseY - obj.y - c.ty - c.ydiff;
          var li = constrain(round(dy/(s.textSize*1.25)),0,lines.length);
          //
          var last = lines[li]||"";
          var xdiff = 0;
          var ax = s.horizAlign;
          if (ax != "left") {
            xdiff = c.max - textWidth(last);
            if (ax == "center") xdiff /= 2;
          }
          var dx = mouseX - obj.x - c.tx - xdiff;
          var e = textWidth("_");
          var cpos = min(round(dx/e),last.length);
          while (textWidth(last.substring(0,cpos))>dx && cpos >= 0) cpos--;
          while (textWidth(last.substring(0,cpos))<dx-e/2 && cpos < last.length) cpos++;
          for (var i = 0; i < li; i++) {
            cpos += lines[i].length;
          }
          cpos += (obj._value.substring(0,cpos).match(/\n/g)||[]).length;
          obj.cursorPos = cpos;
          obj.update();
          obj._cache.val = obj._value;
          obj.onfocus();
        };
        obj.onclick = function() {};
        obj.update = function(e) {
          obj.text(obj.style,window);
          obj.updateSizes();
          var bef = [];
          var c = obj.cursorPos;
          for (var i = 0; i < obj.lines.length; i++) {
            bef.push(obj.lines[i]);
            c -= obj.lines[i].length;
            if (c < 0) break;
          }
          bef[bef.length-1] = bef[bef.length-1].substring(0,bef[bef.length-1].length+c);
          obj.beflines = bef;
          obj.calculateCursorPos();
          obj.oninput(e);
        };
        obj.validateinput = function() {};
        obj.oninput = function() {};
        obj.onfocus = function() {};
        obj.onunfocus = function() {};
        obj.onchange = function() {};
      };
      Components.canvas = function(obj,gl) {
        obj.buff = createGraphics(obj.width,obj.height,gl);
        obj._cache.width = obj.width;
        obj._cache.height = obj.height;
        obj.children = [];
        obj.canvas = function(s,b) {
          obj.buff.clear();
          if (obj._cache.width != obj.width || obj._cache.width != obj.width) {
            obj.buff = createGraphics(obj.width,obj.height);
          }
          b.mouseFrame(obj.buff,obj.x,obj.y,obj.width,obj.height);
          obj.draw(obj.buff,s);
          obj.buff.shadow(false);
          b.image(obj.buff,obj.x,obj.y,obj.width,obj.height);
        };
        obj.drawers.push("canvas");
      };
      Components.div = function(obj) {
        Components.canvas(obj);
        obj.children = [];
        obj.appendChild = function(elem) {
          elem.parent = obj;
          obj.children.push(elem);
          obj.children = obj.children.sort(function(a,b){return a.zindex - b.zindex;});
        };
        obj.prependChild = function(elem) {
          elem.parent = obj;
          obj.children.shift(elem);
          obj.children = obj.children.sort(function(a,b){return a.zindex - b.zindex;});
        };
        obj.removeChild = function(elem) {
          var i = obj.children.indexOf(elem);
          if (i == -1) return;
          delete elem.parent;
          obj.children.splice(i,1);
        };
        obj.removeChildAtIndex = function(i) {
          if (!obj.children[i]) return;
          delete obj.children[i].parent;
          obj.children.splice(i,1);
        };
        obj.beforedraw = function(){};
        obj.afterdraw = function(){};
        obj.draw = function(b,s) {
          b.translate(-obj.scrollX,-obj.scrollY);
          obj.afterdraw(b);
          for (var i = 0; i < obj.children.length; i++) {
            obj.children[i].show(b);
          }
          obj.beforedraw(b);
          b.translate(obj.scrollX,obj.scrollY);
        };
        obj.scrollX = 0;
        obj.scrollY = 0;
        obj.minScrollX = 0;
        obj.minScrollY = 0;
        obj.maxScrollX = 0;
        obj.maxScrollY = 0;
        obj.onscroll = function(){};
        obj.scroll = function(x,y) {
          if (x == 0 && y == 0) return;
          var px = obj.scrollX, py = obj.scrollY;
          obj.scrollX = constrain(px+x,obj.minScrollX,obj.maxScrollX);
          obj.scrollY = constrain(py+y,obj.minScrollY,obj.maxScrollY);
          px -= obj.scrollX;
          py -= obj.scrollY;
          if (px != 0 || py != 0) obj.onscroll(px,py);
        };
        obj.div = true;
      };
      Components.draggable = function(obj) {
        Components.button(obj);
        obj.getStyle = function() {
          var s = obj.style;
          if (obj.hover) {
            if (mouseDown("left")) s = obj.clickStyle;
            else if (obj.hoverStyle) s = obj.hoverStyle;
          }
          if (activeElement == obj) s = obj.clickStyle;
          return s;
        }
        obj.dragX = true;
        obj.dragY = true;
        obj.minX = -Infinity;
        obj.maxX = Infinity;
        obj.minY = -Infinity;
        obj.maxY = Infinity;
        obj.drag = function(s,b) {
          if (activeElement != obj) return;
          if (obj.dragX) {
            obj.x += mouseX-pmouseX;
            obj.x = constrain(obj.x,obj.minX,obj.maxX);
          }
          if (obj.dragY) {
            obj.y += mouseY-pmouseY;
            obj.y = constrain(obj.y,obj.minY,obj.maxY);
          }
        }
        obj.ongrab = function() {};
        obj.onrelease = function(){};
        obj.click = function() {
          obj.onclick();
          activeElement = obj;
          obj.ongrab();
        };
        obj.onclick = function() {};
        obj.onunclick = function(){
          activeElement = false;
          obj.onrelease();
        };
      };
      Components.link = function(obj,url,pmt) {
        obj.url = url;
        obj.prompt = pmt || "Paste this URL in new tab:";
        obj.icon = loadIcon(url);
        obj.hoverExtra = 5;
        obj.body = function(s,b) {
          if (obj.hover) {
            var s = obj.hoverExtra;
            b.image(obj.icon,obj.x-s/2,obj.y-s/2,obj.width+s,obj.height+s);
          } else {
            b.image(obj.icon,obj.x,obj.y,obj.width,obj.height);
          }
        };
        obj.hover = false;
        obj.onclick = function() {
          prompt(obj.prompt, obj.url);
          obj.onopen();
        };
        obj.onopen = function() {};
        obj.onunclick = function(){};
        obj.interactive = true;
        delete obj.style;
      };
      window.createComponent = function(name,callback) {
        Components[name] = callback;
      };
      
      // Links
      var linkIcon = loadImage("https://cdn-icons-png.flaticon.com/512/3214/3214746.png");
      var linkIcons = {
        "discord." : loadImage("https://cdn3.iconfinder.com/data/icons/popular-services-brands-vol-2/512/discord-512.png"),
        "youtube.com" : loadImage("https://www.iconpacks.net/icons/2/free-youtube-logo-icon-2431-thumb.png"),
        "youtu.be" : loadImage("https://www.iconpacks.net/icons/2/free-youtube-logo-icon-2431-thumb.png"),
        "spotify.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/8/84/Spotify_icon.svg/1982px-Spotify_icon.svg.png"),
        "code.org" : loadImage("https://studio.code.org/assets/logo-2acd4ebc69c447786b866b98034bb3c0777b5f67cd8bd7955e97bba0b16f2bd1.svg"),
        "pumpkin-smasher" : loadImage("https://pumpkin-smasher-2.onrender.com/assets/pumpkins/objective.png"),
        "sites.google.com/view/dragonfiregames" : loadImage("https://i.ibb.co/PGFTDGy/ship.png"),
        /*
        "google.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/1200px-Google_%22G%22_Logo.svg.png"),
        "reddit.com" : loadImage("https://upload.wikimedia.org/wikipedia/en/b/bd/Reddit_Logo_Icon.svg"),
        "twitch.tv" : loadImage("https://cdn-icons-png.flaticon.com/512/5968/5968819.png"),
        "twitter.com" : loadImage("https://cdn-icons-png.flaticon.com/512/3536/3536424.png"),
        "facebook.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/2021_Facebook_icon.svg/2048px-2021_Facebook_icon.svg.png"),
        "instagram.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Instagram_logo_2022.svg/1200px-Instagram_logo_2022.svg.png"),
        "snapchat.com" : loadImage("https://play-lh.googleusercontent.com/KxeSAjPTKliCErbivNiXrd6cTwfbqUJcbSRPe_IBVK_YmwckfMRS1VIHz-5cgT09yMo"),
        "tiktok.com" : loadImage("https://play-lh.googleusercontent.com/Ui_-OW6UJI147ySDX9guWWDiCPSq1vtxoC-xG17BU2FpU0Fi6qkWwuLdpddmT9fqrA=w240-h480-rw"),
        "outlook.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/d/df/Microsoft_Office_Outlook_%282018%E2%80%93present%29.svg/2203px-Microsoft_Office_Outlook_%282018%E2%80%93present%29.svg.png"),
        "gmail.com" : loadImage("https://freelogopng.com/images/all_img/1657906383gmail-icon-png.png"),
        "roblox.com" : loadImage("https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/c3207cf8-6638-4073-979c-092861231689/de2fzyg-29a1af41-1242-46f4-a33b-6f8a6ae2e1ef.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7InBhdGgiOiJcL2ZcL2MzMjA3Y2Y4LTY2MzgtNDA3My05NzljLTA5Mjg2MTIzMTY4OVwvZGUyZnp5Zy0yOWExYWY0MS0xMjQyLTQ2ZjQtYTMzYi02ZjhhNmFlMmUxZWYucG5nIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmZpbGUuZG93bmxvYWQiXX0.1XioYcpOV0zu8MOTbrQBWT4qFjx1l0lt91tlis6hvkc"),
        "microsoft.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/4/44/Microsoft_logo.svg/2048px-Microsoft_logo.svg.png"),
        "prodigygame.com" : loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Prodigy_icon_2020.svg/1200px-Prodigy_icon_2020.svg.png"),
        */
      };
      var cachedIcons = {};
      window.loadIcon = function(url) {
        for (var i in linkIcons) {
          if (url.includes(i)) return linkIcons[i];
        }
        var domain = url.match(/^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n?]+)/img);
        if (cachedIcons[domain]) return cachedIcons[domain];
        cachedIcons[domain] = linkIcon;
        loadImage("https://www.google.com/s2/favicons?sz=64&domain="+domain,function(img){
          cachedIcons[domain] = img;
        });
        return linkIcon;
      };
      
      // Draw elements
      window.drawElements = function() {
        cursor(ARROW);
        clickerElement(function(obj) {
          if (obj.onclick && obj.changecursor) {
            if (obj.cursor) cursor(TEXT);
            else cursor(HAND);
          }
          if (!obj.div) return;
          var sx = 0, sy = 0;
          if (mouseDown("left") && !activeElement.drag) {
            sx += pmouseX-mouseX;
            sy += pmouseY-mouseY;
          }
          if (keyDown("left")) sx -= 5;
          if (keyDown("right")) sx += 5;
          if (keyDown("up")) sy -= 5;
          if (keyDown("down")) sy += 5;
          obj.scroll(sx,sy);
        });
        mouseX += camera.x-200;
        mouseY += camera.y-200;
        activePage.before();
        for (var i = 0; i < activePage.elementList.length; i++) {
          var obj = activePage.elementList[i];
          if (obj.parent) continue;
          obj.show();
        }
        shadow(false);
        activePage.after();
        mouseX -= camera.x-200;
        mouseY -= camera.y-200;
      };
      var ElementHandler = createEventHandler().start();
      ElementHandler.mousePressed = function() {
        var celem = activeElement;
        activeElement = false;
        clickerElement(function(obj) {
          obj.click();
        });
        if (celem != activeElement && celem.cursor) {
          if (celem._cache.val !== celem._value) celem.onchange(celem._value);
          celem.onunfocus();
        }
      };
      ElementHandler.mouseReleased = function() {
        clickerElement(function(obj) {
          if (!obj.onunclick) return;
          obj.onunclick();
        });
      };
      function addtoinput(inp,def) {
        var k = activeElement.validateinput(inp);
        if (k === false) return;
        if (k === undefined) k = inp||def;
        activeElement._value = activeElement._value.substring(0, activeElement.cursorPos) + k + activeElement._value.substring(activeElement.cursorPos, activeElement._value.length);
        activeElement.cursorPos += k.length;
        activeElement.update(k);
      };
      ElementHandler.keyTyped = function() {
        if (!activeElement) return;
        if (key == "\r") return;
        var oldkey = key;
        onHold(keyCode,key,function(){
          addtoinput(key,oldkey);
        });
      };
      ElementHandler.keyPressed = function() {
        if (!activeElement || !activeElement.cursor) return;
        onHold(BACKSPACE,"backspace",function() {
          if (activeElement.validateinput("backspace") === false) return;
          activeElement._value = activeElement._value.substring(0, activeElement.cursorPos-1) + activeElement._value.substring(activeElement.cursorPos, activeElement._value.length);
          activeElement.cursorPos--;
          activeElement.cursorPos = max(0,activeElement.cursorPos);
          activeElement.update("backspace");
        });
        onHold(LEFT_ARROW,"left",function() {
          activeElement.cursorPos = max(activeElement.cursorPos-1,0);
          activeElement.update("left");
        });
        onHold(RIGHT_ARROW,"right",function() {
          activeElement.cursorPos = min(activeElement.cursorPos+1,activeElement._value.length);
          activeElement.update("right");
        });
        if (keyCode == ENTER) {
          addtoinput("\n");
          return;
        }
        var lines, cursorLine, cursorOffset;
        var updateLines = function() {
          activeElement.update();
          lines = activeElement.lines;
          cursorLine = activeElement.beflines.length-1;
          cursorOffset = activeElement.cursorPos;
          for (var i = 0; i < cursorLine; i++) {
            cursorOffset -= lines[i].length+1;
          }
        }
        onHold(UP_ARROW,"up",function() {
          updateLines();
          if (cursorLine <= 0) return;
          var newPos = 0;
          for (var i = 0; i < cursorLine-1; i++) {
            newPos += lines[i].length+1;
          }
          activeElement.cursorPos = newPos + min(cursorOffset,lines[cursorLine-1].length);
        });
        onHold(DOWN_ARROW,"down",function() {
          updateLines();
          if (cursorLine >= lines.length-1) return;
          var newPos = 0;
          for (var i = 0; i < cursorLine+1; i++) {
            newPos += lines[i].length+1;
          }
          activeElement.cursorPos = newPos + min(cursorOffset,lines[cursorLine+1].length);
        });
        if (keyCode == 86 && keyDown(CONTROL)) {
          var pasted = prompt("Paste here:");
          if (!pasted) return;
          pasted = pasted.replace(/\\n/g,"\n");
          addtoinput(pasted);
        }
        if (keyCode == 67 && keyDown(CONTROL)) {
          prompt("Copy here:",activeElement._value.replace(/\n/g,"\\n"));
        }
      };
      ElementHandler.mouseWheel = function(dx,dy) {
        clickerElement(function(obj) {
          if (!obj.scroll) return;
          obj.scroll(dx,dy);
        });
      };
      function clickerElement(callback) {
        mouseX += camera.x-200;
        mouseY += camera.y-200;
        var done = false;
        var hov = function(obj) {
          if (!obj.visible || (!obj.interactive && !obj.div)) return;
          var h = !done && obj.testhover();
          obj.hover = h;
          if (obj.div) {
            if (!h) return;
            mouseX -= obj.x - obj.scrollX;
            mouseY -= obj.y - obj.scrollY;
            for (var j = obj.children.length-1; j >= 0; j--) {
              hov(obj.children[j]);
            }
            mouseX += obj.x - obj.scrollX;
            mouseY += obj.y - obj.scrollY;
          } else {
            if (h) done = true;
          }
          if (h) callback(obj);
        };
        for (var i = activePage.elementList.length-1; i >= 0; i--) {
          var obj = activePage.elementList[i];
          if (obj.parent) continue;
          hov(obj);
        }
        mouseX -= camera.x-200;
        mouseY -= camera.y-200;
      };
      function onHold(kc,k,funct) {
        if (keyCode != kc && key != k) return;
        funct();
        setTimeout(function() {
          if (!keyDown(k)) return;
          funct();
          var int = setInterval(function() {
            if (!keyDown(k)) {
              clearInterval(int);
              return;
            }
            funct();
          },100);
        },600);
      };
      
      // Gradients
      var gbuf = _createGraphics(400,400);
      gbuf.noStroke();
      gbuf.angleMode(DEGREES);
      gbuf.translate(200,200);
      gbuf.scale(400*SQRT2,400*SQRT2);
      window.Gradient = {};
      Gradient.density = 400;
      Gradient.linear = function(angle,callback,density) {
        density = density || Gradient.density;
        gbuf.push();
        gbuf.rotate(angle);
        for (var i = 0; i < density; i++) {
          var t0 = i/density;
          var t1 = (i+1)/density;
          gbuf.fill(callback(t1));
          gbuf.rect(t0-0.5,-0.5,t1,1);
        }
        gbuf.pop();
        return gbuf.get(0,0,400,400);
      }
      Gradient.radial = function(cx,cy,callback,density) {
        cx /= 2*SQRT2;
        cy /= 2*SQRT2;
        density = density || Gradient.density;
        gbuf.background(callback(1));
        gbuf.push();
        for (var i = 0; i < density; i++) {
          var t = 1-i/density;
          gbuf.fill(callback(t));
          gbuf.ellipse(cx,cy,t,t);
        }
        gbuf.pop();
        return gbuf.get(0,0,400,400);
      }
      Gradient.conic = function(cx,cy,callback,density) {
        cx /= 2*SQRT2;
        cy /= 2*SQRT2;
        density = density || Gradient.density;
        gbuf.push();
        for (var i = 0; i < density; i++) {
          var t0 = i/density;
          var t1 = (i+1)/density;
          var c = callback(t1);
          gbuf.fill(c);
          gbuf.stroke(c);
          gbuf.strokeWeight(0.001);
          gbuf.beginShape(TRIANGLES);
          gbuf.vertex(cx,cy);
          gbuf.vertex(cos(t0*360),sin(t0*360));
          gbuf.vertex(cos(t1*360),sin(t1*360));
          gbuf.endShape();
        }
        gbuf.pop();
        return gbuf.get(0,0,400,400);
      }
      
      // Color Interpolation
      window.simpleInterp = function(c1,c2,easing) {
        if (typeof c1 !== 'object') c1 = color(c1);
        if (typeof c2 !== 'object') c2 = color(c2);
        return function(t) {
          if (easing) t = easing(t);
          return lerpColor(c1,c2,t);
        };
      }
      window.colorStopInterp = function(stopList,colorList,easeList) {
        for (var j = 0; j < colorList.length; j++) {
          if (typeof colorList[j] !== 'object') colorList[j] = color(colorList[j]);
        }
        return function(t) {
          for (var i = 0; i < stopList.length-1; i++) {
            if (stopList[i] <= t && t <= stopList[i+1]) {
              t = (t-stopList[i])/(stopList[i+1]-stopList[i]);
              break;
            }
          }
          if (easeList && easeList[i]) t = easeList[i](t);
          return lerpColor(colorList[i],colorList[i+1],t);
        };
      };
      
      // Avatars
      var circleMask = _createGraphics(128,128);
      circleMask.noStroke();
      circleMask.ellipse(64,64,128,128);
      circleMask = circleMask.get();
      _bindGraphicsMethod("avatarImage",function(img,type,x,y,d) {
        if (img.width === 1 || img.height === 1) return;
        var n = "avatar"+type;
        if (!img[n]) {
          var minside = min(img.width,img.height);
          var bufferX = img.width-minside;
          var bufferY = img.height-minside;
          var img1 = img.get(bufferX/2,bufferY/2,minside,minside);
          if (type == "circle") img1.mask(circleMask);
          img[n] = img1;
        }
        this.image(img[n],x-d/2,y-d/2,d,d);
      });
      
      // FANIM Fire Animations
      // By: DragonFireGames
      // Version: 1.3
      // Description: Animation functions
      
      window.SequenceList = [];
      // Creates an animation sequence
      window.createSequence = function(length) {
        var seq = {};
        // Animation Handlers
        seq.animations = [];
        // Creates Animations
        seq.createAnimation = function(start,end,drawer,interp) {
          var len = end-start;
          interp = interp || function(t){return t};
          seq.animations.push(function(time){
            if (time > end || time < start) return;
            drawer(interp((time-start)/len),time);
          });
        };
        //
        seq.length = length; // in ms
        // Callbacks
        seq.before = function() {};
        seq.after = function() {};
        seq.onstart = function() {};
        seq.onend = function() {};
        // Draws Animations
        seq.draw = function() {
          if (!seq.startTime) return;
          var time = Date.now()-seq.startTime;
          seq.before(time);
          for (var i = 0; i < seq.animations.length; i++) {
            seq.animations[i](time);
          }
          seq.after(time);
          if (seq.length && time >= seq.length) seq.end();
        };
        seq.start = function(time) {
          time = time || 0;
          seq.onstart(time);
          seq.startTime = Date.now()+time;
        };
        seq.end = function() {
          seq.onend();
          delete seq.startTime;
        };
        SequenceList.push(seq);
        return seq;
      }
      // Draw animation sequences
      window.drawSequences = function() {
        for (var i = 0; i < SequenceList.length; i++) {
          SequenceList[i].draw();
        }
      }
      
      //easings from: https://easings.net
      window.Ease = {
        in: {
          line: function(t) {
            return t;
          },
          sine: function(t) {
            return 1-Math.cos((t*Math.PI)/2);
          },
          quad: function(t) {
            return t*t;
          },
          cubic: function(t) {
            return t*t*t;
          },
          quart: function(t) {
            return t*t*t*t;
          },
          quint: function(t) {
            return t*t*t*t*t;
          },
          expo: function(t) {
            if (t === 0) return 0;
            return Math.pow(2,10*t-10);
          },
          circ: function(t) {
            return 1-Math.sqrt(1-t*t);
          },
          back: function(t) {
            var c1 = 1.70158;
            var c3 = c1 + 1;  
            return c3*t*t*t-c1*t*t;
          },
          elastic: function(t) {
            if (t === 0 || t === 1) return t;
            var c4 = (2 * Math.PI) / 3;
            return -Math.pow(2,10*t-10)*Math.sin((t*10-10.75)*c4);
          },
          bounce: function(t) {
            return 1-Ease.out.bounce(1-t);
          },
        },
        out: {
          line: function(t) {
            return 1-t;
          },
          sine: function(t) {
            return Math.sin((t*Math.PI)/2);
          },
          quad: function(t) {
            t = 1-t;
            return 1-t*t;
          },
          cubic: function(t) {
            t = 1-t;
            return 1-t*t*t;
          },
          quart: function(t) {
            t = 1-t;
            return 1-t*t*t*t;
          },
          quint: function(t) {
            t = 1-t;
            return 1-t*t*t*t*t;
          },
          expo: function(t) {
            if (t === 1) return 1;
            return 1-Math.pow(2,-10*t);
          },
          circ: function(t) {
            t = 1-t;
            return Math.sqrt(1-t*t);
          },
          back: function(t) {
            var c1 = 1.70158;
            var c3 = c1 + 1;
            t = t-1;
            return 1+c3*t*t*t-c1*t*t;
          },
          elastic: function(t) {
            if (t === 0 || t === 1) return t;
            var c4 = (2 * Math.PI) / 3;
            return -Math.pow(2,10*t-10)*Math.sin((t*10-10.75)*c4);
          },
          bounce: function(t) {
            var n1 = 7.5625;
            var d1 = 2.75;
            if (t < 1/d1) {
              return n1*t*t;
            } else if (t < 2/d1) {
              return n1*(t-=1.5/d1)*t+0.75;
            } else if (t < 2.5/d1) {
              return n1*(t-=2.25/d1)*t+0.9375;
            } else {
              return n1*(t-=2.625/d1)*t+0.984375;
            }
          },
        },
        inout: {
          line: function(t) {
            return t;
          },
          sine: function(t) {
            return -(Math.cos(Math.PI*t)-1)/2;
          },
          quad: function(t) {
            if (t < 0.5) return 2*t*t;
            var x = -2*t+2;
            return 1-x*x/2;
          },
          cubic: function(t) {
            if (t < 0.5) return 4*t*t*t;
            var x = -2*t+2;
            return 1-x*x*x/2;
          },
          quart: function(t) {
            if (t < 0.5) return 8*t*t*t*t;
            var x = -2*t+2;
            return 1-x*x*x*x/2;
          },
          quint: function(t) {
            if (t < 0.5) return 16*t*t*t*t*t;
            var x = -2*t+2;
            return 1-x*x*x*x*x/2;
          },
          expo: function(t) {
            if (t === 0 || t === 1) return t;
            if (t < 0.5) return Math.pow(2,20*t-10)/2;
            else return (2-Math.pow(2,-20*t+10))/2;
          },
          circ: function(t) {
            if (t < 0.5) return (1-Math.sqrt(1-4*t*t))/2
            var x = -2*x+2;
            return (Math.sqrt(1-x*x)+1)/2;
          },
          back: function(t) {
            var c1 = 1.70158;
            var c2 = c1 * 1.525;
            if (t < 0.5) return (4*t*t*((c2+1)*2*t-c2))/2;
            var x = -2*x+2;
            return (x*x*((c2+1)*(t*2-2)+c2)+2)/2;
          },
          elastic: function(t) {
            if (t === 0 || t === 1) return t;
            var c5 = (2 * Math.PI) / 4.5;
            if (t < 0.5) return -(Math.pow(2,20*t-10)*Math.sin((20*t-11.125)*c5))/2;
            return (Math.pow(2,-20*t+10)*Math.sin((20*t-11.125)*c5))/2+1;
          },
          bounce: function(t) {
            if (t < 0.5) return 1-Ease.out.bounce(1-2*t)/2;
            return 1+Ease.out.bounce(2*t-1)/2;
          }
        },
        outin: {
          line: function(t) {
            return 1-t;
          },
          sine: function(t) {
            return 1-Ease.inout.sine(1-t);
          },
          quad: function(t) {
            return 1-Ease.inout.quad(1-t);
          },
          cubic: function(t) {
            return 1-Ease.inout.cubic(1-t);
          },
          quart: function(t) {
            return 1-Ease.inout.quart(1-t);
          },
          quint: function(t) {
            return 1-Ease.inout.quint(1-t);
          },
          expo: function(t) {
            return 1-Ease.inout.expo(1-t);
          },
          circ: function(t) {
            return 1-Ease.inout.circ(1-t);
          },
          back: function(t) {
            return 1-Ease.inout.back(1-t);
          },
          elastic: function(t) {
            return 1-Ease.inout.elastic(1-t);
          },
          bounce: function(t) {
            return 1-Ease.inout.bounce(1-t);
          }
        },
      }
      // https://cubic-bezier.com/
      window.cubicBezier = function(p1x,p1y,p2x,p2y) {
        //https://probablymarcus.com/blocks/2015/02/26/using-bezier-curves-as-easing-functions.html
        // Horner's method:
        var cx = 3*p1x;
        var bx = 3*(p2x-p1x)-cx;
        var ax = 1-cx-bx;
        var Bx = function(t) {
          return ((ax*t+bx)*t+cx)*t;
        };
        var BxPrime = function(t) {
          return 3*ax*t*t + 2*bx*t + cx;
        };
        var cy = 3*p1y;
        var by = 3*(p2y-p1y)-cy;
        var ay = 1-cy-by;
        var By = function(t) {
          return ((ay*t+by)*t+cy)*t;
        };
        var invBx = function(t) {
          // Newton method
          var x0 = t;
          for (var i = 0; i < 8; i++) {
            var y0 = Bx(x0)-t;
            if (abs(y0) <= 0.001) return x0;
            x0 -= y0/BxPrime(x0);
          }
          // Otherwise do bisection:
          var a = 0;
          var b = 1;
          x0 = 0.5;
          var step = 0.25;
          for (var i = 0; i < 16; i++) {
            y0 = Bx(x0)-t;
            if (abs(y0) <= 0.001) return x0;
            if (sign(y0) == sign(Bx(a)-t)) a = x0;
            else b = x0;
            x0 = (a+b)/2;
          }
          return x0;
        };
        return function(t) {
          return By(invBx(t));
        }
      }
      
      // ----
      // Misc
      // ----
      
      // Transforms
      _bindGraphicsMethod("transformWorldToCanvas",function(x,y) {
        // from world to canvas space
        if (typeof x == "object") {
          y = x.y;
          x = x.x;
        }
        var s = this._pixelDensity; // Get pixel density
        var matrix = this._renderer.drawingContext.getTransform(); // This is a DOMMatrix
        matrix = matrix.invertSelf(); // Invert the matrix
        var p = matrix.transformPoint({x:x*s,y:y*s,z:0,w:1}); // Transform the point
        return {x:p.x,y:p.y};
      });
      _bindGraphicsMethod("transformCanvasToWorld",function(x,y) {
        // from canvas to world space
        if (typeof x == "object") {
          y = x.y;
          x = x.x;
        }
        var s = this._pixelDensity; // Get pixel density
        var matrix = this._renderer.drawingContext.getTransform(); // This is a DOMMatrix
        var p = matrix.transformPoint({x:x,y:y,z:0,w:1}); // Transform the point
        return {x:p.x/s,y:p.y/s};
      });
      _bindGraphicsMethod("transformBufferToCanvas",function(buf,x,y,width,height,px,py) {
        // from canvas to world space
        if (typeof px == "object") {
          py = px.y;
          px = px.x;
        }
        px = px/buf.twidth*width+x;
        py = py/buf.theight*height+y;
        return {x:px,y:py};
      });
      _bindGraphicsMethod("transformCanvasToBuffer",function(buf,x,y,width,height,px,py) {
        // from canvas to world space
        if (typeof px == "object") {
          py = px.y;
          px = px.x;
        }
        px = (px-x)*buf.twidth/width;
        py = (py-y)*buf.theight/height;
        return {x:px,y:py};
      });
      // Mouse Hovering
      _bindGraphicsMethod("mouseFrame",function(buf,x,y,width,height) {
        var p = this.transformWorldToCanvas(this.mouseX,this.mouseY);
        buf.hover = (
          p.x > x &&
          p.x < x+width &&
          p.y > y && 
          p.y < y+height
        );
        p = this.transformCanvasToBuffer(buf,x,y,width,height,p);
        buf.mouseX = p.x;
        buf.mouseY = p.y;
      });
      _bindGraphicsMethod("mouseRect",function(x,y,width,height) {
        if (this.hover === false) return false;
        var p = this.transformWorldToCanvas(this.mouseX,this.mouseY);
        return (
          p.x > x &&
          p.x < x+width &&
          p.y > y && 
          p.y < y+height
        );
      });
      _bindGraphicsMethod("mouseEllipse",function(cx,cy,width,height) {
        if (this.hover === false) return false;
        var p = this.transformWorldToCanvas(this.mouseX,this.mouseY);
        var dx = (p.x - cx) / width;
        var dy = (p.y - cy) / (height||width);
        var dist = dx*dx + dy*dy;
        return dist < 0.25;
      });
      // Format Anything
      _bindGraphicsMethod("format",function(f,s,sw,ts,tax,tay) {
        if (!f && f !== 0) this.noFill();
        else this.fill(f);
        if (!s && s !== 0) this.noStroke(s);
        else this.stroke(s);
        this.strokeWeight(sw||1);
        this.textSize(ts||12);
        this.textAlign(tax||CENTER,tay||CENTER);
      });
      _bindGraphicsMethod("typewrite",function(str,t,x,y,mw,mh){
        var str = str.substring(0,Math.round(str.length*t));
        this.text(str,x,y,mw,mh);
        return str;
      });
      _bindGraphicsMethod("shadow",function(color,blur,offx,offy) {
        this.drawingContext.shadowColor = color||"rgba(0,0,0,0)";
        if (color == false) return;
        this.drawingContext.shadowBlur = blur||0;
        this.drawingContext.shadowOffsetX = offx||0;
        this.drawingContext.shadowOffsetY = offy||0;
      });
      window.centerRect = function(w1,h1,w2,h2) {
        var fatness1 = w1 / h1;
        var fatness2 = w2 / h2;
      
        var scale;
        if (fatness2 >= fatness1) scale = w1 / w2;
        else scale = h1 / h2;
        var w = w2 * scale;
        var h = h2 * scale;
      
        var xCenter = 0 + (w1 / 2);
        var yCenter = 0 + (h1 / 2);
      
        var x = xCenter - (w / 2);
        var y = yCenter - (h / 2);
      
        return { x:x, y:y, w:w, h:h };
      }
      _bindGraphicsMethod("getLines",function(string,width) {
        if (typeof string !== 'string') return [];
        var lines = string.split("\n");
        for (var i = 0; i < lines.length; i++) {
          if (this.textWidth(lines[i]) <= width) continue;
          if (!lines[i].match(/[\s-_]/)) continue;
          var off = "";
          while (this.textWidth(lines[i]) > width || (!lines[i].match(/[\s-_]$/) && lines[i].match(/[\s-_]/))) {
            off = lines[i].charAt(lines[i].length-1)+off;
            lines[i] = lines[i].substr(0,lines[i].length-1);
          }
          lines.splice(i+1,0,off);
        }
        return lines;
      });
      _bindGraphicsMethod("textHeight",function(string,width) {
        var lines = this.getLines(string);
        return lines *= this._renderer._textLeading;
      });
      var bbuf = _createGraphics(1600,1600);
      _bindGraphicsMethod("beveledImage",function(img,sx,sy,sw,sh,x,y,w,h,bev) {
        if (y === undefined) {
          bev = x;
          x = sx; y = sy;
          w = sw; h = sh;
          sx = 0; sy = 0;
          sw = img.width;
          sh = img.height;
        }
        if (img.width !== 1 && bev) {
          var n = "bev"+[sx,sy,sw,sh,w,h,bev].join(",");
          if (!img[n]) {
            var w1 = w*bbuf._pixelDensity;
            var h1 = h*bbuf._pixelDensity;
            bbuf.clear();
            bbuf.image(img,sx,sy,sw,sh,0,0,w1,h1);
            var get1 = bbuf.get(0,0,w1,h1);
            bbuf.clear();
            bbuf.noStroke();
            bbuf.rect(0,0,w1,h1,bev*bbuf._pixelDensity);
            var get2 = bbuf.get(0,0,w1,h1);
            get1.mask(get2);
            img[n] = get1;
          }
          this.image(img[n],x,y,w,h);
        } else this.image(img,sx,sy,sw,sh,x,y,w,h)
      });
      _bindGraphicsMethod("hsl",function(hue,saturation,lightness) {
        colorMode(HSL);
        var c = color(hue,saturation,lightness);
        colorMode(RGB);
        return c;
      });
      _bindGraphicsMethod("hsv",function(hue,saturation,value) {
        colorMode(HSB);
        var c = color(hue,saturation,value);
        colorMode(RGB);
        return c;
      });
      _bindGraphicsMethod("alpha",function(c,a){
        if (typeof a != 'number') return c;
        a = constrain(a,0,1);
        var col = color(c);
        col._array[3] = a;
        return col;
      });
      Math.sign = function(x) {
        if (x == 0) return 0;
        if (x > 0) return 1;
        return -1;
      } // Why does this not exist on code.org
      window.sign = Math.sign;
      
      // Pub
      console.log("Loaded library: FGUI\n A graphics library by DragonFireGames");
      
      // ---------
      // Defaults:
      // ---------
      
      // Default Draw
      function draw() {
        drawElements();
        drawSprites();
        drawSequences();
      }

      Also function mousePressed() {} and stuff won't work anymore, you have to use Event.mousePressed = function(){}; instead

      Chat