diff --git a/Makefile b/Makefile
index d934d5af..2e5358cb 100644
--- a/Makefile
+++ b/Makefile
@@ -15,10 +15,13 @@ www:
mkdir -p /var/www/css
cp web/css/*.css /var/www/css
mkdir -p /var/www/js
- cp web/js/*.js /var/www/js
+ cp web/js/main.js /var/www/js
+ cp web/js/addtohomescreen.min.js /var/www/js
+ cp web/js/j3di-all.min.js /var/www/js
mkdir -p /var/www/img
cp web/img/logo*.png /var/www/img
cp web/img/screen*.png /var/www/img
+ cp web/img/world.png /var/www/img
mkdir -p /var/www/maui
mkdir -p /var/www/maui/js
cp web/maui/js/angular-ui-router.min.js /var/www/maui/js
diff --git a/web/css/main.css b/web/css/main.css
index 3b9c5a59..135b85ad 100755
--- a/web/css/main.css
+++ b/web/css/main.css
@@ -6,16 +6,48 @@
.weather-page {}
+.gps_page {}
+
+.map-container {
+ position: relative;
+}
+
+.world-map {
+ background-image: url(../img/world.png);
+ background-position: 1186px 591px;
+ margin-bottom: 4px;
+ /* this will be set dynamically to center map at gps location */
+ /*
+ width: 100%;
+ height: 300px;
+*/
+}
+
+.mark-position {
+ position: absolute;
+ top: 0;
+ left: 0;
+ font-size: 18px;
+}
+
+.washout {
+ background-color: rgba(255, 255, 255, 0.65);
+ display: inline-block;
+ width: 100%;
+ text-align: center;
+ padding: 4px;
+}
+
.section_invisible {
display: none;
}
.text-normal {
- font-weight:100;
+ font-weight: 100;
}
.reset-flow {
- clear:both;
+ clear: both;
}
.separator {
@@ -86,20 +118,23 @@
}
.flight_condition_VFR {
- background-color:forestgreen;
- color:white;
+ background-color: forestgreen;
+ color: white;
}
+
.flight_condition_MVFR {
- background-color:blue;
- color:white;
+ background-color: blue;
+ color: white;
}
+
.flight_condition_IFR {
- background-color:crimson;
- color:white;
+ background-color: crimson;
+ color: white;
}
+
.flight_condition_LIFR {
- background-color:darkorchid;
- color:white;
+ background-color: darkorchid;
+ color: white;
}
.traffic-style1 {
@@ -129,16 +164,18 @@
}
.bar_container {
- display:inline-block;
- border:1px solid #cccccc;
+ display: inline-block;
+ border: 1px solid #cccccc;
width: 100%;
- border-radius: 2px;
- font-size: 0.75em;
+ border-radius: 2px;
+ font-size: 0.75em;
}
+
.bar_display {
padding: 1px 2px 1px 3px;
}
+
/* ***************************************************************************
everything below this comment represents tweeks to the mobile-angular-uis CSS
*************************************************************************** */
diff --git a/web/img/map-world-medium.png b/web/img/map-world-medium.png
new file mode 100755
index 00000000..47a1004c
Binary files /dev/null and b/web/img/map-world-medium.png differ
diff --git a/web/img/world.png b/web/img/world.png
new file mode 100755
index 00000000..3ead9b72
Binary files /dev/null and b/web/img/world.png differ
diff --git a/web/index.html b/web/index.html
index a867e77c..9d022712 100755
--- a/web/index.html
+++ b/web/index.html
@@ -39,9 +39,7 @@
@@ -57,12 +55,14 @@
-
+
+
+
diff --git a/web/js/j3di-all.js b/web/js/j3di-all.js
new file mode 100755
index 00000000..dd476d13
--- /dev/null
+++ b/web/js/j3di-all.js
@@ -0,0 +1,3344 @@
+/*
+ * Copyright 2010, Google Inc.
+ * 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 Google Inc. nor the names of its
+ * 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.
+ */
+
+
+/**
+ * @fileoverview This file contains functions every webgl program will need
+ * a version of one way or another.
+ *
+ * Instead of setting up a context manually it is recommended to
+ * use. This will check for success or failure. On failure it
+ * will attempt to present an approriate message to the user.
+ *
+ * gl = WebGLUtils.setupWebGL(canvas);
+ *
+ * For animated WebGL apps use of setTimeout or setInterval are
+ * discouraged. It is recommended you structure your rendering
+ * loop like this.
+ *
+ * function render() {
+ * window.requestAnimFrame(render, canvas);
+ *
+ * // do rendering
+ * ...
+ * }
+ * render();
+ *
+ * This will call your rendering function up to the refresh rate
+ * of your display but will stop rendering if your app is not
+ * visible.
+ */
+
+WebGLUtils = function () {
+
+ /**
+ * Creates the HTLM for a failure message
+ * @param {string} canvasContainerId id of container of th
+ * canvas.
+ * @return {string} The html.
+ */
+ var makeFailHTML = function (msg) {
+ return '' +
+ '' +
+ '' +
+ '' +
+ '
' + msg + '
' +
+ '
' +
+ '
';
+ };
+
+ /**
+ * Mesasge for getting a webgl browser
+ * @type {string}
+ */
+ var GET_A_WEBGL_BROWSER = '' +
+ 'This page requires a browser that supports WebGL. ' +
+ 'Click here to upgrade your browser. ';
+
+ /**
+ * Mesasge for need better hardware
+ * @type {string}
+ */
+ var OTHER_PROBLEM = '' +
+ "It doesn't appear your computer can support WebGL. " +
+ 'Click here for more information. ';
+
+ /**
+ * Creates a webgl context. If creation fails it will
+ * change the contents of the container of the
+ * tag to an error message with the correct links for WebGL.
+ * @param {Element} canvas. The canvas element to create a
+ * context from.
+ * @param {WebGLContextCreationAttirbutes} opt_attribs Any
+ * creation attributes you want to pass in.
+ * @return {WebGLRenderingContext} The created context.
+ */
+ var setupWebGL = function (canvas, opt_attribs) {
+ function showLink(str) {
+ var container = canvas.parentNode;
+ if (container) {
+ container.innerHTML = makeFailHTML(str);
+ }
+ };
+
+ if (!window.WebGLRenderingContext) {
+ showLink(GET_A_WEBGL_BROWSER);
+ return null;
+ }
+
+ var context = create3DContext(canvas, opt_attribs);
+ if (!context) {
+ showLink(OTHER_PROBLEM);
+ }
+ return context;
+ };
+
+ /**
+ * Creates a webgl context.
+ * @param {!Canvas} canvas The canvas tag to get context
+ * from. If one is not passed in one will be created.
+ * @return {!WebGLContext} The created context.
+ */
+ var create3DContext = function (canvas, opt_attribs) {
+ var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
+ var context = null;
+ for (var ii = 0; ii < names.length; ++ii) {
+ try {
+ context = canvas.getContext(names[ii], opt_attribs);
+ } catch (e) {}
+ if (context) {
+ break;
+ }
+ }
+ return context;
+ };
+
+ return {
+ create3DContext: create3DContext,
+ setupWebGL: setupWebGL
+ };
+}();
+
+/**
+ * Provides requestAnimationFrame in a cross browser way.
+ */
+window.requestAnimFrame = (function () {
+ return window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function ( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
+ return window.setTimeout(callback, 1000 / 60);
+ };
+})();
+
+/**
+ * Provides cancelAnimationFrame in a cross browser way.
+ */
+window.cancelAnimFrame = (function () {
+ return window.cancelAnimationFrame ||
+ window.webkitCancelAnimationFrame ||
+ window.mozCancelAnimationFrame ||
+ window.oCancelAnimationFrame ||
+ window.msCancelAnimationFrame ||
+ window.clearTimeout;
+})();
+
+
+/*
+ ** 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.
+ */
+
+// Various functions for helping debug WebGL apps.
+
+WebGLDebugUtils = function () {
+
+ /**
+ * Wrapped logging function.
+ * @param {string} msg Message to log.
+ */
+ var log = function (msg) {
+ if (window.console && window.console.log) {
+ window.console.log(msg);
+ }
+ };
+
+ /**
+ * Wrapped error logging function.
+ * @param {string} msg Message to log.
+ */
+ var error = function (msg) {
+ if (window.console && window.console.error) {
+ window.console.error(msg);
+ } else {
+ log(msg);
+ }
+ };
+
+
+ /**
+ * 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
+ *
+ * @type {!Object.}
+ */
+ var 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
+ }
+ },
+
+ // 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
+ }
+ },
+
+ // 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
+ }
+ },
+ 'texSubImage2D': {
+ 9: {
+ 0: true,
+ 6: true,
+ 7: true
+ },
+ 7: {
+ 0: true,
+ 4: true,
+ 5: true
+ }
+ },
+ 'copyTexImage2D': {
+ 8: {
+ 0: true,
+ 2: true
+ }
+ },
+ 'copyTexSubImage2D': {
+ 8: {
+ 0: true
+ }
+ },
+ 'generateMipmap': {
+ 1: {
+ 0: true
+ }
+ },
+ 'compressedTexImage2D': {
+ 7: {
+ 0: true,
+ 2: true
+ }
+ },
+ 'compressedTexSubImage2D': {
+ 8: {
+ 0: true,
+ 6: true
+ }
+ },
+
+ // Buffer objects
+
+ 'bindBuffer': {
+ 2: {
+ 0: true
+ }
+ },
+ 'bufferData': {
+ 3: {
+ 0: true,
+ 2: true
+ }
+ },
+ 'bufferSubData': {
+ 3: {
+ 0: true
+ }
+ },
+ 'getBufferParameter': {
+ 2: {
+ 0: true,
+ 1: true
+ }
+ },
+
+ // Renderbuffers and framebuffers
+
+ 'pixelStorei': {
+ 2: {
+ 0: true,
+ 1: true
+ }
+ },
+ 'readPixels': {
+ 7: {
+ 4: true,
+ 5: true
+ }
+ },
+ 'bindRenderbuffer': {
+ 2: {
+ 0: true
+ }
+ },
+ 'bindFramebuffer': {
+ 2: {
+ 0: true
+ }
+ },
+ 'checkFramebufferStatus': {
+ 1: {
+ 0: true
+ }
+ },
+ 'framebufferRenderbuffer': {
+ 4: {
+ 0: true,
+ 1: true,
+ 2: true
+ }
+ },
+ 'framebufferTexture2D': {
+ 5: {
+ 0: true,
+ 1: true,
+ 2: true
+ }
+ },
+ 'getFramebufferAttachmentParameter': {
+ 3: {
+ 0: true,
+ 1: true,
+ 2: true
+ }
+ },
+ 'getRenderbufferParameter': {
+ 2: {
+ 0: true,
+ 1: true
+ }
+ },
+ 'renderbufferStorage': {
+ 4: {
+ 0: true,
+ 1: true
+ }
+ },
+
+ // Frame buffer operations (clear, blend, depth test, stencil)
+
+ 'clear': {
+ 1: {
+ 0: {
+ 'enumBitwiseOr': ['COLOR_BUFFER_BIT', 'DEPTH_BUFFER_BIT', 'STENCIL_BUFFER_BIT']
+ }
+ }
+ },
+ '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
+ }
+ }
+ };
+
+ /**
+ * Map of numbers to names.
+ * @type {Object}
+ */
+ var glEnums = null;
+
+ /**
+ * Map of names to numbers.
+ * @type {Object}
+ */
+ var enumStringToValue = null;
+
+ /**
+ * 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 init(ctx) {
+ if (glEnums == null) {
+ glEnums = {};
+ enumStringToValue = {};
+ for (var propertyName in ctx) {
+ if (typeof ctx[propertyName] == 'number') {
+ glEnums[ctx[propertyName]] = propertyName;
+ enumStringToValue[propertyName] = ctx[propertyName];
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks the utils have been initialized.
+ */
+ function checkInit() {
+ if (glEnums == null) {
+ throw 'WebGLDebugUtils.init(ctx) not called';
+ }
+ }
+
+ /**
+ * Returns true or false if value matches any WebGL enum
+ * @param {*} value Value to check if it might be an enum.
+ * @return {boolean} True if value matches one of the WebGL defined enums
+ */
+ function mightBeEnum(value) {
+ checkInit();
+ return (glEnums[value] !== undefined);
+ }
+
+ /**
+ * 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) {
+ checkInit();
+ var 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) {
+ var funcInfo = glValidEnumContexts[functionName];
+ if (funcInfo !== undefined) {
+ var funcInfo = funcInfo[numArgs];
+ if (funcInfo !== undefined) {
+ if (funcInfo[argumentIndex]) {
+ if (typeof funcInfo[argumentIndex] === 'object' &&
+ funcInfo[argumentIndex]['enumBitwiseOr'] !== undefined) {
+ var enums = funcInfo[argumentIndex]['enumBitwiseOr'];
+ var orResult = 0;
+ var orEnums = [];
+ for (var i = 0; i < enums.length; ++i) {
+ var enumValue = enumStringToValue[enums[i]];
+ if ((value & enumValue) !== 0) {
+ orResult |= enumValue;
+ orEnums.push(glEnumToString(enumValue));
+ }
+ }
+ if (orResult === value) {
+ return orEnums.join(' | ');
+ } else {
+ return glEnumToString(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(",");
+ var argStr = "";
+ var numArgs = args.length;
+ for (var ii = 0; ii < numArgs; ++ii) {
+ argStr += ((ii == 0) ? '' : ', ') +
+ glFunctionArgToString(functionName, numArgs, ii, args[ii]);
+ }
+ return argStr;
+ };
+
+
+ function makePropertyWrapper(wrapper, original, propertyName) {
+ //log("wrap prop: " + propertyName);
+ wrapper.__defineGetter__(propertyName, function () {
+ return original[propertyName];
+ });
+ // TODO(gmane): this needs to handle properties that take more than
+ // one value?
+ wrapper.__defineSetter__(propertyName, function (value) {
+ //log("set: " + propertyName);
+ original[propertyName] = value;
+ });
+ }
+
+ // Makes a function that calls a function on another object.
+ function makeFunctionWrapper(original, functionName) {
+ //log("wrap fn: " + functionName);
+ var f = original[functionName];
+ return function () {
+ //log("call: " + functionName);
+ var result = f.apply(original, arguments);
+ return result;
+ };
+ }
+
+ /**
+ * 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, opt_onErrorFunc, opt_onFunc, opt_err_ctx) {
+ opt_err_ctx = opt_err_ctx || ctx;
+ init(ctx);
+ opt_onErrorFunc = opt_onErrorFunc || function (err, functionName, args) {
+ // apparently we can't do args.join(",");
+ var argStr = "";
+ var numArgs = args.length;
+ for (var ii = 0; ii < numArgs; ++ii) {
+ argStr += ((ii == 0) ? '' : ', ') +
+ glFunctionArgToString(functionName, numArgs, ii, args[ii]);
+ }
+ error("WebGL error " + glEnumToString(err) + " in " + functionName +
+ "(" + argStr + ")");
+ };
+
+ // Holds booleans for each GL error so after we get the error ourselves
+ // we can still return it to the client app.
+ var glErrorShadow = {};
+
+ // Makes a function that calls a WebGL function and then calls getError.
+ function makeErrorWrapper(ctx, functionName) {
+ return function () {
+ if (opt_onFunc) {
+ opt_onFunc(functionName, arguments);
+ }
+ var result = ctx[functionName].apply(ctx, arguments);
+ var err = opt_err_ctx.getError();
+ if (err != 0) {
+ glErrorShadow[err] = true;
+ opt_onErrorFunc(err, functionName, arguments);
+ }
+ return result;
+ };
+ }
+
+ // Make a an object that has a copy of every property of the WebGL context
+ // but wraps all functions.
+ var wrapper = {};
+ for (var propertyName in ctx) {
+ if (typeof ctx[propertyName] == 'function') {
+ if (propertyName != 'getExtension') {
+ wrapper[propertyName] = makeErrorWrapper(ctx, propertyName);
+ } else {
+ var wrapped = makeErrorWrapper(ctx, propertyName);
+ wrapper[propertyName] = function () {
+ var result = wrapped.apply(ctx, arguments);
+ return makeDebugContext(result, opt_onErrorFunc, opt_onFunc, opt_err_ctx);
+ };
+ }
+ } else {
+ makePropertyWrapper(wrapper, ctx, propertyName);
+ }
+ }
+
+ // Override the getError function with one that returns our saved results.
+ wrapper.getError = function () {
+ for (var err in glErrorShadow) {
+ if (glErrorShadow.hasOwnProperty(err)) {
+ if (glErrorShadow[err]) {
+ glErrorShadow[err] = false;
+ return err;
+ }
+ }
+ }
+ return ctx.NO_ERROR;
+ };
+
+ return wrapper;
+ }
+
+ function resetToInitialState(ctx) {
+ var numAttribs = ctx.getParameter(ctx.MAX_VERTEX_ATTRIBS);
+ var tmp = ctx.createBuffer();
+ ctx.bindBuffer(ctx.ARRAY_BUFFER, tmp);
+ for (var ii = 0; ii < numAttribs; ++ii) {
+ ctx.disableVertexAttribArray(ii);
+ ctx.vertexAttribPointer(ii, 4, ctx.FLOAT, false, 0, 0);
+ ctx.vertexAttrib1f(ii, 0);
+ }
+ ctx.deleteBuffer(tmp);
+
+ var numTextureUnits = ctx.getParameter(ctx.MAX_TEXTURE_IMAGE_UNITS);
+ for (var ii = 0; ii < numTextureUnits; ++ii) {
+ ctx.activeTexture(ctx.TEXTURE0 + ii);
+ ctx.bindTexture(ctx.TEXTURE_CUBE_MAP, null);
+ ctx.bindTexture(ctx.TEXTURE_2D, null);
+ }
+
+ ctx.activeTexture(ctx.TEXTURE0);
+ ctx.useProgram(null);
+ ctx.bindBuffer(ctx.ARRAY_BUFFER, null);
+ ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null);
+ ctx.bindFramebuffer(ctx.FRAMEBUFFER, null);
+ ctx.bindRenderbuffer(ctx.RENDERBUFFER, null);
+ ctx.disable(ctx.BLEND);
+ ctx.disable(ctx.CULL_FACE);
+ ctx.disable(ctx.DEPTH_TEST);
+ ctx.disable(ctx.DITHER);
+ ctx.disable(ctx.SCISSOR_TEST);
+ ctx.blendColor(0, 0, 0, 0);
+ ctx.blendEquation(ctx.FUNC_ADD);
+ ctx.blendFunc(ctx.ONE, ctx.ZERO);
+ ctx.clearColor(0, 0, 0, 0);
+ ctx.clearDepth(1);
+ ctx.clearStencil(-1);
+ ctx.colorMask(true, true, true, true);
+ ctx.cullFace(ctx.BACK);
+ ctx.depthFunc(ctx.LESS);
+ ctx.depthMask(true);
+ ctx.depthRange(0, 1);
+ ctx.frontFace(ctx.CCW);
+ ctx.hint(ctx.GENERATE_MIPMAP_HINT, ctx.DONT_CARE);
+ ctx.lineWidth(1);
+ ctx.pixelStorei(ctx.PACK_ALIGNMENT, 4);
+ ctx.pixelStorei(ctx.UNPACK_ALIGNMENT, 4);
+ ctx.pixelStorei(ctx.UNPACK_FLIP_Y_WEBGL, false);
+ ctx.pixelStorei(ctx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
+ // TODO: Delete this IF.
+ if (ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL) {
+ ctx.pixelStorei(ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL, ctx.BROWSER_DEFAULT_WEBGL);
+ }
+ ctx.polygonOffset(0, 0);
+ ctx.sampleCoverage(1, false);
+ ctx.scissor(0, 0, ctx.canvas.width, ctx.canvas.height);
+ ctx.stencilFunc(ctx.ALWAYS, 0, 0xFFFFFFFF);
+ ctx.stencilMask(0xFFFFFFFF);
+ ctx.stencilOp(ctx.KEEP, ctx.KEEP, ctx.KEEP);
+ ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height);
+ ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT);
+
+ // TODO: This should NOT be needed but Firefox fails with 'hint'
+ while (ctx.getError());
+ }
+
+ function makeLostContextSimulatingCanvas(canvas) {
+ var unwrappedContext_;
+ var wrappedContext_;
+ var onLost_ = [];
+ var onRestored_ = [];
+ var wrappedContext_ = {};
+ var contextId_ = 1;
+ var contextLost_ = false;
+ var resourceId_ = 0;
+ var resourceDb_ = [];
+ var numCallsToLoseContext_ = 0;
+ var numCalls_ = 0;
+ var canRestore_ = false;
+ var restoreTimeout_ = 0;
+
+ // Holds booleans for each GL error so can simulate errors.
+ var glErrorShadow_ = {};
+
+ canvas.getContext = function (f) {
+ return function () {
+ var ctx = f.apply(canvas, arguments);
+ // Did we get a context and is it a WebGL context?
+ if (ctx instanceof WebGLRenderingContext) {
+ if (ctx != unwrappedContext_) {
+ if (unwrappedContext_) {
+ throw "got different context"
+ }
+ unwrappedContext_ = ctx;
+ wrappedContext_ = makeLostContextSimulatingContext(unwrappedContext_);
+ }
+ return wrappedContext_;
+ }
+ return ctx;
+ }
+ }(canvas.getContext);
+
+ function wrapEvent(listener) {
+ if (typeof (listener) == "function") {
+ return listener;
+ } else {
+ return function (info) {
+ listener.handleEvent(info);
+ }
+ }
+ }
+
+ var addOnContextLostListener = function (listener) {
+ onLost_.push(wrapEvent(listener));
+ };
+
+ var addOnContextRestoredListener = function (listener) {
+ onRestored_.push(wrapEvent(listener));
+ };
+
+
+ function wrapAddEventListener(canvas) {
+ var f = canvas.addEventListener;
+ canvas.addEventListener = function (type, listener, bubble) {
+ switch (type) {
+ case 'webglcontextlost':
+ addOnContextLostListener(listener);
+ break;
+ case 'webglcontextrestored':
+ addOnContextRestoredListener(listener);
+ break;
+ default:
+ f.apply(canvas, arguments);
+ }
+ };
+ }
+
+ wrapAddEventListener(canvas);
+
+ canvas.loseContext = function () {
+ if (!contextLost_) {
+ contextLost_ = true;
+ numCallsToLoseContext_ = 0;
+ ++contextId_;
+ while (unwrappedContext_.getError());
+ clearErrors();
+ glErrorShadow_[unwrappedContext_.CONTEXT_LOST_WEBGL] = true;
+ var event = makeWebGLContextEvent("context lost");
+ var callbacks = onLost_.slice();
+ setTimeout(function () {
+ //log("numCallbacks:" + callbacks.length);
+ for (var ii = 0; ii < callbacks.length; ++ii) {
+ //log("calling callback:" + ii);
+ callbacks[ii](event);
+ }
+ if (restoreTimeout_ >= 0) {
+ setTimeout(function () {
+ canvas.restoreContext();
+ }, restoreTimeout_);
+ }
+ }, 0);
+ }
+ };
+
+ canvas.restoreContext = function () {
+ if (contextLost_) {
+ if (onRestored_.length) {
+ setTimeout(function () {
+ if (!canRestore_) {
+ throw "can not restore. webglcontestlost listener did not call event.preventDefault";
+ }
+ freeResources();
+ resetToInitialState(unwrappedContext_);
+ contextLost_ = false;
+ numCalls_ = 0;
+ canRestore_ = false;
+ var callbacks = onRestored_.slice();
+ var event = makeWebGLContextEvent("context restored");
+ for (var ii = 0; ii < callbacks.length; ++ii) {
+ callbacks[ii](event);
+ }
+ }, 0);
+ }
+ }
+ };
+
+ canvas.loseContextInNCalls = function (numCalls) {
+ if (contextLost_) {
+ throw "You can not ask a lost contet to be lost";
+ }
+ numCallsToLoseContext_ = numCalls_ + numCalls;
+ };
+
+ canvas.getNumCalls = function () {
+ return numCalls_;
+ };
+
+ canvas.setRestoreTimeout = function (timeout) {
+ restoreTimeout_ = timeout;
+ };
+
+ function isWebGLObject(obj) {
+ //return false;
+ return (obj instanceof WebGLBuffer ||
+ obj instanceof WebGLFramebuffer ||
+ obj instanceof WebGLProgram ||
+ obj instanceof WebGLRenderbuffer ||
+ obj instanceof WebGLShader ||
+ obj instanceof WebGLTexture);
+ }
+
+ function checkResources(args) {
+ for (var ii = 0; ii < args.length; ++ii) {
+ var arg = args[ii];
+ if (isWebGLObject(arg)) {
+ return arg.__webglDebugContextLostId__ == contextId_;
+ }
+ }
+ return true;
+ }
+
+ function clearErrors() {
+ var k = Object.keys(glErrorShadow_);
+ for (var ii = 0; ii < k.length; ++ii) {
+ delete glErrorShadow_[k];
+ }
+ }
+
+ function loseContextIfTime() {
+ ++numCalls_;
+ if (!contextLost_) {
+ if (numCallsToLoseContext_ == numCalls_) {
+ canvas.loseContext();
+ }
+ }
+ }
+
+ // Makes a function that simulates WebGL when out of context.
+ function makeLostContextFunctionWrapper(ctx, functionName) {
+ var f = ctx[functionName];
+ return function () {
+ // log("calling:" + functionName);
+ // Only call the functions if the context is not lost.
+ loseContextIfTime();
+ if (!contextLost_) {
+ //if (!checkResources(arguments)) {
+ // glErrorShadow_[wrappedContext_.INVALID_OPERATION] = true;
+ // return;
+ //}
+ var result = f.apply(ctx, arguments);
+ return result;
+ }
+ };
+ }
+
+ function freeResources() {
+ for (var ii = 0; ii < resourceDb_.length; ++ii) {
+ var resource = resourceDb_[ii];
+ if (resource instanceof WebGLBuffer) {
+ unwrappedContext_.deleteBuffer(resource);
+ } else if (resource instanceof WebGLFramebuffer) {
+ unwrappedContext_.deleteFramebuffer(resource);
+ } else if (resource instanceof WebGLProgram) {
+ unwrappedContext_.deleteProgram(resource);
+ } else if (resource instanceof WebGLRenderbuffer) {
+ unwrappedContext_.deleteRenderbuffer(resource);
+ } else if (resource instanceof WebGLShader) {
+ unwrappedContext_.deleteShader(resource);
+ } else if (resource instanceof WebGLTexture) {
+ unwrappedContext_.deleteTexture(resource);
+ }
+ }
+ }
+
+ function makeWebGLContextEvent(statusMessage) {
+ return {
+ statusMessage: statusMessage,
+ preventDefault: function () {
+ canRestore_ = true;
+ }
+ };
+ }
+
+ return canvas;
+
+ function makeLostContextSimulatingContext(ctx) {
+ // copy all functions and properties to wrapper
+ for (var propertyName in ctx) {
+ if (typeof ctx[propertyName] == 'function') {
+ wrappedContext_[propertyName] = makeLostContextFunctionWrapper(
+ ctx, propertyName);
+ } else {
+ makePropertyWrapper(wrappedContext_, ctx, propertyName);
+ }
+ }
+
+ // Wrap a few functions specially.
+ wrappedContext_.getError = function () {
+ loseContextIfTime();
+ if (!contextLost_) {
+ var err;
+ while (err = unwrappedContext_.getError()) {
+ glErrorShadow_[err] = true;
+ }
+ }
+ for (var err in glErrorShadow_) {
+ if (glErrorShadow_[err]) {
+ delete glErrorShadow_[err];
+ return err;
+ }
+ }
+ return wrappedContext_.NO_ERROR;
+ };
+
+ var creationFunctions = [
+ "createBuffer",
+ "createFramebuffer",
+ "createProgram",
+ "createRenderbuffer",
+ "createShader",
+ "createTexture"
+ ];
+ for (var ii = 0; ii < creationFunctions.length; ++ii) {
+ var functionName = creationFunctions[ii];
+ wrappedContext_[functionName] = function (f) {
+ return function () {
+ loseContextIfTime();
+ if (contextLost_) {
+ return null;
+ }
+ var obj = f.apply(ctx, arguments);
+ obj.__webglDebugContextLostId__ = contextId_;
+ resourceDb_.push(obj);
+ return obj;
+ };
+ }(ctx[functionName]);
+ }
+
+ var functionsThatShouldReturnNull = [
+ "getActiveAttrib",
+ "getActiveUniform",
+ "getBufferParameter",
+ "getContextAttributes",
+ "getAttachedShaders",
+ "getFramebufferAttachmentParameter",
+ "getParameter",
+ "getProgramParameter",
+ "getProgramInfoLog",
+ "getRenderbufferParameter",
+ "getShaderParameter",
+ "getShaderInfoLog",
+ "getShaderSource",
+ "getTexParameter",
+ "getUniform",
+ "getUniformLocation",
+ "getVertexAttrib"
+ ];
+ for (var ii = 0; ii < functionsThatShouldReturnNull.length; ++ii) {
+ var functionName = functionsThatShouldReturnNull[ii];
+ wrappedContext_[functionName] = function (f) {
+ return function () {
+ loseContextIfTime();
+ if (contextLost_) {
+ return null;
+ }
+ return f.apply(ctx, arguments);
+ }
+ }(wrappedContext_[functionName]);
+ }
+
+ var isFunctions = [
+ "isBuffer",
+ "isEnabled",
+ "isFramebuffer",
+ "isProgram",
+ "isRenderbuffer",
+ "isShader",
+ "isTexture"
+ ];
+ for (var ii = 0; ii < isFunctions.length; ++ii) {
+ var functionName = isFunctions[ii];
+ wrappedContext_[functionName] = function (f) {
+ return function () {
+ loseContextIfTime();
+ if (contextLost_) {
+ return false;
+ }
+ return f.apply(ctx, arguments);
+ }
+ }(wrappedContext_[functionName]);
+ }
+
+ wrappedContext_.checkFramebufferStatus = function (f) {
+ return function () {
+ loseContextIfTime();
+ if (contextLost_) {
+ return wrappedContext_.FRAMEBUFFER_UNSUPPORTED;
+ }
+ return f.apply(ctx, arguments);
+ };
+ }(wrappedContext_.checkFramebufferStatus);
+
+ wrappedContext_.getAttribLocation = function (f) {
+ return function () {
+ loseContextIfTime();
+ if (contextLost_) {
+ return -1;
+ }
+ return f.apply(ctx, arguments);
+ };
+ }(wrappedContext_.getAttribLocation);
+
+ wrappedContext_.getVertexAttribOffset = function (f) {
+ return function () {
+ loseContextIfTime();
+ if (contextLost_) {
+ return 0;
+ }
+ return f.apply(ctx, arguments);
+ };
+ }(wrappedContext_.getVertexAttribOffset);
+
+ wrappedContext_.isContextLost = function () {
+ return contextLost_;
+ };
+
+ return wrappedContext_;
+ }
+ }
+
+ return {
+ /**
+ * 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.
+ */
+ 'init': init,
+
+ /**
+ * Returns true or false if value matches any WebGL enum
+ * @param {*} value Value to check if it might be an enum.
+ * @return {boolean} True if value matches one of the WebGL defined enums
+ */
+ 'mightBeEnum': mightBeEnum,
+
+ /**
+ * Gets an string version of an WebGL enum.
+ *
+ * Example:
+ * WebGLDebugUtil.init(ctx);
+ * var str = WebGLDebugUtil.glEnumToString(ctx.getError());
+ *
+ * @param {number} value Value to return an enum for
+ * @return {string} The string version of the enum.
+ */
+ 'glEnumToString': glEnumToString,
+
+ /**
+ * Converts the argument of a WebGL function to a string.
+ * Attempts to convert enum arguments to strings.
+ *
+ * Example:
+ * WebGLDebugUtil.init(ctx);
+ * var str = WebGLDebugUtil.glFunctionArgToString('bindTexture', 2, 0, gl.TEXTURE_2D);
+ *
+ * would return 'TEXTURE_2D'
+ *
+ * @param {string} functionName the name of the WebGL function.
+ * @param {number} numArgs The number of arguments
+ * @param {number} argumentIndx the index of the argument.
+ * @param {*} value The value of the argument.
+ * @return {string} The value as a string.
+ */
+ 'glFunctionArgToString': glFunctionArgToString,
+
+ /**
+ * 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.
+ */
+ 'glFunctionArgsToString': glFunctionArgsToString,
+
+ /**
+ * Given a WebGL context returns a wrapped context that calls
+ * gl.getError after every command and calls a function if the
+ * result is not NO_ERROR.
+ *
+ * You can supply your own function if you want. For example, if you'd like
+ * an exception thrown on any GL error you could do this
+ *
+ * function throwOnGLError(err, funcName, args) {
+ * throw WebGLDebugUtils.glEnumToString(err) +
+ * " was caused by call to " + funcName;
+ * };
+ *
+ * ctx = WebGLDebugUtils.makeDebugContext(
+ * canvas.getContext("webgl"), throwOnGLError);
+ *
+ * @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.
+ */
+ 'makeDebugContext': makeDebugContext,
+
+ /**
+ * Given a canvas element returns a wrapped canvas element that will
+ * simulate lost context. The canvas returned adds the following functions.
+ *
+ * loseContext:
+ * simulates a lost context event.
+ *
+ * restoreContext:
+ * simulates the context being restored.
+ *
+ * lostContextInNCalls:
+ * loses the context after N gl calls.
+ *
+ * getNumCalls:
+ * tells you how many gl calls there have been so far.
+ *
+ * setRestoreTimeout:
+ * sets the number of milliseconds until the context is restored
+ * after it has been lost. Defaults to 0. Pass -1 to prevent
+ * automatic restoring.
+ *
+ * @param {!Canvas} canvas The canvas element to wrap.
+ */
+ 'makeLostContextSimulatingCanvas': makeLostContextSimulatingCanvas,
+
+ /**
+ * Resets a context to the initial state.
+ * @param {!WebGLRenderingContext} ctx The webgl context to
+ * reset.
+ */
+ 'resetToInitialState': resetToInitialState
+ };
+
+}();
+
+
+/*
+ * Copyright (C) 2009 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
+ */
+
+//
+// initWebGL
+//
+// Initialize the Canvas element with the passed name as a WebGL object and return the
+// WebGLRenderingContext.
+function initWebGL(canvasName, vshader, fshader, attribs, clearColor, clearDepth) {
+ var canvas = document.getElementById(canvasName);
+ return gl = WebGLUtils.setupWebGL(canvas);
+}
+
+function log(msg) {
+ if (window.console && window.console.log) {
+ window.console.log(msg);
+ }
+}
+
+// Load shaders with the passed names and create a program with them. Return this program
+// in the 'program' property of the returned context.
+//
+// For each string in the passed attribs array, bind an attrib with that name at that index.
+// Once the attribs are bound, link the program and then use it.
+//
+// Set the clear color to the passed array (4 values) and set the clear depth to the passed value.
+// Enable depth testing and blending with a blend func of (SRC_ALPHA, ONE_MINUS_SRC_ALPHA)
+//
+// A console function is added to the context: console(string). This can be replaced
+// by the caller. By default, it maps to the window.console() function on WebKit and to
+// an empty function on other browsers.
+//
+function simpleSetup(gl, vshader, fshader, attribs, clearColor, clearDepth) {
+ // create our shaders
+ var vertexShader = loadShader(gl, vshader);
+ var fragmentShader = loadShader(gl, fshader);
+
+ // Create the program object
+ var program = gl.createProgram();
+
+ // Attach our two shaders to the program
+ gl.attachShader(program, vertexShader);
+ gl.attachShader(program, fragmentShader);
+
+ // Bind attributes
+ for (var i = 0; i < attribs.length; ++i)
+ gl.bindAttribLocation(program, i, attribs[i]);
+
+ // Link the program
+ gl.linkProgram(program);
+
+ // Check the link status
+ var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
+ if (!linked && !gl.isContextLost()) {
+ // something went wrong with the link
+ var error = gl.getProgramInfoLog(program);
+ log("Error in program linking:" + error);
+
+ gl.deleteProgram(program);
+ gl.deleteProgram(fragmentShader);
+ gl.deleteProgram(vertexShader);
+
+ return null;
+ }
+
+ gl.useProgram(program);
+
+ gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
+ gl.clearDepth(clearDepth);
+
+ gl.enable(gl.DEPTH_TEST);
+ gl.enable(gl.BLEND);
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
+
+ return program;
+}
+
+//
+// loadShader
+//
+// 'shaderId' is the id of a