From f16a22c346b2d26a0413ca5d7a09d67c5384b1c5 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 13 Jan 2016 15:02:03 -0500
Subject: [PATCH] Switching lib to use function exports

---
 src/utils/lib.js | 1201 +++++++++++++++++++++++-----------------------
 1 file changed, 600 insertions(+), 601 deletions(-)

diff --git a/src/utils/lib.js b/src/utils/lib.js
index e02721c..ce8cdfb 100755
--- a/src/utils/lib.js
+++ b/src/utils/lib.js
@@ -10,646 +10,645 @@ export const scaleMultiplier = WINDOW_INNER_HEIGHT / 768.0;
 export const isiOS = (typeof AndroidInterface == 'undefined');
 export const isAndroid = (typeof AndroidInterface != 'undefined');
 
-export default class Lib {
-    static libInit () {
-        frame = document.getElementById('frame');
-    }
-    /**
-     * Takes a string and evaluates all ${} as JavaScript and returns the resulting string.
-     */
-    static preprocess (s) {
-        var result = '';
-        var len = s.length;
-        var i = 0;
-        var j;
-        while ((i < len) && ((j = s.indexOf('$', i)) != -1)) {
-            result += s.substring(i, j);
-            i = j + 1;
-            if ((i < (len - 1)) && (s[i] === '{')) {
-                var start = i + 1;
-                var end = s.indexOf('}', start);
-                if (end != -1) {
-                    var expression = s.substring(start, end);
-                    result += eval(expression);
-                    i = end + 1;
-                } else {
-                    result += '$';
-                }
+export function libInit () {
+    frame = document.getElementById('frame');
+}
+/**
+ * Takes a string and evaluates all ${} as JavaScript and returns the resulting string.
+ */
+export function preprocess (s) {
+    var result = '';
+    var len = s.length;
+    var i = 0;
+    var j;
+    while ((i < len) && ((j = s.indexOf('$', i)) != -1)) {
+        result += s.substring(i, j);
+        i = j + 1;
+        if ((i < (len - 1)) && (s[i] === '{')) {
+            var start = i + 1;
+            var end = s.indexOf('}', start);
+            if (end != -1) {
+                var expression = s.substring(start, end);
+                result += eval(expression);
+                i = end + 1;
             } else {
                 result += '$';
             }
-        }
-        if (i < len) {
-            result += s.substring(i);
-        }
-        return result;
-    }
-
-    /**
-     * Load the URL synchronously (fine because it's file://), preprocess the result and return the string.
-     */
-    static preprocessAndLoad (url) {
-        var xmlhttp = new XMLHttpRequest();
-        xmlhttp.open('GET', url, false);
-        xmlhttp.send();
-        return this.preprocess(xmlhttp.responseText);
-    }
-
-    /**
-     * Load a CSS file, preprocess it using preprocessAndLoad() and then returns it as a style tag.
-     * Also rewrites all instances of url() with a different base
-     */
-    static preprocessAndLoadCss (baseUrl, url) {
-        document.write('<!-- ' + url + '-->\n');
-        document.write('<style type=\'text/css\'>\n');
-        var cssData = this.preprocessAndLoad(url);
-        cssData = cssData.replace(/url\('/g, 'url(\'' + baseUrl + '/');
-        cssData = cssData.replace(/url\(([^'])/g, 'url(' + baseUrl + '/$1');
-        document.write(cssData);
-        document.write('\n</style>\n');
-    }
-
-    static rl () {
-        window.location.reload();
-    }
-
-    static newDiv (parent, x, y, w, h, styles) {
-        var el = document.createElement('div');
-        el.style.position = 'absolute';
-        el.style.top = y + 'px';
-        el.style.left = x + 'px';
-        if (w) {
-            el.style.width = w + 'px';
-        }
-        if (h) {
-            el.style.height = h + 'px';
-        }
-        this.setProps(el.style, styles);
-        parent.appendChild(el);
-        return el;
-    }
-
-    static newImage (parent, src, styles) {
-        var img = document.createElement('img');
-        img.src = src;
-        this.setProps(img.style, styles);
-        if (parent) {
-            parent.appendChild(img);
-        }
-        return img;
-    }
-
-    static newCanvas (parent, x, y, w, h, styles) {
-        var canvas = document.createElement('canvas');
-        canvas.style.position = 'absolute';
-        canvas.style.top = y + 'px';
-        canvas.style.left = x + 'px';
-        this.setCanvasSize(canvas, w, h);
-        this.setProps(canvas.style, styles);
-        parent.appendChild(canvas);
-        return canvas;
-    }
-
-    static newHTML (type, c, p) {
-        var e = document.createElement(type);
-        if (c) {
-            e.setAttribute('class', c);
-        }
-        if (p) {
-            p.appendChild(e);
-        }
-        return e;
-    }
-
-    static newP (parent, text, styles) {
-        var p = document.createElement('p');
-        p.appendChild(document.createTextNode(text));
-        this.setProps(p.style, styles);
-        parent.appendChild(p);
-        return p;
-    }
-
-    static hitRect (c, pt) {
-        if (!pt) {
-            return false;
-        }
-        if (!c) {
-            return false;
-        }
-        var x = pt.x;
-        var y = pt.y;
-        if (c.offsetLeft == undefined) {
-            return false;
-        }
-        if (c.offsetTop == undefined) {
-            return false;
-        }
-        if (x < c.offsetLeft) {
-            return false;
-        }
-        if (x > c.offsetLeft + c.offsetWidth) {
-            return false;
-        }
-        if (y < c.offsetTop) {
-            return false;
-        }
-        if (y > c.offsetTop + c.offsetHeight) {
-            return false;
-        }
-        return true;
-    }
-
-    static hit3DRect (c, pt) {
-        if (!pt) {
-            return;
-        }
-        var x = pt.x;
-        var y = pt.y;
-        var mtx = new WebKitCSSMatrix(window.getComputedStyle(c).webkitTransform);
-        if (mtx.m41 == undefined) {
-            return false;
-        }
-        if (mtx.m42 == undefined) {
-            return false;
-        }
-        if (x < mtx.m41) {
-            return false;
-        }
-        if (x > mtx.m41 + c.offsetWidth) {
-            return false;
-        }
-        if (y < mtx.m42) {
-            return false;
-        }
-        if (y > mtx.m42 + c.offsetHeight) {
-            return false;
-        }
-        return true;
-    }
-
-    static hitTest (c, pt) {
-        if (!pt) {
-            return;
-        }
-        var x = pt.x;
-        var y = pt.y;
-        if (x < c.offsetLeft) {
-            return false;
-        }
-        if (x > c.offsetLeft + c.offsetWidth) {
-            return false;
-        }
-        if (y < c.offsetTop) {
-            return false;
-        }
-        if (y > c.offsetTop + c.offsetHeight) {
-            return false;
-        }
-        var dx = pt.x - c.offsetLeft,
-            dy = pt.y - c.offsetTop;
-        var ctx = c.getContext('2d');
-        var pixel = ctx.getImageData(dx, dy, 1, 1).data;
-        if (pixel[3] == 0) {
-            return false;
-        }
-        return true;
-    }
-
-    static setCanvasSize (c, w, h) {
-        c.width = w;
-        c.height = h;
-        c.style.width = w + 'px';
-        c.style.height = h + 'px';
-    }
-
-    static setCanvasSizeScaledToWindowDocumentHeight (c, w, h) {
-        var multiplier = window.devicePixelRatio * scaleMultiplier;
-        var scaledWidth = Math.floor(w * multiplier);
-        var scaledHeight = Math.floor(h * multiplier);
-        c.width = scaledWidth;
-        c.height = scaledHeight;
-        c.style.width = scaledWidth + 'px';
-        c.style.height = scaledHeight + 'px';
-        c.style.zoom = scaleMultiplier / multiplier;
-    }
-
-    static localx (el, gx) {
-        var lx = gx;
-        while (el && el.offsetTop != undefined) {
-            lx -= el.offsetLeft + el.clientLeft +
-                (new WebKitCSSMatrix(window.getComputedStyle(el).webkitTransform)).m41;
-            el = el.parentNode;
-        }
-        return lx;
-    }
-
-    static globalx (el) {
-        var lx = 0;
-        while (el && el.offsetLeft != undefined) {
-            var webkitTransform = new WebKitCSSMatrix(window.getComputedStyle(el).webkitTransform);
-            var transformScale = webkitTransform.m11;
-            lx += (el.clientWidth - (transformScale * el.clientWidth)) / 2;
-            var transformX = webkitTransform.m41;
-            lx += transformX;
-            lx += el.offsetLeft + el.clientLeft;
-            el = el.parentNode;
-        }
-        return lx;
-    }
-
-    static localy (el, gy) {
-        var ly = gy;
-        while (el && el.offsetTop != undefined) {
-            ly -= el.offsetTop + el.clientTop + (new WebKitCSSMatrix(window.getComputedStyle(el).webkitTransform)).m42;
-            el = el.parentNode;
-        }
-        return ly;
-    }
-
-    static globaly (el) {
-        var ly = 0;
-        while (el && el.offsetTop != undefined) {
-            var webkitTransform = new WebKitCSSMatrix(window.getComputedStyle(el).webkitTransform);
-            var transformScale = webkitTransform.m22;
-            ly += (el.clientHeight - (transformScale * el.clientHeight)) / 2;
-            var transformY = webkitTransform.m42;
-            ly += transformY;
-            ly += el.offsetTop + el.clientTop;
-            el = el.parentNode;
-        }
-        return ly;
-    }
-
-    static setProps (object, props) {
-        for (var i in props) {
-            object[i] = props[i];
+        } else {
+            result += '$';
         }
     }
-
-    // ["ease", "linear", "ease-in", "ease-out", "ease-in-out", "step-start", "step-end"];
-    static CSSTransition (el, obj) {
-        // default
-        var duration = 1;
-        var transition = 'ease';
-        var style = {
-            left: el.offsetLeft + 'px',
-            top: el.offsetTop + 'px'
-        };
-        if (obj.duration) {
-            duration = obj.duration;
-        }
-        if (obj.transition) {
-            transition = obj.transition;
-        }
-        if (obj.style) {
-            style = obj.style;
-        }
-        var items = '';
-        for (var key in style) {
-            items += key + ' ' + duration + 's ' + transition + ', ';
-        }
-        items = items.substring(0, items.length - 2);
-        el.style.webkitTransition = items;
-        el.addEventListener('webkitTransitionEnd', transitionDene, true);
-        this.setProps(el.style, style);
-        function transitionDene () {
-            el.style.webkitTransition = '';
-            if (obj.onComplete) {
-                obj.onComplete();
-            }
-        }
+    if (i < len) {
+        result += s.substring(i);
     }
+    return result;
+}
 
-    static CSSTransition3D (el, obj) {
-        // default
-        var duration = 1;
-        var transition = 'ease';
-        var style = {
-            left: el.left + 'px',
-            top: el.top + 'px'
-        }; // keepit where it is
-        if (obj.duration) {
-            duration = obj.duration;
-        }
-        if (obj.transition) {
-            transition = obj.transition;
-        }
-        if (obj.style) {
-            for (var key in obj.style) {
-                style[key] = obj.style[key];
-            }
-        }
-        var items = '-webkit-transform ' + duration + 's ' + transition;
-        var translate = 'translate3d(' + style.left + ',' + style.top + ',0px)';
-        el.addEventListener('webkitTransitionEnd', transitionDone, true);
-        el.style.webkitTransition = items;
-        el.style.webkitTransform = translate;
-        function transitionDone () {
-            el.style.webkitTransition = '';
-            var mtx = new WebKitCSSMatrix(window.getComputedStyle(el).webkitTransform);
-            el.left = mtx.m41;
-            el.top = mtx.m42;
-            if (obj.onComplete) {
-                obj.onComplete();
-            }
-        }
+/**
+ * Load the URL synchronously (fine because it's file://), preprocess the result and return the string.
+ */
+export function preprocessAndLoad (url) {
+    var xmlhttp = new XMLHttpRequest();
+    xmlhttp.open('GET', url, false);
+    xmlhttp.send();
+    return preprocess(xmlhttp.responseText);
+}
+
+/**
+ * Load a CSS file, preprocess it using preprocessAndLoad() and then returns it as a style tag.
+ * Also rewrites all instances of url() with a different base
+ */
+export function preprocessAndLoadCss (baseUrl, url) {
+    document.write('<!-- ' + url + '-->\n');
+    document.write('<style type=\'text/css\'>\n');
+    var cssData = preprocessAndLoad(url);
+    cssData = cssData.replace(/url\('/g, 'url(\'' + baseUrl + '/');
+    cssData = cssData.replace(/url\(([^'])/g, 'url(' + baseUrl + '/$1');
+    document.write(cssData);
+    document.write('\n</style>\n');
+}
+
+export function rl () {
+    window.location.reload();
+}
+
+export function newDiv (parent, x, y, w, h, styles) {
+    var el = document.createElement('div');
+    el.style.position = 'absolute';
+    el.style.top = y + 'px';
+    el.style.left = x + 'px';
+    if (w) {
+        el.style.width = w + 'px';
     }
-
-    static drawThumbnail (img, c) {
-        // naturalWidth Height it gets the zoom scaling properly
-        var w = img.naturalWidth ? img.naturalWidth : img.width;
-        var h = img.naturalHeight ? img.naturalHeight : img.height;
-        var dx = (c.width - w) / 2;
-        var dy = (c.height - h) / 2;
-        var dw = c.width / w;
-        var dh = c.height / h;
-        var wi = w;
-        var he = h;
-        switch (this.getFit(dw, dh)) {
-        case 'noscale':
-            break;
-        case 'scaleh':
-            wi = w * dh;
-            he = h * dh;
-            dx = (c.width - wi) / 2;
-            dy = (c.height - he) / 2;
-            break;
-        case 'scalew':
-            wi = w * dw;
-            he = h * dw;
-            dx = (c.width - wi) / 2;
-            dy = (c.height - he) / 2;
-            break;
-        }
-        var ctx = c.getContext('2d');
-        ctx.drawImage(img, dx, dy, wi, he);
+    if (h) {
+        el.style.height = h + 'px';
     }
+    setProps(el.style, styles);
+    parent.appendChild(el);
+    return el;
+}
 
-    // Like drawThumbnail, but scales up if needed
-    static drawScaled (img, c) {
-        var imgWidth = img.naturalWidth ? img.naturalWidth : img.width;
-        var imgHeight = img.naturalHeight ? img.naturalHeight : img.height;
-        var boxWidth = c.width;
-        var boxHeight = c.height;
-        var scale = boxWidth / imgWidth;
-        var w = imgWidth * scale;
-        var h = imgHeight * scale;
-        if (h > boxHeight) {
-            scale = boxHeight / imgHeight;
-            w = imgWidth * scale;
-            h = imgHeight * scale;
-        }
-        var x0 = (boxWidth - w) / 2;
-        var y0 = (boxHeight - h) / 2;
-        var ctx = c.getContext('2d');
-        ctx.drawImage(img, x0, y0, w, h);
+export function newImage (parent, src, styles) {
+    var img = document.createElement('img');
+    img.src = src;
+    setProps(img.style, styles);
+    if (parent) {
+        parent.appendChild(img);
     }
+    return img;
+}
 
-    static fitInRect (srcw, srch, destw, desth) {
-        var dx = (destw - srcw) / 2;
-        var dy = (desth - srch) / 2;
-        var dw = destw / srcw;
-        var dh = desth / srch;
-        var wi = srcw;
-        var he = srch;
-        switch (this.getFit(dw, dh)) {
-        case 'noscale':
-            break;
-        case 'scaleh':
-            wi = srcw * dh;
-            he = srch * dh;
-            dx = (destw - wi) / 2;
-            dy = (desth - he) / 2;
-            break;
-        case 'scalew':
-            wi = srcw * dw;
-            he = srch * dw;
-            dx = (destw - wi) / 2;
-            dy = (desth - he) / 2;
-            break;
-        }
-        return [dx, dy, wi, he];
+export function newCanvas (parent, x, y, w, h, styles) {
+    var canvas = document.createElement('canvas');
+    canvas.style.position = 'absolute';
+    canvas.style.top = y + 'px';
+    canvas.style.left = x + 'px';
+    setCanvasSize(canvas, w, h);
+    setProps(canvas.style, styles);
+    parent.appendChild(canvas);
+    return canvas;
+}
+
+export function newHTML (type, c, p) {
+    var e = document.createElement(type);
+    if (c) {
+        e.setAttribute('class', c);
     }
-
-    static getFit (dw, dh) {
-        if ((dw >= 1) && (dh >= 1)) {
-            return 'noscale';
-        }
-        if ((dw >= 1) && (dh < 1)) {
-            return 'scaleh';
-        }
-        if ((dw < 1) && (dh >= 1)) {
-            return 'scalew';
-        }
-        if (dw < dh) {
-            return 'scalew';
-        }
-        return 'scaleh';
+    if (p) {
+        p.appendChild(e);
     }
+    return e;
+}
 
-    static getDocumentHeight () {
-        return Math.max(document.body.clientHeight, document.documentElement.clientHeight);
+export function newP (parent, text, styles) {
+    var p = document.createElement('p');
+    p.appendChild(document.createTextNode(text));
+    setProps(p.style, styles);
+    parent.appendChild(p);
+    return p;
+}
+
+export function hitRect (c, pt) {
+    if (!pt) {
+        return false;
     }
-
-    static getDocumentWidth () {
-        return Math.max(document.body.clientWidth, document.documentElement.clientWidth);
+    if (!c) {
+        return false;
     }
-
-    static getStringSize (ctx, f, label) {
-        ctx.font = f;
-        return ctx.measureText(label);
+    var x = pt.x;
+    var y = pt.y;
+    if (c.offsetLeft == undefined) {
+        return false;
     }
-
-    static writeText (ctx, f, c, label, dy, dx) {
-        dx = (dx == undefined) ? 0 : dx;
-        ctx.font = f;
-        ctx.fillStyle = c;
-        ctx.textAlign = 'left';
-        ctx.textBaseline = 'bottom';
-        ctx.fillText(label, dx, dy);
+    if (c.offsetTop == undefined) {
+        return false;
     }
-
-    static gn (str) {
-        return document.getElementById(str);
+    if (x < c.offsetLeft) {
+        return false;
     }
-
-    static newForm (parent, str, x, y, w, h, styles) {
-        var el = document.createElement('form');
-        el.style.position = 'absolute';
-        el.style.top = y + 'px';
-        el.style.left = x + 'px';
-        if (w) {
-            el.style.width = w + 'px';
-        }
-        if (h) {
-            el.style.height = h + 'px';
-        }
-        this.setProps(el.style, styles);
-        parent.appendChild(el);
-        el.name = str;
-        return el;
+    if (x > c.offsetLeft + c.offsetWidth) {
+        return false;
     }
-
-    static newTextInput (p, type, str, mstyle) {
-        var input = document.createElement('input');
-        input.value = str;
-        this.setProps(input.style, mstyle);
-        input.type = type;
-        p.appendChild(input);
-        return input;
+    if (y < c.offsetTop) {
+        return false;
     }
-
-    static getUrlVars () {
-        if (window.location.href.indexOf('?') < 0) {
-            return [];
-        }
-        var args = window.location.href.slice(window.location.href.indexOf('?') + 1);
-        var vars = [], hash;
-        var hashes = args.split('&');
-        for (var i = 0; i < hashes.length; i++) {
-            hash = hashes[i].split('=');
-            vars.push(hash[0]);
-            vars[hash[0]] = hash[1];
-        }
-        return vars;
+    if (y > c.offsetTop + c.offsetHeight) {
+        return false;
     }
+    return true;
+}
 
-    static getIdFor (name) {
-        var n = 1;
-        while (this.gn(name + ' ' + n) != undefined) {
-            n++;
-        }
-        return name + ' ' + n;
+export function hit3DRect (c, pt) {
+    if (!pt) {
+        return;
     }
-
-
-    static getIdForCamera (name) {
-        var n = 1;
-        while (this.gn(name + '_' + n) != undefined) {
-            n++;
-        }
-        return name + '_' + n;
+    var x = pt.x;
+    var y = pt.y;
+    var mtx = new WebKitCSSMatrix(window.getComputedStyle(c).webkitTransform);
+    if (mtx.m41 == undefined) {
+        return false;
     }
-
-    ////////////////////
-    // Color
-    /////////////////////
-
-    static rgb2hsb (str) {
-        if (str == null) {
-            return [24, 1, 1];
-        }
-        var min, val, f, i, hue, sat;
-        str = (str.indexOf('rgb') > -1) ? this.rgbToHex(str) : this.rgbaToHex(str);
-        var num = parseInt(str.substring(1, str.length), 16);
-        var rgb = this.getRGB(num);
-        var red = rgb[0];
-        red /= 255;
-        var grn = rgb[1];
-        grn /= 255;
-        var blu = rgb[2];
-        blu /= 255;
-        min = Math.min(Math.min(red, grn), blu);
-        val = Math.max(Math.max(red, grn), blu);
-        if (min == val) {
-            return new Array(0, 0, val);
-        }
-        f = (red == min) ? grn - blu : ((grn == min) ? blu - red : red - grn);
-        i = (red == min) ? 3 : ((grn == min) ? 5 : 1);
-        hue = Math.round((i - f / (val - min)) * 60) % 360;
-        sat = Math.round(((val - min) / val) * 100);
-        val = Math.round(val * 100);
-        return new Array(hue, sat / 100, val / 100);
+    if (mtx.m42 == undefined) {
+        return false;
     }
-
-    static rgbToHex (str) {
-        if (str.indexOf('rgb') < 0) {
-            return str;
-        }
-        var res = str.substring(4, str.length - 1);
-        var a = res.split(',');
-        var red = Number(a[0]);
-        var grn = Number(a[1]);
-        var blu = Number(a[2]);
-        return this.rgbToString({
-            r: red,
-            g: grn,
-            b: blu
-        });
+    if (x < mtx.m41) {
+        return false;
     }
-
-    static rgbaToHex (str) {
-        if (str.indexOf('rgba') < 0) {
-            return str;
-        }
-        var res = str.substring(5, str.length - 1);
-        var a = res.split(',');
-        var red = Number(a[0]);
-        var grn = Number(a[1]);
-        var blu = Number(a[2]);
-        return this.rgbToString({
-            r: red,
-            g: grn,
-            b: blu
-        });
+    if (x > mtx.m41 + c.offsetWidth) {
+        return false;
     }
-
-
-    static rgbToString (obj) {
-        return '#' + this.getHex(obj.r) + this.getHex(obj.g) + this.getHex(obj.b);
+    if (y < mtx.m42) {
+        return false;
     }
-
-    static getRGB (color) {
-        return [
-            (Number((color >> 16) & 255)),
-            (Number((color >> 8) & 255)),
-            (Number(color & 255))
-        ];
+    if (y > mtx.m42 + c.offsetHeight) {
+        return false;
     }
+    return true;
+}
 
-    static getHex (num) {
-        var hex = num.toString(16);
-        if (hex.length == 1) {
-            return '0' + hex;
-        }
-        return hex;
+export function hitTest (c, pt) {
+    if (!pt) {
+        return;
     }
-
-    // findKeyframesRule ("swing");
-
-    static findKeyframesRule (rule) {
-        var ss = document.styleSheets;
-        for (var i = 0; i < ss.length; ++i) {
-            for (var j = 0; j < ss[i].cssRules.length; ++j) {
-                var styles = ss[i].cssRules[j].styleSheet.rules;
-                for (var k = 0; k < styles.length; ++k) {
-                    if (styles[k].type == window.CSSRule.WEBKIT_KEYFRAMES_RULE && styles[k].name == rule) {
-                        return styles[k];
-                    }
-                }
-            }
-        } // rule not found
-        return null;
+    var x = pt.x;
+    var y = pt.y;
+    if (x < c.offsetLeft) {
+        return false;
     }
-
-    static colorToRGBA (color, opacity) {
-        var val = parseInt('0x' + color.substr(1, color.length));
-        return 'rgba(' + (val >> 16) % 256 + ',' + (val >> 8) % 256 + ',' + (val % 256) + ',' + opacity + ')';
+    if (x > c.offsetLeft + c.offsetWidth) {
+        return false;
     }
-
-    /**
-     * css units vh and vw (for % of height and width) are not supported in Android 4.3 and earlier, so
-     * here we introduce functioncs (called from the preprocessed css) that emulate their behavior by
-     * turning them into pixel values.
-     */
-    static css_vh (y) {
-        return (y * WINDOW_INNER_HEIGHT / 100.0) + 'px';
+    if (y < c.offsetTop) {
+        return false;
     }
+    if (y > c.offsetTop + c.offsetHeight) {
+        return false;
+    }
+    var dx = pt.x - c.offsetLeft,
+        dy = pt.y - c.offsetTop;
+    var ctx = c.getContext('2d');
+    var pixel = ctx.getImageData(dx, dy, 1, 1).data;
+    if (pixel[3] == 0) {
+        return false;
+    }
+    return true;
+}
 
-    static css_vw (x) {
-        return (x * WINDOW_INNER_WIDTH / 100.0) + 'px';
+export function setCanvasSize (c, w, h) {
+    c.width = w;
+    c.height = h;
+    c.style.width = w + 'px';
+    c.style.height = h + 'px';
+}
+
+export function setCanvasSizeScaledToWindowDocumentHeight (c, w, h) {
+    var multiplier = window.devicePixelRatio * scaleMultiplier;
+    var scaledWidth = Math.floor(w * multiplier);
+    var scaledHeight = Math.floor(h * multiplier);
+    c.width = scaledWidth;
+    c.height = scaledHeight;
+    c.style.width = scaledWidth + 'px';
+    c.style.height = scaledHeight + 'px';
+    c.style.zoom = scaleMultiplier / multiplier;
+}
+
+export function localx (el, gx) {
+    var lx = gx;
+    while (el && el.offsetTop != undefined) {
+        lx -= el.offsetLeft + el.clientLeft +
+            (new WebKitCSSMatrix(window.getComputedStyle(el).webkitTransform)).m41;
+        el = el.parentNode;
+    }
+    return lx;
+}
+
+export function globalx (el) {
+    var lx = 0;
+    while (el && el.offsetLeft != undefined) {
+        var webkitTransform = new WebKitCSSMatrix(window.getComputedStyle(el).webkitTransform);
+        var transformScale = webkitTransform.m11;
+        lx += (el.clientWidth - (transformScale * el.clientWidth)) / 2;
+        var transformX = webkitTransform.m41;
+        lx += transformX;
+        lx += el.offsetLeft + el.clientLeft;
+        el = el.parentNode;
+    }
+    return lx;
+}
+
+export function localy (el, gy) {
+    var ly = gy;
+    while (el && el.offsetTop != undefined) {
+        ly -= el.offsetTop + el.clientTop + (new WebKitCSSMatrix(window.getComputedStyle(el).webkitTransform)).m42;
+        el = el.parentNode;
+    }
+    return ly;
+}
+
+export function globaly (el) {
+    var ly = 0;
+    while (el && el.offsetTop != undefined) {
+        var webkitTransform = new WebKitCSSMatrix(window.getComputedStyle(el).webkitTransform);
+        var transformScale = webkitTransform.m22;
+        ly += (el.clientHeight - (transformScale * el.clientHeight)) / 2;
+        var transformY = webkitTransform.m42;
+        ly += transformY;
+        ly += el.offsetTop + el.clientTop;
+        el = el.parentNode;
+    }
+    return ly;
+}
+
+export function setProps (object, props) {
+    for (var i in props) {
+        object[i] = props[i];
     }
 }
+
+// ["ease", "linear", "ease-in", "ease-out", "ease-in-out", "step-start", "step-end"];
+export function CSSTransition (el, obj) {
+    // default
+    var duration = 1;
+    var transition = 'ease';
+    var style = {
+        left: el.offsetLeft + 'px',
+        top: el.offsetTop + 'px'
+    };
+    if (obj.duration) {
+        duration = obj.duration;
+    }
+    if (obj.transition) {
+        transition = obj.transition;
+    }
+    if (obj.style) {
+        style = obj.style;
+    }
+    var items = '';
+    for (var key in style) {
+        items += key + ' ' + duration + 's ' + transition + ', ';
+    }
+    items = items.substring(0, items.length - 2);
+    el.style.webkitTransition = items;
+    el.addEventListener('webkitTransitionEnd', transitionDene, true);
+    setProps(el.style, style);
+    function transitionDene () {
+        el.style.webkitTransition = '';
+        if (obj.onComplete) {
+            obj.onComplete();
+        }
+    }
+}
+
+export function CSSTransition3D (el, obj) {
+    // default
+    var duration = 1;
+    var transition = 'ease';
+    var style = {
+        left: el.left + 'px',
+        top: el.top + 'px'
+    }; // keepit where it is
+    if (obj.duration) {
+        duration = obj.duration;
+    }
+    if (obj.transition) {
+        transition = obj.transition;
+    }
+    if (obj.style) {
+        for (var key in obj.style) {
+            style[key] = obj.style[key];
+        }
+    }
+    var items = '-webkit-transform ' + duration + 's ' + transition;
+    var translate = 'translate3d(' + style.left + ',' + style.top + ',0px)';
+    el.addEventListener('webkitTransitionEnd', transitionDone, true);
+    el.style.webkitTransition = items;
+    el.style.webkitTransform = translate;
+    function transitionDone () {
+        el.style.webkitTransition = '';
+        var mtx = new WebKitCSSMatrix(window.getComputedStyle(el).webkitTransform);
+        el.left = mtx.m41;
+        el.top = mtx.m42;
+        if (obj.onComplete) {
+            obj.onComplete();
+        }
+    }
+}
+
+export function drawThumbnail (img, c) {
+    // naturalWidth Height it gets the zoom scaling properly
+    var w = img.naturalWidth ? img.naturalWidth : img.width;
+    var h = img.naturalHeight ? img.naturalHeight : img.height;
+    var dx = (c.width - w) / 2;
+    var dy = (c.height - h) / 2;
+    var dw = c.width / w;
+    var dh = c.height / h;
+    var wi = w;
+    var he = h;
+    switch (getFit(dw, dh)) {
+    case 'noscale':
+        break;
+    case 'scaleh':
+        wi = w * dh;
+        he = h * dh;
+        dx = (c.width - wi) / 2;
+        dy = (c.height - he) / 2;
+        break;
+    case 'scalew':
+        wi = w * dw;
+        he = h * dw;
+        dx = (c.width - wi) / 2;
+        dy = (c.height - he) / 2;
+        break;
+    }
+    var ctx = c.getContext('2d');
+    ctx.drawImage(img, dx, dy, wi, he);
+}
+
+// Like drawThumbnail, but scales up if needed
+export function drawScaled (img, c) {
+    var imgWidth = img.naturalWidth ? img.naturalWidth : img.width;
+    var imgHeight = img.naturalHeight ? img.naturalHeight : img.height;
+    var boxWidth = c.width;
+    var boxHeight = c.height;
+    var scale = boxWidth / imgWidth;
+    var w = imgWidth * scale;
+    var h = imgHeight * scale;
+    if (h > boxHeight) {
+        scale = boxHeight / imgHeight;
+        w = imgWidth * scale;
+        h = imgHeight * scale;
+    }
+    var x0 = (boxWidth - w) / 2;
+    var y0 = (boxHeight - h) / 2;
+    var ctx = c.getContext('2d');
+    ctx.drawImage(img, x0, y0, w, h);
+}
+
+export function fitInRect (srcw, srch, destw, desth) {
+    var dx = (destw - srcw) / 2;
+    var dy = (desth - srch) / 2;
+    var dw = destw / srcw;
+    var dh = desth / srch;
+    var wi = srcw;
+    var he = srch;
+    switch (getFit(dw, dh)) {
+    case 'noscale':
+        break;
+    case 'scaleh':
+        wi = srcw * dh;
+        he = srch * dh;
+        dx = (destw - wi) / 2;
+        dy = (desth - he) / 2;
+        break;
+    case 'scalew':
+        wi = srcw * dw;
+        he = srch * dw;
+        dx = (destw - wi) / 2;
+        dy = (desth - he) / 2;
+        break;
+    }
+    return [dx, dy, wi, he];
+}
+
+export function getFit (dw, dh) {
+    if ((dw >= 1) && (dh >= 1)) {
+        return 'noscale';
+    }
+    if ((dw >= 1) && (dh < 1)) {
+        return 'scaleh';
+    }
+    if ((dw < 1) && (dh >= 1)) {
+        return 'scalew';
+    }
+    if (dw < dh) {
+        return 'scalew';
+    }
+    return 'scaleh';
+}
+
+export function getDocumentHeight () {
+    return Math.max(document.body.clientHeight, document.documentElement.clientHeight);
+}
+
+export function getDocumentWidth () {
+    return Math.max(document.body.clientWidth, document.documentElement.clientWidth);
+}
+
+export function getStringSize (ctx, f, label) {
+    ctx.font = f;
+    return ctx.measureText(label);
+}
+
+export function writeText (ctx, f, c, label, dy, dx) {
+    dx = (dx == undefined) ? 0 : dx;
+    ctx.font = f;
+    ctx.fillStyle = c;
+    ctx.textAlign = 'left';
+    ctx.textBaseline = 'bottom';
+    ctx.fillText(label, dx, dy);
+}
+
+export function gn (str) {
+    return document.getElementById(str);
+}
+
+export function newForm (parent, str, x, y, w, h, styles) {
+    var el = document.createElement('form');
+    el.style.position = 'absolute';
+    el.style.top = y + 'px';
+    el.style.left = x + 'px';
+    if (w) {
+        el.style.width = w + 'px';
+    }
+    if (h) {
+        el.style.height = h + 'px';
+    }
+    setProps(el.style, styles);
+    parent.appendChild(el);
+    el.name = str;
+    return el;
+}
+
+export function newTextInput (p, type, str, mstyle) {
+    var input = document.createElement('input');
+    input.value = str;
+    setProps(input.style, mstyle);
+    input.type = type;
+    p.appendChild(input);
+    return input;
+}
+
+export function getUrlVars () {
+    if (window.location.href.indexOf('?') < 0) {
+        return [];
+    }
+    var args = window.location.href.slice(window.location.href.indexOf('?') + 1);
+    var vars = [], hash;
+    var hashes = args.split('&');
+    for (var i = 0; i < hashes.length; i++) {
+        hash = hashes[i].split('=');
+        vars.push(hash[0]);
+        vars[hash[0]] = hash[1];
+    }
+    return vars;
+}
+
+export function getIdFor (name) {
+    var n = 1;
+    while (gn(name + ' ' + n) != undefined) {
+        n++;
+    }
+    return name + ' ' + n;
+}
+
+
+export function getIdForCamera (name) {
+    var n = 1;
+    while (gn(name + '_' + n) != undefined) {
+        n++;
+    }
+    return name + '_' + n;
+}
+
+////////////////////
+// Color
+/////////////////////
+
+export function rgb2hsb (str) {
+    if (str == null) {
+        return [24, 1, 1];
+    }
+    var min, val, f, i, hue, sat;
+    str = (str.indexOf('rgb') > -1) ? rgbToHex(str) : rgbaToHex(str);
+    var num = parseInt(str.substring(1, str.length), 16);
+    var rgb = getRGB(num);
+    var red = rgb[0];
+    red /= 255;
+    var grn = rgb[1];
+    grn /= 255;
+    var blu = rgb[2];
+    blu /= 255;
+    min = Math.min(Math.min(red, grn), blu);
+    val = Math.max(Math.max(red, grn), blu);
+    if (min == val) {
+        return new Array(0, 0, val);
+    }
+    f = (red == min) ? grn - blu : ((grn == min) ? blu - red : red - grn);
+    i = (red == min) ? 3 : ((grn == min) ? 5 : 1);
+    hue = Math.round((i - f / (val - min)) * 60) % 360;
+    sat = Math.round(((val - min) / val) * 100);
+    val = Math.round(val * 100);
+    return new Array(hue, sat / 100, val / 100);
+}
+
+export function rgbToHex (str) {
+    if (str.indexOf('rgb') < 0) {
+        return str;
+    }
+    var res = str.substring(4, str.length - 1);
+    var a = res.split(',');
+    var red = Number(a[0]);
+    var grn = Number(a[1]);
+    var blu = Number(a[2]);
+    return rgbToString({
+        r: red,
+        g: grn,
+        b: blu
+    });
+}
+
+export function rgbaToHex (str) {
+    if (str.indexOf('rgba') < 0) {
+        return str;
+    }
+    var res = str.substring(5, str.length - 1);
+    var a = res.split(',');
+    var red = Number(a[0]);
+    var grn = Number(a[1]);
+    var blu = Number(a[2]);
+    return rgbToString({
+        r: red,
+        g: grn,
+        b: blu
+    });
+}
+
+
+export function rgbToString (obj) {
+    return '#' + getHex(obj.r) + getHex(obj.g) + getHex(obj.b);
+}
+
+export function getRGB (color) {
+    return [
+        (Number((color >> 16) & 255)),
+        (Number((color >> 8) & 255)),
+        (Number(color & 255))
+    ];
+}
+
+export function getHex (num) {
+    var hex = num.toString(16);
+    if (hex.length == 1) {
+        return '0' + hex;
+    }
+    return hex;
+}
+
+// findKeyframesRule ("swing");
+
+export function findKeyframesRule (rule) {
+    var ss = document.styleSheets;
+    for (var i = 0; i < ss.length; ++i) {
+        for (var j = 0; j < ss[i].cssRules.length; ++j) {
+            var styles = ss[i].cssRules[j].styleSheet.rules;
+            for (var k = 0; k < styles.length; ++k) {
+                if (styles[k].type == window.CSSRule.WEBKIT_KEYFRAMES_RULE && styles[k].name == rule) {
+                    return styles[k];
+                }
+            }
+        }
+    } // rule not found
+    return null;
+}
+
+export function colorToRGBA (color, opacity) {
+    var val = parseInt('0x' + color.substr(1, color.length));
+    return 'rgba(' + (val >> 16) % 256 + ',' + (val >> 8) % 256 + ',' + (val % 256) + ',' + opacity + ')';
+}
+
+/**
+ * css units vh and vw (for % of height and width) are not supported in Android 4.3 and earlier, so
+ * here we introduce functioncs (called from the preprocessed css) that emulate their behavior by
+ * turning them into pixel values.
+ */
+export function css_vh (y) {
+    return (y * WINDOW_INNER_HEIGHT / 100.0) + 'px';
+}
+
+export function css_vw (x) {
+    return (x * WINDOW_INNER_WIDTH / 100.0) + 'px';
+}
+
 Number.prototype.mod = function (n) {
     return ((this % n) + n) % n;
 };