webglimpse
Version:
Webglimpse is a data visualization library for the web.
442 lines • 84.7 kB
JavaScript
/*
* Copyright (c) 2014, Metron, 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.
*
* 3. Neither the name of the copyright holder 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 HOLDER 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.
*/
import { Cache } from './util/cache';
import { Texture2D } from './texture';
import { black, parseRgba } from './color';
import { TwoKeyCache, ThreeKeyCache, SixKeyCache } from './util/multikey_cache';
import { GL, concatLines, hasval } from './util/util';
import { Program, Uniform2f, Uniform1f, UniformSampler2D, Attribute } from './shader';
import { newStaticBuffer } from './buffer';
import { nearestPixel } from './misc';
const textDim = (function () {
// Use div to figure out how big our texture needs to be
const div = document.createElement('div');
div.style.setProperty('position', 'absolute');
div.style.setProperty('padding', '0');
div.style.setProperty('margin', '0');
div.style.setProperty('width', 'auto');
div.style.setProperty('height', 'auto');
div.style.setProperty('visibility', 'hidden');
return function (s, font) {
div.style.setProperty('font', font);
div.textContent = s;
document.body.appendChild(div);
const width = div.clientWidth;
const height = div.clientHeight;
document.body.removeChild(div);
return { w: width, h: height };
};
})();
function newFontMetricsCache() {
return new Cache({
create: function (font) {
const dim = textDim('fMgyj', font);
const w = dim.w;
const h = dim.h;
const canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
const g = canvas.getContext('2d', { willReadFrequently: true });
g.font = font;
g.textAlign = 'left';
g.textBaseline = 'top';
g.fillStyle = 'black';
g.clearRect(0, 0, w, h);
g.fillText('fM', 0, 0);
let rgbaData = g.getImageData(0, 0, w, h).data;
let jTop = -1;
for (let j = 0; j < h && jTop < 0; j++) {
for (let i = 0; i < w && jTop < 0; i++) {
const alpha = rgbaData[(j * w + i) * 4 + 3];
if (alpha !== 0) {
jTop = j;
}
}
}
let jBaseline = -1;
for (let j = h - 1; j >= 0 && jBaseline < 0; j--) {
for (let i = 0; i < w && jBaseline < 0; i++) {
const alpha = rgbaData[(j * w + i) * 4 + 3];
if (alpha !== 0) {
jBaseline = j;
}
}
}
g.clearRect(0, 0, w, h);
g.fillText('gyj', 0, 0);
rgbaData = g.getImageData(0, 0, w, h).data;
let jBottom = -1;
for (let j = h - 1; j >= 0 && jBottom < 0; j--) {
for (let i = 0; i < w && jBottom < 0; i++) {
const alpha = rgbaData[(j * w + i) * 4 + 3];
if (alpha !== 0) {
jBottom = j;
}
}
}
return { jTop: jTop, jBaseline: jBaseline, jBottom: jBottom };
},
dispose: function () { }
});
}
const getRawFontMetrics = (function () {
const cache = newFontMetricsCache();
return function (font) {
return cache.value(font);
};
})();
export const getTextWidth = (function () {
const canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 1;
const g = canvas.getContext('2d');
g.textAlign = 'left';
g.textBaseline = 'top';
return function (font, text) {
g.font = font;
return g.measureText(text).width;
};
})();
export class TextTexture2D extends Texture2D {
constructor(w, h, jBaseline, minFilter, magFilter, draw) {
super(w, h, minFilter, magFilter, draw);
this._jBaseline = jBaseline;
}
get jBaseline() { return this._jBaseline; }
yAnchor(textFrac) {
// Things tend to look the way you expect if textFrac is interpreted as
// a fraction of the way from the bottom of the baseline pixel up to the
// top of the top pixel
//
const bottom = this.jBaseline + 1;
const h = this.h;
return 1 - ((1 - textFrac) * bottom / h);
}
}
export function newTextTextureCache(font, color) {
const createTextTexture = createTextTextureFactory(font);
return new Cache({
create: function (text) {
return createTextTexture(color, text);
},
dispose: function (texture) {
texture.dispose();
}
});
}
export function newTextTextureCache2(font) {
const createTextTexture = createTextTextureFactory(font);
return new TwoKeyCache({
create: function (rgbaString, text) {
const color = parseRgba(rgbaString);
return createTextTexture(color, text);
},
dispose: function (texture) {
texture.dispose();
}
});
}
export function newTextTextureCache3() {
return new ThreeKeyCache({
create: function (font, rgbaString, text) {
const createTextTexture = createTextTextureFactory(font);
const color = parseRgba(rgbaString);
return createTextTexture(color, text);
},
dispose: function (texture) {
texture.dispose();
}
});
}
export function newTextTextureCache6() {
return new SixKeyCache({
create: function (font, rgbaString, text, rgbaBgString, bgPadding, bgBorderRadius) {
const createTextTexture = createTextTextureFactory(font);
const color = parseRgba(rgbaString);
const bgColor = parseRgba(rgbaBgString);
const bgBorderRadiusNumber = parseInt(bgBorderRadius, 10);
const paddingNumber = parseInt(bgPadding, 10);
return createTextTexture(color, text, bgColor, paddingNumber, bgBorderRadiusNumber);
},
dispose: function (texture) {
texture.dispose();
}
});
}
export function createTextTextureFactory(font) {
const rawFontMetrics = getRawFontMetrics(font);
let jBaseline = rawFontMetrics.jBaseline - rawFontMetrics.jTop;
let h = rawFontMetrics.jBottom - rawFontMetrics.jTop + 1;
return function (color, text, bgColor, bgPadding = 0, bgBorderRadius = 0) {
let w = Math.ceil(getTextWidth(font, text));
if (bgColor && bgColor.a !== 0) {
const addedPadding = bgPadding * 2;
w = w + addedPadding + 4;
h = h + addedPadding;
jBaseline = jBaseline + addedPadding;
}
return new TextTexture2D(w, h, jBaseline, GL.NEAREST, GL.NEAREST, function (g) {
// Some browsers use hinting for canvas fillText! This behaves poorly on a transparent
// background -- so we draw white text onto a black background, then infer alpha from
// the pixel color (black = transparent, white = opaque).
//
// We compute alpha as (R+G+B)/3. This grayscales the image the browser drew, effectively
// de-hinting it.
//
// If background color not transparent show background color, else follow comment above.
if (bgColor && bgColor.a !== 0) {
g.fillStyle = bgColor.cssString;
// Round corners of text background
if (bgBorderRadius) {
bgBorderRadius = Math.min(bgBorderRadius, h / 2, w / 2);
g.beginPath();
g.moveTo(bgBorderRadius, 0);
g.lineTo(w - bgBorderRadius, 0);
g.quadraticCurveTo(w, 0, w, bgBorderRadius);
g.lineTo(w, h - bgBorderRadius);
g.quadraticCurveTo(w, h, w - bgBorderRadius, h);
g.lineTo(bgBorderRadius, h);
g.quadraticCurveTo(0, h, 0, h - bgBorderRadius);
g.lineTo(0, bgBorderRadius);
g.quadraticCurveTo(0, 0, bgBorderRadius, 0);
g.closePath();
g.fill();
}
else {
g.fillRect(0, 0, w, h);
}
g.font = font;
g.textAlign = 'center';
g.textBaseline = 'middle';
g.fillStyle = color.cssString;
g.fillText(text, w / 2, h / 2);
}
else {
g.fillStyle = 'black';
g.fillRect(0, 0, w, h);
g.font = font;
g.textAlign = 'left';
g.textBaseline = 'top';
g.fillStyle = 'white';
g.save();
g.translate(0, -rawFontMetrics.jTop);
g.fillText(text, 0, 0);
g.restore();
const r255 = 255 * color.r;
const g255 = 255 * color.g;
const b255 = 255 * color.b;
const aFactor = color.a / 3;
const pixels = g.getImageData(0, 0, w, h);
for (let j = 0; j < pixels.height; j++) {
for (let i = 0; i < pixels.width; i++) {
const pixelOffset = (j * pixels.width + i) * 4;
const a255 = aFactor * (pixels.data[pixelOffset + 0] + pixels.data[pixelOffset + 1] + pixels.data[pixelOffset + 2]);
pixels.data[pixelOffset + 0] = r255;
pixels.data[pixelOffset + 1] = g255;
pixels.data[pixelOffset + 2] = b255;
pixels.data[pixelOffset + 3] = a255;
}
}
g.putImageData(pixels, 0, 0);
}
});
};
}
export function newTextHintsCache(font) {
const rawFontMetrics = getRawFontMetrics(font);
const jBaseline = rawFontMetrics.jBaseline - rawFontMetrics.jTop;
const h = rawFontMetrics.jBottom - rawFontMetrics.jTop + 1;
return new Cache({
create: function (text) {
const w = getTextWidth(font, text);
// XXX: For now, assuming subpixels are horizontal-RGB
// Draw text triple-sized, to get an alpha for each r,g,b subpixel
const canvas3 = document.createElement('canvas');
canvas3.width = 3 * w;
canvas3.height = h;
const g3 = canvas3.getContext('2d');
g3.fillStyle = 'black';
g3.fillRect(0, 0, canvas3.width, canvas3.height);
g3.save();
g3.translate(0, -rawFontMetrics.jTop);
g3.scale(3, 1);
g3.font = font;
g3.textAlign = 'left';
g3.textBaseline = 'top';
g3.fillStyle = 'white';
g3.fillText(text, 0, 0);
g3.restore();
const srcRgba = g3.getImageData(0, 0, canvas3.width, canvas3.height).data;
return new Texture2D(w, h, GL.NEAREST, GL.NEAREST, function (g) {
const destImage = g.createImageData(w, h);
const destRgba = destImage.data;
const weightLeft = 1;
const weightCenter = 2;
const weightRight = 1;
const weightNorm = 1 / (weightLeft + weightCenter + weightRight);
for (let j = 0; j < h; j++) {
for (let i = 0; i < w; i++) {
// Get alpha values for relevant src-pixels: one from just left of the dest-pixel, all
// three from inside the dest-pixel, and one from just right of the dest-pixel.
//
// Some browsers use hinting for canvas fillText! This behaves poorly on a transparent
// background -- so we draw white text onto a black background, then infer alpha from
// the pixel color (black = transparent, white = opaque).
//
// We compute alpha as (R+G+B)/3. This grayscales the image the browser drew, effectively
// de-hinting it so that we can re-hint it ourselves later (during blending, when the
// background color is known).
//
const srcPixelIndex = (j * 3 * w + 3 * i) * 4;
const srcAlphaL = (i > 0 ? (srcRgba[srcPixelIndex - 4] + srcRgba[srcPixelIndex - 3] + srcRgba[srcPixelIndex - 2]) / (3 * 255) : 0);
const srcAlpha0 = (srcRgba[srcPixelIndex + 0] + srcRgba[srcPixelIndex + 1] + srcRgba[srcPixelIndex + 2]) / (3 * 255);
const srcAlpha1 = (srcRgba[srcPixelIndex + 4] + srcRgba[srcPixelIndex + 5] + srcRgba[srcPixelIndex + 6]) / (3 * 255);
const srcAlpha2 = (srcRgba[srcPixelIndex + 8] + srcRgba[srcPixelIndex + 9] + srcRgba[srcPixelIndex + 10]) / (3 * 255);
const srcAlphaR = (i < w - 1 ? (srcRgba[srcPixelIndex + 12] + srcRgba[srcPixelIndex + 13] + srcRgba[srcPixelIndex + 14]) / (3 * 255) : 0);
// Weighted averages to find subpixel alphas
const alphaLeft = weightNorm * (weightLeft * srcAlphaL + weightCenter * srcAlpha0 + weightRight * srcAlpha1);
const alphaCenter = weightNorm * (weightLeft * srcAlpha0 + weightCenter * srcAlpha1 + weightRight * srcAlpha2);
const alphaRight = weightNorm * (weightLeft * srcAlpha1 + weightCenter * srcAlpha2 + weightRight * srcAlphaR);
// Store subpixel alphas in dest-pixel
const destPixelIndex = (j * w + i) * 4;
destRgba[destPixelIndex + 0] = Math.round(255 * alphaLeft);
destRgba[destPixelIndex + 1] = Math.round(255 * alphaCenter);
destRgba[destPixelIndex + 2] = Math.round(255 * alphaRight);
// Alpha will be used in computing final alpha of blended result -- use the average of
// the subpixel alphas
//
// If alpha is 1, Firefox will interpret it as 100%. Causes some pixels that should be
// very dim to come out very bright. As a workaround, nudge them down to zero.
//
const alphaAvg = Math.round(255 * (alphaLeft + alphaCenter + alphaRight) / 3);
destRgba[destPixelIndex + 3] = (alphaAvg === 1 ? 0 : alphaAvg);
}
}
g.putImageData(destImage, 0, 0);
});
},
dispose: function (texture) {
texture.dispose();
}
});
}
export class HintedTextRenderer {
constructor() {
this.textRenderer_VERTSHADER = concatLines(' ', ' uniform vec2 u_XyFrac; ', ' uniform vec2 u_Anchor; ', ' uniform vec2 u_ImageSize; ', ' uniform vec2 u_ViewportSize; ', ' ', ' attribute vec2 a_ImageFrac; ', ' ', ' varying vec2 v_StCoord; ', ' ', ' void main( ) { ', ' vec2 xy = -1.0 + 2.0*( u_XyFrac + u_ImageSize*( a_ImageFrac - u_Anchor )/u_ViewportSize ); ', ' gl_Position = vec4( xy, 0.0, 1.0 ); ', ' ', ' v_StCoord = vec2( a_ImageFrac.x, 1.0 - a_ImageFrac.y ); ', ' } ', ' ');
this.textRenderer_FRAGSHADER = concatLines(' ', ' precision mediump float; ', ' ', ' uniform sampler2D u_Hints; ', ' uniform float u_Alpha; ', ' ', ' varying vec2 v_StCoord; ', ' ', ' void main( ) { ', ' gl_FragColor = u_Alpha * texture2D( u_Hints, v_StCoord ); ', ' } ', ' ');
this.program = new Program(this.textRenderer_VERTSHADER, this.textRenderer_FRAGSHADER);
this.u_XyFrac = new Uniform2f(this.program, 'u_XyFrac');
this.u_Anchor = new Uniform2f(this.program, 'u_Anchor');
this.u_ImageSize = new Uniform2f(this.program, 'u_ImageSize');
this.u_ViewportSize = new Uniform2f(this.program, 'u_ViewportSize');
this.u_Alpha = new Uniform1f(this.program, 'u_Alpha');
this.u_Hints = new UniformSampler2D(this.program, 'u_Hints');
this.a_ImageFrac = new Attribute(this.program, 'a_ImageFrac');
this.imageFracData = newStaticBuffer(new Float32Array([0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]));
this.wViewport = 0;
this.hViewport = 0;
}
begin(gl, viewport) {
this.program.use(gl);
this.u_ViewportSize.setData(gl, viewport.w, viewport.h);
this.a_ImageFrac.setDataAndEnable(gl, this.imageFracData, 2, GL.FLOAT);
this.wViewport = viewport.w;
this.hViewport = viewport.h;
}
draw(gl, hints, xFrac, yFrac, options) {
const xAnchor = (hasval(options) && hasval(options.xAnchor) ? options.xAnchor : 0.5);
const yAnchor = (hasval(options) && hasval(options.yAnchor) ? options.yAnchor : 0.5);
const color = (hasval(options) && hasval(options.color) ? options.color : black);
// The hints texture stores subpixel alphas in its R,G,B components. For example, a red hint-pixel indicates
// that, at that pixel, the final R component should be color.r (R_hint=1, so red is opaque), but the
// final G and B components should be the G and B from the background pixel (G_hint=0, B_hint=0, so green
// and blue are transparent).
//
// GL does not allow arbitrary blending, but we can use the blending options it does provide to get the effect
// we want.
//
// There are 4 things to keep an eye on:
//
// 1. The RGB part of color, which is put into glBlendColor
// 2. The A part of color, which is sent to the frag-shader (NOT passed to glBlendColor)
// 3. The subpixel alphas, which are sent to the frag-shader in the hints-texture
// 4. The glBlendFunc, which is set up in an unusual way
//
//
// With this setup, we get:
//
// R_final = ( R_frag )*R_foreground + ( 1 - R_frag )*R_background
// G_final = ( G_frag )*G_foreground + ( 1 - G_frag )*G_background
// B_final = ( B_frag )*B_foreground + ( 1 - B_frag )*B_background
// A_final = ( A_frag )*1 + ( 1 - A_frag )*A_background
//
// So R_frag, the output from the frag shader, is a weight that is used to take a weighted average between
// foreground and background colors.
//
// The frag-shader is doing A_foreground*RGBA_hint. Conceptually, R_hint is really a subpixel alpha for the
// red subpixel, so it is helpful to call it A_redhint (and analogously for G and B):
//
// R_frag = A_foreground * A_redhint
// G_frag = A_foreground * A_greenhint
// B_frag = A_foreground * A_bluehint
//
// So the blended RGB is a weighted average of RGB_foreground and RGB_background, with a weighting factor of
// ( A_foreground * A_subpixel ), where A_subpixel is either A_redhint, A_greenhint, or A_bluehint. Basically,
// we have a separate alpha for each subpixel!
//
//
// For the final alpha component, we want:
//
// A_final = ( 1 - (1-A_background)*(1-A_frag) )
//
// A little algebra shows that to be equivalent to:
//
// A_final = ( A_frag )*1 + ( 1 - A_frag )*A_background
//
// Which is exactly what we get, as long as we pass an alpha of 1 into glBlendColor.
//
//
this.u_XyFrac.setData(gl, nearestPixel(xFrac, this.wViewport, xAnchor, hints.w), nearestPixel(yFrac, this.hViewport, yAnchor, hints.h));
this.u_Anchor.setData(gl, xAnchor, yAnchor);
this.u_ImageSize.setData(gl, hints.w, hints.h);
this.u_Alpha.setData(gl, color.a);
this.u_Hints.setDataAndBind(gl, 0, hints);
gl.enable(GL.BLEND);
gl.blendColor(color.r, color.g, color.b, 1);
gl.blendFunc(GL.CONSTANT_COLOR, GL.ONE_MINUS_SRC_COLOR);
gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
}
end(gl) {
this.a_ImageFrac.disable(gl);
this.u_Hints.unbind(gl);
this.program.endUse(gl);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGV4dC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy93ZWJnbGltcHNlL3RleHQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E0Qkc7QUFDSCxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ3JDLE9BQU8sRUFBRSxTQUFTLEVBQWUsTUFBTSxXQUFXLENBQUM7QUFDbkQsT0FBTyxFQUFTLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDbEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxhQUFhLEVBQUUsV0FBVyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDaEYsT0FBTyxFQUFFLEVBQUUsRUFBRSxXQUFXLEVBQUUsTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3RELE9BQU8sRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxnQkFBZ0IsRUFBRSxTQUFTLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDdEYsT0FBTyxFQUFVLGVBQWUsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUVuRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBUXRDLE1BQU0sT0FBTyxHQUFHLENBQUM7SUFDYix3REFBd0Q7SUFDeEQsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMxQyxHQUFHLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDOUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsU0FBUyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ3RDLEdBQUcsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLFFBQVEsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUNyQyxHQUFHLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxPQUFPLEVBQUUsTUFB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QUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN4SSxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzVDLElBQUksQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLEVBQUUsRUFBRSxLQUFLLENBQUMsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMvQyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2xDLElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDLEVBQUUsRUFBRSxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFMUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDcEIsRUFBRSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUM1QyxFQUFFLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxjQUFjLEVBQUUsRUFBRSxDQUFDLG1CQUFtQixDQUFDLENBQUM7UUFFeEQsRUFBRSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsY0FBYyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRUQsR0FBRyxDQUFDLEVBQXlCO1FBQ3pCLElBQUksQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzdCLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3hCLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzVCLENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbIi8qXHJcbiAqIENvcHlyaWdodCAoYykgMjAxNCwgTWV0cm9uLCBJbmMuXHJcbiAqIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbiAqXHJcbiAqIFJlZGlzdHJpYnV0aW9uIGFuZCB1c2UgaW4gc291cmNlIGFuZCBiaW5hcnkgZm9ybXMsIHdpdGggb3Igd2l0aG91dCBtb2RpZmljYXRpb24sXHJcbiAqIGFyZSBwZXJtaXR0ZWQgcHJvdmlkZWQgdGhhdCB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnMgYXJlIG1ldDpcclxuICpcclxuICogMS4gUmVkaXN0cmlidXRpb25zIG9mIHNvdXJjZSBjb2RlIG11c3QgcmV0YWluIHRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlLCB0aGlzXHJcbiAqICAgIGxpc3Qgb2YgY29uZGl0aW9ucyBhbmQgdGhlIGZvbGxvd2luZyBkaXNjbGFpbWVyLlxyXG4gKlxyXG4gKiAyLiBSZWRpc3RyaWJ1dGlvbnMgaW4gYmluYXJ5IGZvcm0gbXVzdCByZXByb2R1Y2UgdGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UsXHJcbiAqICAgIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIgaW4gdGhlIGRvY3VtZW50YXRpb25cclxuICogICAgYW5kL29yIG90aGVyIG1hdGVyaWFscyBwcm92aWRlZCB3aXRoIHRoZSBkaXN0cmlidXRpb24uXHJcbiAqXHJcbiAqIDMuIE5laXRoZXIgdGhlIG5hbWUgb2YgdGhlIGNvcHlyaWdodCBob2xkZXIgbm9yIHRoZSBuYW1lcyBvZiBpdHMgY29udHJpYnV0b3JzXHJcbiAqICAgIG1heSBiZSB1c2VkIHRvIGVuZG9yc2Ugb3IgcHJvbW90ZSBwcm9kdWN0cyBkZXJpdmVkIGZyb20gdGhpcyBzb2Z0d2FyZSB3aXRob3V0XHJcbiAqICAgIHNwZWNpZmljIHByaW9yIHdyaXR0ZW4gcGVybWlzc2lvbi5cclxuICpcclxuICogVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUyBcIkFTIElTXCIgQU5EXHJcbiAqIEFOWSBFWFBSRVNTIE9SIElNUExJRUQgV0FSUkFOVElFUywgSU5DTFVESU5HLCBCVVQgTk9UIExJTUlURUQgVE8sIFRIRSBJTVBMSUVEXHJcbiAqIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZIEFORCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBUkVcclxuICogRElTQ0xBSU1FRC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIENPUFlSSUdIVCBIT0xERVIgT1IgQ09OVFJJQlVUT1JTIEJFIExJQUJMRSBGT1JcclxuICogQU5ZIERJUkVDVCwgSU5ESVJFQ1QsIElOQ0lERU5UQUwsIFNQRUNJQUwsIEVYRU1QTEFSWSwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTXHJcbiAqIChJTkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgUFJPQ1VSRU1FTlQgT0YgU1VCU1RJVFVURSBHT09EUyBPUiBTRVJWSUNFUztcclxuICogTE9TUyBPRiBVU0UsIERBVEEsIE9SIFBST0ZJVFM7IE9SIEJVU0lORVNTIElOVEVSUlVQVElPTikgSE9XRVZFUiBDQVVTRUQgQU5EIE9OXHJcbiAqIEFOWSBUSEVPUlkgT0YgTElBQklMSVRZLCBXSEVUSEVSIElO