function Renderer(options) {
  this.version = options.version || 5, this.canvasId = options.canvasId, this.session = options.session,
  this.host = options.host, this.canvas = document.getElementById(this.canvasId),
  this.connected = !0;
  var ngl, path = window.top === window.self ? "" : "../", me = this;
  this.gl = this.context = null;
  try {
    ngl = this.canvas.getContext("webgl", {
      premultipliedAlpha: !1,
      alpha: !1,
      preserveDrawingBuffer: !0
    }), ngl && (ngl.viewport(0, 0, ngl.canvas.width, ngl.canvas.height), this.rc_texture = ngl.createTexture(),
    ngl.bindTexture(ngl.TEXTURE_2D, this.rc_texture), ngl.texImage2D(ngl.TEXTURE_2D, 0, ngl.RGBA, 1024, 768, 0, ngl.RGBA, ngl.UNSIGNED_SHORT_5_5_5_1, null),
    ngl.texParameteri(ngl.TEXTURE_2D, ngl.TEXTURE_MIN_FILTER, ngl.LINEAR), ngl.texParameteri(ngl.TEXTURE_2D, ngl.TEXTURE_WRAP_S, ngl.CLAMP_TO_EDGE),
    ngl.texParameteri(ngl.TEXTURE_2D, ngl.TEXTURE_WRAP_T, ngl.CLAMP_TO_EDGE), this.vertexShader = ngl.createShader(ngl.VERTEX_SHADER),
    ngl.shaderSource(this.vertexShader, "\n      attribute vec4 vert;  /// [ [ 304, 672 ], [320, 672], [304,688], [320,688] ] \n      uniform vec4 dimensions; /// [ 0, 0, 1024, 768 ] \n      varying vec2 vTexCoord; \n      \n      // set scaling and translation from (-1,1) clipspace to (0,1) tex coords. \n      void main() \n      { \n        // incoming coordinates are 0-dimensions.x, 0-dimensions.y \n        vTexCoord.st = vert.xy / dimensions.zw;     // [304/1024, 672/768 ] = [0.296875, 0.875] \n        gl_Position.xy = (vTexCoord.st - vec2(0.5)) * vec2(2.0,-2.0); // = [ -0.203125, 0.375] * [2.0, -2.0] = [-0.40625, -0.75] \n        gl_Position.zw = vec2(0.0,1.0); \n      }\n      "),
    ngl.compileShader(this.vertexShader), this.textureShader = ngl.createShader(ngl.FRAGMENT_SHADER),
    ngl.shaderSource(this.textureShader, "\n      varying highp vec2 vTexCoord; \n      uniform sampler2D uTexture; \n      \n      void main() \n      { \n        gl_FragColor = texture2D(uTexture, vTexCoord); \n      } \n      "),
    ngl.compileShader(this.textureShader), this.renderProgram = ngl.createProgram(),
    ngl.attachShader(this.renderProgram, this.textureShader), ngl.attachShader(this.renderProgram, this.vertexShader),
    ngl.linkProgram(this.renderProgram), this.vertDataPtr = ngl.getAttribLocation(this.renderProgram, "vert"),
    this.sizeBufferPtr = ngl.getUniformLocation(this.renderProgram, "dimensions"), this.rectCoords = ngl.createBuffer(),
    ngl.bindBuffer(ngl.ARRAY_BUFFER, this.rectCoords), ngl.bufferData(ngl.ARRAY_BUFFER, new Float32Array([ 1, 1, -1, 1, 1, -1, -1, -1 ]), ngl.DYNAMIC_DRAW),
    ngl.useProgram(this.renderProgram), ngl.vertexAttribPointer(this.vertDataPtr, 2, ngl.FLOAT, !1, 0, 0),
    ngl.enableVertexAttribArray(this.vertDataPtr), ngl.disable(ngl.DEPTH_TEST), this.gl = ngl,
    this.context = this.gl);
  } catch (e) {
    $.log("getWebGLContext failure: " + e.message);
  }
  if (this.gl || ($.log("webgl not found, using 2d context."), me.context = this.canvas.getContext("2d", {
    preserveDrawingBuffer: !0
  })), iLOGlobal.ie || window.navigator.userAgent.indexOf("Edge") >= 0) me.decoder = new PseudoWorker(path + "js/worker_decoder.js"); else try {
    me.decoder = new Worker(path + "js/worker_decoder.js");
  } catch (e) {
    e instanceof SecurityError ? $.log("SecurityError: " + e.message) : e instanceof SyntaxError && $.log("SyntaxError: " + e.message),
    me.decoder = new PseudoWorker(path + "js/worker_decoder.js");
  }
  me._terminate = me.decoder.terminate, me.decoder.terminate = function() {
    me.connected = !1, me.keyboard.unbind_all(), me._terminate();
  }, this.worker = {
    kbd_send: function(kcmd) {
      me.decoder.postMessage({
        func: "kbd_send",
        data: kcmd
      });
    },
    mouse_send: function(mcmd) {
      me.decoder.postMessage({
        func: "mouse_send",
        data: mcmd
      });
    },
    call_func: function(func, data) {
      me.decoder.postMessage({
        func: func,
        data: data
      });
    },
    close: function() {
      me.decoder.postMessage({
        func: "worker_close",
        data: {}
      });
    }
  }, me.decoder.postMessage({
    func: "init",
    data: {
      session: me.session,
      openCommand: 0,
      host: me.host,
      version: me.version,
      gl: this.gl ? !0 : !1,
      ie: iLOGlobal.ie,
      path: path
    }
  }), me.keyboard = new KeyboardMouse({
    canvas: me.canvas,
    worker: me.worker
  }), me.show_text = function(channel, message) {
    this.onstatuschange && this.onstatuschange(message);
  }, me.post_code = function(code) {
    if (this.onstatuschange) {
      var lbl = iLO.translateString("IRC.status.POSTCode"), msg = "<span data-localize='IRC.status.POSTCode'>" + lbl + "</span>:<span>" + code + "</span>";
      code.length > 0 && this.onstatuschange(msg, 0);
    }
  }, me.refresh = function() {}, me.confirm = function(msgbox) {
    iLO.confirm(msgbox, function(bool) {
      me.worker.call_func(msgbox.callback, bool);
    });
  }, me.alert = function(msgbox) {
    iLO.alert(msgbox, function(bool) {
      me.worker.call_func(msgbox.callback, bool);
    });
  }, me.toast = function(msgbox) {
    showIrcToast(msgbox);
  }, me.handle_share = function(user, addr) {
    var dlgMsg = iLO.translateString("IRC.message.JoinSharedSession", "User {0} at network address {1} would like to join the multi-user session. Allow this?"), noteMsg = iLO.translateString("IRC.message.FeatureNotSupportedWebIrc", "NOTICE: This feature is not supported with the web-based remote console and the request was automatically denied."), msgBox = {
      message: dlgMsg + "<br/><br/>" + noteMsg,
      messageInserts: [ {
        placeHolder: "{0}",
        value: user,
        valueKey: ""
      }, {
        placeHolder: "{1}",
        value: addr,
        valueKey: ""
      } ],
      closeOnClick: !1,
      stayTime: 10
    };
    iLO.alert(msgBox);
  }, this.ready = function() {
    me.keyboard.bind_all(), me.onready && me.onready();
  }, this.decoder_callback = function(event) {
    var data = event.data;
    switch (data.operation) {
     case "clear":
      me.clear();
      break;

     case "ready":
      me.ready();
      break;

     case "show_text":
      me.show_text(data.channel, data.message);
      break;

     case "post_code":
      me.post_code(data.code);
      break;

     case "set_size":
      me.set_size(data.width, data.height);
      break;

     case "blit_gl":
      me.gl.texSubImage2D(me.gl.TEXTURE_2D, 0, data.x0, data.y0, data.w, data.h, me.gl.RGBA, me.gl.UNSIGNED_SHORT_5_5_5_1, data.data);
      break;

     case "blit_imagedata":
      me.imageData || (me.imageData = me.context.createImageData(data.w, data.h)), me.imageData.data.set(data.data),
      me.context.putImageData(me.imageData, data.x0, data.y0);
      break;

     case "render":
      me.render(data.x0, data.y0, data.w, data.h);
      break;

     case "closed":
      me.connected = !1, me.keyboard.unbind_all(), me.onclose && me.onclose();
      break;

     case "confirm":
      me.confirm(data.msgBox);
      break;

     case "handle_share":
      me.handle_share(data.user, data.addr);
      break;

     case "alert":
      me.alert(data.msgBox);
      break;

     case "toast":
      me.toast(data.msgBox);
      break;

     case "health_change":
      me.onhealthchange(data.health_state);
      break;

     case "power_change":
      me.onpowerchange(data.power_state);
      break;

     case "status_change":
      me.onstatuschange(data.status);
      break;

     case "vm_change":
      me.onvmchange(data.vm_state);
      break;

     case "license":
      me.onlicensechange(data.license);
      break;

     default:
      console.log("handle this case in decoder_callback: ", data.operation, data);
    }
  }, this.decoder.addEventListener("message", this.decoder_callback, !1), this.decoder.addEventListener("error", function(evt) {
    console.log("Line #" + evt.lineno + " - " + evt.message + " in " + evt.filename, evt);
  }, !1), this.close = function() {
    this.worker.close();
  }, this.width = 300, this.height = 150, this.render = function(x0, y0, w, h) {
    me.gl && (me.gl.bufferSubData(me.gl.ARRAY_BUFFER, 0, new Float32Array([ x0, y0, x0 + w, y0, x0, y0 + h, x0 + w, y0 + h ])),
    me.gl.drawArrays(me.gl.TRIANGLE_STRIP, 0, 4));
  }, this.blit_gl = function(x0, y0, w, h, data) {
    this.gl.texSubImage2D(this.gl.TEXTURE_2D, 0, x0, y0, w, h, this.gl.RGBA, this.gl.UNSIGNED_SHORT_5_5_5_1, data);
  }, this.set_size = function(width, height) {
    if (this.width !== width || this.height !== height) {
      if (this.width = width, this.height = height, this.canvas.setAttribute("width", width),
      this.canvas.setAttribute("height", height), this.gl) {
        var ngl = this.gl;
        ngl.viewport(0, 0, width, height), ngl.uniform4f(me.sizeBufferPtr, 0, 0, width, height),
        ngl.texImage2D(ngl.TEXTURE_2D, 0, ngl.RGBA, width, height, 0, ngl.RGBA, ngl.UNSIGNED_SHORT_5_5_5_1, null);
      }
      this.onsizechange && this.onsizechange(width, height), this.clear();
    }
  }, me.clear = function() {
    me.gl ? (me.gl.clearColor(.5, .5, .5, 1), me.gl.clear(me.gl.COLOR_BUFFER_BIT)) : (me.context.fillStyle = "#000",
    me.context.fillRect(0, 0, me.width, me.height));
  };
}