text: (function(global) {
      var LiteGraph = global.LiteGraph;
  
      function GraphicsPlot() {
          this.addInput("A", "Number");
          this.addInput("B", "Number");
          this.addInput("C", "Number");
          this.addInput("D", "Number");
  
          this.values = [[], [], [], []];
          this.properties = { scale: 2 };
      }
  
      GraphicsPlot.title = "Plot";
      GraphicsPlot.desc = "Plots data over time";
      GraphicsPlot.colors = ["#FFF", "#F99", "#9F9", "#99F"];
  
      GraphicsPlot.prototype.onExecute = function(ctx) {
          if (this.flags.collapsed) {
              return;
          }
  
          var size = this.size;
  
          for (var i = 0; i < 4; ++i) {
              var v = this.getInputData(i);
              if (v == null) {
                  continue;
              }
              var values = this.values[i];
              values.push(v);
              if (values.length > size[0]) {
                  values.shift();
              }
          }
      };
  
      GraphicsPlot.prototype.onDrawBackground = function(ctx) {
          if (this.flags.collapsed) {
              return;
          }
  
          var size = this.size;
  
          var scale = (0.5 * size[1]) / this.properties.scale;
          var colors = GraphicsPlot.colors;
          var offset = size[1] * 0.5;
  
          ctx.fillStyle = "#000";
          ctx.fillRect(0, 0, size[0], size[1]);
          ctx.strokeStyle = "#555";
          ctx.beginPath();
          ctx.moveTo(0, offset);
          ctx.lineTo(size[0], offset);
          ctx.stroke();
  
          if (this.inputs) {
              for (var i = 0; i < 4; ++i) {
                  var values = this.values[i];
                  if (!this.inputs[i] || !this.inputs[i].link) {
                      continue;
                  }
                  ctx.strokeStyle = colors[i];
                  ctx.beginPath();
                  var v = values[0] * scale * -1 + offset;
                  ctx.moveTo(0, Math.clamp(v, 0, size[1]));
                  for (var j = 1; j < values.length && j < size[0]; ++j) {
                      var v = values[j] * scale * -1 + offset;
                      ctx.lineTo(j, Math.clamp(v, 0, size[1]));
                  }
                  ctx.stroke();
              }
          }
      };
  
      LiteGraph.registerNodeType("graphics/plot", GraphicsPlot);
  
      function GraphicsImage() {
          this.addOutput("frame", "image");
          this.properties = { url: "" };
      }
  
      GraphicsImage.title = "Image";
      GraphicsImage.desc = "Image loader";
      GraphicsImage.widgets = [{ name: "load", text: "Load", type: "button" }];
  
      GraphicsImage.supported_extensions = ["jpg", "jpeg", "png", "gif"];
  
      GraphicsImage.prototype.onAdded = function() {
          if (this.properties["url"] != "" && this.img == null) {
              this.loadImage(this.properties["url"]);
          }
      };
  
      GraphicsImage.prototype.onDrawBackground = function(ctx) {
          if (this.flags.collapsed) {
              return;
          }
          if (this.img && this.size[0] > 5 && this.size[1] > 5 && this.img.width) {
              ctx.drawImage(this.img, 0, 0, this.size[0], this.size[1]);
          }
      };
  
      GraphicsImage.prototype.onExecute = function() {
          if (!this.img) {
              this.boxcolor = "#000";
          }
          if (this.img && this.img.width) {
              this.setOutputData(0, this.img);
          } else {
              this.setOutputData(0, null);
          }
          if (this.img && this.img.dirty) {
              this.img.dirty = false;
          }
      };
  
      GraphicsImage.prototype.onPropertyChanged = function(name, value) {
          this.properties[name] = value;
          if (name == "url" && value != "") {
              this.loadImage(value);
          }
  
          return true;
      };
  
      GraphicsImage.prototype.loadImage = function(url, callback) {
          if (url == "") {
              this.img = null;
              return;
          }
  
          this.img = document.createElement("img");
  
          if (url.substr(0, 4) == "http" && LiteGraph.proxy) {
              url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3);
          }
  
          this.img.src = url;
          this.boxcolor = "#F95";
          var that = this;
          this.img.onload = function() {
              if (callback) {
                  callback(this);
              }
              console.log( "Image loaded, size: " + that.img.width + "x" + that.img.height );
              this.dirty = true;
              that.boxcolor = "#9F9";
              that.setDirtyCanvas(true);
          };
          this.img.onerror = function() {
  			console.log("error loading the image:" + url);
  		}
      };
  
      GraphicsImage.prototype.onWidget = function(e, widget) {
          if (widget.name == "load") {
              this.loadImage(this.properties["url"]);
          }
      };
  
      GraphicsImage.prototype.onDropFile = function(file) {
          var that = this;
          if (this._url) {
              URL.revokeObjectURL(this._url);
          }
          this._url = URL.createObjectURL(file);
          this.properties.url = this._url;
          this.loadImage(this._url, function(img) {
              that.size[1] = (img.height / img.width) * that.size[0];
          });
      };
  
      LiteGraph.registerNodeType("graphics/image", GraphicsImage);
  
      function ColorPalette() {
          this.addInput("f", "number");
          this.addOutput("Color", "color");
          this.properties = {
              colorA: "#444444",
              colorB: "#44AAFF",
              colorC: "#44FFAA",
              colorD: "#FFFFFF"
          };
      }
  
      ColorPalette.title = "Palette";
      ColorPalette.desc = "Generates a color";
  
      ColorPalette.prototype.onExecute = function() {
          var c = [];
  
          if (this.properties.colorA != null) {
              c.push(hex2num(this.properties.colorA));
          }
          if (this.properties.colorB != null) {
              c.push(hex2num(this.properties.colorB));
          }
          if (this.properties.colorC != null) {
              c.push(hex2num(this.properties.colorC));
          }
          if (this.properties.colorD != null) {
              c.push(hex2num(this.properties.colorD));
          }
  
          var f = this.getInputData(0);
          if (f == null) {
              f = 0.5;
          }
          if (f > 1.0) {
              f = 1.0;
          } else if (f < 0.0) {
              f = 0.0;
          }
  
          if (c.length == 0) {
              return;
          }
  
          var result = [0, 0, 0];
          if (f == 0) {
              result = c[0];
          } else if (f == 1) {
              result = c[c.length - 1];
          } else {
              var pos = (c.length - 1) * f;
              var c1 = c[Math.floor(pos)];
              var c2 = c[Math.floor(pos) + 1];
              var t = pos - Math.floor(pos);
              result[0] = c1[0] * (1 - t) + c2[0] * t;
              result[1] = c1[1] * (1 - t) + c2[1] * t;
              result[2] = c1[2] * (1 - t) + c2[2] * t;
          }
  
          /*
  	c[0] = 1.0 - Math.abs( Math.sin( 0.1 * reModular.getTime() * Math.PI) );
  	c[1] = Math.abs( Math.sin( 0.07 * reModular.getTime() * Math.PI) );
  	c[2] = Math.abs( Math.sin( 0.01 * reModular.getTime() * Math.PI) );
  	*/
  
          for (var i in result) {
              result[i] /= 255;
          }
  
          this.boxcolor = colorToString(result);
          this.setOutputData(0, result);
      };
  
      LiteGraph.registerNodeType("color/palette", ColorPalette);
  
      function ImageFrame() {
          this.addInput("", "image,canvas");
          this.size = [200, 200];
      }
  
      ImageFrame.title = "Frame";
      ImageFrame.desc = "Frame viewerew";
      ImageFrame.widgets = [
          { name: "resize", text: "Resize box", type: "button" },
          { name: "view", text: "View Image", type: "button" }
      ];
  
      ImageFrame.prototype.onDrawBackground = function(ctx) {
          if (this.frame && !this.flags.collapsed) {
              ctx.drawImage(this.frame, 0, 0, this.size[0], this.size[1]);
          }
      };
  
      ImageFrame.prototype.onExecute = function() {
          this.frame = this.getInputData(0);
          this.setDirtyCanvas(true);
      };
  
      ImageFrame.prototype.onWidget = function(e, widget) {
          if (widget.name == "resize" && this.frame) {
              var width = this.frame.width;
              var height = this.frame.height;
  
              if (!width && this.frame.videoWidth != null) {
                  width = this.frame.videoWidth;
                  height = this.frame.videoHeight;
              }
  
              if (width && height) {
                  this.size = [width, height];
              }
              this.setDirtyCanvas(true, true);
          } else if (widget.name == "view") {
              this.show();
          }
      };
  
      ImageFrame.prototype.show = function() {
          //var str = this.canvas.toDataURL("image/png");
          if (showElement && this.frame) {
              showElement(this.frame);
          }
      };
  
      LiteGraph.registerNodeType("graphics/frame", ImageFrame);
  
      function ImageFade() {
          this.addInputs([
              ["img1", "image"],
              ["img2", "image"],
              ["fade", "number"]
          ]);
          this.addOutput("", "image");
          this.properties = { fade: 0.5, width: 512, height: 512 };
      }
  
      ImageFade.title = "Image fade";
      ImageFade.desc = "Fades between images";
      ImageFade.widgets = [
          { name: "resizeA", text: "Resize to A", type: "button" },
          { name: "resizeB", text: "Resize to B", type: "button" }
      ];
  
      ImageFade.prototype.onAdded = function() {
          this.createCanvas();
          var ctx = this.canvas.getContext("2d");
          ctx.fillStyle = "#000";
          ctx.fillRect(0, 0, this.properties["width"], this.properties["height"]);
      };
  
      ImageFade.prototype.createCanvas = function() {
          this.canvas = document.createElement("canvas");
          this.canvas.width = this.properties["width"];
          this.canvas.height = this.properties["height"];
      };
  
      ImageFade.prototype.onExecute = function() {
          var ctx = this.canvas.getContext("2d");
          this.canvas.width = this.canvas.width;
  
          var A = this.getInputData(0);
          if (A != null) {
              ctx.drawImage(A, 0, 0, this.canvas.width, this.canvas.height);
          }
  
          var fade = this.getInputData(2);
          if (fade == null) {
              fade = this.properties["fade"];
          } else {
              this.properties["fade"] = fade;
          }
  
          ctx.globalAlpha = fade;
          var B = this.getInputData(1);
          if (B != null) {
              ctx.drawImage(B, 0, 0, this.canvas.width, this.canvas.height);
          }
          ctx.globalAlpha = 1.0;
  
          this.setOutputData(0, this.canvas);
          this.setDirtyCanvas(true);
      };
  
      LiteGraph.registerNodeType("graphics/imagefade", ImageFade);
  
      function ImageCrop() {
          this.addInput("", "image");
          this.addOutput("", "image");
          this.properties = { width: 256, height: 256, x: 0, y: 0, scale: 1.0 };
          this.size = [50, 20];
      }
  
      ImageCrop.title = "Crop";
      ImageCrop.desc = "Crop Image";
  
      ImageCrop.prototype.onAdded = function() {
          this.createCanvas();
      };
  
      ImageCrop.prototype.createCanvas = function() {
          this.canvas = document.createElement("canvas");
          this.canvas.width = this.properties["width"];
          this.canvas.height = this.properties["height"];
      };
  
      ImageCrop.prototype.onExecute = function() {
          var input = this.getInputData(0);
          if (!input) {
              return;
          }
  
          if (input.width) {
              var ctx = this.canvas.getContext("2d");
  
              ctx.drawImage(
                  input,
                  -this.properties["x"],
                  -this.properties["y"],
                  input.width * this.properties["scale"],
                  input.height * this.properties["scale"]
              );
              this.setOutputData(0, this.canvas);
          } else {
              this.setOutputData(0, null);
          }
      };
  
      ImageCrop.prototype.onDrawBackground = function(ctx) {
          if (this.flags.collapsed) {
              return;
          }
          if (this.canvas) {
              ctx.drawImage(
                  this.canvas,
                  0,
                  0,
                  this.canvas.width,
                  this.canvas.height,
                  0,
                  0,
                  this.size[0],
                  this.size[1]
              );
          }
      };
  
      ImageCrop.prototype.onPropertyChanged = function(name, value) {
          this.properties[name] = value;
  
          if (name == "scale") {
              this.properties[name] = parseFloat(value);
              if (this.properties[name] == 0) {
                  console.error("Error in scale");
                  this.properties[name] = 1.0;
              }
          } else {
              this.properties[name] = parseInt(value);
          }
  
          this.createCanvas();
  
          return true;
      };
  
      LiteGraph.registerNodeType("graphics/cropImage", ImageCrop);
  
      //CANVAS stuff
  
      function CanvasNode() {
          this.addInput("clear", LiteGraph.ACTION);
          this.addOutput("", "canvas");
          this.properties = { width: 512, height: 512, autoclear: true };
  
          this.canvas = document.createElement("canvas");
          this.ctx = this.canvas.getContext("2d");
      }
  
      CanvasNode.title = "Canvas";
      CanvasNode.desc = "Canvas to render stuff";
  
      CanvasNode.prototype.onExecute = function() {
          var canvas = this.canvas;
          var w = this.properties.width | 0;
          var h = this.properties.height | 0;
          if (canvas.width != w) {
              canvas.width = w;
          }
          if (canvas.height != h) {
              canvas.height = h;
          }
  
          if (this.properties.autoclear) {
              this.ctx.clearRect(0, 0, canvas.width, canvas.height);
          }
          this.setOutputData(0, canvas);
      };
  
      CanvasNode.prototype.onAction = function(action, param) {
          if (action == "clear") {
              this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
          }
      };
  
      LiteGraph.registerNodeType("graphics/canvas", CanvasNode);
  
      function DrawImageNode() {
          this.addInput("canvas", "canvas");
          this.addInput("img", "image,canvas");
          this.addInput("x", "number");
          this.addInput("y", "number");
          this.properties = { x: 0, y: 0, opacity: 1 };
      }
  
      DrawImageNode.title = "DrawImage";
      DrawImageNode.desc = "Draws image into a canvas";
  
      DrawImageNode.prototype.onExecute = function() {
          var canvas = this.getInputData(0);
          if (!canvas) {
              return;
          }
  
          var img = this.getInputOrProperty("img");
          if (!img) {
              return;
          }
  
          var x = this.getInputOrProperty("x");
          var y = this.getInputOrProperty("y");
          var ctx = canvas.getContext("2d");
          ctx.drawImage(img, x, y);
      };
  
      LiteGraph.registerNodeType("graphics/drawImage", DrawImageNode);
  
      function DrawRectangleNode() {
          this.addInput("canvas", "canvas");
          this.addInput("x", "number");
          this.addInput("y", "number");
          this.addInput("w", "number");
          this.addInput("h", "number");
          this.properties = {
              x: 0,
              y: 0,
              w: 10,
              h: 10,
              color: "white",
              opacity: 1
          };
      }
  
      DrawRectangleNode.title = "DrawRectangle";
      DrawRectangleNode.desc = "Draws rectangle in canvas";
  
      DrawRectangleNode.prototype.onExecute = function() {
          var canvas = this.getInputData(0);
          if (!canvas) {
              return;
          }
  
          var x = this.getInputOrProperty("x");
          var y = this.getInputOrProperty("y");
          var w = this.getInputOrProperty("w");
          var h = this.getInputOrProperty("h");
          var ctx = canvas.getContext("2d");
          ctx.fillRect(x, y, w, h);
      };
  
      LiteGraph.registerNodeType("graphics/drawRectangle", DrawRectangleNode);
  
      function ImageVideo() {
          this.addInput("t", "number");
          this.addOutputs([["frame", "image"], ["t", "number"], ["d", "number"]]);
          this.properties = { url: "", use_proxy: true };
      }
  
      ImageVideo.title = "Video";
      ImageVideo.desc = "Video playback";
      ImageVideo.widgets = [
          { name: "play", text: "PLAY", type: "minibutton" },
          { name: "stop", text: "STOP", type: "minibutton" },
          { name: "demo", text: "Demo video", type: "button" },
          { name: "mute", text: "Mute video", type: "button" }
      ];
  
      ImageVideo.prototype.onExecute = function() {
          if (!this.properties.url) {
              return;
          }
  
          if (this.properties.url != this._video_url) {
              this.loadVideo(this.properties.url);
          }
  
          if (!this._video || this._video.width == 0) {
              return;
          }
  
          var t = this.getInputData(0);
          if (t && t >= 0 && t <= 1.0) {
              this._video.currentTime = t * this._video.duration;
              this._video.pause();
          }
  
          this._video.dirty = true;
          this.setOutputData(0, this._video);
          this.setOutputData(1, this._video.currentTime);
          this.setOutputData(2, this._video.duration);
          this.setDirtyCanvas(true);
      };
  
      ImageVideo.prototype.onStart = function() {
          this.play();
      };
  
      ImageVideo.prototype.onStop = function() {
          this.stop();
      };
  
      ImageVideo.prototype.loadVideo = function(url) {
          this._video_url = url;
  
  		var pos = url.substr(0,10).indexOf(":");
  		var protocol = "";
  		if(pos != -1)
  			protocol = url.substr(0,pos);
  
  		var host = "";
  		if(protocol)
  		{
  			host = url.substr(0,url.indexOf("/",protocol.length + 3));
  			host = host.substr(protocol.length+3);
  		}
  
          if (
              this.properties.use_proxy &&
              protocol &&
              LiteGraph.proxy &&
  			host != location.host
          ) {
              url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3);
          }
  
          this._video = document.createElement("video");
          this._video.src = url;
          this._video.type = "type=video/mp4";
  
          this._video.muted = true;
          this._video.autoplay = true;
  
          var that = this;
          this._video.addEventListener("loadedmetadata", function(e) {
              //onload
              console.log("Duration: " + this.duration + " seconds");
              console.log("Size: " + this.videoWidth + "," + this.videoHeight);
              that.setDirtyCanvas(true);
              this.width = this.videoWidth;
              this.height = this.videoHeight;
          });
          this._video.addEventListener("progress", function(e) {
              //onload
              console.log("video loading...");
          });
          this._video.addEventListener("error", function(e) {
              console.error("Error loading video: " + this.src);
              if (this.error) {
                  switch (this.error.code) {
                      case this.error.MEDIA_ERR_ABORTED:
                          console.error("You stopped the video.");
                          break;
                      case this.error.MEDIA_ERR_NETWORK:
                          console.error("Network error - please try again later.");
                          break;
                      case this.error.MEDIA_ERR_DECODE:
                          console.error("Video is broken..");
                          break;
                      case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
                          console.error("Sorry, your browser can't play this video.");
                          break;
                  }
              }
          });
  
          this._video.addEventListener("ended", function(e) {
              console.log("Video Ended.");
              this.play(); //loop
          });
  
          //document.body.appendChild(this.video);
      };
  
      ImageVideo.prototype.onPropertyChanged = function(name, value) {
          this.properties[name] = value;
          if (name == "url" && value != "") {
              this.loadVideo(value);
          }
  
          return true;
      };
  
      ImageVideo.prototype.play = function() {
          if (this._video && this._video.videoWidth ) { //is loaded
              this._video.play();
          }
      };
  
      ImageVideo.prototype.playPause = function() {
          if (!this._video) {
              return;
          }
          if (this._video.paused) {
              this.play();
          } else {
              this.pause();
          }
      };
  
      ImageVideo.prototype.stop = function() {
          if (!this._video) {
              return;
          }
          this._video.pause();
          this._video.currentTime = 0;
      };
  
      ImageVideo.prototype.pause = function() {
          if (!this._video) {
              return;
          }
          console.log("Video paused");
          this._video.pause();
      };
  
      ImageVideo.prototype.onWidget = function(e, widget) {
          /*
  	if(widget.name == "demo")
  	{
  		this.loadVideo();
  	}
  	else if(widget.name == "play")
  	{
  		if(this._video)
  			this.playPause();
  	}
  	if(widget.name == "stop")
  	{
  		this.stop();
  	}
  	else if(widget.name == "mute")
  	{
  		if(this._video)
  			this._video.muted = !this._video.muted;
  	}
  	*/
      };
  
      LiteGraph.registerNodeType("graphics/video", ImageVideo);
  
      // Texture Webcam *****************************************
      function ImageWebcam() {
          this.addOutput("Webcam", "image");
          this.properties = { facingMode: "user" };
          this.boxcolor = "black";
          this.frame = 0;
      }
  
      ImageWebcam.title = "Webcam";
      ImageWebcam.desc = "Webcam image";
      ImageWebcam.is_webcam_open = false;
  
      ImageWebcam.prototype.openStream = function() {
          if (!navigator.getUserMedia) {
              //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags');
              return;
          }
  
          this._waiting_confirmation = true;
  
          // Not showing vendor prefixes.
          var constraints = {
              audio: false,
              video: { facingMode: this.properties.facingMode }
          };
          navigator.mediaDevices
              .getUserMedia(constraints)
              .then(this.streamReady.bind(this))
              .catch(onFailSoHard);
  
          var that = this;
          function onFailSoHard(e) {
              console.log("Webcam rejected", e);
              that._webcam_stream = false;
              ImageWebcam.is_webcam_open = false;
              that.boxcolor = "red";
              that.trigger("stream_error");
          }
      };
  
      ImageWebcam.prototype.closeStream = function() {
          if (this._webcam_stream) {
              var tracks = this._webcam_stream.getTracks();
              if (tracks.length) {
                  for (var i = 0; i < tracks.length; ++i) {
                      tracks[i].stop();
                  }
              }
              ImageWebcam.is_webcam_open = false;
              this._webcam_stream = null;
              this._video = null;
              this.boxcolor = "black";
              this.trigger("stream_closed");
          }
      };
  
      ImageWebcam.prototype.onPropertyChanged = function(name, value) {
          if (name == "facingMode") {
              this.properties.facingMode = value;
              this.closeStream();
              this.openStream();
          }
      };
  
      ImageWebcam.prototype.onRemoved = function() {
          this.closeStream();
      };
  
      ImageWebcam.prototype.streamReady = function(localMediaStream) {
          this._webcam_stream = localMediaStream;
          //this._waiting_confirmation = false;
          this.boxcolor = "green";
  
          var video = this._video;
          if (!video) {
              video = document.createElement("video");
              video.autoplay = true;
              video.srcObject = localMediaStream;
              this._video = video;
              //document.body.appendChild( video ); //debug
              //when video info is loaded (size and so)
              video.onloadedmetadata = function(e) {
                  // Ready to go. Do some stuff.
                  console.log(e);
                  ImageWebcam.is_webcam_open = true;
              };
          }
  
          this.trigger("stream_ready", video);
      };
  
      ImageWebcam.prototype.onExecute = function() {
          if (this._webcam_stream == null && !this._waiting_confirmation) {
              this.openStream();
          }
  
          if (!this._video || !this._video.videoWidth) {
              return;
          }
  
          this._video.frame = ++this.frame;
          this._video.width = this._video.videoWidth;
          this._video.height = this._video.videoHeight;
          this.setOutputData(0, this._video);
          for (var i = 1; i < this.outputs.length; ++i) {
              if (!this.outputs[i]) {
                  continue;
              }
              switch (this.outputs[i].name) {
                  case "width":
                      this.setOutputData(i, this._video.videoWidth);
                      break;
                  case "height":
                      this.setOutputData(i, this._video.videoHeight);
                      break;
              }
          }
      };
  
      ImageWebcam.prototype.getExtraMenuOptions = function(graphcanvas) {
          var that = this;
          var txt = !that.properties.show ? "Show Frame" : "Hide Frame";
          return [
              {
                  content: txt,
                  callback: function() {
                      that.properties.show = !that.properties.show;
                  }
              }
          ];
      };
  
      ImageWebcam.prototype.onDrawBackground = function(ctx) {
          if (
              this.flags.collapsed ||
              this.size[1] <= 20 ||
              !this.properties.show
          ) {
              return;
          }
  
          if (!this._video) {
              return;
          }
  
          //render to graph canvas
          ctx.save();
          ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]);
          ctx.restore();
      };
  
      ImageWebcam.prototype.onGetOutputs = function() {
          return [
              ["width", "number"],
              ["height", "number"],
              ["stream_ready", LiteGraph.EVENT],
              ["stream_closed", LiteGraph.EVENT],
              ["stream_error", LiteGraph.EVENT]
          ];
      };
  
      LiteGraph.registerNodeType("graphics/webcam", ImageWebcam);
  })(this);
  