<commit_before>(function(T) {
    "use strict";

    if (T.env !== "webkit") {
        return;
    }

    var fn = T.fn;
    var context = fn._audioContext;
    var BUFFERSIZE = 1024;

    function WebAudioAPINode(_args) {
        T.Object.call(this, 2, _args);
        fn.fixAR(this);

        var _ = this._;
        _.mode = "";
        _.bufferL = new fn.SignalArray(BUFFERSIZE << 2);
        _.bufferR = new fn.SignalArray(BUFFERSIZE << 2);
        _.buffermask = _.bufferL.length - 1;
        _.node   = null;
        _.script = context.createJavaScriptNode(BUFFERSIZE, 2, 2);
        _.writeIndex = 0;
        _.readIndex  = 0;
        _.totalRead  = 0;
        _.totalWrite = 0;
    }
    fn.extend(WebAudioAPINode);

    var $ = WebAudioAPINode.prototype;

    Object.defineProperties($, {
        context: {
            get: function() {
                return context;
            }
        },
        mode: {
            get: function() {
                return this._.mode;
            }
        }
    });

    $.cancel = function() {
        var _ = this._;
        var cell = this.cells[0];
        for (var i = 0, imax = cell.length; i < imax; ++i) {
            cell[i] = 0;
        }
        _.node = null;
    };

    (function() {
        function WebAudioAPIRecvNode(_args) {
            WebAudioAPINode.call(this, _args);

            var _ = this._;
            _.mode = "recv";
            _.script.onaudioprocess = make_recv_process(this);
            _.gain = context.createGainNode();
            _.gain.gain.value = 0;
            _.script.connect(_.gain);
        }
        fn.extend(WebAudioAPIRecvNode, WebAudioAPINode);

        var make_recv_process = function(self) {
            return function(e) {
                var _ = self._;
                var ins = e.inputBuffer;
                var inputL = ins.getChannelData(0);
                var inputR = ins.getChannelData(1);
                var length = ins.length;
                var writeIndex = _.writeIndex;
                _.bufferL.set(inputL, writeIndex);
                _.bufferR.set(inputR, writeIndex);
                _.writeIndex = (writeIndex + length) & _.buffermask;
                _.totalWrite += length;
            };
        };

        var $ = WebAudioAPIRecvNode.prototype;

        $.cancel = function() {
            WebAudioAPINode.prototype.cancel.call(this);
            this._.gain.disconnect();
            if(this._.node) {
                this._.node.disconnect();
            }
        };

        $.recv = function(node) {
            var _ = this._;
            try {
                _.node = node;
                _.node.connect(_.script);
                _.gain.connect(context.destination);
            } catch(e) {
                _.node = null;
            }
            _.writeIndex = 0;
            _.readIndex  = 0;
            _.totalWrite = 0;
            _.totalRead  = 0;
            return this;
        };

        $.process = function(tickID) {
            var _ = this._;

            if (_.node === null) {
                return this;
            }

            if (this.tickID !== tickID) {
                this.tickID = tickID;

                var cellsize = _.cellsize;
                var bufferL = _.bufferL;
                var bufferR = _.bufferR;

                if (_.totalWrite > _.totalRead + cellsize) {
                    var begin = _.readIndex;
                    var end = begin + cellsize;
                    this.cells[1].set(bufferL.subarray(begin, end));
                    this.cells[2].set(bufferR.subarray(begin, end));
                    _.readIndex = end & _.buffermask;
                    _.totalRead += cellsize;
                }
                fn.outputSignalAR(this);
            }
            return this;
        };

        fn.register("WebAudioAPI:recv", WebAudioAPIRecvNode);
    })();

    (function() {
        function WebAudioAPISendNode(_args) {
            WebAudioAPINode.call(this, _args);
            fn.listener(this);

            var _ = this._;
            _.mode = "send";
            _.script.onaudioprocess = make_send_process(this);
            _.connectIndex = null;
        }
        fn.extend(WebAudioAPISendNode, WebAudioAPINode);

        var make_send_process = function(self) {
            return function(e) {
                var _ = self._;
                var outs = e.outputBuffer;
                var length  = outs.length;

                if (_.totalWrite > _.totalRead + length) {
                    var begin = _.readIndex;
                    var end = begin + length;
                    outs.getChannelData(0).set(_.bufferL.subarray(begin, end));
                    outs.getChannelData(1).set(_.bufferR.subarray(begin, end));
                    _.readIndex = end & _.buffermask;
                    _.totalRead += length;
                }
            };
        };

        var $ = WebAudioAPISendNode.prototype;

        $.cancel = function() {
            WebAudioAPINode.prototype.cancel.call(this);
            var _ = this._;
            if (_.connectIndex !== null) {
                _.script.disconnect(_.connectIndex);
            } else {
                _.script.disconnect();
            }
            this.unlisten();
        };

        $.send = function(node, index) {
            var _ = this._;
            try {
                _.node = node;
                if (typeof index === "number") {
                    _.script.connect(_.node, index);
                    _.connectIndex = index;
                } else {
                    _.script.connect(_.node);
                    _.connectIndex = null;
                }
                this.listen();
            } catch(e) {
                _.node = null;
            }
            _.writeIndex = 0;
            _.readIndex  = 0;
            _.totalWrite = 0;
            _.totalRead  = 0;
            return this;
        };

        $.process = function(tickID) {
            var _ = this._;

            if (_.script === null) {
                return this;
            }

            if (this.tickID !== tickID) {
                this.tickID = tickID;

                var cellL = this.cells[1];
                var cellR = this.cells[2];
                var cellsize = _.cellsize;
                var writeIndex = _.writeIndex;

                fn.inputSignalAR(this);

                _.bufferL.set(cellL, writeIndex);
                _.bufferR.set(cellR, writeIndex);
                _.writeIndex = (writeIndex + cellsize) & _.buffermask;
                _.totalWrite += cellsize;

                fn.outputSignalAR(this);
            }
            return this;
        };

        fn.register("WebAudioAPI:send", WebAudioAPISendNode);
    })();

})(timbre);
<commit_msg>updated webaudioapi.js extension to latest webaudio browser API<commit_after>(function(T) {
    "use strict";

    if (T.env !== "webkit") {
        return;
    }

    var fn = T.fn;
    var context = fn._audioContext;
    var BUFFERSIZE = 1024;

    function WebAudioAPINode(_args) {
        T.Object.call(this, 2, _args);
        fn.fixAR(this);

        var _ = this._;
        _.mode = "";
        _.bufferL = new fn.SignalArray(BUFFERSIZE << 2);
        _.bufferR = new fn.SignalArray(BUFFERSIZE << 2);
        _.buffermask = _.bufferL.length - 1;
        _.node   = null;
        _.script = context.createScriptProcessor(BUFFERSIZE, 2, 2);
        _.writeIndex = 0;
        _.readIndex  = 0;
        _.totalRead  = 0;
        _.totalWrite = 0;
    }
    fn.extend(WebAudioAPINode);

    var $ = WebAudioAPINode.prototype;

    Object.defineProperties($, {
        context: {
            get: function() {
                return context;
            }
        },
        mode: {
            get: function() {
                return this._.mode;
            }
        }
    });

    $.cancel = function() {
        var _ = this._;
        var cell = this.cells[0];
        for (var i = 0, imax = cell.length; i < imax; ++i) {
            cell[i] = 0;
        }
        _.node = null;
    };

    (function() {
        function WebAudioAPIRecvNode(_args) {
            WebAudioAPINode.call(this, _args);

            var _ = this._;
            _.mode = "recv";
            _.script.onaudioprocess = make_recv_process(this);
            _.gain = context.createGain();
            _.gain.gain.value = 0;
            _.script.connect(_.gain);
        }
        fn.extend(WebAudioAPIRecvNode, WebAudioAPINode);

        var make_recv_process = function(self) {
            return function(e) {
                var _ = self._;
                var ins = e.inputBuffer;
                var inputL = ins.getChannelData(0);
                var inputR = ins.getChannelData(1);
                var length = ins.length;
                var writeIndex = _.writeIndex;
                _.bufferL.set(inputL, writeIndex);
                _.bufferR.set(inputR, writeIndex);
                _.writeIndex = (writeIndex + length) & _.buffermask;
                _.totalWrite += length;
            };
        };

        var $ = WebAudioAPIRecvNode.prototype;

        $.cancel = function() {
            WebAudioAPINode.prototype.cancel.call(this);
            this._.gain.disconnect();
            if(this._.node) {
                this._.node.disconnect();
            }
        };

        $.recv = function(node) {
            var _ = this._;
            try {
                _.node = node;
                _.node.connect(_.script);
                _.gain.connect(context.destination);
            } catch(e) {
                _.node = null;
            }
            _.writeIndex = 0;
            _.readIndex  = 0;
            _.totalWrite = 0;
            _.totalRead  = 0;
            return this;
        };

        $.process = function(tickID) {
            var _ = this._;

            if (_.node === null) {
                return this;
            }

            if (this.tickID !== tickID) {
                this.tickID = tickID;

                var cellsize = _.cellsize;
                var bufferL = _.bufferL;
                var bufferR = _.bufferR;

                if (_.totalWrite > _.totalRead + cellsize) {
                    var begin = _.readIndex;
                    var end = begin + cellsize;
                    this.cells[1].set(bufferL.subarray(begin, end));
                    this.cells[2].set(bufferR.subarray(begin, end));
                    _.readIndex = end & _.buffermask;
                    _.totalRead += cellsize;
                }
                fn.outputSignalAR(this);
            }
            return this;
        };

        fn.register("WebAudioAPI:recv", WebAudioAPIRecvNode);
    })();

    (function() {
        function WebAudioAPISendNode(_args) {
            WebAudioAPINode.call(this, _args);
            fn.listener(this);

            var _ = this._;
            _.mode = "send";
            _.script.onaudioprocess = make_send_process(this);
            _.connectIndex = null;
        }
        fn.extend(WebAudioAPISendNode, WebAudioAPINode);

        var make_send_process = function(self) {
            return function(e) {
                var _ = self._;
                var outs = e.outputBuffer;
                var length  = outs.length;

                if (_.totalWrite > _.totalRead + length) {
                    var begin = _.readIndex;
                    var end = begin + length;
                    outs.getChannelData(0).set(_.bufferL.subarray(begin, end));
                    outs.getChannelData(1).set(_.bufferR.subarray(begin, end));
                    _.readIndex = end & _.buffermask;
                    _.totalRead += length;
                }
            };
        };

        var $ = WebAudioAPISendNode.prototype;

        $.cancel = function() {
            WebAudioAPINode.prototype.cancel.call(this);
            var _ = this._;
            if (_.connectIndex !== null) {
                _.script.disconnect(_.connectIndex);
            } else {
                _.script.disconnect();
            }
            this.unlisten();
        };

        $.send = function(node, index) {
            var _ = this._;
            try {
                _.node = node;
                if (typeof index === "number") {
                    _.script.connect(_.node, index);
                    _.connectIndex = index;
                } else {
                    _.script.connect(_.node);
                    _.connectIndex = null;
                }
                this.listen();
            } catch(e) {
                _.node = null;
            }
            _.writeIndex = 0;
            _.readIndex  = 0;
            _.totalWrite = 0;
            _.totalRead  = 0;
            return this;
        };

        $.process = function(tickID) {
            var _ = this._;

            if (_.script === null) {
                return this;
            }

            if (this.tickID !== tickID) {
                this.tickID = tickID;

                var cellL = this.cells[1];
                var cellR = this.cells[2];
                var cellsize = _.cellsize;
                var writeIndex = _.writeIndex;

                fn.inputSignalAR(this);

                _.bufferL.set(cellL, writeIndex);
                _.bufferR.set(cellR, writeIndex);
                _.writeIndex = (writeIndex + cellsize) & _.buffermask;
                _.totalWrite += cellsize;

                fn.outputSignalAR(this);
            }
            return this;
        };

        fn.register("WebAudioAPI:send", WebAudioAPISendNode);
    })();

})(timbre);
<|endoftext|>


<commit_before>/**
 * DomSlot
 */
(function(superClass) {
    var TextureAtlas = dragonBones.TextureAtlas;
    var DomSlot = function() {
        superClass.call(this, this);
        this._display = null;
    };

    __extends(DomSlot, superClass, {
        dispose: function() {
            if (this._displayList) {
                var length = this._displayList.length;
                for (var i = 0; i < length; i++) {
                    var content = this._displayList[i];
                    if (content instanceof Armature) {
                        content.dispose();
                    }
                }
            }

            superClass.prototype.dispose();
            this._display = null;
        },
        _updateDisplay: function(value) {
            this._display = value;
        },
        _getDisplayIndex: function() {
            if (this._display && this._display.parentNode) {
                return Array.prototype.indexOf.call(this._display.parentNode.childNodes, this._display);
            }
            return -1;
        },
        _addDisplayToContainer: function(container, index) {
            if (this._display && container) {
                container.appendChild(this._display);
                this._display.style.zIndex = index||container.childNodes.length;
            }
        },
        _removeDisplayFromContainer: function() {
            if (this._display && this._display.parentNode) {
                this._display.parentNode.removeChild(this._display);
            }
        },
        _updateTransform: function() {
            if (this._display) {
                var m = this._globalTransformMatrix;
                this._display.style.transform = 'matrix3d(' + m.a + ',' + m.b + ',0,0,' + m.c + ',' + m.d + ',0,0,0,0,1,0,' +(m.tx-this._display.pivotX) + ',' + (m.ty-this._display.pivotY) + ',0,1)';
            }
        },
        _updateDisplayVisible: function(value) {
            if (this._display && this._parent) {
                var visible = this._parent._visible && this._visible && value;
                this._display.style.display = visible?'block':'none';
            }
        },
        _updateDisplayColor: function(aOffset, rOffset, gOffset, bOffset, aMultiplier, rMultiplier, gMultiplier, bMultiplier, colorChange) {
            superClass.prototype._updateDisplayColor.call(this, aOffset, rOffset, gOffset, bOffset, aMultiplier, rMultiplier, gMultiplier, bMultiplier, colorChange);
            if (this._display) {
                this._display.style.opacity = aMultiplier;
            }
        },
        _updateDisplayBlendMode: function(value) {
            // if (this._display && value) {
            //     this._display.blendMode = value;
            // }
        },
        _calculateRelativeParentTransform: function() {
            this._global.scaleX = this._origin.scaleX * this._offset.scaleX;
            this._global.scaleY = this._origin.scaleY * this._offset.scaleY;
            this._global.skewX = this._origin.skewX + this._offset.skewX;
            this._global.skewY = this._origin.skewY + this._offset.skewY;
            this._global.x = this._origin.x + this._offset.x + this._parent._tweenPivot.x;
            this._global.y = this._origin.y + this._offset.y + this._parent._tweenPivot.y;

            if (this._displayDataList &&
                this._currentDisplayIndex >= 0 &&
                this._displayDataList[this._currentDisplayIndex] &&
                TextureAtlas.rotatedDic[this._displayDataList[this._currentDisplayIndex].name] == 1) {
                this._global.skewX -= 1.57;
                this._global.skewY -= 1.57;
            }
        }
    });

    dragonBones.DomSlot = DomSlot;
})(dragonBones.Slot);

/**
 * DomFactory
 */
(function(superClass){
    var Armature = dragonBones.Armature;
    var DomSlot = dragonBones.DomSlot;

    var DomFactory = function(){
        superClass.call(this, this);
    };
    __extends(DomFactory, superClass, {
        _generateArmature:function(){
            var container = document.createElement('div');
            container.style.position = 'relative';
            var armature = new Armature(container);
            return armature;
        },
        _generateSlot:function(){
            var slot = new DomSlot();
            return slot;
        },
        _generateDisplay:function(textureAtlas, fullName, pivotX, pivotY){
            var bitmap = document.createElement('div');
            var texture = textureAtlas.getTexture(fullName);
            bitmap.style.background = 'url(' + textureAtlas.texture.src + ')';
            bitmap.style.width = texture.region.width + 'px';
            bitmap.style.height = texture.region.height + 'px';
            bitmap.style.backgroundPosition = -texture.region.x + 'px -' + texture.region.y + 'px';
            bitmap.style.position = 'absolute';
            if(isNaN(pivotX)||isNaN(pivotY))
            {
                var subTextureFrame = textureAtlas.getFrame(fullName);
                if(subTextureFrame != null)
                {
                    pivotX = subTextureFrame.width/2;
                    pivotY = subTextureFrame.height/2;
                }
                else
                {
                    pivotX = texture.region.width/2;
                    pivotY = texture.region.height/2;
                }
            }
            bitmap.style.transformOrigin = pivotX + 'px ' + pivotY + 'px';
            bitmap.pivotX = pivotX;
            bitmap.pivotY = pivotY;
            return bitmap;
        }
    });

    dragonBones.DomFactory = DomFactory;
}(dragonBones.BaseFactory));<commit_msg>fix: add browser prefix<commit_after>/**
 * DomSlot
 */
(function(superClass) {
    var TextureAtlas = dragonBones.TextureAtlas;
    var DomSlot = function() {
        superClass.call(this, this);
        this._display = null;
    };

    __extends(DomSlot, superClass, {
        dispose: function() {
            if (this._displayList) {
                var length = this._displayList.length;
                for (var i = 0; i < length; i++) {
                    var content = this._displayList[i];
                    if (content instanceof Armature) {
                        content.dispose();
                    }
                }
            }

            superClass.prototype.dispose();
            this._display = null;
        },
        _updateDisplay: function(value) {
            this._display = value;
        },
        _getDisplayIndex: function() {
            if (this._display && this._display.parentNode) {
                return Array.prototype.indexOf.call(this._display.parentNode.childNodes, this._display);
            }
            return -1;
        },
        _addDisplayToContainer: function(container, index) {
            if (this._display && container) {
                container.appendChild(this._display);
                this._display.style.zIndex = index||container.childNodes.length;
            }
        },
        _removeDisplayFromContainer: function() {
            if (this._display && this._display.parentNode) {
                this._display.parentNode.removeChild(this._display);
            }
        },
        _updateTransform: function() {
            if (this._display) {
                var m = this._globalTransformMatrix;
                this._display.style.webkitTransform = 'matrix3d(' + m.a + ',' + m.b + ',0,0,' + m.c + ',' + m.d + ',0,0,0,0,1,0,' +(m.tx-this._display.pivotX) + ',' + (m.ty-this._display.pivotY) + ',0,1)';
            }
        },
        _updateDisplayVisible: function(value) {
            if (this._display && this._parent) {
                var visible = this._parent._visible && this._visible && value;
                this._display.style.display = visible?'block':'none';
            }
        },
        _updateDisplayColor: function(aOffset, rOffset, gOffset, bOffset, aMultiplier, rMultiplier, gMultiplier, bMultiplier, colorChange) {
            superClass.prototype._updateDisplayColor.call(this, aOffset, rOffset, gOffset, bOffset, aMultiplier, rMultiplier, gMultiplier, bMultiplier, colorChange);
            if (this._display) {
                this._display.style.opacity = aMultiplier;
            }
        },
        _updateDisplayBlendMode: function(value) {
            // if (this._display && value) {
            //     this._display.blendMode = value;
            // }
        },
        _calculateRelativeParentTransform: function() {
            this._global.scaleX = this._origin.scaleX * this._offset.scaleX;
            this._global.scaleY = this._origin.scaleY * this._offset.scaleY;
            this._global.skewX = this._origin.skewX + this._offset.skewX;
            this._global.skewY = this._origin.skewY + this._offset.skewY;
            this._global.x = this._origin.x + this._offset.x + this._parent._tweenPivot.x;
            this._global.y = this._origin.y + this._offset.y + this._parent._tweenPivot.y;

            if (this._displayDataList &&
                this._currentDisplayIndex >= 0 &&
                this._displayDataList[this._currentDisplayIndex] &&
                TextureAtlas.rotatedDic[this._displayDataList[this._currentDisplayIndex].name] == 1) {
                this._global.skewX -= 1.57;
                this._global.skewY -= 1.57;
            }
        }
    });

    dragonBones.DomSlot = DomSlot;
})(dragonBones.Slot);

/**
 * DomFactory
 */
(function(superClass){
    var Armature = dragonBones.Armature;
    var DomSlot = dragonBones.DomSlot;

    var DomFactory = function(){
        superClass.call(this, this);
    };
    __extends(DomFactory, superClass, {
        _generateArmature:function(){
            var container = document.createElement('div');
            container.style.position = 'relative';
            var armature = new Armature(container);
            return armature;
        },
        _generateSlot:function(){
            var slot = new DomSlot();
            return slot;
        },
        _generateDisplay:function(textureAtlas, fullName, pivotX, pivotY){
            var bitmap = document.createElement('div');
            var texture = textureAtlas.getTexture(fullName);
            bitmap.style.background = 'url(' + textureAtlas.texture.src + ')';
            bitmap.style.width = texture.region.width + 'px';
            bitmap.style.height = texture.region.height + 'px';
            bitmap.style.backgroundPosition = -texture.region.x + 'px -' + texture.region.y + 'px';
            bitmap.style.position = 'absolute';
            if(isNaN(pivotX)||isNaN(pivotY))
            {
                var subTextureFrame = textureAtlas.getFrame(fullName);
                if(subTextureFrame != null)
                {
                    pivotX = subTextureFrame.width/2;
                    pivotY = subTextureFrame.height/2;
                }
                else
                {
                    pivotX = texture.region.width/2;
                    pivotY = texture.region.height/2;
                }
            }
            bitmap.style.webkitTransformOrigin = pivotX + 'px ' + pivotY + 'px';
            bitmap.pivotX = pivotX;
            bitmap.pivotY = pivotY;
            return bitmap;
        }
    });

    dragonBones.DomFactory = DomFactory;
}(dragonBones.BaseFactory));<|endoftext|>


<commit_before>/**
 * Project: wampy.js
 *
 * https://github.com/KSDaemon/wampy.js
 *
 * A lightweight client-side implementation of
 * WAMP (The WebSocket Application Messaging Protocol)
 * http://wamp.ws
 *
 * Provides asynchronous RPC/PubSub over WebSocket.
 *
 * Copyright 2014 KSDaemon. Licensed under the MIT License.
 * See license text at http://www.opensource.org/licenses/mit-license.php
 *
 */

;(function(window, undefined) {

	var WAMP_SPEC = {
		TYPE_ID_WELCOME: 0,
		TYPE_ID_PREFIX: 1,
		TYPE_ID_CALL: 2,
		TYPE_ID_CALLRESULT: 3,
		TYPE_ID_CALLERROR: 4,
		TYPE_ID_SUBSCRIBE: 5,
		TYPE_ID_UNSUBSCRIBE: 6,
		TYPE_ID_PUBLISH: 7,
		TYPE_ID_EVENT: 8
	};

	function getServerUrl (url) {
		var scheme, port, path;

		if (!url) {
			scheme = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
			port = window.location.port !== "" ? ':' + window.location.port : '';
			return scheme + window.location.hostname + port + "/ws";
		} else if (/^ws/.test(url)) {   // ws scheme is specified
			return url;
		} else if (/:\d{1,5}/.test(url)) {  // port is specified
			scheme = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
			return scheme + url;
		} else {    // just path on current server
			scheme = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
			port = window.location.port !== "" ? ':' + window.location.port : '';
			path = url[0] === '/' ? url : '/' + url;
			return scheme + window.location.hostname + port + path;
		}
	};

	function generateId () {
		var keyChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
			keyLen = 16,
			key = "",
			l = keyChars.length;

		while (keyLen--) {
			key += keyChars.charAt(Math.floor(Math.random() * l));
		}
		return key;
	};

	function getWebSocket (url, protocols) {
		var parsedUrl = getServerUrl(url);

		if ("WebSocket" in window) {
		// Chrome, MSIE, newer Firefox
			if (protocols) {
				return new WebSocket(parsedUrl, protocols);
			} else {
				return new WebSocket(parsedUrl);
			}
		} else if ("MozWebSocket" in window) {
			// older versions of Firefox
			if (protocols) {
				return new MozWebSocket(parsedUrl, protocols);
			} else {
				return new MozWebSocket(parsedUrl);
			}
		} else {
			return null;
		}
	};

	/**
	 * Prefixes table object
	 */
	var prefixMap = function () {
		this._pref2uri = {};
		this._uri2pref = {};
	};

	prefixMap.prototype.get = function (prefix) {
		return this._pref2uri[prefix];
	};

	prefixMap.prototype.set = function (prefix, uri) {
		this._pref2uri[prefix] = uri;
		this._uri2pref[prefix] = uri;
	}

	prefixMap.prototype.remove = function (prefix) {
		if (this._pref2uri[prefix]) {
			delete this._uri2pref[this._pref2uri[prefix]];
			delete this._pref2uri[prefix];
		}
	};

	prefixMap.prototype.reset = function () {
		this._pref2uri = {};
		this._uri2pref = {};
	};

	prefixMap.prototype.resolve = function (uri) {
		var isSchema = /^http(s)?:\/\//.test(uri),
			i = uri.indexOf(":"), prefix;

		if ((!isSchema) && (i >= 0)) {
			prefix = uri.substring(0, i);
			if (this._pref2uri[prefix]) {
				return this._pref2uri[prefix] + uri.substring(i + 1);
			} else {
				return null;
			}
		} else {
			return uri;
		}
	};

	/**
	 * WAMP Client Class
	 * @param {string} url
	 * @param {Array} protocols
	 * @param {Object} options
	 */
	var Wampy = function (url, protocols, options) {

		/**
		 * Wampy version
		 * @type {string}
		 * @private
		 */
		this.version = 'v0.1.0';

		/**
		 * WS Url
		 * @type {string}
		 * @private
		 */
		this._url = url;

		/**
		 * WS protocols
		 * @type {Array}
		 * @private
		 */
		this._protocols = [];

		/**
		 * Internal cache for object lifetime
		 * @type {Object}
		 * @private
		 */
		this._cache = {
			/**
			 * WAMP Session ID
			 * @type {string}
			 */
			sessionId: null,

			/**
			 * WAMP Protocol version
			 * @type {number}
			 */
			protocolVersion: 1,

			/**
			 * Timer for reconnection
			 * @type {null}
			 */
			timer: null,

			/**
			 * Reconnection attempts
			 * @type {number}
			 */
			reconnectingAttempts: 0,

			/**
			 * Did we received welcome message
			 * @type {boolean}
			 */
			welcome: false,

			/**
			 * onConnect callback
			 * @type {function}
			 */
			onConnect: null,

			/**
			 * onClose callback
			 * @type {function}
			 */
			onClose: null,

			/**
			 * onError callback
			 * @type {function}
			 */
			onError: null,

			/**
			 * onReconnect callback
			 * @type {function}
			 */
			onReconnect: null
		};

		/**
		 * WAMP Server info
		 * @type {string}
		 * @private
		 */
		this._serverIdent = null;

		/**
		 * WebSocket object
		 * @type {Object}
		 * @private
		 */
		this._ws = null;

		/**
		 * Internal queue for requests, for case of disconnect
		 * @type {Array}
		 * @private
		 */
		this._queue = [];

		/**
		 * Stored RPC
		 * @type {object}
		 * @private
		 */
		this._calls = {};

		/**
		 * Stored Pub/Sub
		 * @type {object}
		 * @private
		 */
		this._subscriptions = {};

		/**
		 * Wampy State
		 * @type {boolean}
		 * @private
		 */
		this._isInitialized = false;

		/**
		 * Options hash-table
		 * @type {Object}
		 * @private
		 */
		this._options = {
			autoReconnect: true,
			reconnectInterval: 2 * 1000,
			maxRetries: 25
		};

		/**
		 * Prefixes hash-table for instance
		 * @type {prefixMap}
		 * @private
		 */
		this._prefixMap = new prefixMap();

		switch (arguments.length) {
			case 1:
				if (typeof arguments[0] === 'string') {
					this._ws = getWebSocket(arguments[0]);
				} else {
					this._options = this._merge(this._options, arguments[0]);
				}
				break;
			case 2:
				if (typeof arguments[0] === 'string' && arguments[1] instanceof Array) {
					this._protocols = arguments[1];
					this._ws = getWebSocket(arguments[0], arguments[1]);
				} else {
					this._ws = getWebSocket(arguments[0]);
					this._options = this._merge(this._options, arguments[1]);
				}
				break;
			case 3:
				this._protocols = arguments[1];
				this._ws = getWebSocket(arguments[0], arguments[1]);
				this._options = this._merge(this._options, arguments[2]);
				break;
		}

		//TODO Catch if no websocket object

		this._initWsCallbacks();
	};

	/* Internal utils methods */

	/**
	 * Merge argument objects in first on
	 * @returns {Object}
	 * @private
	 */
	Wampy.prototype._merge = function () {
		var obj = {}, i, l = arguments.length, attr;

		for (i = 0; i < l; i++) {
			for (attr in arguments[i]) {
				obj[attr] = arguments[i][attr];
			}
		}

		return obj;
	};

	/* Internal websocket related functions */

	Wampy.prototype._send = function (msg) {
		if(msg) {
			this._queue.push(JSON.stringify(msg));
		}

		if (this._isInitialized && this._ws.readyState === 1) {
			while (this._queue.length) {
				this._ws.send(this._queue.shift());
			}
		}
	};

	Wampy.prototype._initWsCallbacks = function () {
		var self = this;

		this._ws.onopen = function () { self._wsOnOpen.call(self); };
		this._ws.onclose = function (event) { self._wsOnClose.call(self, event); };
		this._ws.onmessage = function (event) { self._wsOnMessage.call(self, event); };
		this._ws.onerror = function (error) { self._wsOnError.call(self, error); };

	};

	Wampy.prototype._wsOnOpen = function () {
		console.log("[wampy] websocket connected");

		//TODO Make subprotocol check
	};

	Wampy.prototype._wsOnClose = function (event) {
		var self = this;
		console.log("[wampy] websocket disconnected");

		this._cache.welcome = false;

		// Automatic reconnection
		if (this._isInitialized && this._options.autoReconnect && this._cache.reconnectingAttempts < this._options.maxRetries) {
			this._cache.timer = window.setTimeout(function () { self._wsReconnect.call(self); }, this._options.reconnectInterval);
		} else {
			// No reconnection needed or reached max retries count
			if (this._options.onClose) {
				this._options.onClose();
			}

			this.disconnect();
		}
	};

	Wampy.prototype._wsOnMessage = function (event) {
		var data, i, uri;

		console.log("[wampy] websocket message received: ", event.data);

		data = JSON.parse(event.data);

		switch (data[0]) {
			case WAMP_SPEC.TYPE_ID_WELCOME:
				this._cache.sessionId = data[1];
				this._cache.protocolVersion = data[2];
				this._serverIdent = data[3];
				this._isInitialized = true;

				// Firing onConnect event on real connection to WAMP server
				if (this._options.onConnect) {
					this._options.onConnect();
				}

				// Send local queue if there is something out there
				this._send();

				break;
			case WAMP_SPEC.TYPE_ID_CALLRESULT:
				if (this._calls[data[1]] && this._calls[data[1]].callRes) {
					this._calls[data[1]]['callRes'](data[2]);
				}
				break;
			case WAMP_SPEC.TYPE_ID_CALLERROR:
				if (this._calls[data[1]] && this._calls[data[1]].callErr) {
					// I don't think client is interested in URI of error
					this._calls[data[1]]['callErr'](data[3], data[4]);
				}
				break;
			case WAMP_SPEC.TYPE_ID_EVENT:
				// Ratchet does not return fully qualified URI for the topic as in spec
				// so we need to resolve it manually :(
				uri = this._prefixMap.resolve(data[1]);
				if (this._subscriptions[uri]) {
					i = this._subscriptions[uri].length;
					while (i--) {
						this._subscriptions[uri][i](data[2]);
					}
				}
				break;
		}

	};

	Wampy.prototype._wsOnError = function (error) {
		console.log("[wampy] websocket error");

		if (this._options.onError) {
			this._options.onError();
		}
	};

	Wampy.prototype._wsReconnect = function () {
		console.log("[wampy] websocket reconnecting...");

		if (this._options.onReconnect) {
			this._options.onReconnect();
		}

		this._cache.reconnectingAttempts++;
		this._ws = getWebSocket(this._url, this._protocols);
		this._initWsCallbacks();
	};

	/* Wampy public API */

	Wampy.prototype.options = function (opts) {
		if (opts === undefined) {
			return this._options;
		} else if (typeof opts === 'object') {
			this._options = this._merge(this._options, opts);
			return this;
		}
	};

	Wampy.prototype.connect = function (url, protocols) {
		if (url) {
			this._url = url;
		}

		if (protocols instanceof Array) {
			this._protocols = protocols;
		} else {
			this._protocols = [];
		}

		this._ws = getWebSocket(this._url, this._protocols);

		return this;
	};

	Wampy.prototype.disconnect = function () {
		if (this._isInitialized) {
			this._isInitialized = false;
			this._ws.close();
			this._serverIdent = null;
			this._prefixMap.reset();
			this._ws = null;
			this._queue = [];
			this._subscriptions = {};
			this._calls = {};

			// Just keep attrs that are have to be present
			this._cache = {
				protocolVersion: 1,
				reconnectingAttempts: 0
			};

		}

		return this;
	};

	Wampy.prototype.prefix = function (prefix, uri) {
		this._prefixMap.set(prefix, uri);
		this._send([WAMP_SPEC.TYPE_ID_PREFIX, prefix, uri]);

		return this;
	};

	Wampy.prototype.unprefix = function (prefix) {
		this._prefixMap.remove(prefix);

		return this;
	};

	Wampy.prototype.call = function (procURI, callbacks) {
		var callId = generateId(), i,
			l = arguments.length,
			msg = [WAMP_SPEC.TYPE_ID_CALL];

		// If we've got for some reason nonunique key
		while (callId in this._calls) {
			callId = generateId();
		}

		this._calls[callId] = callbacks;

		msg.push(callId);
		msg.push(procURI);

		for (i = 2; i < l; i++) {
			msg.push(arguments[i]);
		}

		this._send(msg);

		return this;
	};

	Wampy.prototype.subscribe = function (topicURI, callback) {
		var uri = this._prefixMap.resolve(topicURI);

		if (!this._subscriptions[uri]) {
			this._subscriptions[uri] = [];
			this._send([WAMP_SPEC.TYPE_ID_SUBSCRIBE, topicURI]);
		}

		// There is no such callback yet
		if (this._subscriptions[uri].indexOf(callback) < 0) {
			this._subscriptions[uri].push(callback);
		}

		return this;
	};

	Wampy.prototype.unsubscribe = function (topicURI, callback) {
		var i, uri = this._prefixMap.resolve(topicURI);

		if (this._subscriptions[uri]) {
			if (callback !== undefined) {
				i = this._subscriptions[uri].indexOf(callback);
				if (i >= 0) {
					this._subscriptions[uri].splice(i, 1);
				}

				if (this._subscriptions[uri].length) {
					// There are another callbacks for this topic
					return this;
				}
			}

			this._send([WAMP_SPEC.TYPE_ID_UNSUBSCRIBE, topicURI]);
			delete this._subscriptions[uri];
		}

		return this;
	};

	Wampy.prototype.publish = function (topicURI, event, exclude, eligible) {
		var msg = [WAMP_SPEC.TYPE_ID_PUBLISH, topicURI, event];

		switch (arguments.length) {
			case 2:
				this._send(msg);
				break;
			case 3:
				if ((typeof(exclude) === 'boolean') || (exclude instanceof Array)) {
					msg.push(exclude);
					this._send(msg);
				}
				break;
			case 4:
				if ((exclude instanceof Array) && (eligible instanceof Array)) {
					msg.push(exclude);
					msg.push(eligible);
					this._send(msg);
				}
				break;
		}

		return this;
	};

	window.Wampy = Wampy;

})(window);
<commit_msg>Deleted v1 version additional file<commit_after><|endoftext|>


<commit_before>/*
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
  The Requires Axiom is used to declare that a class requires/creates objects
  of a particular class. Required classes can be accessed without fully
  qualifying their package names. Required classes are automatically
  created in the sub-context of the creating object.

  Ex.
  foam.CLASS({
    package: 'demo.bank',
    name: 'AccountTester',
    requires: [
      // Require demo.bank.Account so that it can be accessed as this.Account
      'demo.bank.Account',

      // Require SavingsAccount and alias it so that it can be accessed
      // as this.SAccount
      'demo.bank.SavingsAccount as SAccount'
    ],
    methods: [ function init() {
      var a = this.Account.create();
      var s = this.SAccount.create();
    } ]
  });

*/
foam.CLASS({
  package: 'foam.core',
  name: 'Requires',

  documentation: 'Require Class Axiom',

  properties: [ 'name', 'path' ],

  methods: [
    function installInProto(proto) {
      var name = this.name;
      var path = this.path;

      // Create a private_ clone of the Class with the create() method decorated
      // to pass 'this' as the context if not explicitly provided.  This ensures
      // that the created object has access to this object's exported bindings.
      Object.defineProperty(proto, name, {
        get: function requiresGetter() {
          if ( ! this.hasOwnPrivate_(name) ) {
            var cls    = (this.__context__ || foam).lookup(path);
            var parent = this;
console.assert(this.__context__, "Requires with no context!"); // TODO: remove
            this.assert(cls, 'Requires: Unknown class: ', path);

            var c = Object.create(cls);
            c.create = function requiresCreate(args, ctx) { return cls.create(args, ctx || parent); };
            this.setPrivate_(name, c);
          }

          return this.getPrivate_(name);
        },
        configurable: true,
        enumerable: false
      });
    }
  ]
});

foam.CLASS({
  refines: 'foam.core.Model',
  properties: [
    {
      class: 'AxiomArray',
      of: 'Requires',
      name: 'requires',
      adaptArrayElement: function(o) {
        if ( typeof o === 'string' ) {
          var a     = o.split(' as ');
          var path  = a[0];
          var parts = path.split('.');
          var name  = a[1] || parts[parts.length-1];
          return foam.core.Requires.create({name: name, path: path});
        }

        return foam.core.Requires.create(o);
      }
    }
  ]
});
<commit_msg>Removed debug assert<commit_after>/*
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
  The Requires Axiom is used to declare that a class requires/creates objects
  of a particular class. Required classes can be accessed without fully
  qualifying their package names. Required classes are automatically
  created in the sub-context of the creating object.

  Ex.
  foam.CLASS({
    package: 'demo.bank',
    name: 'AccountTester',
    requires: [
      // Require demo.bank.Account so that it can be accessed as this.Account
      'demo.bank.Account',

      // Require SavingsAccount and alias it so that it can be accessed
      // as this.SAccount
      'demo.bank.SavingsAccount as SAccount'
    ],
    methods: [ function init() {
      var a = this.Account.create();
      var s = this.SAccount.create();
    } ]
  });

*/
foam.CLASS({
  package: 'foam.core',
  name: 'Requires',

  documentation: 'Require Class Axiom',

  properties: [ 'name', 'path' ],

  methods: [
    function installInProto(proto) {
      var name = this.name;
      var path = this.path;

      // Create a private_ clone of the Class with the create() method decorated
      // to pass 'this' as the context if not explicitly provided.  This ensures
      // that the created object has access to this object's exported bindings.
      Object.defineProperty(proto, name, {
        get: function requiresGetter() {
          if ( ! this.hasOwnPrivate_(name) ) {
            var cls    = (this.__context__ || foam).lookup(path);
            var parent = this;
            this.assert(cls, 'Requires: Unknown class: ', path);

            var c = Object.create(cls);
            c.create = function requiresCreate(args, ctx) { return cls.create(args, ctx || parent); };
            this.setPrivate_(name, c);
          }

          return this.getPrivate_(name);
        },
        configurable: true,
        enumerable: false
      });
    }
  ]
});

foam.CLASS({
  refines: 'foam.core.Model',
  properties: [
    {
      class: 'AxiomArray',
      of: 'Requires',
      name: 'requires',
      adaptArrayElement: function(o) {
        if ( typeof o === 'string' ) {
          var a     = o.split(' as ');
          var path  = a[0];
          var parts = path.split('.');
          var name  = a[1] || parts[parts.length-1];
          return foam.core.Requires.create({name: name, path: path});
        }

        return foam.core.Requires.create(o);
      }
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.u2',
  name: 'DetailView',
  extends: 'foam.u2.View',

  requires: [
    'foam.core.Property',
    'foam.u2.DetailPropertyView'
  ],

  exports: [
    'controllerMode'
  ],

  properties: [
    {
      name: 'data',
      preSet: function(_, data) {
        if ( data && data.cls_ !== this.of ) {
          this.of = data.cls_;
        }
        return data;
      }
    },
    {
      class: 'Class',
      name: 'of'
    },
    {
      class: 'Boolean',
      name: 'showActions'
    },
    {
      name: 'properties',
      // TODO: Make an FObjectArray when it validates properly
      preSet: function(_, ps) {
        this.assert(ps, 'Properties required.');
        for ( var i = 0 ; i < ps.length ; i++ ) {
          this.assert(
              foam.core.Property.isInstance(ps[i]),
              "Non-Property in 'properties' list:",
              ps);
        }
        return ps;
      },
      expression: function(of) {
        if ( ! of ) return [];
        return this.of$cls.getAxiomsByClass(foam.core.Property).
            filter(function(p) { return ! p.hidden; });
      }
    },
    {
      name: 'config'
      // Map of property-name: {map of property overrides} for configuring properties
      // values include 'label', 'units', and 'view'
    },
    {
      name: 'actions',
      expression: function(of) {
        return this.of$cls.getAxiomsByClass(foam.core.Action);
      }
    },
    {
      name: 'controllerMode',
      attribute: true
    },
    {
      name: 'title',
      attribute: true,
      expression: function(of) { return this.of$cls.label; },
      // documentation: function() {/*
      //
The display title for the $$DOC{ref:'foam.ui.View'}.
      //

      //*/}
    },
    [ 'nodeName', 'div' ]
  ],

  templates: [
    function CSS() {/*
      ^ {
        background: #fdfdfd;
        border: solid 1px #dddddd;
        box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
        display: inline-block;
        margin: 5px;
        padding: 3px;
      }
      ^ table {
        padding-bottom: 2px;
      }
      ^title {
        color: #333;
        float: left;
        font-size: 14px;
        font-weight: bold;
        margin-bottom: 8px;
        padding: 2px;
      }
      ^toolbar {
        margin-left: 5px;
      }
      ^ input {
        border: solid 1px #aacfe4;
        font-size: 10px;
        margin: 2px 0 0px 2px;
        padding: 4px 2px;
      }
      ^ textarea {
        border: solid 1px #aacfe4;
        float: left;
        font-size: 10px;
        margin: 2px 0 0px 2px;
        overflow: auto;
        padding: 4px 2px;
        width: 98%;
      }
      ^ select {
        border: solid 1px #aacfe4;
        font-size: 10px;
        margin: 2px 0 0px 2px;
        padding: 4px 2px;
      }
    */}
  ],

  methods: [
    function initE() {
      var self = this;
      this.add(this.slot(function(of, properties) {
        if ( ! of ) return '';

        var title = self.title && self.E('tr').
          start('td').cssClass(self.myCls('title')).attrs({colspan: 2}).
            add(this.title$).
          end();

        return self.actionBorder(
          self.
            E('table').
            cssClass(self.myCls()).
            add(title).
            add(properties.map(function(p) {
              var config = self.config && self.config[p.name];

              if ( config ) {
                p = p.clone();
                for ( var key in config ) {
                  p[key] = config[key];
                }
              }

              return self.DetailPropertyView.create({prop: p});
            })));
      }));
    },

    function actionBorder(e) {
      if ( ! this.showActions || ! this.actions.length ) return e;

      return this.E().add(e).
        start('div').cssClass(this.myCls('toolbar')).add(this.actions).end();
    },

    function elementForFeature(fName) {
      var f = this.cls_.getFeature(fName) || this.X.data.model_.getFeature(fName);
      return f ? f.toE(this.Y) : this.E('Unknown feature: ' + fName).style({color: 'red'});
    }
  ]
});
<commit_msg>Fix for DetailView title expression.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.u2',
  name: 'DetailView',
  extends: 'foam.u2.View',

  requires: [
    'foam.core.Property',
    'foam.u2.DetailPropertyView'
  ],

  exports: [
    'controllerMode'
  ],

  properties: [
    {
      name: 'data',
      preSet: function(_, data) {
        if ( data && data.cls_ !== this.of$cls ) {
          this.of = data.cls_;
        }
        return data;
      }
    },
    {
      class: 'Class',
      name: 'of'
    },
    {
      class: 'Boolean',
      name: 'showActions'
    },
    {
      name: 'properties',
      // TODO: Make an FObjectArray when it validates properly
      preSet: function(_, ps) {
        this.assert(ps, 'Properties required.');
        for ( var i = 0 ; i < ps.length ; i++ ) {
          this.assert(
              foam.core.Property.isInstance(ps[i]),
              "Non-Property in 'properties' list:",
              ps);
        }
        return ps;
      },
      expression: function(of) {
        if ( ! of ) return [];
        return this.of$cls.getAxiomsByClass(foam.core.Property).
            filter(function(p) { return ! p.hidden; });
      }
    },
    {
      name: 'config'
      // Map of property-name: {map of property overrides} for configuring properties
      // values include 'label', 'units', and 'view'
    },
    {
      name: 'actions',
      expression: function(of) {
        return this.of$cls.getAxiomsByClass(foam.core.Action);
      }
    },
    {
      name: 'controllerMode',
      attribute: true
    },
    {
      name: 'title',
      attribute: true,
      expression: function(of) { return this.of$cls.model_.label; },
      // documentation: function() {/*
      //
The display title for the $$DOC{ref:'foam.ui.View'}.
      //

      //*/}
    },
    [ 'nodeName', 'div' ]
  ],

  templates: [
    function CSS() {/*
      ^ {
        background: #fdfdfd;
        border: solid 1px #dddddd;
        box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
        display: inline-block;
        margin: 5px;
        padding: 3px;
      }
      ^ table {
        padding-bottom: 2px;
      }
      ^title {
        color: #333;
        float: left;
        font-size: 14px;
        font-weight: bold;
        margin-bottom: 8px;
        padding: 2px;
      }
      ^toolbar {
        margin-left: 5px;
      }
      ^ input {
        border: solid 1px #aacfe4;
        font-size: 10px;
        margin: 2px 0 0px 2px;
        padding: 4px 2px;
      }
      ^ textarea {
        border: solid 1px #aacfe4;
        float: left;
        font-size: 10px;
        margin: 2px 0 0px 2px;
        overflow: auto;
        padding: 4px 2px;
        width: 98%;
      }
      ^ select {
        border: solid 1px #aacfe4;
        font-size: 10px;
        margin: 2px 0 0px 2px;
        padding: 4px 2px;
      }
    */}
  ],

  methods: [
    function initE() {
      var self = this;
      this.add(this.slot(function(of, properties) {
        if ( ! of ) return '';

        var title = self.title && self.E('tr').
          start('td').cssClass(self.myCls('title')).attrs({colspan: 2}).
            add(this.title$).
          end();

        return self.actionBorder(
          self.
            E('table').
            cssClass(self.myCls()).
            add(title).
            add(properties.map(function(p) {
              var config = self.config && self.config[p.name];

              if ( config ) {
                p = p.clone();
                for ( var key in config ) {
                  p[key] = config[key];
                }
              }

              return self.DetailPropertyView.create({prop: p});
            })));
      }));
    },

    function actionBorder(e) {
      if ( ! this.showActions || ! this.actions.length ) return e;

      return this.E().add(e).
        start('div').cssClass(this.myCls('toolbar')).add(this.actions).end();
    },

    function elementForFeature(fName) {
      debugger;
      var f = this.cls_.getFeature(fName) || this.X.data.model_.getFeature(fName);
      return f ? f.toE(this.Y) : this.E('Unknown feature: ' + fName).style({color: 'red'});
    }
  ]
});
<|endoftext|>


<commit_before>import { useNavigation } from '@react-navigation/native';
import { useTheme } from '@ui-kitten/components';
import * as Linking from 'expo-linking';
import * as Location from 'expo-location';
import { pick } from 'lodash';
import propTypes from 'prop-types';
import React from 'react';
import { Alert, Platform, StyleSheet, useWindowDimensions, View } from 'react-native';
import { Marker, Polyline } from 'react-native-maps';
import { useImmerReducer } from 'use-immer';
import useAuth from '../auth/context';
import Map from '../components/Map';
import MapButton from '../components/MapButton';
import RootView from '../components/RootView';
import VehicleTracking, { initialVehicleTrackingState, vehicleTrackingReducer } from '../components/VehicleTracking';
import backgroundLocationService, { verifyPermissions } from '../services/backgroundLocation';
import config from '../services/config';
import { getIcon } from '../services/icons';
import { getLocation, locationToRegion, useFollowUser } from '../services/location';
import { useOfflineCache } from '../services/offline';
import { pointStringToCoordinates, wrapAsyncWithDelay } from '../services/utilities';
import Report from './Report';

const initialReportState = {
  reportType: config.REPORT_TYPES.report,
  showReport: false,
};

const reportReducer = (draft, action) => {
  switch (action.type) {
    case 'show':
      draft.showReport = true;
      draft.reportType = action.meta;

      break;
    case 'hide':
      draft.showReport = false;

      break;
    default:
      throw new Error(`Unknown action type: ${action.type}`);
  }
};

export default function MainScreen() {
  const navigation = useNavigation();
  const [initialLocation, setInitialLocation] = React.useState(null);
  const [showSpinner, setShowSpinner] = React.useState(false);
  const mapView = React.useRef(null);
  const { authInfo } = useAuth();
  // eslint-disable-next-line no-unused-vars
  const { cachedSubmissionIds } = useOfflineCache();
  const theme = useTheme();
  const mapDimensions = React.useRef(null);
  const [carcassCoordinates, setCarcassCoordinates] = React.useState(null);
  const [reportState, dispatchReportState] = useImmerReducer(reportReducer, initialReportState);
  const { followUser, stopFollowUser, isFollowing } = useFollowUser(mapView);
  const [vehicleTrackingState, vehicleTrackingDispatch] = useImmerReducer(
    vehicleTrackingReducer,
    initialVehicleTrackingState
  );

  // these three functions had to be hoisted from VehicleTracking
  // so that they could be used in the alerts for contractors in the
  // showAddReport function
  const startTracking = async () => {
    console.log('startTracking');

    const startTime = vehicleTrackingState.start?.getTime() || Date.now();
    backgroundLocationService.subscribe((locations) => {
      if (Date.now() > startTime + config.MAX_TRACKING_TIME) {
        vehicleTrackingDispatch({ type: 'PAUSE' });
        vehicleTrackingDispatch({ type: 'SHOW' });
        backgroundLocationService.unsubscribe();

        Location.stopLocationUpdatesAsync(backgroundLocationService.taskName);
      }

      vehicleTrackingDispatch({
        type: 'ADD_ROUTE_COORDINATES',
        payload: locations
          // make sure that we don't get any old locations
          .filter((location) => location.timestamp >= startTime)
          .map((location) => pick(location.coords, 'latitude', 'longitude')),
      });
    });

    await Location.startLocationUpdatesAsync(backgroundLocationService.taskName, {
      activityType: Location.ActivityType.AutomotiveNavigation,
      accuracy: Location.Accuracy.High,
      showsBackgroundLocationIndicator: true,
    });
  };

  const startRoute = async () => {
    console.log('startRoute');
    if (await verifyPermissions()) {
      await startTracking();

      vehicleTrackingDispatch({ type: 'START' });
    }
  };

  const resumeRoute = async () => {
    console.log('resumeRoute');

    await startTracking();

    vehicleTrackingDispatch({ type: 'RESUME' });
  };

  const onRouteButtonPress = async () => {
    console.log('vehicleTrackingState.status', vehicleTrackingState.status);
    switch (vehicleTrackingState.status) {
      case 'tracking':
        vehicleTrackingDispatch({ type: 'SHOW' });
        break;
      case 'paused':
        await resumeRoute();
        break;
      case 'idle':
        await startRoute();
        break;
    }
  };

  const initLocation = async () => {
    console.log('initLocation');

    const result = await Location.requestForegroundPermissionsAsync();
    if (!result.granted) {
      Alert.alert('Error', 'Location permissions are required to submit reports.', [
        { text: 'OK', onPress: () => Linking.openSettings() },
      ]);

      return;
    }

    const enabled = await Location.hasServicesEnabledAsync();
    if (!enabled) {
      Alert.alert('Error', 'Location services are required to submit reports.', [
        { text: 'OK', onPress: () => Linking.openSettings() },
      ]);
    }

    const location = await getLocation();

    setInitialLocation(location);
  };

  React.useEffect(() => {
    wrapAsyncWithDelay(
      initLocation,
      () => setShowSpinner(true),
      () => setShowSpinner(false),
      config.SPINNER_DELAY
    );
  }, []);

  const setMarker = async () => {
    const points = { ...crosshairCoordinates };

    const coordinates = await mapView.current.coordinateForPoint(points);

    setCarcassCoordinates(coordinates);
  };

  const showAddReport = () => {
    const displayReport = (newReportType) => {
      dispatchReportState({
        type: 'show',
        meta: newReportType,
      });

      const zoom = async () => {
        await followUser(true);
      };

      if (Platform.OS === 'android') {
        // give map padding time to catch up
        setTimeout(zoom, 350);
      } else {
        zoom();
      }
    };

    if (authInfo.user.role === config.USER_TYPES.public) {
      displayReport(config.REPORT_TYPES.report);
    } else if (authInfo.user.role === config.USER_TYPES.agency || authInfo.user.role === config.USER_TYPES.admin) {
      Alert.alert('I would like to...', null, [
        {
          text: 'Report and pick up a carcass',
          onPress: () => displayReport(config.REPORT_TYPES.pickup),
        },
        {
          text: 'Report a carcass',
          onPress: () => displayReport(config.REPORT_TYPES.report),
        },
      ]);
    } else {
      // contractor
      if (!vehicleTrackingState.isTracking) {
        Alert.alert(
          'No vehicle tracking route found',
          'A vehicle tracking route must be started to report a carcass.\n\nWould you like to start a route?',
          [
            {
              text: 'Start route',
              onPress: () => {
                startRoute();

                displayReport(config.REPORT_TYPES.pickup);
              },
            },
            {
              text: 'Cancel',
            },
          ]
        );

        return;
      }

      if (vehicleTrackingState.isTracking && vehicleTrackingState.isPaused) {
        Alert.alert(
          'Paused vehicle tracking route',
          'A vehicle tracking route has been started but it is currently paused.\n\nWould you to resume?',
          [
            {
              text: 'Resume tracking',
              onPress: () => {
                resumeRoute();

                displayReport(config.REPORT_TYPES.pickup);
              },
            },
            {
              text: 'Cancel',
            },
          ]
        );

        return;
      }

      displayReport(config.REPORT_TYPES.pickup);
    }
  };

  const onPanDrag = () => {
    if (isFollowing) {
      stopFollowUser();
    }
  };

  const hideAddReport = () => {
    dispatchReportState({
      type: 'hide',
    });
    setCarcassCoordinates(null);
  };

  const { height, width } = useWindowDimensions();
  const mapSizeStyle = {
    height,
    width,
  };

  const CrosshairIcon = getIcon({
    pack: 'font-awesome-5',
    name: 'crosshairs',
    size: CROSSHAIR_SIZE,
    color: theme['color-basic-900'],
  });

  const crosshairCoordinates = mapDimensions.current
    ? {
        y: mapDimensions.current.height / 2 + Platform.select({ ios: 8, android: -1 }),
        x: mapDimensions.current.width / 2,
      }
    : null;
  const VehicleIcon = getIcon({ pack: 'material', name: 'drive-eta', size: 36, color: theme['color-basic-900'] });
  const showTrackingMarker =
    vehicleTrackingState.isTracking && !vehicleTrackingState.isPaused && vehicleTrackingState.routeCoordinates.length;

  return (
    <RootView showSpinner={showSpinner} spinnerMessage="getting current location" style={styles.root}>
      {initialLocation ? (
        <>
          <Map
            initialRegion={locationToRegion(initialLocation)}
            onPanDrag={onPanDrag}
            onLayout={(event) => {
              const { width, height } = event.nativeEvent.layout;
              mapDimensions.current = { width, height };
            }}
            innerRef={mapView}
            style={[styles.map, mapSizeStyle]}
            showsUserLocation={!showTrackingMarker}
          >
            {vehicleTrackingState.routeCoordinates?.length ? (
              <Polyline
                coordinates={vehicleTrackingState.routeCoordinates}
                strokeColor={theme[`color-${vehicleTrackingState.buttonStatus}-500`]}
                strokeWidth={8}
                zIndex={1}
              />
            ) : null}
            {vehicleTrackingState.pickups?.length > 0
              ? vehicleTrackingState.pickups.map((pickup) => (
                  <Marker
                    coordinate={pointStringToCoordinates(pickup.animal_location)}
                    key={pickup.submit_date}
                    zIndex={2}
                    pinColor="navy"
                    tappable={false}
                  />
                ))
              : null}
            {showTrackingMarker ? (
              <Marker
                coordinate={vehicleTrackingState.routeCoordinates[vehicleTrackingState.routeCoordinates.length - 1]}
                key="current-location"
                zIndex={10}
                tappable={false}
                anchor={{ x: 0.5, y: 0.5 }}
              >
                {
                  <View
                    style={{
                      backgroundColor: theme['color-basic-200'],
                      borderRadius: 18,
                      padding: 2,
                      borderWidth: 1,
                      borderColor: theme['color-basic-900'],
                    }}
                  >
                    <VehicleIcon />
                  </View>
                }
              </Marker>
            ) : null}
            {carcassCoordinates ? <Marker coordinate={carcassCoordinates} zIndex={2} tappable={false} /> : null}
          </Map>
          {reportState.showReport ? (
            <View
              pointerEvents="none"
              style={[styles.crosshairContainer, { top: crosshairCoordinates.y, left: crosshairCoordinates.x }]}
            >
              <CrosshairIcon />
            </View>
          ) : null}
        </>
      ) : null}
      <View style={styles.controlContainer}>
        <View>
          <MapButton
            iconPack="material-community"
            iconName="folder-account"
            onPress={navigation.openDrawer}
            alertNumber={cachedSubmissionIds.length || null}
          />
        </View>
        <View>
          {authInfo.user.role === config.USER_TYPES.public ? null : (
            <MapButton
              iconPack="material-community"
              iconName="truck-fast"
              onPress={onRouteButtonPress}
              status={vehicleTrackingState.buttonStatus}
              color={theme['color-basic-800']}
            >
              Track
            </MapButton>
          )}
        </View>
        <View>
          <MapButton
            iconPack="material-community"
            iconName={isFollowing ? 'crosshairs-gps' : 'crosshairs'}
            onPress={() => followUser()}
          />
        </View>
      </View>
      <View style={styles.controlContainer}>
        <View></View>
        <View style={styles.bottomContainer}>
          <MapButton
            iconPack="material"
            iconName="add-circle"
            onPress={showAddReport}
            status="primary"
            color={theme['color-basic-800']}
          >
            Report
          </MapButton>
        </View>
        <View></View>
      </View>
      <Report
        show={reportState.showReport}
        reportType={reportState.reportType}
        hideReport={hideAddReport}
        setMarker={setMarker}
        carcassCoordinates={carcassCoordinates}
        vehicleTrackingDispatch={vehicleTrackingDispatch}
        vehicleTrackingState={vehicleTrackingState}
      />
      <VehicleTracking
        state={vehicleTrackingState}
        dispatch={vehicleTrackingDispatch}
        startTracking={startTracking}
        resumeRoute={resumeRoute}
        startRoute={startRoute}
      />
    </RootView>
  );
}

MainScreen.propTypes = {
  navigation: propTypes.object,
};

const MAP_PADDING = 20;
const CROSSHAIR_SIZE = 40;
const styles = StyleSheet.create({
  root: {
    justifyContent: 'space-between',
  },
  map: {
    position: 'absolute',
    top: 0,
  },
  controlContainer: {
    paddingHorizontal: MAP_PADDING,
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  bottomContainer: {
    paddingBottom: MAP_PADDING,
  },
  crosshairContainer: {
    position: 'absolute',
    marginLeft: -CROSSHAIR_SIZE / 2,
    marginTop: -CROSSHAIR_SIZE / 2,
  },
});
<commit_msg>fix(front): get crosshair point coords directly from map component<commit_after>import { useNavigation } from '@react-navigation/native';
import { useTheme } from '@ui-kitten/components';
import * as Linking from 'expo-linking';
import * as Location from 'expo-location';
import { pick } from 'lodash';
import propTypes from 'prop-types';
import React from 'react';
import { Alert, Platform, StyleSheet, useWindowDimensions, View } from 'react-native';
import { Marker, Polyline } from 'react-native-maps';
import { useImmerReducer } from 'use-immer';
import useAuth from '../auth/context';
import Map from '../components/Map';
import MapButton from '../components/MapButton';
import RootView from '../components/RootView';
import VehicleTracking, { initialVehicleTrackingState, vehicleTrackingReducer } from '../components/VehicleTracking';
import backgroundLocationService, { verifyPermissions } from '../services/backgroundLocation';
import config from '../services/config';
import { getIcon } from '../services/icons';
import { getLocation, locationToRegion, useFollowUser } from '../services/location';
import { useOfflineCache } from '../services/offline';
import { pointStringToCoordinates, wrapAsyncWithDelay } from '../services/utilities';
import Report from './Report';

const initialReportState = {
  reportType: config.REPORT_TYPES.report,
  showReport: false,
};

const reportReducer = (draft, action) => {
  switch (action.type) {
    case 'show':
      draft.showReport = true;
      draft.reportType = action.meta;

      break;
    case 'hide':
      draft.showReport = false;

      break;
    default:
      throw new Error(`Unknown action type: ${action.type}`);
  }
};

export default function MainScreen() {
  const navigation = useNavigation();
  const [initialLocation, setInitialLocation] = React.useState(null);
  const [showSpinner, setShowSpinner] = React.useState(false);
  const mapView = React.useRef(null);
  const { authInfo } = useAuth();
  // eslint-disable-next-line no-unused-vars
  const { cachedSubmissionIds } = useOfflineCache();
  const theme = useTheme();
  const [carcassCoordinates, setCarcassCoordinates] = React.useState(null);
  const [reportState, dispatchReportState] = useImmerReducer(reportReducer, initialReportState);
  const { followUser, stopFollowUser, isFollowing } = useFollowUser(mapView);
  const [vehicleTrackingState, vehicleTrackingDispatch] = useImmerReducer(
    vehicleTrackingReducer,
    initialVehicleTrackingState
  );

  // these three functions had to be hoisted from VehicleTracking
  // so that they could be used in the alerts for contractors in the
  // showAddReport function
  const startTracking = async () => {
    console.log('startTracking');

    const startTime = vehicleTrackingState.start?.getTime() || Date.now();
    backgroundLocationService.subscribe((locations) => {
      if (Date.now() > startTime + config.MAX_TRACKING_TIME) {
        vehicleTrackingDispatch({ type: 'PAUSE' });
        vehicleTrackingDispatch({ type: 'SHOW' });
        backgroundLocationService.unsubscribe();

        Location.stopLocationUpdatesAsync(backgroundLocationService.taskName);
      }

      vehicleTrackingDispatch({
        type: 'ADD_ROUTE_COORDINATES',
        payload: locations
          // make sure that we don't get any old locations
          .filter((location) => location.timestamp >= startTime)
          .map((location) => pick(location.coords, 'latitude', 'longitude')),
      });
    });

    await Location.startLocationUpdatesAsync(backgroundLocationService.taskName, {
      activityType: Location.ActivityType.AutomotiveNavigation,
      accuracy: Location.Accuracy.High,
      showsBackgroundLocationIndicator: true,
    });
  };

  const startRoute = async () => {
    console.log('startRoute');
    if (await verifyPermissions()) {
      await startTracking();

      vehicleTrackingDispatch({ type: 'START' });
    }
  };

  const resumeRoute = async () => {
    console.log('resumeRoute');

    await startTracking();

    vehicleTrackingDispatch({ type: 'RESUME' });
  };

  const onRouteButtonPress = async () => {
    console.log('vehicleTrackingState.status', vehicleTrackingState.status);
    switch (vehicleTrackingState.status) {
      case 'tracking':
        vehicleTrackingDispatch({ type: 'SHOW' });
        break;
      case 'paused':
        await resumeRoute();
        break;
      case 'idle':
        await startRoute();
        break;
    }
  };

  const initLocation = async () => {
    console.log('initLocation');

    const result = await Location.requestForegroundPermissionsAsync();
    if (!result.granted) {
      Alert.alert('Error', 'Location permissions are required to submit reports.', [
        { text: 'OK', onPress: () => Linking.openSettings() },
      ]);

      return;
    }

    const enabled = await Location.hasServicesEnabledAsync();
    if (!enabled) {
      Alert.alert('Error', 'Location services are required to submit reports.', [
        { text: 'OK', onPress: () => Linking.openSettings() },
      ]);
    }

    const location = await getLocation();

    setInitialLocation(location);
  };

  React.useEffect(() => {
    wrapAsyncWithDelay(
      initLocation,
      () => setShowSpinner(true),
      () => setShowSpinner(false),
      config.SPINNER_DELAY
    );
  }, []);

  const setMarker = async () => {
    const points = { ...crosshairCoordinates };

    const coordinates = await mapView.current.coordinateForPoint(points);

    setCarcassCoordinates(coordinates);
  };

  const showAddReport = () => {
    const displayReport = (newReportType) => {
      dispatchReportState({
        type: 'show',
        meta: newReportType,
      });

      const zoom = async () => {
        await followUser(true);
      };

      if (Platform.OS === 'android') {
        // give map padding time to catch up
        setTimeout(zoom, 350);
      } else {
        zoom();
      }
    };

    if (authInfo.user.role === config.USER_TYPES.public) {
      displayReport(config.REPORT_TYPES.report);
    } else if (authInfo.user.role === config.USER_TYPES.agency || authInfo.user.role === config.USER_TYPES.admin) {
      Alert.alert('I would like to...', null, [
        {
          text: 'Report and pick up a carcass',
          onPress: () => displayReport(config.REPORT_TYPES.pickup),
        },
        {
          text: 'Report a carcass',
          onPress: () => displayReport(config.REPORT_TYPES.report),
        },
      ]);
    } else {
      // contractor
      if (!vehicleTrackingState.isTracking) {
        Alert.alert(
          'No vehicle tracking route found',
          'A vehicle tracking route must be started to report a carcass.\n\nWould you like to start a route?',
          [
            {
              text: 'Start route',
              onPress: () => {
                startRoute();

                displayReport(config.REPORT_TYPES.pickup);
              },
            },
            {
              text: 'Cancel',
            },
          ]
        );

        return;
      }

      if (vehicleTrackingState.isTracking && vehicleTrackingState.isPaused) {
        Alert.alert(
          'Paused vehicle tracking route',
          'A vehicle tracking route has been started but it is currently paused.\n\nWould you to resume?',
          [
            {
              text: 'Resume tracking',
              onPress: () => {
                resumeRoute();

                displayReport(config.REPORT_TYPES.pickup);
              },
            },
            {
              text: 'Cancel',
            },
          ]
        );

        return;
      }

      displayReport(config.REPORT_TYPES.pickup);
    }
  };

  const onPanDrag = () => {
    if (isFollowing) {
      stopFollowUser();
    }
  };

  const hideAddReport = () => {
    dispatchReportState({
      type: 'hide',
    });
    setCarcassCoordinates(null);
  };

  const { height, width } = useWindowDimensions();
  const mapSizeStyle = {
    height,
    width,
  };

  const CrosshairIcon = getIcon({
    pack: 'font-awesome-5',
    name: 'crosshairs',
    size: CROSSHAIR_SIZE,
    color: theme['color-basic-900'],
  });

  const [crosshairCoordinates, setCrosshairCoordinates] = React.useState(null);
  React.useEffect(() => {
    const giddyUp = async () => {
      const camera = await mapView.current.getCamera();
      const newCoords = await mapView.current.pointForCoordinate(camera.center);
      setCrosshairCoordinates(newCoords);
    };

    if (reportState.showReport) {
      giddyUp();
    } else {
      setCrosshairCoordinates(null);
    }
  }, [reportState.showReport]);
  const VehicleIcon = getIcon({ pack: 'material', name: 'drive-eta', size: 36, color: theme['color-basic-900'] });
  const showTrackingMarker =
    vehicleTrackingState.isTracking && !vehicleTrackingState.isPaused && vehicleTrackingState.routeCoordinates.length;

  return (
    <RootView showSpinner={showSpinner} spinnerMessage="getting current location" style={styles.root}>
      {initialLocation ? (
        <>
          <Map
            initialRegion={locationToRegion(initialLocation)}
            onPanDrag={onPanDrag}
            innerRef={mapView}
            style={[styles.map, mapSizeStyle]}
            showsUserLocation={!showTrackingMarker}
          >
            {vehicleTrackingState.routeCoordinates?.length ? (
              <Polyline
                coordinates={vehicleTrackingState.routeCoordinates}
                strokeColor={theme[`color-${vehicleTrackingState.buttonStatus}-500`]}
                strokeWidth={8}
                zIndex={1}
              />
            ) : null}
            {vehicleTrackingState.pickups?.length > 0
              ? vehicleTrackingState.pickups.map((pickup) => (
                  <Marker
                    coordinate={pointStringToCoordinates(pickup.animal_location)}
                    key={pickup.submit_date}
                    zIndex={2}
                    pinColor="navy"
                    tappable={false}
                  />
                ))
              : null}
            {showTrackingMarker ? (
              <Marker
                coordinate={vehicleTrackingState.routeCoordinates[vehicleTrackingState.routeCoordinates.length - 1]}
                key="current-location"
                zIndex={10}
                tappable={false}
                anchor={{ x: 0.5, y: 0.5 }}
              >
                {
                  <View
                    style={{
                      backgroundColor: theme['color-basic-200'],
                      borderRadius: 18,
                      padding: 2,
                      borderWidth: 1,
                      borderColor: theme['color-basic-900'],
                    }}
                  >
                    <VehicleIcon />
                  </View>
                }
              </Marker>
            ) : null}
            {carcassCoordinates ? <Marker coordinate={carcassCoordinates} zIndex={2} tappable={false} /> : null}
          </Map>
          {crosshairCoordinates ? (
            <View
              pointerEvents="none"
              style={[styles.crosshairContainer, { top: crosshairCoordinates.y, left: crosshairCoordinates.x }]}
            >
              <CrosshairIcon />
            </View>
          ) : null}
        </>
      ) : null}
      <View style={styles.controlContainer}>
        <View>
          <MapButton
            iconPack="material-community"
            iconName="folder-account"
            onPress={navigation.openDrawer}
            alertNumber={cachedSubmissionIds.length || null}
          />
        </View>
        <View>
          {authInfo.user.role === config.USER_TYPES.public ? null : (
            <MapButton
              iconPack="material-community"
              iconName="truck-fast"
              onPress={onRouteButtonPress}
              status={vehicleTrackingState.buttonStatus}
              color={theme['color-basic-800']}
            >
              Track
            </MapButton>
          )}
        </View>
        <View>
          <MapButton
            iconPack="material-community"
            iconName={isFollowing ? 'crosshairs-gps' : 'crosshairs'}
            onPress={() => followUser()}
          />
        </View>
      </View>
      <View style={styles.controlContainer}>
        <View></View>
        <View style={styles.bottomContainer}>
          <MapButton
            iconPack="material"
            iconName="add-circle"
            onPress={showAddReport}
            status="primary"
            color={theme['color-basic-800']}
          >
            Report
          </MapButton>
        </View>
        <View></View>
      </View>
      <Report
        show={reportState.showReport}
        reportType={reportState.reportType}
        hideReport={hideAddReport}
        setMarker={setMarker}
        carcassCoordinates={carcassCoordinates}
        vehicleTrackingDispatch={vehicleTrackingDispatch}
        vehicleTrackingState={vehicleTrackingState}
      />
      <VehicleTracking
        state={vehicleTrackingState}
        dispatch={vehicleTrackingDispatch}
        startTracking={startTracking}
        resumeRoute={resumeRoute}
        startRoute={startRoute}
      />
    </RootView>
  );
}

MainScreen.propTypes = {
  navigation: propTypes.object,
};

const MAP_PADDING = 20;
const CROSSHAIR_SIZE = 40;
const styles = StyleSheet.create({
  root: {
    justifyContent: 'space-between',
  },
  map: {
    position: 'absolute',
    top: 0,
  },
  controlContainer: {
    paddingHorizontal: MAP_PADDING,
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  bottomContainer: {
    paddingBottom: MAP_PADDING,
  },
  crosshairContainer: {
    position: 'absolute',
    marginLeft: -CROSSHAIR_SIZE / 2,
    marginTop: -CROSSHAIR_SIZE / 2,
  },
});
<|endoftext|>