text: /*
   * Copyright 2012, Gregg Tavares.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are
   * met:
   *
   *     * Redistributions of source code must retain the above copyright
   * notice, this list of conditions and the following disclaimer.
   *     * Redistributions in binary form must reproduce the above
   * copyright notice, this list of conditions and the following disclaimer
   * in the documentation and/or other materials provided with the
   * distribution.
   *     * Neither the name of Gregg Tavares. nor the names of his
   * contributors may be used to endorse or promote products derived from
   * this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  /* global define */
  
  (function(root, factory) {  // eslint-disable-line
    if (typeof define === 'function' && define.amd) {
      // AMD. Register as an anonymous module.
      define([], function() {
        return factory.call(root);
      });
    } else {
      // Browser globals
      root.webglDebugHelper = factory.call(root);
    }
  }(this, function() {
    'use strict';  // eslint-disable-line
  
    //------------ [ from https://github.com/KhronosGroup/WebGLDeveloperTools ]
  
    /*
    ** Copyright (c) 2012 The Khronos Group Inc.
    **
    ** Permission is hereby granted, free of charge, to any person obtaining a
    ** copy of this software and/or associated documentation files (the
    ** "Materials"), to deal in the Materials without restriction, including
    ** without limitation the rights to use, copy, modify, merge, publish,
    ** distribute, sublicense, and/or sell copies of the Materials, and to
    ** permit persons to whom the Materials are furnished to do so, subject to
    ** the following conditions:
    **
    ** The above copyright notice and this permission notice shall be included
    ** in all copies or substantial portions of the Materials.
    **
    ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
    */
  
    /**
     * Types of contexts we have added to map
     */
    const mappedContextTypes = {};
  
    /**
     * Map of numbers to names.
     * @type {Object}
     */
    const glEnums = {};
  
    /**
     * Map of names to numbers.
     * @type {Object}
     */
    const enumStringToValue = {};
  
    /**
     * Initializes this module. Safe to call more than once.
     * @param {!WebGLRenderingContext} ctx A WebGL context. If
     *    you have more than one context it doesn't matter which one
     *    you pass in, it is only used to pull out constants.
     */
    function addEnumsForContext(ctx, type) {
      if (!mappedContextTypes[type]) {
        mappedContextTypes[type] = true;
        for (const propertyName in ctx) {
          if (typeof ctx[propertyName] === 'number') {
            glEnums[ctx[propertyName]] = propertyName;
            enumStringToValue[propertyName] = ctx[propertyName];
          }
        }
      }
    }
  
    function enumArrayToString(enums) {
      const enumStrings = [];
      if (enums.length) {
        for (let i = 0; i < enums.length; ++i) {
          enums.push(glEnumToString(enums[i]));  // eslint-disable-line
        }
        return '[' + enumStrings.join(', ') + ']';
      }
      return enumStrings.toString();
    }
  
    function makeBitFieldToStringFunc(enums) {
      return function(value) {
        let orResult = 0;
        const orEnums = [];
        for (let i = 0; i < enums.length; ++i) {
          const enumValue = enumStringToValue[enums[i]];
          if ((value & enumValue) !== 0) {
            orResult |= enumValue;
            orEnums.push(glEnumToString(enumValue));  // eslint-disable-line
          }
        }
        if (orResult === value) {
          return orEnums.join(' | ');
        } else {
          return glEnumToString(value);  // eslint-disable-line
        }
      };
    }
  
    const destBufferBitFieldToString = makeBitFieldToStringFunc([
      'COLOR_BUFFER_BIT',
      'DEPTH_BUFFER_BIT',
      'STENCIL_BUFFER_BIT',
    ]);
  
    /**
     * Which arguments are enums based on the number of arguments to the function.
     * So
     *    'texImage2D': {
     *       9: { 0:true, 2:true, 6:true, 7:true },
     *       6: { 0:true, 2:true, 3:true, 4:true },
     *    },
     *
     * means if there are 9 arguments then 6 and 7 are enums, if there are 6
     * arguments 3 and 4 are enums. Maybe a function as well in which case
     * value is passed to function and returns a string
     *
     * @type {!Object.<number, (!Object.<number, string>|function)}
     */
    const glValidEnumContexts = {
      // Generic setters and getters
  
      'enable': {1: { 0:true }},
      'disable': {1: { 0:true }},
      'getParameter': {1: { 0:true }},
  
      // Rendering
  
      'drawArrays': {3:{ 0:true }},
      'drawElements': {4:{ 0:true, 2:true }},
      'drawArraysInstanced': {4: { 0:true }},
      'drawElementsInstanced': {5: {0:true, 2: true }},
      'drawRangeElements': {6: {0:true, 4: true }},
  
      // Shaders
  
      'createShader': {1: { 0:true }},
      'getShaderParameter': {2: { 1:true }},
      'getProgramParameter': {2: { 1:true }},
      'getShaderPrecisionFormat': {2: { 0: true, 1:true }},
  
      // Vertex attributes
  
      'getVertexAttrib': {2: { 1:true }},
      'vertexAttribPointer': {6: { 2:true }},
      'vertexAttribIPointer': {5: { 2:true }},  // WebGL2
  
      // Textures
  
      'bindTexture': {2: { 0:true }},
      'activeTexture': {1: { 0:true }},
      'getTexParameter': {2: { 0:true, 1:true }},
      'texParameterf': {3: { 0:true, 1:true }},
      'texParameteri': {3: { 0:true, 1:true, 2:true }},
      'texImage2D': {
        9: { 0:true, 2:true, 6:true, 7:true },
        6: { 0:true, 2:true, 3:true, 4:true },
        10: { 0:true, 2:true, 6:true, 7:true },  // WebGL2
      },
      'texImage3D': {
        10: { 0:true, 2:true, 7:true, 8:true },  // WebGL2
        11: { 0:true, 2:true, 7:true, 8:true },  // WebGL2
      },
      'texSubImage2D': {
        9: { 0:true, 6:true, 7:true },
        7: { 0:true, 4:true, 5:true },
        10: { 0:true, 6:true, 7:true },  // WebGL2
      },
      'texSubImage3D': {
        11: { 0:true, 8:true, 9:true },  // WebGL2
        12: { 0:true, 8:true, 9:true },  // WebGL2
      },
      'texStorage2D': { 5: { 0:true, 2:true }},  // WebGL2
      'texStorage3D': { 6: { 0:true, 2:true }},  // WebGL2
      'copyTexImage2D': {8: { 0:true, 2:true }},
      'copyTexSubImage2D': {8: { 0:true }},
      'copyTexSubImage3D': {9: { 0:true }},  // WebGL2
      'generateMipmap': {1: { 0:true }},
      'compressedTexImage2D': {
        7: { 0: true, 2:true },
        8: { 0: true, 2:true },  // WebGL2
      },
      'compressedTexSubImage2D': {
        8: { 0: true, 6:true },
        9: { 0: true, 6:true },  // WebGL2
      },
      'compressedTexImage3D': {
        8: { 0: true, 2: true, },  // WebGL2
        9: { 0: true, 2: true, },  // WebGL2
      },
      'compressedTexSubImage3D': {
        9: { 0: true, 8: true, },  // WebGL2
        10: { 0: true, 8: true, },  // WebGL2
      },
  
      // Buffer objects
  
      'bindBuffer': {2: { 0:true }},
      'bufferData': {
        3: { 0:true, 2:true },
        4: { 0:true, 2:true },  // WebGL2
        5: { 0:true, 2:true },  // WebGL2
      },
      'bufferSubData': {
        3: { 0:true },
        4: { 0:true },  // WebGL2
        5: { 0:true },  // WebGL2
      },
      'copyBufferSubData': {
        5: { 0:true },  // WeBGL2
      },
      'getBufferParameter': {2: { 0:true, 1:true }},
      'getBufferSubData': {
        3: { 0: true, },  // WebGL2
        4: { 0: true, },  // WebGL2
        5: { 0: true, },  // WebGL2
      },
  
      // Renderbuffers and framebuffers
  
      'pixelStorei': {2: { 0:true, 1:true }},
      'readPixels': {
        7: { 4:true, 5:true },
        8: { 4:true, 5:true },  // WebGL2
      },
      'bindRenderbuffer': {2: { 0:true }},
      'bindFramebuffer': {2: { 0:true }},
      'blitFramebuffer': {10: { 8: destBufferBitFieldToString, 9:true }},  // WebGL2
      'checkFramebufferStatus': {1: { 0:true }},
      'framebufferRenderbuffer': {4: { 0:true, 1:true, 2:true }},
      'framebufferTexture2D': {5: { 0:true, 1:true, 2:true }},
      'framebufferTextureLayer': {5: {0:true, 1:true }},  // WebGL2
      'getFramebufferAttachmentParameter': {3: { 0:true, 1:true, 2:true }},
      'getInternalformatParameter': {3: {0:true, 1:true, 2:true }},  // WebGL2
      'getRenderbufferParameter': {2: { 0:true, 1:true }},
      'invalidateFramebuffer': {2: { 0:true, 1: enumArrayToString, }},  // WebGL2
      'invalidateSubFramebuffer': {6: {0: true, 1: enumArrayToString, }},  // WebGL2
      'readBuffer': {1: {0: true}},  // WebGL2
      'renderbufferStorage': {4: { 0:true, 1:true }},
      'renderbufferStorageMultisample': {5: { 0: true, 2: true }},  // WebGL2
  
      // Frame buffer operations (clear, blend, depth test, stencil)
  
      'clear': {1: { 0: destBufferBitFieldToString }},
      'depthFunc': {1: { 0:true }},
      'blendFunc': {2: { 0:true, 1:true }},
      'blendFuncSeparate': {4: { 0:true, 1:true, 2:true, 3:true }},
      'blendEquation': {1: { 0:true }},
      'blendEquationSeparate': {2: { 0:true, 1:true }},
      'stencilFunc': {3: { 0:true }},
      'stencilFuncSeparate': {4: { 0:true, 1:true }},
      'stencilMaskSeparate': {2: { 0:true }},
      'stencilOp': {3: { 0:true, 1:true, 2:true }},
      'stencilOpSeparate': {4: { 0:true, 1:true, 2:true, 3:true }},
  
      // Culling
  
      'cullFace': {1: { 0:true }},
      'frontFace': {1: { 0:true }},
  
      // ANGLE_instanced_arrays extension
  
      'drawArraysInstancedANGLE': {4: { 0:true }},
      'drawElementsInstancedANGLE': {5: { 0:true, 2:true }},
  
      // EXT_blend_minmax extension
  
      'blendEquationEXT': {1: { 0:true }},
  
      // Multiple Render Targets
  
      'drawBuffersWebGL': {1: {0: enumArrayToString, }},  // WEBGL_draw_bufers
      'drawBuffers': {1: {0: enumArrayToString, }},  // WebGL2
      'clearBufferfv': {
        4: {0: true },  // WebGL2
        5: {0: true },  // WebGL2
      },
      'clearBufferiv': {
        4: {0: true },  // WebGL2
        5: {0: true },  // WebGL2
      },
      'clearBufferuiv': {
        4: {0: true },  // WebGL2
        5: {0: true },  // WebGL2
      },
      'clearBufferfi': { 4: {0: true}},  // WebGL2
  
      // QueryObjects
  
      'beginQuery': { 2: { 0: true }},  // WebGL2
      'endQuery': { 1: { 0: true }},  // WebGL2
      'getQuery': { 2: { 0: true, 1: true }},  // WebGL2
      'getQueryParameter': { 2: { 1: true }},  // WebGL2
  
      //  Sampler Objects
  
      'samplerParameteri': { 3: { 1: true }},  // WebGL2
      'samplerParameterf': { 3: { 1: true }},  // WebGL2
      'getSamplerParameter': { 2: { 1: true }},  // WebGL2
  
      //  Sync objects
  
      'clientWaitSync': { 3: { 1: makeBitFieldToStringFunc(['SYNC_FLUSH_COMMANDS_BIT']) }},  // WebGL2
      'fenceSync': { 2: { 0: true }},  // WebGL2
      'getSyncParameter': { 2: { 1: true }},  // WebGL2
  
      //  Transform Feedback
  
      'bindTransformFeedback': { 2: { 0: true }},  // WebGL2
      'beginTransformFeedback': { 1: { 0: true }},  // WebGL2
  
      // Uniform Buffer Objects and Transform Feedback Buffers
      'bindBufferBase': { 3: { 0: true }},  // WebGL2
      'bindBufferRange': { 5: { 0: true }},  // WebGL2
      'getIndexedParameter': { 2: { 0: true }},  // WebGL2
      'getActiveUniforms': { 3: { 2: true }},  // WebGL2
      'getActiveUniformBlockParameter': { 3: { 2: true }},  // WebGL2
    };
  
    /**
     * Gets an string version of an WebGL enum.
     *
     * Example:
     *   var str = WebGLDebugUtil.glEnumToString(ctx.getError());
     *
     * @param {number} value Value to return an enum for
     * @return {string} The string version of the enum.
     */
    function glEnumToString(value) {
      const name = glEnums[value];
      return (name !== undefined)
          ? `gl.${name}`
          : `/*UNKNOWN WebGL ENUM*/ 0x${value.toString(16)}`;
    }
  
    /**
     * Returns the string version of a WebGL argument.
     * Attempts to convert enum arguments to strings.
     * @param {string} functionName the name of the WebGL function.
     * @param {number} numArgs the number of arguments passed to the function.
     * @param {number} argumentIndx the index of the argument.
     * @param {*} value The value of the argument.
     * @return {string} The value as a string.
     */
    function glFunctionArgToString(functionName, numArgs, argumentIndex, value) {
      const funcInfos = glValidEnumContexts[functionName];
      if (funcInfos !== undefined) {
        const funcInfo = funcInfos[numArgs];
        if (funcInfo !== undefined) {
          const argType = funcInfo[argumentIndex];
          if (argType) {
            if (typeof argType === 'function') {
              return argType(value);
            } else {
              return glEnumToString(value);
            }
          }
        }
      }
      if (value === null) {
        return 'null';
      } else if (value === undefined) {
        return 'undefined';
      } else {
        return value.toString();
      }
    }
  
    /**
     * Converts the arguments of a WebGL function to a string.
     * Attempts to convert enum arguments to strings.
     *
     * @param {string} functionName the name of the WebGL function.
     * @param {number} args The arguments.
     * @return {string} The arguments as a string.
     */
    function glFunctionArgsToString(functionName, args) {
      // apparently we can't do args.join(",");
      const argStrs = [];
      const numArgs = args.length;
      for (let ii = 0; ii < numArgs; ++ii) {
        argStrs.push(glFunctionArgToString(functionName, numArgs, ii, args[ii]));
      }
      return argStrs.join(', ');
    }
  
    function makePropertyWrapper(wrapper, original, propertyName) {
      wrapper.__defineGetter__(propertyName, function() {  // eslint-disable-line
        return original[propertyName];
      });
      // TODO(gmane): this needs to handle properties that take more than
      // one value?
      wrapper.__defineSetter__(propertyName, function(value) {  // eslint-disable-line
        original[propertyName] = value;
      });
    }
  
    /**
     * Given a WebGL context returns a wrapped context that calls
     * gl.getError after every command and calls a function if the
     * result is not gl.NO_ERROR.
     *
     * @param {!WebGLRenderingContext} ctx The webgl context to
     *        wrap.
     * @param {!function(err, funcName, args): void} opt_onErrorFunc
     *        The function to call when gl.getError returns an
     *        error. If not specified the default function calls
     *        console.log with a message.
     * @param {!function(funcName, args): void} opt_onFunc The
     *        function to call when each webgl function is called.
     *        You can use this to log all calls for example.
     * @param {!WebGLRenderingContext} opt_err_ctx The webgl context
     *        to call getError on if different than ctx.
     */
    function makeDebugContext(ctx, options) {
      options = options || {};
      const errCtx = options.errCtx || ctx;
      const onFunc = options.funcFunc;
      const sharedState = options.sharedState || {
        numDrawCallsRemaining: options.maxDrawCalls || -1,
        wrappers: {},
      };
      options.sharedState = sharedState;
  
      const errorFunc = options.errorFunc || function(err, functionName, args) {
        console.error(`WebGL error ${glEnumToString(err)} in ${functionName}(${glFunctionArgsToString(functionName, args)})`);  /* eslint-disable-line no-console */
      };
  
      // Holds booleans for each GL error so after we get the error ourselves
      // we can still return it to the client app.
      const glErrorShadow = { };
      const wrapper = {};
  
      function removeChecks() {
        Object.keys(sharedState.wrappers).forEach(function(name) {
          const pair = sharedState.wrappers[name];
          const wrapper = pair.wrapper;
          const orig = pair.orig;
          for (const propertyName in wrapper) {
            if (typeof wrapper[propertyName] === 'function') {
              wrapper[propertyName] = orig[propertyName].bind(orig);
            }
          }
        });
      }
  
      function checkMaxDrawCalls() {
        if (sharedState.numDrawCallsRemaining === 0) {
          removeChecks();
        }
        --sharedState.numDrawCallsRemaining;
      }
  
      function noop() {
      }
  
      // Makes a function that calls a WebGL function and then calls getError.
      function makeErrorWrapper(ctx, functionName) {
        const check = functionName.substring(0, 4) === 'draw' ? checkMaxDrawCalls : noop;
        return function() {
          if (onFunc) {
            onFunc(functionName, arguments);
          }
          const result = ctx[functionName].apply(ctx, arguments);
          const err = errCtx.getError();
          if (err !== 0) {
            glErrorShadow[err] = true;
            errorFunc(err, functionName, arguments);
          }
          check();
          return result;
        };
      }
  
      function makeGetExtensionWrapper(ctx, wrapped) {
        return function() {
          const extensionName = arguments[0];
          let ext = sharedState.wrappers[extensionName];
          if (!ext) {
            ext = wrapped.apply(ctx, arguments);
            if (ext) {
              const origExt = ext;
              ext = makeDebugContext(ext, Object.assign({}, options, {errCtx: ctx}));
              sharedState.wrappers[extensionName] = { wrapper: ext, orig: origExt };
              addEnumsForContext(origExt, extensionName);
            }
          }
          return ext;
        };
      }
  
      // Make a an object that has a copy of every property of the WebGL context
      // but wraps all functions.
      for (const propertyName in ctx) {
        if (typeof ctx[propertyName] === 'function') {
          if (propertyName !== 'getExtension') {
            wrapper[propertyName] = makeErrorWrapper(ctx, propertyName);
          } else {
            const wrapped = makeErrorWrapper(ctx, propertyName);
            wrapper[propertyName] = makeGetExtensionWrapper(ctx, wrapped);
          }
        } else {
          makePropertyWrapper(wrapper, ctx, propertyName);
        }
      }
  
      // Override the getError function with one that returns our saved results.
      if (wrapper.getError) {
        wrapper.getError = function() {
          for (const err of Object.keys(glErrorShadow)) {
            if (glErrorShadow[err]) {
              glErrorShadow[err] = false;
              return err;
            }
          }
          return ctx.NO_ERROR;
        };
      }
  
      if (wrapper.bindBuffer) {
        sharedState.wrappers['webgl'] = { wrapper: wrapper, orig: ctx };
        addEnumsForContext(ctx, ctx.bindBufferBase ? 'WebGL2' : 'WebGL');
      }
  
      return wrapper;
    }
  
    return {
      makeDebugContext,
      glFunctionArgsToString,
      glFunctionArgToString,
      glEnumToString,
    };
  
  }));
  
  