<commit_before>/**
 * Created by jrobinso on 2/7/17.
 */
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016-2017 James Robinson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNJuicebox web demo appECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

var hic = (function (hic) {

    dragThreshold = 2;

    hic.ContactMatrixView = function (browser) {

        var w,
            h,
            $x_axis_scrollbar_container,
            $y_axis_scrollbar_container;

        this.browser = browser;

        this.$viewport = $('
');

        //content canvas
        this.$canvas = $('<canvas>');
        w = this.$viewport.width();
        h = this.$viewport.height();
        this.$canvas.attr('width', w);
        this.$canvas.attr('height', h);
        this.ctx = this.$canvas.get(0).getContext("2d");

        //spinner
        this.$spinner = $('
');
        this.$spinner.append($(''));
        this.stopSpinner();

        this.$viewport.append(this.$canvas);
        this.$viewport.append(this.$spinner);

        addMouseHandlers.call(this, this.$viewport);

        this.$viewport_container = $('
');
        this.$viewport_container.append(this.$viewport);

        this.scrollbarWidget = new hic.ScrollbarWidget(browser);
        this.$viewport_container.append(this.scrollbarWidget.$x_axis_scrollbar_container);
        this.$viewport_container.append(this.scrollbarWidget.$y_axis_scrollbar_container);

        hic.GlobalEventBus.subscribe("LocusChange", this);
        hic.GlobalEventBus.subscribe("DataLoad", this);

        this.matrixCache = {};
        this.blockCache = {};
        this.imageTileCache = {};

        this.colorScale = new igv.GradientColorScale(
            {
                low: 0,
                lowR: 255,
                lowG: 255,
                lowB: 255,
                high: 2000,
                highR: 255,
                highG: 0,
                highB: 0
            }
        );

    };

    hic.ContactMatrixView.prototype.clearCaches = function() {
        this.matrixCache = {};
        this.blockCache = {};
        this.imageTileCache = {};
    }

    hic.ContactMatrixView.prototype.getViewDimensions = function () {
        return {
            width: this.$viewport.width(),
            height: this.$viewport.height()
        }
    }

    hic.ContactMatrixView.prototype.receiveEvent = function (event) {
        // Perhaps in the future we'll do something special based on event type & properties
        this.update();

    }

    hic.ContactMatrixView.prototype.update = function () {

        if (!this.browser.hicReader) return;

        var self = this,
            state = this.browser.state,
            sameChr = state.chr1 === state.chr2;

        self.updating = true;

        this.getMatrix(state.chr1, state.chr2)

            .then(function (matrix) {

                var widthInBins = self.$viewport.width() / state.pixelSize,
                    heightInBins = self.$viewport.height() / state.pixelSize,
                    zd = matrix.bpZoomData[state.zoom],
                    blockBinCount = zd.blockBinCount,
                    col1 = Math.floor(state.x / blockBinCount),
                    col2 = Math.floor((state.x + widthInBins) / blockBinCount),
                    row1 = Math.floor(state.y / blockBinCount),
                    row2 = Math.floor((state.y + heightInBins) / blockBinCount),
                    r, c, promises = [];

                    for (r = row1; r <= row2; r++) {
                        for (c = col1; c <= col2; c++) {
                                promises.push(self.getImageTile(zd, r, c));
                            }
                        }

                Promise.all(promises).then(function (imageTiles) {
                    self.stopSpinner();
                    self.draw(imageTiles, zd);
                    self.updating = false;
                }).catch(function (error) {
                    self.stopSpinner(self);
                    self.updating = false;
                    console.error(error);
                })
            })
            .catch(function (error) {
                self.stopSpinner(self);
                self.updating = false;
                console.error(error);
            })
    };

    hic.ContactMatrixView.prototype.draw = function (imageTiles, zd) {

        var self = this,
            state = this.browser.state,
            blockBinCount = zd.blockBinCount,
            blockColumnCount = zd.blockColumnCount,
            viewportWidth = self.$viewport.width(),
            viewportHeight = self.$viewport.height();

        self.$canvas.attr('width', viewportWidth);
        self.$canvas.attr('height', viewportHeight);
        imageTiles.forEach(function (imageTile) {

            var image = imageTile.image;

            if (image != null) {
                var row = imageTile.row,
                    col = imageTile.column,
                    x0 = blockBinCount * col,
                    y0 = blockBinCount * row;

                var offsetX = x0 - state.x;
                var offsetY = y0 - state.y;
                if (offsetX <= viewportWidth && offsetX + image.width >= 0 &&
                    offsetY <= viewportHeight && offsetY + image.height >= 0) {
                    self.ctx.drawImage(image, offsetX, offsetY);
                }

            }
        })

    };

    hic.ContactMatrixView.prototype.getMatrix = function (chr1, chr2) {

        var self = this,
            reader = this.browser.hicReader,
            key = "" + chr1 + "_" + chr2;
        if (this.matrixCache.hasOwnProperty(key)) {
            return Promise.resolve(self.matrixCache[key]);
        } else {
            return new Promise(function (fulfill, reject) {
                self.startSpinner();
                reader
                    .readMatrix(key)
                    .then(function (matrix) {
                        self.matrixCache[key] = matrix;
                        fulfill(matrix);
                    })
                    .catch(reject);
            })

        }
    };

    hic.ContactMatrixView.prototype.getImageTile = function (zd, row, column) {

        var self = this,
            sameChr = zd.chr1 === zd.chr2,
            key = "" + zd.chr1.name + "_" + zd.chr2.name + "_" + zd.zoom.binSize + "_" + zd.zoom.unit + "_" + row + "_" + column;

        if (this.imageTileCache.hasOwnProperty(key)) {
            return Promise.resolve(this.imageTileCache[key]);
        } else {
            return new Promise(function (fulfill, reject) {

                var state = self.browser.state,
                    blockBinCount = zd.blockBinCount,
                    blockColumnCount = zd.blockColumnCount,
                    widthInBins = zd.blockBinCount,
                    imageSize = widthInBins * state.pixelSize,
                    transpose = sameChr && row < column,
                    blockNumber,
                    t;

                function drawBlock(block, transpose) {

                    var blockNumber, row, col, x0, y0, image, ctx;
                    blockNumber = block.blockNumber;
                    row = Math.floor(blockNumber / blockColumnCount);
                    col = blockNumber - row * blockColumnCount;
                    x0 = blockBinCount * col;
                    y0 = blockBinCount * row;

                    image = document.createElement('canvas');
                    image.width = imageSize;
                    image.height = imageSize;
                    ctx = image.getContext('2d');

                    // Draw the image
                    var i, rec, x, y, rgb;
                    for (i = 0; i < block.records.length; i++) {
                        rec = block.records[i];
                        x = (rec.bin1 - x0) * state.pixelSize;
                        y = (rec.bin2 - y0) * state.pixelSize;

                        if(transpose) {
                            t = y;
                            y = x;
                            x = t;
                        }

                        rgb = self.colorScale.getColor(rec.counts);

                        ctx.fillStyle = rgb;
                        ctx.fillRect(x, y, state.pixelSize, state.pixelSize);
                        if(row === col) {
                            ctx.fillRect(y, x, state.pixelSize, state.pixelSize);
                        }
                    }
                    return image;
                }

                if(sameChr && row < column) {
                    blockNumber = column * blockColumnCount + row;
                }
                else {
                    blockNumber = row * blockColumnCount + column;
                }

                self.getBlock(zd, blockNumber)

                    .then(function (block) {

                        var image;
                        if (block) {
                            image = drawBlock(block, transpose);
                        }
                        else {
                            console.log("No block for " + blockNumber);
                        }

                        var imageTile = {row: row, column: column, image: image};
                        self.imageTileCache[key] = imageTile;
                        fulfill(imageTile);

                    })
                    .catch(reject)
            })
        }
    };
    hic.ContactMatrixView.prototype.getBlock = function (zd, blockNumber) {

        var self = this,
            key = "" + zd.chr1.name + "_" + zd.chr2.name + "_" + zd.zoom.binSize + "_" + zd.zoom.unit + "_" + blockNumber;

        if (this.blockCache.hasOwnProperty(key)) {
            return Promise.resolve(this.blockCache[key]);
        } else {
            return new Promise(function (fulfill, reject) {

                var reader = self.browser.hicReader,
                    state = self.browser.state,
                    blockBinCount = zd.blockBinCount,
                    blockColumnCount = zd.blockColumnCount,
                    widthInBins = zd.blockBinCount,
                    imageSize = widthInBins * state.pixelSize;

                self.startSpinner();

                reader.readBlock(blockNumber, zd)
                    .then(function (block) {

                        self.blockCache[key] = block

                        self.stopSpinner();

                        fulfill(block);

                    })
                    .catch(reject)
            })
        }
    };

    hic.ContactMatrixView.prototype.startSpinner = function () {
        var $spinner = this.$spinner;
        $spinner.addClass("fa-spin");
        $spinner.show();
    };

    hic.ContactMatrixView.prototype.stopSpinner = function () {
        var $spinner = this.$spinner;
        $spinner.hide();
        $spinner.removeClass("fa-spin");
    };

    function addMouseHandlers($viewport) {

        var self = this,
            viewport = $viewport[0],
            isMouseDown = false,
            isDragging = false,
            lastMouseX,
            lastMouseY,
            mouseDownX,
            mouseDownY;

        $viewport.on('mousedown', function (e) {

            var coords;

            isMouseDown = true;
            coords = translateMouseCoordinates(e, $viewport);
            mouseDownX = lastMouseX = coords.x;
            mouseDownY = lastMouseY = coords.y;

        });

        // Guide line is bound within track area, and offset by 5 pixels so as not to interfere mouse clicks.
        // $(trackContainerDiv).mousemove(function (e) {
        //     var xy,
        //         _left,
        //         $element = igv.browser.$cursorTrackingGuide;
        //
        //     e.preventDefault();
        //
        //     xy = igv.translateMouseCoordinates(e, trackContainerDiv);
        //     _left = Math.max(50, xy.x - 5);
        //
        //     _left = Math.min(igv.browser.trackContainerDiv.width() - 65, _left);
        //     $element.css({left: _left + 'px'});
        // });

        $viewport.on('mousemove', hic.throttle(function (e) {

            var coords,
                maxEnd,
                maxStart;

            if (self.updating) return;

            e.preventDefault();

            coords = translateMouseCoordinates(e, $viewport);

            if (isMouseDown) { // Possibly dragging

                if (mouseDownX && Math.abs(coords.x - mouseDownX) > dragThreshold) {

                    isDragging = true;

                    if (self.updating) return;   // Freeze frame during updates

                    self.browser.shiftPixels(lastMouseX - coords.x, lastMouseY - coords.y);

                }

                lastMouseX = coords.x;
                lastMouseY = coords.y;
            }

        }, 10));

        $viewport.on('mouseup', mouseUpOrOut);

        $viewport.on('mouseleave', mouseUpOrOut);

        function mouseUpOrOut(e) {

            //
            // // Don't let vertical line interfere with dragging
            // if (igv.browser.$cursorTrackingGuide && e.toElement === igv.browser.$cursorTrackingGuide.get(0) && e.type === 'mouseleave') {
            //     return;
            // }

            if (isDragging) {
                isDragging = false;
                hic.GlobalEventBus.post(new hic.DragStoppedEvent());
            }

            isMouseDown = false;
            mouseDownX = lastMouseX = undefined;
            mouseDownY = lastMouseY = undefined;

        }

    }

    function translateMouseCoordinates(e, $target) {

        var eFixed,
            posx,
            posy;

        // Sets pageX and pageY for browsers that don't support them
        eFixed = $.event.fix(e);

        if (undefined === $target.offset()) {
            console.log('igv.translateMouseCoordinates - $target.offset() is undefined.');
        }
        posx = eFixed.pageX - $target.offset().left;
        posy = eFixed.pageY - $target.offset().top;

        return {x: posx, y: posy}
    }

    return hic;

})
(hic || {});
<commit_msg>spinner tweaks, minor cleanup<commit_after>/**
 * Created by jrobinso on 2/7/17.
 */
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016-2017 James Robinson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNJuicebox web demo appECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

var hic = (function (hic) {

    dragThreshold = 2;

    hic.ContactMatrixView = function (browser) {

        var w,
            h,
            $x_axis_scrollbar_container,
            $y_axis_scrollbar_container;

        this.browser = browser;

        this.$viewport = $('
');

        //content canvas
        this.$canvas = $('<canvas>');
        w = this.$viewport.width();
        h = this.$viewport.height();
        this.$canvas.attr('width', w);
        this.$canvas.attr('height', h);
        this.ctx = this.$canvas.get(0).getContext("2d");

        //spinner
        this.$spinner = $('
');
        this.$spinner.append($(''));
        this.stopSpinner();

        this.$viewport.append(this.$canvas);
        this.$viewport.append(this.$spinner);

        addMouseHandlers.call(this, this.$viewport);

        this.$viewport_container = $('
');
        this.$viewport_container.append(this.$viewport);

        this.scrollbarWidget = new hic.ScrollbarWidget(browser);
        this.$viewport_container.append(this.scrollbarWidget.$x_axis_scrollbar_container);
        this.$viewport_container.append(this.scrollbarWidget.$y_axis_scrollbar_container);

        hic.GlobalEventBus.subscribe("LocusChange", this);
        hic.GlobalEventBus.subscribe("DataLoad", this);

        this.matrixCache = {};
        this.blockCache = {};
        this.imageTileCache = {};

        this.colorScale = new igv.GradientColorScale(
            {
                low: 0,
                lowR: 255,
                lowG: 255,
                lowB: 255,
                high: 2000,
                highR: 255,
                highG: 0,
                highB: 0
            }
        );

    };

    hic.ContactMatrixView.prototype.clearCaches = function () {
        this.matrixCache = {};
        this.blockCache = {};
        this.imageTileCache = {};
    }

    hic.ContactMatrixView.prototype.getViewDimensions = function () {
        return {
            width: this.$viewport.width(),
            height: this.$viewport.height()
        }
    }

    hic.ContactMatrixView.prototype.receiveEvent = function (event) {
        // Perhaps in the future we'll do something special based on event type & properties
        this.update();

    }

    hic.ContactMatrixView.prototype.update = function () {

        if (!this.browser.hicReader) return;

        var self = this,
            state = this.browser.state,
            sameChr = state.chr1 === state.chr2;

        self.updating = true;

        this.getMatrix(state.chr1, state.chr2)

            .then(function (matrix) {

                var widthInBins = self.$viewport.width() / state.pixelSize,
                    heightInBins = self.$viewport.height() / state.pixelSize,
                    zd = matrix.bpZoomData[state.zoom],
                    blockBinCount = zd.blockBinCount,
                    col1 = Math.floor(state.x / blockBinCount),
                    col2 = Math.floor((state.x + widthInBins) / blockBinCount),
                    row1 = Math.floor(state.y / blockBinCount),
                    row2 = Math.floor((state.y + heightInBins) / blockBinCount),
                    r, c, promises = [];

                for (r = row1; r <= row2; r++) {
                    for (c = col1; c <= col2; c++) {
                        promises.push(self.getImageTile(zd, r, c));
                    }
                }

                Promise.all(promises).then(function (imageTiles) {
                    self.stopSpinner();
                    self.draw(imageTiles, zd);
                    self.updating = false;
                }).catch(function (error) {
                    self.stopSpinner(self);
                    self.updating = false;
                    console.error(error);
                })
            })
            .catch(function (error) {
                self.stopSpinner(self);
                self.updating = false;
                console.error(error);
            })
    };

    hic.ContactMatrixView.prototype.draw = function (imageTiles, zd) {

        var self = this,
            state = this.browser.state,
            blockBinCount = zd.blockBinCount,
            blockColumnCount = zd.blockColumnCount,
            viewportWidth = self.$viewport.width(),
            viewportHeight = self.$viewport.height();

        self.$canvas.attr('width', viewportWidth);
        self.$canvas.attr('height', viewportHeight);
        imageTiles.forEach(function (imageTile) {

            var image = imageTile.image;

            if (image != null) {
                var row = imageTile.row,
                    col = imageTile.column,
                    x0 = blockBinCount * col,
                    y0 = blockBinCount * row;

                var offsetX = x0 - state.x;
                var offsetY = y0 - state.y;
                if (offsetX <= viewportWidth && offsetX + image.width >= 0 &&
                    offsetY <= viewportHeight && offsetY + image.height >= 0) {
                    self.ctx.drawImage(image, offsetX, offsetY);
                }

            }
        })

    };

    hic.ContactMatrixView.prototype.getMatrix = function (chr1, chr2) {

        var self = this,
            reader = this.browser.hicReader,
            key = "" + chr1 + "_" + chr2;
        if (this.matrixCache.hasOwnProperty(key)) {
            return Promise.resolve(self.matrixCache[key]);
        } else {
            return new Promise(function (fulfill, reject) {
                reader
                    .readMatrix(key)
                    .then(function (matrix) {
                        self.matrixCache[key] = matrix;
                        fulfill(matrix);
                    })
                    .catch(reject);
            })

        }
    };

    hic.ContactMatrixView.prototype.getImageTile = function (zd, row, column) {

        var self = this,
            sameChr = zd.chr1 === zd.chr2,
            key = "" + zd.chr1.name + "_" + zd.chr2.name + "_" + zd.zoom.binSize + "_" + zd.zoom.unit + "_" + row + "_" + column;

        if (this.imageTileCache.hasOwnProperty(key)) {
            return Promise.resolve(this.imageTileCache[key]);
        } else {
            return new Promise(function (fulfill, reject) {

                var state = self.browser.state,
                    blockBinCount = zd.blockBinCount,
                    blockColumnCount = zd.blockColumnCount,
                    widthInBins = zd.blockBinCount,
                    imageSize = widthInBins * state.pixelSize,
                    transpose = sameChr && row < column,
                    blockNumber,
                    t;

                function drawBlock(block, transpose) {

                    var blockNumber, row, col, x0, y0, image, ctx;
                    blockNumber = block.blockNumber;
                    row = Math.floor(blockNumber / blockColumnCount);
                    col = blockNumber - row * blockColumnCount;
                    x0 = blockBinCount * col;
                    y0 = blockBinCount * row;

                    image = document.createElement('canvas');
                    image.width = imageSize;
                    image.height = imageSize;
                    ctx = image.getContext('2d');

                    // Draw the image
                    var i, rec, x, y, rgb;
                    for (i = 0; i < block.records.length; i++) {
                        rec = block.records[i];
                        x = (rec.bin1 - x0) * state.pixelSize;
                        y = (rec.bin2 - y0) * state.pixelSize;

                        if (transpose) {
                            t = y;
                            y = x;
                            x = t;
                        }

                        rgb = self.colorScale.getColor(rec.counts);

                        ctx.fillStyle = rgb;
                        ctx.fillRect(x, y, state.pixelSize, state.pixelSize);
                        if (row === col) {
                            ctx.fillRect(y, x, state.pixelSize, state.pixelSize);
                        }
                    }
                    return image;
                }

                if (sameChr && row < column) {
                    blockNumber = column * blockColumnCount + row;
                }
                else {
                    blockNumber = row * blockColumnCount + column;
                }

                self.getBlock(zd, blockNumber)

                    .then(function (block) {

                        var image;
                        if (block) {
                            image = drawBlock(block, transpose);
                        }
                        else {
                            console.log("No block for " + blockNumber);
                        }

                        var imageTile = {row: row, column: column, image: image};
                        self.imageTileCache[key] = imageTile;
                        fulfill(imageTile);

                    })
                    .catch(reject)
            })
        }
    };
    hic.ContactMatrixView.prototype.getBlock = function (zd, blockNumber) {

        var self = this,
            key = "" + zd.chr1.name + "_" + zd.chr2.name + "_" + zd.zoom.binSize + "_" + zd.zoom.unit + "_" + blockNumber;

        if (this.blockCache.hasOwnProperty(key)) {
            return Promise.resolve(this.blockCache[key]);
        } else {
            return new Promise(function (fulfill, reject) {

                var reader = self.browser.hicReader;
                self.startSpinner();
                reader.readBlock(blockNumber, zd)
                    .then(function (block) {

                        self.blockCache[key] = block

                        fulfill(block);

                    })
                    .catch(reject)
            })
        }
    };

    hic.ContactMatrixView.prototype.startSpinner = function () {
        var $spinner = this.$spinner;
        $spinner.addClass("fa-spin");
        $spinner.show();
    };

    hic.ContactMatrixView.prototype.stopSpinner = function () {
        var $spinner = this.$spinner;
        $spinner.hide();
        $spinner.removeClass("fa-spin");
    };

    function addMouseHandlers($viewport) {

        var self = this,
            viewport = $viewport[0],
            isMouseDown = false,
            isDragging = false,
            lastMouseX,
            lastMouseY,
            mouseDownX,
            mouseDownY;

        $viewport.on('mousedown', function (e) {

            var coords;

            isMouseDown = true;
            coords = translateMouseCoordinates(e, $viewport);
            mouseDownX = lastMouseX = coords.x;
            mouseDownY = lastMouseY = coords.y;

        });

        // Guide line is bound within track area, and offset by 5 pixels so as not to interfere mouse clicks.
        // $(trackContainerDiv).mousemove(function (e) {
        //     var xy,
        //         _left,
        //         $element = igv.browser.$cursorTrackingGuide;
        //
        //     e.preventDefault();
        //
        //     xy = igv.translateMouseCoordinates(e, trackContainerDiv);
        //     _left = Math.max(50, xy.x - 5);
        //
        //     _left = Math.min(igv.browser.trackContainerDiv.width() - 65, _left);
        //     $element.css({left: _left + 'px'});
        // });

        $viewport.on('mousemove', hic.throttle(function (e) {

            var coords,
                maxEnd,
                maxStart;

            if (self.updating) return;

            e.preventDefault();

            coords = translateMouseCoordinates(e, $viewport);

            if (isMouseDown) { // Possibly dragging

                if (mouseDownX && Math.abs(coords.x - mouseDownX) > dragThreshold) {

                    isDragging = true;

                    if (self.updating) return;   // Freeze frame during updates

                    self.browser.shiftPixels(lastMouseX - coords.x, lastMouseY - coords.y);

                }

                lastMouseX = coords.x;
                lastMouseY = coords.y;
            }

        }, 10));

        $viewport.on('mouseup', mouseUpOrOut);

        $viewport.on('mouseleave', mouseUpOrOut);

        function mouseUpOrOut(e) {

            //
            // // Don't let vertical line interfere with dragging
            // if (igv.browser.$cursorTrackingGuide && e.toElement === igv.browser.$cursorTrackingGuide.get(0) && e.type === 'mouseleave') {
            //     return;
            // }

            if (isDragging) {
                isDragging = false;
                hic.GlobalEventBus.post(new hic.DragStoppedEvent());
            }

            isMouseDown = false;
            mouseDownX = lastMouseX = undefined;
            mouseDownY = lastMouseY = undefined;

        }

    }

    function translateMouseCoordinates(e, $target) {

        var eFixed,
            posx,
            posy;

        // Sets pageX and pageY for browsers that don't support them
        eFixed = $.event.fix(e);

        if (undefined === $target.offset()) {
            console.log('igv.translateMouseCoordinates - $target.offset() is undefined.');
        }
        posx = eFixed.pageX - $target.offset().left;
        posy = eFixed.pageY - $target.offset().top;

        return {x: posx, y: posy}
    }

    return hic;

})
(hic || {});
<|endoftext|>


<commit_before>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == 'object' && typeof module == 'object') { // CommonJS
    mod(require('../../lib/codemirror'));
  } else if (typeof define == 'function' && define.amd) { // AMD
    define(['../../lib/codemirror'], mod);
  } else { // Plain browser env
    mod(CodeMirror);
  }
})(function(CodeMirror) {
'use strict';

var TOKEN_STYLES = {
  addition: 'positive',
  attributes: 'attribute',
  bold: 'strong',
  cite: 'keyword',
  code: 'atom',
  definitionList: 'number',
  deletion: 'negative',
  div: 'punctuation',
  em: 'em',
  footnote: 'variable',
  footCite: 'qualifier',
  header: 'header',
  html: 'comment',
  image: 'string',
  italic: 'em',
  link: 'link',
  linkDefinition: 'link',
  list1: 'variable-2',
  list2: 'variable-3',
  list3: 'keyword',
  notextile: 'string-2',
  pre: 'operator',
  p: 'property',
  quote: 'bracket',
  span: 'quote',
  specialChar: 'tag',
  strong: 'strong',
  sub: 'builtin',
  sup: 'builtin',
  table: 'variable-3',
  tableHeading: 'operator'
};

function Parser(regExpFactory, state, stream) {
  this.regExpFactory = regExpFactory;
  this.state = state;
  this.stream = stream;
  this.styles = TOKEN_STYLES;

  this.state.specialChar = null;
}

Parser.prototype.eat = function(name) {
  return this.stream.match(this.regExpFactory.pattern(name), true);
};

Parser.prototype.check = function(name) {
  return this.stream.match(this.regExpFactory.pattern(name), false);
};

Parser.prototype.setModeForNextToken = function(mode) {
  return this.state.mode = mode;
};

Parser.prototype.execMode = function(newMode) {
  return this.setModeForNextToken(newMode).call(this);
};

Parser.prototype.startNewLine = function() {
  this.setModeForNextToken(Modes.newLayout);
  this.state.tableHeading = false;

  if (this.state.layoutType === 'definitionList' && this.state.spanningLayout) {
    if (this.check('definitionListEnd')) {
      this.state.spanningLayout = false;
    }
  }
};

Parser.prototype.nextToken = function() {
  return this.state.mode.call(this);
};

Parser.prototype.styleFor = function(token) {
  if (this.styles.hasOwnProperty(token)) {
    return this.styles[token];
  }
  throw 'unknown token';
};

Parser.prototype.handlePhraseModifier = function(ch) {
  if (ch === '_') {
    if (this.stream.eat('_')) {
      return this.togglePhraseModifier('italic', /^.*__/);
    }
    return this.togglePhraseModifier('em', /^.*_/);
  }

  if (ch === '*') {
    if (this.stream.eat('*')) {
      return this.togglePhraseModifier('bold', /^.*\*\*/);
    }
    return this.togglePhraseModifier('strong', /^.*\*/);
  }

  if (ch === '[') {
    if (this.stream.match(/\d+\]/)) {
      this.state.footCite = true;
    }
    return this.tokenStyles();
  }

  if (ch === '(') {
    if (this.stream.match('r)')) {
      this.state.specialChar = 'r';
    } else if (this.stream.match('tm)')) {
      this.state.specialChar = 'tm';
    } else if (this.stream.match('c)')) {
      this.state.specialChar = 'c';
    }
    return this.tokenStyles();
  }

  if (ch === '<') {
    if (this.stream.match(/(\w+)[^>]+>[^<]+<\/\1>/)) {
      return this.tokenStylesWith(this.styleFor('html'));
    }
  }

  if (ch === '?' && this.stream.eat('?')) {
    return this.togglePhraseModifier('cite', /^.*\?\?/);
  }
  if (ch === '=' && this.stream.eat('=')) {
    return this.togglePhraseModifier('notextile', /^.*==/);
  }
  if (ch === '-') {
    return this.togglePhraseModifier('deletion', /^.*-/);
  }
  if (ch === '+') {
    return this.togglePhraseModifier('addition', /^.*\+/);
  }
  if (ch === '~') {
    return this.togglePhraseModifier('sub', /^.*~/);
  }
  if (ch === '^') {
    return this.togglePhraseModifier('sup', /^.*\^/);
  }
  if (ch === '%') {
    return this.togglePhraseModifier('span', /^.*%/);
  }
  if (ch === '@') {
    return this.togglePhraseModifier('code', /^.*@/);
  }
  if (ch === '!') {
    var type = this.togglePhraseModifier('image', /^.*(?:\([^\)]+\))?!/);
    this.stream.match(/^:\S+/); // optional Url portion
    return type;
  }
  return this.tokenStyles();
};

Parser.prototype.togglePhraseModifier = function(phraseModifier, closeRE) {
  if (this.state[phraseModifier]) { // remove phrase modifier
    var type = this.tokenStyles();
    this.state[phraseModifier] = false;
    return type;
  }
  if (this.stream.match(closeRE, false)) { // add phrase modifier
    this.state[phraseModifier] = true;
    this.setModeForNextToken(Modes.attributes);
  }
  return this.tokenStyles();
};

Parser.prototype.tokenStyles = function() {
  var disabled = this.textileDisabled(),
      styles = [];

  if (disabled) return disabled;

  if (this.state.layoutType) {
    styles.push(this.styleFor(this.state.layoutType));
  }

  styles = styles.concat(this.activeStyles('addition', 'bold', 'cite', 'code',
      'deletion', 'em', 'footCite', 'image', 'italic', 'link', 'span', 'specialChar', 'strong',
      'sub', 'sup', 'table', 'tableHeading'));

  if (this.state.layoutType === 'header') {
    styles.push(this.styleFor('header') + '-' + this.state.header);
  }
  return styles.length ? styles.join(' ') : null;
};

Parser.prototype.textileDisabled = function() {
  var type = this.state.layoutType;

  switch(type) {
    case 'notextile':
    case 'code':
    case 'pre':
      return this.styleFor(type);
    default:
      if (this.state.notextile) {
        return this.styleFor('notextile') + (type ? (' ' + this.styleFor(type)) : '');
      }

      return null;
  }
};

Parser.prototype.tokenStylesWith = function(extraStyles) {
  var disabled = this.textileDisabled(),
      type;

  if (disabled) return disabled;

  type = this.tokenStyles();
  if(extraStyles) {
    return type ? (type + ' ' + extraStyles) : extraStyles;
  }
  return type;
};

Parser.prototype.activeStyles = function() {
  var styles = [],
      i;
  for (i = 0; i < arguments.length; ++i) {
    if (this.state[arguments[i]]) {
      styles.push(this.styleFor(arguments[i]));
    }
  }
  return styles;
};

Parser.prototype.blankLine = function() {
  var spanningLayout = this.state.spanningLayout,
      type = this.state.layoutType,
      key;

  for (key in this.state) {
    if (this.state.hasOwnProperty(key)) {
      delete this.state[key];
    }
  }

  this.setModeForNextToken(Modes.newLayout);
  if (spanningLayout) {
    this.state.layoutType = type;
    this.state.spanningLayout = true;
  }
};

function RegExpFactory() {
  this.cache = {};
  this.single = {
    bc: 'bc',
    bq: 'bq',
    definitionList: /- [^(?::=)]+:=+/,
    definitionListEnd: /.*=:\s*$/,
    div: 'div',
    drawTable: /\|.*\|/,
    foot: /fn\d+/,
    header: /h[1-6]/,
    html: /\s*<(?:\/)?(\w+)(?:[^>]+)?>(?:[^<]+<\/\1>)?/,
    link: /[^"]+":\S/,
    linkDefinition: /\[[^\s\]]+\]\S+/,
    list: /(?:#+|\*+)/,
    notextile: 'notextile',
    para: 'p',
    pre: 'pre',
    table: 'table',
    tableCellAttributes: /[/\\]\d+/,
    tableHeading: /\|_\./,
    tableText: /[^"_\*\[\(\?\+~\^%@|-]+/,
    text: /[^!"_=\*\[\(<\?\+~\^%@-]+/
  };
  this.attributes = {
    align: /(?:<>|<|>|=)/,
    selector: /\([^\(][^\)]+\)/,
    lang: /\[[^\[\]]+\]/,
    pad: /(?:\(+|\)+){1,2}/,
    css: /\{[^\}]+\}/
  };
}

RegExpFactory.prototype.pattern = function(name) {
  return (this.cache[name] || this.createRe(name));
};

RegExpFactory.prototype.createRe = function(name) {
  switch (name) {
    case 'drawTable':
      return this.makeRe('^', this.single.drawTable, '$');
    case 'html':
      return this.makeRe('^', this.single.html, '(?:', this.single.html, ')*', '$');
    case 'linkDefinition':
      return this.makeRe('^', this.single.linkDefinition, '$');
    case 'listLayout':
      return this.makeRe('^', this.single.list, this.pattern('allAttributes'), '*\\s+');
    case 'tableCellAttributes':
      return this.makeRe('^', this.choiceRe(this.single.tableCellAttributes,
          this.pattern('allAttributes')), '+\\.');
    case 'type':
      return this.makeRe('^', this.pattern('allTypes'));
    case 'typeLayout':
      return this.makeRe('^', this.pattern('allTypes'), this.pattern('allAttributes'),
          '*\\.\\.?', '(\\s+|$)');
    case 'attributes':
      return this.makeRe('^', this.pattern('allAttributes'), '+');

    case 'allTypes':
      return this.choiceRe(this.single.div, this.single.foot,
          this.single.header, this.single.bc, this.single.bq,
          this.single.notextile, this.single.pre, this.single.table,
          this.single.para);

    case 'allAttributes':
      return this.choiceRe(this.attributes.selector, this.attributes.css,
          this.attributes.lang, this.attributes.align, this.attributes.pad);

    default:
      return this.makeRe('^', this.single[name]);
  }
};

RegExpFactory.prototype.makeRe = function() {
  var pattern = '',
      i,
      arg;

  for (i = 0; i < arguments.length; ++i) {
    arg = arguments[i];
    pattern += (typeof arg === 'string') ? arg : arg.source;
  }
  return new RegExp(pattern);
};

RegExpFactory.prototype.choiceRe = function() {
  var parts = [arguments[0]],
      i;

  for (i = 1; i < arguments.length; ++i) {
    parts[i * 2 - 1] = '|';
    parts[i * 2] = arguments[i];
  }

  parts.unshift('(?:');
  parts.push(')');
  return this.makeRe.apply(this, parts);
};

var Modes = {
  newLayout: function() {
    if (this.check('typeLayout')) {
      this.state.spanningLayout = false;
      return this.execMode(Modes.blockType);
    }
    if (!this.textileDisabled()) {
      if (this.check('listLayout')) {
        return this.execMode(Modes.list);
      } else if (this.check('drawTable')) {
        return this.execMode(Modes.table);
      } else if (this.check('linkDefinition')) {
        return this.execMode(Modes.linkDefinition);
      } else if (this.check('definitionList')) {
        return this.execMode(Modes.definitionList);
      } else if (this.check('html')) {
        return this.execMode(Modes.html);
      }
    }
    return this.execMode(Modes.text);
  },

  blockType: function() {
    var match,
        type;
    this.state.layoutType = null;

    if (match = this.eat('type')) {
      type = match[0];
    } else {
      return this.execMode(Modes.text);
    }

    if(match = type.match(this.regExpFactory.pattern('header'))) {
      this.state.layoutType = 'header';
      this.state.header = parseInt(match[0][1]);
    } else if (type.match(this.regExpFactory.pattern('bq'))) {
      this.state.layoutType = 'quote';
    } else if (type.match(this.regExpFactory.pattern('bc'))) {
      this.state.layoutType = 'code';
    } else if (type.match(this.regExpFactory.pattern('foot'))) {
      this.state.layoutType = 'footnote';
    } else if (type.match(this.regExpFactory.pattern('notextile'))) {
      this.state.layoutType = 'notextile';
    } else if (type.match(this.regExpFactory.pattern('pre'))) {
      this.state.layoutType = 'pre';
    } else if (type.match(this.regExpFactory.pattern('div'))) {
      this.state.layoutType = 'div';
    } else if (type.match(this.regExpFactory.pattern('table'))) {
      this.state.layoutType = 'table';
    }

    this.setModeForNextToken(Modes.attributes);
    return this.tokenStyles();
  },

  text: function() {
    if (this.eat('text')) {
      return this.tokenStyles();
    }

    var ch = this.stream.next();

    if (ch === '"') {
      return this.execMode(Modes.link);
    }
    return this.handlePhraseModifier(ch);
  },

  attributes: function() {
    this.setModeForNextToken(Modes.layoutLength);

    if (this.eat('attributes')) {
      return this.tokenStylesWith(this.styleFor('attributes'));
    }
    return this.tokenStyles();
  },

  layoutLength: function() {
    if (this.stream.eat('.') && this.stream.eat('.')) {
      this.state.spanningLayout = true;
    }

    this.setModeForNextToken(Modes.text);
    return this.tokenStyles();
  },

  list: function() {
    var match = this.eat('list'),
        listMod;
    this.state.listDepth = match[0].length;
    listMod = (this.state.listDepth - 1) % 3;
    if (!listMod) {
      this.state.layoutType = 'list1';
    } else if (listMod === 1) {
      this.state.layoutType = 'list2';
    } else {
      this.state.layoutType = 'list3';
    }
    this.setModeForNextToken(Modes.attributes);
    return this.tokenStyles();
  },

  link: function() {
    this.setModeForNextToken(Modes.text);
    if (this.eat('link')) {
      this.stream.match(/\S+/);
      return this.tokenStylesWith(this.styleFor('link'));
    }
    return this.tokenStyles();
  },

  linkDefinition: function() {
    this.stream.skipToEnd();
    return this.tokenStylesWith(this.styleFor('linkDefinition'));
  },

  definitionList: function() {
    this.eat('definitionList');

    this.state.layoutType = 'definitionList';

    if (this.stream.match(/\s*$/)) {
      this.state.spanningLayout = true;
    } else {
      this.setModeForNextToken(Modes.attributes);
    }
    return this.tokenStyles();
  },

  html: function() {
    this.stream.skipToEnd();
    return this.tokenStylesWith(this.styleFor('html'));
  },

  table: function() {
    this.state.layoutType = 'table';
    return this.execMode(Modes.tableCell);
  },

  tableCell: function() {
    if (this.eat('tableHeading')) {
      this.state.tableHeading = true;
    } else {
      this.stream.eat('|');
    }
    this.setModeForNextToken(Modes.tableCellAttributes);
    return this.tokenStyles();
  },

  tableCellAttributes: function() {
    this.setModeForNextToken(Modes.tableText);

    if (this.eat('tableCellAttributes')) {
      return this.tokenStylesWith(this.styleFor('attributes'));
    }
    return this.tokenStyles();
  },

  tableText: function() {
    if (this.eat('tableText')) {
      return this.tokenStyles();
    }

    if (this.stream.peek() === '|') { // end of cell
      this.setModeForNextToken(Modes.tableCell);
      return this.tokenStyles();
    }
    return this.handlePhraseModifier(this.stream.next());
  }
};

CodeMirror.defineMode('textile', function() {
  var regExpFactory = new RegExpFactory();

  return {
    startState: function() {
      return { mode: Modes.newLayout };
    },
    token: function(stream, state) {
      var parser = new Parser(regExpFactory, state, stream);
      if (stream.sol()) { parser.startNewLine(); }
      return parser.nextToken();
    },
    blankLine: function(state) {
      new Parser(regExpFactory, state).blankLine();
    }
  };
});

CodeMirror.defineMIME('text/x-textile', 'textile');
});
<commit_msg>[textile mode] Agressively adjust coding style<commit_after>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") { // CommonJS
    mod(require("../../lib/codemirror"));
  } else if (typeof define == "function" && define.amd) { // AMD
    define(["../../lib/codemirror"], mod);
  } else { // Plain browser env
    mod(CodeMirror);
  }
})(function(CodeMirror) {
  "use strict";

  var TOKEN_STYLES = {
    addition: "positive",
    attributes: "attribute",
    bold: "strong",
    cite: "keyword",
    code: "atom",
    definitionList: "number",
    deletion: "negative",
    div: "punctuation",
    em: "em",
    footnote: "variable",
    footCite: "qualifier",
    header: "header",
    html: "comment",
    image: "string",
    italic: "em",
    link: "link",
    linkDefinition: "link",
    list1: "variable-2",
    list2: "variable-3",
    list3: "keyword",
    notextile: "string-2",
    pre: "operator",
    p: "property",
    quote: "bracket",
    span: "quote",
    specialChar: "tag",
    strong: "strong",
    sub: "builtin",
    sup: "builtin",
    table: "variable-3",
    tableHeading: "operator"
  };

  function startNewLine(stream, state) {
    state.mode = Modes.newLayout;
    state.tableHeading = false;

    if (state.layoutType === "definitionList" && state.spanningLayout &&
        stream.match(RE("definitionListEnd"), false))
      state.spanningLayout = false;
  }

  function handlePhraseModifier(stream, state, ch) {
    if (ch === "_") {
      if (stream.eat("_"))
        return togglePhraseModifier(stream, state, "italic", /^.*__/);
      else
        return togglePhraseModifier(stream, state, "em", /^.*_/);
    }

    if (ch === "*") {
      if (stream.eat("*")) {
        return togglePhraseModifier(stream, state, "bold", /^.*\*\*/);
      }
      return togglePhraseModifier(stream, state, "strong", /^.*\*/);
    }

    if (ch === "[") {
      if (stream.match(/\d+\]/)) state.footCite = true;
      return tokenStyles(state);
    }

    if (ch === "(") {
      var spec = stream.match(/^(r|tm|c)\)/);
      if (spec)
        return tokenStylesWith(state, TOKEN_STYLES.specialChar);
    }

    if (ch === "<" && stream.match(/(\w+)[^>]+>[^<]+<\/\1>/))
      return tokenStylesWith(state, TOKEN_STYLES.html);

    if (ch === "?" && stream.eat("?"))
      return togglePhraseModifier(stream, state, "cite", /^.*\?\?/);

    if (ch === "=" && stream.eat("="))
      return togglePhraseModifier(stream, state, "notextile", /^.*==/);

    if (ch === "-")
      return togglePhraseModifier(stream, state, "deletion", /^.*-/);

    if (ch === "+")
      return togglePhraseModifier(stream, state, "addition", /^.*\+/);

    if (ch === "~")
      return togglePhraseModifier(stream, state, "sub", /^.*~/);

    if (ch === "^")
      return togglePhraseModifier(stream, state, "sup", /^.*\^/);

    if (ch === "%")
      return togglePhraseModifier(stream, state, "span", /^.*%/);

    if (ch === "@")
      return togglePhraseModifier(stream, state, "code", /^.*@/);

    if (ch === "!") {
      var type = togglePhraseModifier(stream, state, "image", /^.*(?:\([^\)]+\))?!/);
      stream.match(/^:\S+/); // optional Url portion
      return type;
    }
    return tokenStyles(state);
  }

  function togglePhraseModifier(stream, state, phraseModifier, closeRE) {
    if (state[phraseModifier]) { // remove phrase modifier
      var type = tokenStyles(state);
      state[phraseModifier] = false;
      return type;
    }
    if (stream.match(closeRE, false)) { // add phrase modifier
      state[phraseModifier] = true;
      state.mode = Modes.attributes;
    }
    return tokenStyles(state);
  };

  function tokenStyles(state) {
    var disabled = textileDisabled(state);
    if (disabled) return disabled;

    var styles = [];
    if (state.layoutType) styles.push(TOKEN_STYLES[state.layoutType]);

    styles = styles.concat(activeStyles(
      state, "addition", "bold", "cite", "code", "deletion", "em", "footCite",
      "image", "italic", "link", "span", "strong", "sub", "sup", "table", "tableHeading"));

    if (state.layoutType === "header")
      styles.push(TOKEN_STYLES.header + "-" + state.header);

    return styles.length ? styles.join(" ") : null;
  }

  function textileDisabled(state) {
    var type = state.layoutType;

    switch(type) {
    case "notextile":
    case "code":
    case "pre":
      return TOKEN_STYLES[type];
    default:
      if (state.notextile)
        return TOKEN_STYLES.notextile + (type ? (" " + TOKEN_STYLES[type]) : "");
      return null;
    }
  }

  function tokenStylesWith(state, extraStyles) {
    var disabled = textileDisabled(state);
    if (disabled) return disabled;

    var type = tokenStyles(state);
    if (extraStyles)
      return type ? (type + " " + extraStyles) : extraStyles;
    else
      return type;
  }

  function activeStyles(state) {
    var styles = [];
    for (var i = 1; i < arguments.length; ++i) {
      if (state[arguments[i]])
        styles.push(TOKEN_STYLES[arguments[i]]);
    }
    return styles;
  }

  function blankLine(state) {
    var spanningLayout = state.spanningLayout, type = state.layoutType;

    for (var key in state) if (state.hasOwnProperty(key))
      delete state[key];

    state.mode = Modes.newLayout;
    if (spanningLayout) {
      state.layoutType = type;
      state.spanningLayout = true;
    }
  }

  var REs = {
    cache: {},
    single: {
      bc: "bc",
      bq: "bq",
      definitionList: /- [^(?::=)]+:=+/,
      definitionListEnd: /.*=:\s*$/,
      div: "div",
      drawTable: /\|.*\|/,
      foot: /fn\d+/,
      header: /h[1-6]/,
      html: /\s*<(?:\/)?(\w+)(?:[^>]+)?>(?:[^<]+<\/\1>)?/,
      link: /[^"]+":\S/,
      linkDefinition: /\[[^\s\]]+\]\S+/,
      list: /(?:#+|\*+)/,
      notextile: "notextile",
      para: "p",
      pre: "pre",
      table: "table",
      tableCellAttributes: /[\/\\]\d+/,
      tableHeading: /\|_\./,
      tableText: /[^"_\*\[\(\?\+~\^%@|-]+/,
      text: /[^!"_=\*\[\(<\?\+~\^%@-]+/
    },
    attributes: {
      align: /(?:<>|<|>|=)/,
      selector: /\([^\(][^\)]+\)/,
      lang: /\[[^\[\]]+\]/,
      pad: /(?:\(+|\)+){1,2}/,
      css: /\{[^\}]+\}/
    },
    createRe: function(name) {
      switch (name) {
      case "drawTable":
        return REs.makeRe("^", REs.single.drawTable, "$");
      case "html":
        return REs.makeRe("^", REs.single.html, "(?:", REs.single.html, ")*", "$");
      case "linkDefinition":
        return REs.makeRe("^", REs.single.linkDefinition, "$");
      case "listLayout":
        return REs.makeRe("^", REs.single.list, RE("allAttributes"), "*\\s+");
      case "tableCellAttributes":
        return REs.makeRe("^", REs.choiceRe(REs.single.tableCellAttributes,
                                            RE("allAttributes")), "+\\.");
      case "type":
        return REs.makeRe("^", RE("allTypes"));
      case "typeLayout":
        return REs.makeRe("^", RE("allTypes"), RE("allAttributes"),
                          "*\\.\\.?", "(\\s+|$)");
      case "attributes":
        return REs.makeRe("^", RE("allAttributes"), "+");

      case "allTypes":
        return REs.choiceRe(REs.single.div, REs.single.foot,
                            REs.single.header, REs.single.bc, REs.single.bq,
                            REs.single.notextile, REs.single.pre, REs.single.table,
                            REs.single.para);

      case "allAttributes":
        return REs.choiceRe(REs.attributes.selector, REs.attributes.css,
                            REs.attributes.lang, REs.attributes.align, REs.attributes.pad);

      default:
        return REs.makeRe("^", REs.single[name]);
      }
    },
    makeRe: function() {
      var pattern = "";
      for (var i = 0; i < arguments.length; ++i) {
        var arg = arguments[i];
        pattern += (typeof arg === "string") ? arg : arg.source;
      }
      return new RegExp(pattern);
    },
    choiceRe: function() {
      var parts = [arguments[0]];
      for (var i = 1; i < arguments.length; ++i) {
        parts[i * 2 - 1] = "|";
        parts[i * 2] = arguments[i];
      }

      parts.unshift("(?:");
      parts.push(")");
      return REs.makeRe.apply(null, parts);
    }
  };

  function RE(name) {
    return (REs.cache[name] || (REs.cache[name] = REs.createRe(name)));
  }

  var Modes = {
    newLayout: function(stream, state) {
      if (stream.match(RE("typeLayout"), false)) {
        state.spanningLayout = false;
        return (state.mode = Modes.blockType)(stream, state);
      }
      var newMode;
      if (!textileDisabled(state)) {
        if (stream.match(RE("listLayout"), false))
          newMode = Modes.list;
        else if (stream.match(RE("drawTable"), false))
          newMode = Modes.table;
        else if (stream.match(RE("linkDefinition"), false))
          newMode = Modes.linkDefinition;
        else if (stream.match(RE("definitionList")))
          newMode = Modes.definitionList;
        else if (stream.match(RE("html"), false))
          newMode = Modes.html;
      }
      return (state.mode = (newMode || Modes.text))(stream, state);
    },

    blockType: function(stream, state) {
      var match, type;
      state.layoutType = null;

      if (match = stream.match(RE("type")))
        type = match[0];
      else
        return (state.mode = Modes.text)(stream, state);

      if (match = type.match(RE("header"))) {
        state.layoutType = "header";
        state.header = parseInt(match[0][1]);
      } else if (type.match(RE("bq"))) {
        state.layoutType = "quote";
      } else if (type.match(RE("bc"))) {
        state.layoutType = "code";
      } else if (type.match(RE("foot"))) {
        state.layoutType = "footnote";
      } else if (type.match(RE("notextile"))) {
        state.layoutType = "notextile";
      } else if (type.match(RE("pre"))) {
        state.layoutType = "pre";
      } else if (type.match(RE("div"))) {
        state.layoutType = "div";
      } else if (type.match(RE("table"))) {
        state.layoutType = "table";
      }

      state.mode = Modes.attributes;
      return tokenStyles(state);
    },

    text: function(stream, state) {
      if (stream.match(RE("text"))) return tokenStyles(state);

      var ch = stream.next();
      if (ch === '"')
        return (state.mode = Modes.link)(stream, state);
      return handlePhraseModifier(stream, state, ch);
    },

    attributes: function(stream, state) {
      state.mode = Modes.layoutLength;

      if (stream.match(RE("attributes")))
        return tokenStylesWith(state, TOKEN_STYLES.attributes);
      else
        return tokenStyles(state);
    },

    layoutLength: function(stream, state) {
      if (stream.eat(".") && stream.eat("."))
        state.spanningLayout = true;

      state.mode = Modes.text;
      return tokenStyles(state);
    },

    list: function(stream, state) {
      var match = stream.match(RE("list"));
      state.listDepth = match[0].length;
      var listMod = (state.listDepth - 1) % 3;
      if (!listMod)
        state.layoutType = "list1";
      else if (listMod === 1)
        state.layoutType = "list2";
      else
        state.layoutType = "list3";

      state.mode = Modes.attributes;
      return tokenStyles(state);
    },

    link: function(stream, state) {
      state.mode = Modes.text;
      if (stream.match(RE("link"))) {
        stream.match(/\S+/);
        return tokenStylesWith(state, TOKEN_STYLES.link);
      }
      return tokenStyles(state);
    },

    linkDefinition: function(stream, state) {
      stream.skipToEnd();
      return tokenStylesWith(state, TOKEN_STYLES.linkDefinition);
    },

    definitionList: function(stream, state) {
      stream.match(RE("definitionList"));

      state.layoutType = "definitionList";

      if (stream.match(/\s*$/))
        state.spanningLayout = true;
      else
        state.mode = Modes.attributes;

      return tokenStyles(state);
    },

    html: function(stream, state) {
      stream.skipToEnd();
      return tokenStylesWith(state, TOKEN_STYLES.html);
    },

    table: function(stream, state) {
      state.layoutType = "table";
      return (state.mode = Modes.tableCell)(stream, state);
    },

    tableCell: function(stream, state) {
      if (stream.match(RE("tableHeading")))
        state.tableHeading = true;
      else
        stream.eat("|");

      state.mode = Modes.tableCellAttributes;
      return tokenStyles(state);
    },

    tableCellAttributes: function(stream, state) {
      state.mode = Modes.tableText;

      if (stream.match(RE("tableCellAttributes")))
        return tokenStylesWith(state, TOKEN_STYLES.attributes);
      else
        return tokenStyles(state);
    },

    tableText: function(stream, state) {
      if (stream.match(RE("tableText")))
        return tokenStyles(state);

      if (stream.peek() === "|") { // end of cell
        state.mode = Modes.tableCell;
        return tokenStyles(state);
      }
      return handlePhraseModifier(stream, state, stream.next());
    }
  };

  CodeMirror.defineMode("textile", function() {
    return {
      startState: function() {
        return { mode: Modes.newLayout };
      },
      token: function(stream, state) {
        if (stream.sol()) startNewLine(stream, state);
        return state.mode(stream, state);
      },
      blankLine: blankLine
    };
  });

  CodeMirror.defineMIME("text/x-textile", "textile");
});
<|endoftext|>


<commit_before>'use strict';
global.WEBPACK_ENV = "production";
var path = require("path");
var UglifyJsPlugin = require("webpack-uglify-js-plugin");
var OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
var webpack = require("webpack");
var StatsPlugin = require('stats-webpack-plugin');
var publicPath = '/webpack/';

var conf = {
  mode: "none",
  // Was "eval", but that did not go well with our CSP
  devtool: "eval",
  entry: {
    "app_bundle": "./webpack/entry.tsx",
    "front_page": "./webpack/front_page/index.tsx",
    "password_reset": "./webpack/password_reset/index.tsx",
    "tos_update": "./webpack/tos_update/index.tsx"
  },
  output: {
    path: path.join(__dirname, '..', 'public', 'webpack'),
    publicPath,
    filename: '[name]-[chunkhash].js',
    chunkFilename: '[id].[name].[chunkhash].js'
  },
  module: {
    rules: [
      {
        test: [/\.scss$/, /\.css$/],
        use: ["style-loader", "css-loader", "sass-loader"]
      },
      {
        test: /\.tsx?$/,
        use: "ts-loader"
      },
      {
        test: [/\.woff$/, /\.woff2$/, /\.ttf$/],
        use: "url-loader"
      },
      {
        test: [/\.eot$/, /\.svg(\?v=\d+\.\d+\.\d+)?$/],
        use: "file-loader"
      }
    ]
  },
  // Allows imports without file extensions.
  resolve: {
    extensions: [".js", ".ts", ".tsx", ".css", ".scss", ".json"]
  },
  plugins: [
    new StatsPlugin('manifest.json', {
      // We only need assetsByChunkName
      chunkModules: false,
      source: false,
      chunks: false,
      modules: false,
      assets: true
    }),
    new OptimizeCssAssetsPlugin({
      assetNameRegExp: /\.css$/g,
      cssProcessor: require("cssnano"),
      cssProcessorOptions: { discardComments: { removeAll: true } },
      canPrint: true
    }),
    new UglifyJsPlugin({
      cacheFolder: path.resolve(__dirname, "../public/dist/cached_uglify/"),
      debug: true,
      minimize: true,
      sourceMap: true,
      screw_ie8: true,
      output: { comments: false },
      compressor: { warnings: false }
    }),
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify("production")
      }
    })
  ],
  node: {
    fs: "empty"
  }
};
var accessToken = process.env.ROLLBAR_ACCESS_TOKEN
if (accessToken) {
  console.log("============= PERFORMING ROLLBAR CONFIG")
  var RollbarSourceMapPlugin = require('rollbar-sourcemap-webpack-plugin')
  var version = process.env.BUILT_AT || process.env.HEROKU_SLUG_COMMIT || "????"
  var plugin = new RollbarSourceMapPlugin({accessToken, version, publicPath})
  conf.plugins.push(plugin)
} else {
  console.log("============= SKIPPING ROLLBAR CONFIG")
}
module.exports = conf;
<commit_msg>Inline sourcemap :(<commit_after>'use strict';
global.WEBPACK_ENV = "production";
var path = require("path");
var UglifyJsPlugin = require("webpack-uglify-js-plugin");
var OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
var webpack = require("webpack");
var StatsPlugin = require('stats-webpack-plugin');
var publicPath = '/webpack/';

var conf = {
  mode: "none",
  devtool: "inline-source-map", // inlines SourceMap into original file
  // devtool: "eval-source-map", // inlines SourceMap per module
  // devtool: "hidden-source-map", // SourceMap without reference in original file
  // devtool: "cheap-source-map", // cheap-variant of SourceMap without module mappings
  // devtool: "cheap-module-source-map", // cheap-variant of SourceMap with module mappings
  entry: {
    "app_bundle": "./webpack/entry.tsx",
    "front_page": "./webpack/front_page/index.tsx",
    "password_reset": "./webpack/password_reset/index.tsx",
    "tos_update": "./webpack/tos_update/index.tsx"
  },
  output: {
    path: path.join(__dirname, '..', 'public', 'webpack'),
    publicPath,
    filename: '[name]-[chunkhash].js',
    chunkFilename: '[id].[name].[chunkhash].js'
  },
  module: {
    rules: [
      {
        test: [/\.scss$/, /\.css$/],
        use: ["style-loader", "css-loader", "sass-loader"]
      },
      {
        test: /\.tsx?$/,
        use: "ts-loader"
      },
      {
        test: [/\.woff$/, /\.woff2$/, /\.ttf$/],
        use: "url-loader"
      },
      {
        test: [/\.eot$/, /\.svg(\?v=\d+\.\d+\.\d+)?$/],
        use: "file-loader"
      }
    ]
  },
  // Allows imports without file extensions.
  resolve: {
    extensions: [".js", ".ts", ".tsx", ".css", ".scss", ".json"]
  },
  plugins: [
    new StatsPlugin('manifest.json', {
      // We only need assetsByChunkName
      chunkModules: false,
      source: false,
      chunks: false,
      modules: false,
      assets: true
    }),
    new OptimizeCssAssetsPlugin({
      assetNameRegExp: /\.css$/g,
      cssProcessor: require("cssnano"),
      cssProcessorOptions: { discardComments: { removeAll: true } },
      canPrint: true
    }),
    new UglifyJsPlugin({
      cacheFolder: path.resolve(__dirname, "../public/dist/cached_uglify/"),
      debug: true,
      minimize: true,
      sourceMap: true,
      screw_ie8: true,
      output: { comments: false },
      compressor: { warnings: false }
    }),
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify("production")
      }
    })
  ],
  node: {
    fs: "empty"
  }
};
var accessToken = process.env.ROLLBAR_ACCESS_TOKEN
if (accessToken) {
  console.log("============= PERFORMING ROLLBAR CONFIG")
  var RollbarSourceMapPlugin = require('rollbar-sourcemap-webpack-plugin')
  var version = process.env.BUILT_AT || process.env.HEROKU_SLUG_COMMIT || "????"
  var plugin = new RollbarSourceMapPlugin({accessToken, version, publicPath})
  conf.plugins.push(plugin)
} else {
  console.log("============= SKIPPING ROLLBAR CONFIG")
}
module.exports = conf;
<|endoftext|>


<commit_before>'use strict';

// MAIN //

/**
* Returns the mode of a beta distribution.
*
* @param {PositiveNumber} alpha - first shape parameter
* @param {PositiveNumber} beta - second shape parameter
* @returns {PositiveNumber} mode
*
* @example
* var v = mode( 4.0, 12.0 );
* // returns ~0.214
* @example
* var v = mode( 8.0, 2.0 );
* // returns ~0.875
* @example
* var v = mode( 1.0, 1.0 );
* // returns NaN
* @example
* var v = mode( 2.0, 0.8 );
* // returns NaN
* @example
* var v = mode( -0.1, 2.0 );
* // returns NaN
* @example
* var v = mode( 2.0, NaN );
* // returns NaN
* @example
* var v = mode( NaN, 2.0 );
* // returns NaN
*/
function mode( alpha, beta ) {
	var out;

	if ( alpha <= 1.0 || beta <= 1.0 ) {
		return NaN;
	}
	out = alpha - 1.0;
	out /= alpha + beta - 2.0;
	return out;
} // end FUNCTION mode()

// EXPORTS //

module.exports = mode;
<commit_msg>Simplify<commit_after>'use strict';

// MAIN //

/**
* Returns the mode of a beta distribution.
*
* @param {PositiveNumber} alpha - first shape parameter
* @param {PositiveNumber} beta - second shape parameter
* @returns {PositiveNumber} mode
*
* @example
* var v = mode( 4.0, 12.0 );
* // returns ~0.214
* @example
* var v = mode( 8.0, 2.0 );
* // returns ~0.875
* @example
* var v = mode( 1.0, 1.0 );
* // returns NaN
* @example
* var v = mode( 2.0, 0.8 );
* // returns NaN
* @example
* var v = mode( -0.1, 2.0 );
* // returns NaN
* @example
* var v = mode( 2.0, NaN );
* // returns NaN
* @example
* var v = mode( NaN, 2.0 );
* // returns NaN
*/
function mode( alpha, beta ) {
	if ( alpha <= 1.0 || beta <= 1.0 ) {
		return NaN;
	}
	return ( alpha-1.0 ) / ( alpha+beta-2.0 );
} // end FUNCTION mode()

// EXPORTS //

module.exports = mode;
<|endoftext|>


<commit_before>/* Wrappers, so if we decide to change to UTC, etc. we can do it here. */
function padLeft(input, width, padChar)
{
    if (typeof input != typeof "")
        input = input.toString();

    while (input.length < width)
    {
        input = padChar.toString() +
                  input.toString();
    }
    return input;
}

function prettyParse(dateString)
{
    return moment(dateString, moment.ISO_8601);
}

function prettyDate(date)
{
    if (typeof date == typeof "")
        date = prettyParse(date);

    return time.format("dddd, MMMM Do YYYY")
}

function prettyTime(time)
{
    if (typeof time == typeof "")
        time = prettyParse(time);

    return time.format("HH:mm");
}

function prettyDateTime (datetime)
{
    if (typeof datetime == typeof "")
        datetime = prettyParse(datetime);

    return prettyDate(datetime) + ", " +
        prettyTime(datetime);
}

function prettyGetDate (date)
{
    return date.date();
}
function prettySetDate (date, value)
{
    date.date(value);
}

function prettyGetMonth (date)
{
    return date.month();
}
function prettySetMonth (date, value)
{
    date.month(value);
}

function prettyGetFullYear (date)
{
    return date.year();
}
function prettySetFullYear (date, value)
{
    date.year(value);
}

function prettyGetHours (date)
{
    return date.hours();
}
function prettySetHours (date, value)
{
    date.hours(value);
}

function prettyGetMinutes (date)
{
    return date.minutes();
}
function prettySetMinutes (date, value)
{
    date.minutes(value);
}

function prettyGetDay (date)
{
    return date.day();
}
<commit_msg>Whoops, wrong variable name used....<commit_after>/* Wrappers, so if we decide to change to UTC, etc. we can do it here. */
function padLeft(input, width, padChar)
{
    if (typeof input != typeof "")
        input = input.toString();

    while (input.length < width)
    {
        input = padChar.toString() +
                  input.toString();
    }
    return input;
}

function prettyParse(dateString)
{
    return moment(dateString, moment.ISO_8601);
}

function prettyDate(date)
{
    if (typeof date == typeof "")
        date = prettyParse(date);

    return date.format("dddd, MMMM Do YYYY")
}

function prettyTime(time)
{
    if (typeof time == typeof "")
        time = prettyParse(time);

    return time.format("HH:mm");
}

function prettyDateTime (datetime)
{
    if (typeof datetime == typeof "")
        datetime = prettyParse(datetime);

    return prettyDate(datetime) + ", " +
        prettyTime(datetime);
}

function prettyGetDate (date)
{
    return date.date();
}
function prettySetDate (date, value)
{
    date.date(value);
}

function prettyGetMonth (date)
{
    return date.month();
}
function prettySetMonth (date, value)
{
    date.month(value);
}

function prettyGetFullYear (date)
{
    return date.year();
}
function prettySetFullYear (date, value)
{
    date.year(value);
}

function prettyGetHours (date)
{
    return date.hours();
}
function prettySetHours (date, value)
{
    date.hours(value);
}

function prettyGetMinutes (date)
{
    return date.minutes();
}
function prettySetMinutes (date, value)
{
    date.minutes(value);
}

function prettyGetDay (date)
{
    return date.day();
}
<|endoftext|>


<commit_before>// @flow
import ClientApi from 'daedalus-client-api';
import { action } from 'mobx';
import { ipcRenderer } from 'electron';
import Log from 'electron-log';
import BigNumber from 'bignumber.js';
import Wallet from '../domain/Wallet';
import WalletTransaction from '../domain/WalletTransaction';
import type {
  createWalletRequest,
  getTransactionsRequest,
  createTransactionRequest,
  walletRestoreRequest,
  walletUpdateRequest,
  redeemAdaRequest,
  redeemPaperVendedAdaRequest,
  importKeyRequest,
  deleteWalletRequest
} from './index';
import {
  // ApiMethodNotYetImplementedError,
  GenericApiError,
  WalletAlreadyRestoredError,
  RedeemAdaError,
  WalletKeyImportError,
  NotEnoughMoneyToSendError
} from './errors';
import type { AssuranceModeOption } from '../types/transactionAssuranceTypes';
import { LOVELACES_PER_ADA } from '../config/numbersConfig';

// const notYetImplemented = () => new Promise((_, reject) => {
//   reject(new ApiMethodNotYetImplementedError());
// });

// Commented out helper code for testing async APIs
// (async () => {
//   const result = await ClientApi.nextUpdate();
//   console.log('nextUpdate', result);
// })();

// Commented out helper code for testing sync APIs
// (() => {
//   const result = ClientApi.isValidRedeemCode('HSoXEnt9X541uHvtzBpy8vKfTo1C9TkAX3wat2c6ikg=');
//   console.log('isValidRedeemCode', result);
// })();

export default class CardanoClientApi {

  notifyCallbacks = [];

  constructor() {
    ClientApi.notify(this._onNotify, this._onNotifyError);
  }

  notify(onSuccess: Function, onError: Function = () => {}) {
    this.notifyCallbacks.push({ message: onSuccess, error: onError });
  }

  reset() {
    this.notifyCallbacks = [];
  }

  async getWallets() {
    Log.debug('CardanoClientApi::getWallets called');
    const response = await ClientApi.getWallets();
    return response.map(data => _createWalletFromServerData(data));
  }

  async getTransactions(request: getTransactionsRequest) {
    const { walletId, searchTerm, skip, limit } = request;
    Log.debug('CardanoClientApi::getTransactions called with', request);
    const history = await ClientApi.searchHistory(walletId, searchTerm, skip, limit);
    return new Promise((resolve) => resolve({
      transactions: history[0].map(data => _createTransactionFromServerData(data, walletId)),
      total: history[1]
    }));
  }

  async createWallet(request: createWalletRequest) {
    Log.debug('CardanoClientApi::createWallet called with', request);
    const response = await ClientApi.newWallet('CWTPersonal', 'ADA', request.name, request.mnemonic);
    return _createWalletFromServerData(response);
  }

  async deleteWallet(request: deleteWalletRequest) {
    try {
      await ClientApi.deleteWallet(request.walletId);
      return true;
    } catch (error) {
      throw new GenericApiError();
    }
  }

  async createTransaction(request: createTransactionRequest) {
    Log.debug('CardanoClientApi::createTransaction called with', request);
    const { sender, receiver, amount, currency } = request;
    const description = 'no description provided';
    const title = 'no title provided';
    try {
      const response = await ClientApi.sendExtended(
        sender, receiver, amount, currency, title, description
      );
      return _createTransactionFromServerData(response);
    } catch (error) {
      if (error.message.includes('Not enough money to send')) {
        throw new NotEnoughMoneyToSendError();
      }
      throw new GenericApiError();
    }
  }

  isValidAddress(currency: string, address: string): Promise<bool> {
    return ClientApi.isValidAddress(currency, address);
  }

  isValidMnemonic(mnemonic: string): Promise<bool> { // eslint-disable-line
    return ClientApi.isValidMnemonic(12, mnemonic);
  }

  isValidRedemptionKey(mnemonic: string): Promise<bool> {
    return ClientApi.isValidRedeemCode(mnemonic);
  }

  isValidPostVendRedeemCode(redeemCode: string): Promise<bool> {
    return ClientApi.isValidPostVendRedeemCode(redeemCode);
  }

  isValidRedemptionMnemonic(mnemonic: string): Promise<bool> {
    return ClientApi.isValidMnemonic(9, mnemonic);
  }

  getWalletRecoveryPhrase() {
    return new Promise((resolve) => resolve(ClientApi.generateMnemonic().split(' ')));
  }

  async restoreWallet(request: walletRestoreRequest) {
    const { recoveryPhrase, walletName } = request;
    Log.debug('CardanoClientApi::restoreWallet called with', request);
    try {
      const restoredWallet = await ClientApi.restoreWallet('CWTPersonal', 'ADA', walletName, recoveryPhrase);
      return _createWalletFromServerData(restoredWallet);
    } catch (error) {
      Log.error(error);
      // TODO: backend will return something different here, if multiple wallets
      // are restored from the key and if there are duplicate wallets we will get
      // some kind of error and present the user with message that some wallets
      // where not imported/restored if some where. if no wallets are imported
      // we will error out completely with throw block below
      if (error.message.includes('Wallet with that mnemonics already exists')) {
        throw new WalletAlreadyRestoredError();
      }
      // We don't know what the problem was -> throw generic error
      throw new GenericApiError();
    }
  }

  async importWalletFromKey(request: importKeyRequest) {
    Log.debug('CardanoClientApi::importWalletFromKey called with', request);
    try {
      const importedWallet = await ClientApi.importKey(request.filePath);
      return _createWalletFromServerData(importedWallet);
    } catch (error) {
      console.error(error);
      if (error.message.includes('Wallet with that mnemonics already exists')) {
        throw new WalletAlreadyRestoredError();
      }
      throw new WalletKeyImportError();
    }
  }

  async redeemAda(request: redeemAdaRequest) {
    const { redemptionCode, walletId } = request;
    Log.debug('CardanoClientApi::redeemAda called with', request);
    try {
      const response: ServerWalletStruct = await ClientApi.redeemADA(redemptionCode, walletId);
      return _createTransactionFromServerData(response);
    } catch (error) {
      console.error(error);
      throw new RedeemAdaError();
    }
  }

  async redeemPaperVendedAda(request: redeemPaperVendedAdaRequest) {
    const { shieldedRedemptionKey, mnemonics, walletId } = request;
    console.log('REQUEST', request);
    Log.debug('CardanoClientApi::redeemPaperVendedAda called with', request);
    try {
      const response: ServerWalletStruct =
        await ClientApi.postVendRedeemADA(shieldedRedemptionKey, mnemonics, walletId);
      console.log('RESPONSE', response);
      return _createTransactionFromServerData(response);
    } catch (error) {
      console.error(error);
      throw new RedeemAdaError();
    }
  }

  generateMnemonic() {
    return ClientApi.generateMnemonic().split(' ');
  }

  // PRIVATE

  _onNotify = (rawMessage: string) => {
    Log.debug('CardanoClientApi::notify message: ', rawMessage);
    // TODO: "ConnectionClosed" messages are not JSON parsable … so we need to catch that case here!
    let message = rawMessage;
    if (message !== 'ConnectionClosed') {
      message = JSON.parse(rawMessage);
    }
    this.notifyCallbacks.forEach(cb => cb.message(message));
  };

  _onNotifyError = (error: Error) => {
    Log.debug('CardanoClientApi::notify error: ', error);
    this.notifyCallbacks.forEach(cb => cb.error(error));
  };

  async nextUpdate() {
    Log.debug('CardanoClientApi::nextUpdate called');
    let nextUpdate = null;
    try {
      nextUpdate = JSON.parse(await ClientApi.nextUpdate());
      Log.debug('CardanoClientApi::nextUpdate returned', nextUpdate);
    } catch (error) {
      Log.debug(error);
      // TODO: Api is trowing an error when update is not available, handle other errors
    }
    return nextUpdate;
    // TODO: remove hardcoded response after node update is tested
    // nextUpdate = {
    //   cuiSoftwareVersion: {
    //     svAppName: {
    //       getApplicationName: "cardano"
    //     },
    //     svNumber: 1
    //   },
    //   cuiBlockVesion: {
    //     bvMajor: 0,
    //     bvMinor: 1,
    //     bvAlt: 0
    //   },
    //   cuiScriptVersion: 1,
    //   cuiImplicit: false,
    //   cuiVotesFor: 2,
    //   cuiVotesAgainst: 0,
    //   cuiPositiveStake: {
    //     getCoin: 66666
    //   },
    //   cuiNegativeStake: {
    //     getCoin: 0
    //   }
    // };
    // if (nextUpdate && nextUpdate.cuiSoftwareVersion && nextUpdate.cuiSoftwareVersion.svNumber) {
    //   return { version: nextUpdate.cuiSoftwareVersion.svNumber };
    // } else if (nextUpdate) {
    //   return { version: 'Unknown' };
    // }
    // return null;
  }

  async applyUpdate() {
    await ClientApi.applyUpdate();
    ipcRenderer.send('kill-process');
  }

  async getSyncProgress() {
    Log.debug('CardanoClientApi::syncProgress called');
    const response = await ClientApi.syncProgress();
    Log.debug('CardanoClientApi::syncProgress response', response);
    const localDifficulty = response._spLocalCD.getChainDifficulty;
    // In some cases we dont get network difficulty & we need to wait for it from the notify API
    let networkDifficulty = null;
    if (response._spNetworkCD) networkDifficulty = response._spNetworkCD.getChainDifficulty;
    return { localDifficulty, networkDifficulty };
  }

  async setUserLocale(locale: string) {
    try {
      const response = await ClientApi.updateLocale(locale);
      return response.cpLocale;
    } catch (error) {
      throw new GenericApiError();
    }
  }

  async getUserLocale() {
    return await ClientApi.getLocale();
  }

  async updateWallet(request: walletUpdateRequest) {
    const { walletId, type, currency, name, assurance } = request;
    try {
      return await ClientApi.updateWallet(walletId, type, currency, name, assurance, 0);
    } catch (error) {
      throw new GenericApiError();
    }
  }

  testReset() {
    return ClientApi.testReset();
  }
}

type ServerCoinAmountStruct = {
  getCoin: number,
};

type ServerWalletStruct = {
  cwAddress: string,
  cwAmount: ServerCoinAmountStruct,
  cwMeta: {
    cwName: string,
    cwType: string,
    cwCurrency: string,
    cwUnit: number,
    cwAssurance: AssuranceModeOption,
  },
}

type ServerTransactionStruct = {
  ctId: string,
  ctType: {
    tag: string,
    contents: {
      ctmDate: Date,
      ctmTitle: ?string,
      ctmDescription: ?string,
    }
  },
  ctAmount: ServerCoinAmountStruct,
  ctConfirmations: number,
}

// ========== TRANSFORM SERVER DATA INTO FRONTEND MODELS =========

const _createWalletFromServerData = action((data: ServerWalletStruct) => (
  new Wallet({
    id: data.cwAddress,
    address: data.cwAddress,
    amount: new BigNumber(data.cwAmount.getCoin).dividedBy(LOVELACES_PER_ADA),
    type: data.cwMeta.cwType,
    currency: data.cwMeta.cwCurrency,
    name: data.cwMeta.cwName,
    assurance: data.cwMeta.cwAssurance,
  })
));

const _createTransactionFromServerData = action((data: ServerTransactionStruct) => {
  const isOutgoing = data.ctType.tag === 'CTOut';
  const coins = data.ctAmount.getCoin;
  const { ctmTitle, ctmDescription, ctmDate } = data.ctType.contents;
  return new WalletTransaction({
    id: data.ctId,
    title: ctmTitle || isOutgoing ? 'Ada sent' : 'Ada received',
    type: isOutgoing ? 'adaExpend' : 'adaIncome',
    currency: 'ada',
    amount: new BigNumber(isOutgoing ? -1 * coins : coins).dividedBy(LOVELACES_PER_ADA),
    date: new Date(ctmDate * 1000),
    description: ctmDescription || '',
    numberOfConfirmations: data.ctConfirmations,
  });
});
<commit_msg>Add more logging to investigate locale issue<commit_after>// @flow
import ClientApi from 'daedalus-client-api';
import { action } from 'mobx';
import { ipcRenderer } from 'electron';
import Log from 'electron-log';
import BigNumber from 'bignumber.js';
import Wallet from '../domain/Wallet';
import WalletTransaction from '../domain/WalletTransaction';
import type {
  createWalletRequest,
  getTransactionsRequest,
  createTransactionRequest,
  walletRestoreRequest,
  walletUpdateRequest,
  redeemAdaRequest,
  redeemPaperVendedAdaRequest,
  importKeyRequest,
  deleteWalletRequest
} from './index';
import {
  // ApiMethodNotYetImplementedError,
  GenericApiError,
  WalletAlreadyRestoredError,
  RedeemAdaError,
  WalletKeyImportError,
  NotEnoughMoneyToSendError
} from './errors';
import type { AssuranceModeOption } from '../types/transactionAssuranceTypes';
import { LOVELACES_PER_ADA } from '../config/numbersConfig';

// const notYetImplemented = () => new Promise((_, reject) => {
//   reject(new ApiMethodNotYetImplementedError());
// });

// Commented out helper code for testing async APIs
// (async () => {
//   const result = await ClientApi.nextUpdate();
//   console.log('nextUpdate', result);
// })();

// Commented out helper code for testing sync APIs
// (() => {
//   const result = ClientApi.isValidRedeemCode('HSoXEnt9X541uHvtzBpy8vKfTo1C9TkAX3wat2c6ikg=');
//   console.log('isValidRedeemCode', result);
// })();

export default class CardanoClientApi {

  notifyCallbacks = [];

  constructor() {
    ClientApi.notify(this._onNotify, this._onNotifyError);
  }

  notify(onSuccess: Function, onError: Function = () => {}) {
    this.notifyCallbacks.push({ message: onSuccess, error: onError });
  }

  reset() {
    this.notifyCallbacks = [];
  }

  async getWallets() {
    Log.debug('CardanoClientApi::getWallets called');
    const response = await ClientApi.getWallets();
    return response.map(data => _createWalletFromServerData(data));
  }

  async getTransactions(request: getTransactionsRequest) {
    const { walletId, searchTerm, skip, limit } = request;
    Log.debug('CardanoClientApi::getTransactions called with', request);
    const history = await ClientApi.searchHistory(walletId, searchTerm, skip, limit);
    return new Promise((resolve) => resolve({
      transactions: history[0].map(data => _createTransactionFromServerData(data, walletId)),
      total: history[1]
    }));
  }

  async createWallet(request: createWalletRequest) {
    Log.debug('CardanoClientApi::createWallet called with', request);
    const response = await ClientApi.newWallet('CWTPersonal', 'ADA', request.name, request.mnemonic);
    return _createWalletFromServerData(response);
  }

  async deleteWallet(request: deleteWalletRequest) {
    try {
      await ClientApi.deleteWallet(request.walletId);
      return true;
    } catch (error) {
      throw new GenericApiError();
    }
  }

  async createTransaction(request: createTransactionRequest) {
    Log.debug('CardanoClientApi::createTransaction called with', request);
    const { sender, receiver, amount, currency } = request;
    const description = 'no description provided';
    const title = 'no title provided';
    try {
      const response = await ClientApi.sendExtended(
        sender, receiver, amount, currency, title, description
      );
      return _createTransactionFromServerData(response);
    } catch (error) {
      if (error.message.includes('Not enough money to send')) {
        throw new NotEnoughMoneyToSendError();
      }
      throw new GenericApiError();
    }
  }

  isValidAddress(currency: string, address: string): Promise<bool> {
    return ClientApi.isValidAddress(currency, address);
  }

  isValidMnemonic(mnemonic: string): Promise<bool> { // eslint-disable-line
    return ClientApi.isValidMnemonic(12, mnemonic);
  }

  isValidRedemptionKey(mnemonic: string): Promise<bool> {
    return ClientApi.isValidRedeemCode(mnemonic);
  }

  isValidPostVendRedeemCode(redeemCode: string): Promise<bool> {
    return ClientApi.isValidPostVendRedeemCode(redeemCode);
  }

  isValidRedemptionMnemonic(mnemonic: string): Promise<bool> {
    return ClientApi.isValidMnemonic(9, mnemonic);
  }

  getWalletRecoveryPhrase() {
    return new Promise((resolve) => resolve(ClientApi.generateMnemonic().split(' ')));
  }

  async restoreWallet(request: walletRestoreRequest) {
    const { recoveryPhrase, walletName } = request;
    Log.debug('CardanoClientApi::restoreWallet called with', request);
    try {
      const restoredWallet = await ClientApi.restoreWallet('CWTPersonal', 'ADA', walletName, recoveryPhrase);
      return _createWalletFromServerData(restoredWallet);
    } catch (error) {
      Log.error(error);
      // TODO: backend will return something different here, if multiple wallets
      // are restored from the key and if there are duplicate wallets we will get
      // some kind of error and present the user with message that some wallets
      // where not imported/restored if some where. if no wallets are imported
      // we will error out completely with throw block below
      if (error.message.includes('Wallet with that mnemonics already exists')) {
        throw new WalletAlreadyRestoredError();
      }
      // We don't know what the problem was -> throw generic error
      throw new GenericApiError();
    }
  }

  async importWalletFromKey(request: importKeyRequest) {
    Log.debug('CardanoClientApi::importWalletFromKey called with', request);
    try {
      const importedWallet = await ClientApi.importKey(request.filePath);
      return _createWalletFromServerData(importedWallet);
    } catch (error) {
      console.error(error);
      if (error.message.includes('Wallet with that mnemonics already exists')) {
        throw new WalletAlreadyRestoredError();
      }
      throw new WalletKeyImportError();
    }
  }

  async redeemAda(request: redeemAdaRequest) {
    const { redemptionCode, walletId } = request;
    Log.debug('CardanoClientApi::redeemAda called with', request);
    try {
      const response: ServerWalletStruct = await ClientApi.redeemADA(redemptionCode, walletId);
      return _createTransactionFromServerData(response);
    } catch (error) {
      console.error(error);
      throw new RedeemAdaError();
    }
  }

  async redeemPaperVendedAda(request: redeemPaperVendedAdaRequest) {
    const { shieldedRedemptionKey, mnemonics, walletId } = request;
    console.log('REQUEST', request);
    Log.debug('CardanoClientApi::redeemPaperVendedAda called with', request);
    try {
      const response: ServerWalletStruct =
        await ClientApi.postVendRedeemADA(shieldedRedemptionKey, mnemonics, walletId);
      console.log('RESPONSE', response);
      return _createTransactionFromServerData(response);
    } catch (error) {
      console.error(error);
      throw new RedeemAdaError();
    }
  }

  generateMnemonic() {
    return ClientApi.generateMnemonic().split(' ');
  }

  // PRIVATE

  _onNotify = (rawMessage: string) => {
    Log.debug('CardanoClientApi::notify message: ', rawMessage);
    // TODO: "ConnectionClosed" messages are not JSON parsable … so we need to catch that case here!
    let message = rawMessage;
    if (message !== 'ConnectionClosed') {
      message = JSON.parse(rawMessage);
    }
    this.notifyCallbacks.forEach(cb => cb.message(message));
  };

  _onNotifyError = (error: Error) => {
    Log.debug('CardanoClientApi::notify error: ', error);
    this.notifyCallbacks.forEach(cb => cb.error(error));
  };

  async nextUpdate() {
    Log.debug('CardanoClientApi::nextUpdate called');
    let nextUpdate = null;
    try {
      nextUpdate = JSON.parse(await ClientApi.nextUpdate());
      Log.debug('CardanoClientApi::nextUpdate returned', nextUpdate);
    } catch (error) {
      Log.debug(error);
      // TODO: Api is trowing an error when update is not available, handle other errors
    }
    return nextUpdate;
    // TODO: remove hardcoded response after node update is tested
    // nextUpdate = {
    //   cuiSoftwareVersion: {
    //     svAppName: {
    //       getApplicationName: "cardano"
    //     },
    //     svNumber: 1
    //   },
    //   cuiBlockVesion: {
    //     bvMajor: 0,
    //     bvMinor: 1,
    //     bvAlt: 0
    //   },
    //   cuiScriptVersion: 1,
    //   cuiImplicit: false,
    //   cuiVotesFor: 2,
    //   cuiVotesAgainst: 0,
    //   cuiPositiveStake: {
    //     getCoin: 66666
    //   },
    //   cuiNegativeStake: {
    //     getCoin: 0
    //   }
    // };
    // if (nextUpdate && nextUpdate.cuiSoftwareVersion && nextUpdate.cuiSoftwareVersion.svNumber) {
    //   return { version: nextUpdate.cuiSoftwareVersion.svNumber };
    // } else if (nextUpdate) {
    //   return { version: 'Unknown' };
    // }
    // return null;
  }

  async applyUpdate() {
    await ClientApi.applyUpdate();
    ipcRenderer.send('kill-process');
  }

  async getSyncProgress() {
    Log.debug('CardanoClientApi::syncProgress called');
    const response = await ClientApi.syncProgress();
    Log.debug('CardanoClientApi::syncProgress response', response);
    const localDifficulty = response._spLocalCD.getChainDifficulty;
    // In some cases we dont get network difficulty & we need to wait for it from the notify API
    let networkDifficulty = null;
    if (response._spNetworkCD) networkDifficulty = response._spNetworkCD.getChainDifficulty;
    return { localDifficulty, networkDifficulty };
  }

  async setUserLocale(locale: string) {
    try {
      const response = await ClientApi.updateLocale(locale);
      return response.cpLocale;
    } catch (error) {
      throw new GenericApiError();
    }
  }

  async getUserLocale() {
    Log.debug('CardanoClientApi::getLocale called');
    const response = await ClientApi.getLocale();
    Log.debug('CardanoClientApi::getLocale response: ', response);
    return response;
  }

  async updateWallet(request: walletUpdateRequest) {
    const { walletId, type, currency, name, assurance } = request;
    try {
      return await ClientApi.updateWallet(walletId, type, currency, name, assurance, 0);
    } catch (error) {
      throw new GenericApiError();
    }
  }

  testReset() {
    return ClientApi.testReset();
  }
}

type ServerCoinAmountStruct = {
  getCoin: number,
};

type ServerWalletStruct = {
  cwAddress: string,
  cwAmount: ServerCoinAmountStruct,
  cwMeta: {
    cwName: string,
    cwType: string,
    cwCurrency: string,
    cwUnit: number,
    cwAssurance: AssuranceModeOption,
  },
}

type ServerTransactionStruct = {
  ctId: string,
  ctType: {
    tag: string,
    contents: {
      ctmDate: Date,
      ctmTitle: ?string,
      ctmDescription: ?string,
    }
  },
  ctAmount: ServerCoinAmountStruct,
  ctConfirmations: number,
}

// ========== TRANSFORM SERVER DATA INTO FRONTEND MODELS =========

const _createWalletFromServerData = action((data: ServerWalletStruct) => (
  new Wallet({
    id: data.cwAddress,
    address: data.cwAddress,
    amount: new BigNumber(data.cwAmount.getCoin).dividedBy(LOVELACES_PER_ADA),
    type: data.cwMeta.cwType,
    currency: data.cwMeta.cwCurrency,
    name: data.cwMeta.cwName,
    assurance: data.cwMeta.cwAssurance,
  })
));

const _createTransactionFromServerData = action((data: ServerTransactionStruct) => {
  const isOutgoing = data.ctType.tag === 'CTOut';
  const coins = data.ctAmount.getCoin;
  const { ctmTitle, ctmDescription, ctmDate } = data.ctType.contents;
  return new WalletTransaction({
    id: data.ctId,
    title: ctmTitle || isOutgoing ? 'Ada sent' : 'Ada received',
    type: isOutgoing ? 'adaExpend' : 'adaIncome',
    currency: 'ada',
    amount: new BigNumber(isOutgoing ? -1 * coins : coins).dividedBy(LOVELACES_PER_ADA),
    date: new Date(ctmDate * 1000),
    description: ctmDescription || '',
    numberOfConfirmations: data.ctConfirmations,
  });
});
<|endoftext|>


<commit_before>const configuration = require('src/config/configuration').getInstance();
const Cache = require('src/tmdb/cache');
const TMDB = require('src/tmdb/tmdb');
const RequestRepository = require('src/request/request');
const cache = new Cache();
const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
const request = new RequestRepository();

const requestAsTmdb = (type, id) => {
  if (type !== undefined) {
    type = type.toLowerCase();

    if (type === 'movie') {
      return tmdb.movieInfo(id);
    } else if (type === 'show') {
      return tmdb.showInfo(id);
    } else {
      throw new Error("Unprocessable Entity: Invalid type for body parameter 'type'. Allowed values: movie|show");
    }
  }
  throw new Error("tmdbType body parameter not defined. Allowed values: movie|show")

}
/**
 * Controller: Request by id with type param
 * @param {Request} req http request variable
 * @param {Response} res
 * @returns {Callback}
 */
function requestTmdbIdController(req, res) {
  const { id, type } = req.body;

  Promise.resolve()
  .then(() => requestAsTmdb(type, id))
  .then((requesAsTmdb) => request.addTmdb(requesAsTmdb))
  .then(() => res.send({sucess: true, message: 'Request has been submitted.'}))
  .catch((error) => {
    res.status(404).send({ success: false, error: error.message });
  });
}

module.exports = requestTmdbIdController;
<commit_msg>Rewrote posting request controller to handle body parameters and use new requestsRepository function istead of plexRepository functions<commit_after>const configuration = require('src/config/configuration').getInstance();
const Cache = require('src/tmdb/cache');
const TMDB = require('src/tmdb/tmdb');
const RequestRepository = require('src/request/request');
const cache = new Cache();
const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
const request = new RequestRepository();

const tmdbMovieInfo = (id) => {
  return tmdb.movieInfo(id)
}

const tmdbShowInfo = (id) => {
  return tmdb.showInfo(id)
}

/**
 * Controller: Request by id with type param
 * @param {Request} req http request variable
 * @param {Response} res
 * @returns {Callback}
 */
function requestTmdbIdController(req, res) {
  const { id, type } = req.body
  console.log('body', req.body)
  console.log('id & type', id, type)

  const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
  const user_agent = req.headers['user-agent'];
  const user = req.loggedInUser;
  let mediaFunction = undefined

  if (type === 'movie') {
    console.log('movie')
    mediaFunction = tmdbMovieInfo
  } else if (type === 'show') {
    console.log('show')
    mediaFunction = tmdbShowInfo
  } else {
    res.status(422).send({ success: false, error: 'Incorrect type. Allowed types: "movie" or "show"'})
  }

  mediaFunction(id)
    .catch((error) => { console.error(error); res.status(404).send({ success: false, error: 'Id not found' }) })
    .then((tmdbMedia) => request.requestFromTmdb(tmdbMedia, ip, user_agent, user))
    .then(() => res.send({success: true, message: 'Request has been submitted.'}))
    .catch((error) => {
      res.status(501).send({ success: false, error: error.message });
    })
}

module.exports = requestTmdbIdController;
<|endoftext|>