<commit_before>;(function(factory) {
    if (typeof define === 'function' && define.amd) {
        define(['codemirror'], factory); // AMD
    } else {
        window.AnnoMirror = factory(CodeMirror); // Browser
    }
}(function(CM) {
    "use strict";
    var copyObj = function(obj, target, overwrite) {
        if (!target) target = { };
        for (var prop in obj)
            if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
                target[prop] = obj[prop];
        return target;
    };
    var setData = function(node, data) {
        node.data = node.data || { };
        copyObj(data, node.data);
    };
    var Annotation = function(options) {
        this.id = options.id || -1;
        this.start = options.start;
        this.end = options.end;
        this.text = options.text || '';
        this.title = options.title || this.id.toString();
        this.color = options.color || '#333';
        this.data = options.data || { };
        this.nodes = options.nodes || [];
        this.rendered = false;
        if (this.id == -1)           throw "Annotation.constructor requires an id parameter."
        if (this.start == undefined) throw "Annotation.constructor requires a start parameter.";
        if (this.end == undefined)   throw "Annotation.constructor requires an end parameter.";
    };
    Annotation.prototype.update = function(options) {
        this.title = options.title || this.title;
        this.color = options.color || this.color;
        this.data  = options.data  || this.data;
    };
    Annotation.prototype.toHash = function() {
        return {
            id: this.id,
            start: this.start,
            end: this.end,
            color: this.color,
            title: this.title,
            text: this.text,
            data: copyObj(this.data, { })
        };
    };

    var defaults = {
        codeMirror: {
            lineNumbers: true,
            readOnly: true
        }
    };
    var Doc = function(node, options) {
        this.node = node;
        this._settings = copyObj(defaults, options, false);
        this._annotations = [];
        this._annoId = 1;
        this._init();
    };
    // Public methods
    // ----------------------------------------
    Doc.prototype.addAnnotation = function(start, end, options) {
        if (typeof options !== 'object') options = { };
        if (start == undefined || length == undefined)
            throw 'Start and end offsets must be provided for \'addAnnotation\'.';
        if (typeof start == 'number') start = this._editor.posFromIndex(start);
        if (typeof end   == 'number') end   = this._editor.posFromIndex(end);
        var anno = new Annotation({
            id: options.id || this._annoId++,
            start: this._editor.indexFromPos(start),
            end: this._editor.indexFromPos(end),
            title: options.title,
            color: options.color,
            text: this._editor.getRange(start, end),
            data: options.data
        });
        this._annotations.push(anno);
        var viewport = this._editor.getViewport();
        if (viewport.from <= start.line && start.line <= viewport.to)
            this._displayAnnotation(anno);
        return anno;
    };
    Doc.prototype.getAnnotations = function() {
        var annos = [];
        for (var i = 0; i < this._annotations.length; i++)
            annos.push(this._annotations[i].toHash());
        return annos;
    };
    Doc.prototype.editAnnotation = function(anno, options) {
        if (!anno) throw "An annotation instance must be provided to 'editAnnotation'.";
        // Update the annotation properties.
        anno.update(options);
        // Update the annotation elements
        for (var i = 0; i < anno.nodes.length; i++) {
            var node = anno.nodes[i];
            node.getElementsByClassName("text")[0].innerHTML = anno.title;
            node.getElementsByClassName("indicator")[0].style['background-color'] = anno.color;
            var arrows = node.getElementsByClassName('arrow');
            if (arrows.length > 0)
                for (var j = 0; j < arrows.length; j++)
                    arrows[j].style['border-color'] = anno.color;
        }
    };
    Doc.prototype.removeAnnotation = function(annoId) {
        if (annoId === undefined) throw "An annotation id must be provided to 'removeAnnotation'.";
        var idx;
        for (var i = 0; i < this._annotations.length; i++)
            if (this._annotations[i].id === annoId) {
                idx = i; break;
            }
        if (idx == undefined) return false;
        var anno = this._annotations.splice(idx, 1)[0];
        for (var i = 0; i < anno.nodes.length; i++) {
            var widget = anno.nodes[i].data.widget;
            var node = anno.nodes[i];
            if (node.parentNode) node.parentNode.removeChild(node);
            // If the line widget has no more annotations then remove it.
            if ($('.annotation', widget.node).length === 0) widget.clear();
        }
        return anno;
    };
    Doc.prototype.removeAllAnnotations = function() {
        // Placeholder
    };
    Doc.prototype.editor = function() { return this._editor; };
    Doc.prototype.destroy = function() {
        this._editor.toTextArea();
    };
    // Private methods
    // ----------------------------------------
    Doc.prototype._init = function() {
        var self = this;
        this._editor = CM.fromTextArea(this.node, this._settings.codeMirror);
        this._gutterWidth = this._editor.getGutterElement().offsetWidth;
        this._charWidth = this._editor.defaultCharWidth();
        this._editor.on('viewportChange', function(inst, from, to) {
            console.log("Rendering viewport " + from + "-" + to);
            for (var i = 0; i < self._annotations.length; i++) {
                var anno = self._annotations[i];
                if (anno.rendered === true) continue;
                var start = self._editor.posFromIndex(anno.start);
                // If the start of the annotation is within the viewport, show it.
                if (from <= start.line && start.line <= to)
                    self._displayAnnotation(anno);
            }
        });
    };
    Doc.prototype._displayAnnotation = function(anno) {
        anno.rendered = true;
        var nodes = [];
        var start = this._editor.posFromIndex(anno.start);
        var end = this._editor.posFromIndex(anno.end);
        // Single line annotation scenario
        if (start.line == end.line) {
            var widget = this._getOrCreateLineWidget(start.line, start.ch, end.ch, anno.title);
            var annoNode = this._displayLabel(anno, widget, start.ch, end.ch);
            if (annoNode) nodes.push(annoNode);
        // Multi-line annotation scenario
        } else {
            for (var i = start.line; i <= end.line; i++) {
                var lineHandle = this._editor.getLineHandle(i);
                if (lineHandle.text == '') continue;
                var annoNode;
                if (i == start.line) {
                    var widget = this._getOrCreateLineWidget(i, start.ch, lineHandle.text.length, anno.title);
                    annoNode = this._displayLabel(anno, widget, start.ch, lineHandle.text.length, false, true);
                } else if (i != start.line && i != end.line) {
                    var widget = this._getOrCreateLineWidget(i, 0, lineHandle.text.length, anno.title);
                    annoNode = this._displayLabel(anno, widget, 0, lineHandle.text.length, true, true);
                } else {
                    var widget = this._getOrCreateLineWidget(i, 0, end.ch, anno.title);
                    annoNode = this._displayLabel(anno, widget, 0, end.ch, true, false);
                }
                if (annoNode) nodes.push(annoNode);
            }
        }
        anno.nodes = nodes;
        this._eventsOnAnno(anno);
        for (var i = 0; i < nodes.length; i++)
            setData(nodes[i], { anno: anno });
    };
    // A label is a part of an annotation.
    Doc.prototype._displayLabel = function(anno, widget, fromCh, toCh, leftArrow, rightArrow) {
        var pxPos = this._getAnnoPos(widget.line.lineNo(), fromCh, toCh, anno.title);
        var annoNode = document.createElement('div');
        var lineNo = widget.line.lineNo();
        var indWidth = this._editor.getRange({ line: lineNo, ch: fromCh }, { line: lineNo, ch: toCh }).length * this._charWidth;
        annoNode.className = 'annotation';
        annoNode.style.left = pxPos.left + 'px';
        annoNode.style.width = pxPos.width + 'px';
        annoNode.innerHTML = [
            '', anno.title, '',
            '

'
        ].join('');
        annoNode.dataset.id = anno.id;
        widget.node.appendChild(annoNode);
        setData(annoNode, {
            widget: widget,
            fromCh: fromCh,
            toCh: toCh
        });
        // Add the multi-line arrows as needed.
        if (leftArrow) {
            var arrowDiv = document.createElement('div');
            arrowDiv.className = 'arrow left';
            arrowDiv.style['border-color'] = anno.color;
            annoNode.getElementsByClassName('indicator')[0].appendChild(arrowDiv);
        }
        if (rightArrow) {
            var arrowDiv = document.createElement('div');
            arrowDiv.className = 'arrow right';
            arrowDiv.style['border-color'] = anno.color;
            annoNode.getElementsByClassName('indicator')[0].appendChild(arrowDiv);
        }
        return annoNode;
    };
    Doc.prototype._getOrCreateLineWidget = function(line, fromCh, toCh, text) {
        var widget = false;
        var lineHandle = this._editor.getLineHandle(line);
        var widgets = lineHandle.widgets || [];
        for (var i = widgets.length - 1; i >= 0; i--) {
            var aWidget = widgets[i];
            var didIntersect = false;
            var nodes = aWidget.node.getElementsByClassName('annotation');
            for (var j = 0; j < nodes.length; j++) {
                var labelNode = nodes[j];
                labelNode.offsetRight = labelNode.offsetLeft + labelNode.offsetWidth;
                var newPos = this._getAnnoPos(lineHandle.lineNo(), fromCh, toCh, text);
                // Checks if the "from" and "to" fall within another label.
                var exactMatch = labelNode.data.fromCh === fromCh &&
                                 labelNode.data.toCh === toCh;
                var fromIntersect = parseInt(labelNode.offsetLeft) < parseInt(newPos.left) &&
                                    parseInt(newPos.left) < parseInt(labelNode.offsetRight);
                var toIntersect   = parseInt(labelNode.offsetLeft) < parseInt(newPos.right) &&
                                    parseInt(newPos.right) < parseInt(labelNode.offsetRight);
                // Checks if the new label "wraps" other labels.
                var subsetIntersect = (parseInt(newPos.left) <= parseInt(labelNode.offsetLeft) &&
                                       parseInt(labelNode.offsetRight) <= parseInt(newPos.right)) ||
                                       (parseInt(newPos.left) <= parseInt(labelNode.offsetLeft) &&
                                       parseInt(labelNode.offsetRight) <= parseInt(newPos.right))
                // If the "to" or "from" of the new label lies inside
                // another label then we don't want to use this lineWidget.
                if (exactMatch || fromIntersect || toIntersect || subsetIntersect) {
                    didIntersect = true; break;
                }
            }
            if (!didIntersect) { widget = aWidget; break; }
        }
        if (widget) return widget;
        var lineNode = document.createElement('div');
        lineNode.className = 'anno-line-widget';
        lineNode.innerHTML = ' ';
        return this._editor.addLineWidget(line, lineNode, {
            above: true,
            insertAt: 0
        });
    };
    Doc.prototype._getAnnoPos = function(line, fromCh, toCh, text) {
        var gutterCharWidth = this._gutterWidth + this._charWidth;
        var pos = {
            left:  this._editor.cursorCoords({ line: line, ch: fromCh }).left - gutterCharWidth,
            right: this._editor.cursorCoords({ line: line, ch: toCh   }).left - gutterCharWidth
        };
        pos.width = pos.right - pos.left;
        // Take the text into account.
        var textWidth = text.length * this._charWidth;
        if (textWidth > pos.width) {
            pos.left -= textWidth / 2 - pos.width / 2;
            pos.width = textWidth;
            pos.right = pos.left + pos.width;
        }
        return pos;
    };
    Doc.prototype._eventsOnAnno = function(anno) {
        var self = this;
        for (var i = 0; i < anno.nodes.length; i++) {
            var mark;
            anno.nodes[i].addEventListener('mouseover', function() {
                mark = self._editor.markText(self._editor.posFromIndex(anno.start),
                                             self._editor.posFromIndex(anno.end),
                                             { className: 'anno-mark-text' });
            });
            anno.nodes[i].addEventListener('mouseout', function() {
                if (mark) mark.clear();
            });
        }
    };
    return {
        fromTextArea: function(node, options) {
            return new Doc(node, options);
        }
    };
}));
<commit_msg>Fixed an issue with lines out of the viewport not displaying properly.<commit_after>;(function(factory) {
    if (typeof define === 'function' && define.amd) {
        define(['codemirror'], factory); // AMD
    } else {
        window.AnnoMirror = factory(CodeMirror); // Browser
    }
}(function(CM) {
    "use strict";
    var copyObj = function(obj, target, overwrite) {
        if (!target) target = { };
        for (var prop in obj)
            if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
                target[prop] = obj[prop];
        return target;
    };
    var setData = function(node, data) {
        node.data = node.data || { };
        copyObj(data, node.data);
    };
    var Annotation = function(options) {
        this.id = options.id || -1;
        this.start = options.start;
        this.end = options.end;
        this.text = options.text || '';
        this.title = options.title || this.id.toString();
        this.color = options.color || '#333';
        this.data = options.data || { };
        this.nodes = options.nodes || [];
        this.rendered = false;
        if (this.id == -1)           throw "Annotation.constructor requires an id parameter."
        if (this.start == undefined) throw "Annotation.constructor requires a start parameter.";
        if (this.end == undefined)   throw "Annotation.constructor requires an end parameter.";
    };
    Annotation.prototype.update = function(options) {
        this.title = options.title || this.title;
        this.color = options.color || this.color;
        this.data  = options.data  || this.data;
    };
    Annotation.prototype.toHash = function() {
        return {
            id: this.id,
            start: this.start,
            end: this.end,
            color: this.color,
            title: this.title,
            text: this.text,
            data: copyObj(this.data, { })
        };
    };

    var defaults = {
        codeMirror: {
            lineNumbers: true,
            readOnly: true
        }
    };
    var Doc = function(node, options) {
        this.node = node;
        this._settings = copyObj(defaults, options, false);
        this._annotations = [];
        this._annoId = 1;
        this._init();
    };
    // Public methods
    // ----------------------------------------
    Doc.prototype.addAnnotation = function(start, end, options) {
        if (typeof options !== 'object') options = { };
        if (start == undefined || length == undefined)
            throw 'Start and end offsets must be provided for \'addAnnotation\'.';
        if (typeof start == 'number') start = this._editor.posFromIndex(start);
        if (typeof end   == 'number') end   = this._editor.posFromIndex(end);
        var anno = new Annotation({
            id: options.id || this._annoId++,
            start: this._editor.indexFromPos(start),
            end: this._editor.indexFromPos(end),
            title: options.title,
            color: options.color,
            text: this._editor.getRange(start, end),
            data: options.data
        });
        this._annotations.push(anno);
        var viewport = this._editor.getViewport();
        if (viewport.from <= start.line && start.line < viewport.to)
            this._displayAnnotation(anno);
        return anno;
    };
    Doc.prototype.getAnnotations = function() {
        var annos = [];
        for (var i = 0; i < this._annotations.length; i++)
            annos.push(this._annotations[i].toHash());
        return annos;
    };
    Doc.prototype.editAnnotation = function(anno, options) {
        if (!anno) throw "An annotation instance must be provided to 'editAnnotation'.";
        // Update the annotation properties.
        anno.update(options);
        // Update the annotation elements
        for (var i = 0; i < anno.nodes.length; i++) {
            var node = anno.nodes[i];
            node.getElementsByClassName("text")[0].innerHTML = anno.title;
            node.getElementsByClassName("indicator")[0].style['background-color'] = anno.color;
            var arrows = node.getElementsByClassName('arrow');
            if (arrows.length > 0)
                for (var j = 0; j < arrows.length; j++)
                    arrows[j].style['border-color'] = anno.color;
        }
    };
    Doc.prototype.removeAnnotation = function(annoId) {
        if (annoId === undefined) throw "An annotation id must be provided to 'removeAnnotation'.";
        var idx;
        for (var i = 0; i < this._annotations.length; i++)
            if (this._annotations[i].id === annoId) {
                idx = i; break;
            }
        if (idx == undefined) return false;
        var anno = this._annotations.splice(idx, 1)[0];
        for (var i = 0; i < anno.nodes.length; i++) {
            var widget = anno.nodes[i].data.widget;
            var node = anno.nodes[i];
            if (node.parentNode) node.parentNode.removeChild(node);
            // If the line widget has no more annotations then remove it.
            if ($('.annotation', widget.node).length === 0) widget.clear();
        }
        return anno;
    };
    Doc.prototype.removeAllAnnotations = function() {
        // Placeholder
    };
    Doc.prototype.editor = function() { return this._editor; };
    Doc.prototype.destroy = function() {
        this._editor.toTextArea();
    };
    // Private methods
    // ----------------------------------------
    Doc.prototype._init = function() {
        var self = this;
        this._editor = CM.fromTextArea(this.node, this._settings.codeMirror);
        this._gutterWidth = this._editor.getGutterElement().offsetWidth;
        this._charWidth = this._editor.defaultCharWidth();
        this._editor.on('viewportChange', function(inst, from, to) {
            console.log("Rendering lines " + from + "-" + to);
            for (var i = 0; i < self._annotations.length; i++) {
                var anno = self._annotations[i];
                if (anno.rendered === true) continue;
                var start = self._editor.posFromIndex(anno.start);
                // If the start of the annotation is within the viewport, show it.
                if (from <= start.line && start.line < to)
                    self._displayAnnotation(anno);
            }
        });
    };
    Doc.prototype._displayAnnotation = function(anno) {
        anno.rendered = true;
        var nodes = [];
        var start = this._editor.posFromIndex(anno.start);
        var end = this._editor.posFromIndex(anno.end);
        // Single line annotation scenario
        if (start.line == end.line) {
            var widget = this._getOrCreateLineWidget(start.line, start.ch, end.ch, anno.title);
            var annoNode = this._displayLabel(anno, widget, start.ch, end.ch);
            if (annoNode) nodes.push(annoNode);
        // Multi-line annotation scenario
        } else {
            for (var i = start.line; i <= end.line; i++) {
                var lineHandle = this._editor.getLineHandle(i);
                if (lineHandle.text == '') continue;
                var annoNode;
                if (i == start.line) {
                    var widget = this._getOrCreateLineWidget(i, start.ch, lineHandle.text.length, anno.title);
                    annoNode = this._displayLabel(anno, widget, start.ch, lineHandle.text.length, false, true);
                } else if (i != start.line && i != end.line) {
                    var widget = this._getOrCreateLineWidget(i, 0, lineHandle.text.length, anno.title);
                    annoNode = this._displayLabel(anno, widget, 0, lineHandle.text.length, true, true);
                } else {
                    var widget = this._getOrCreateLineWidget(i, 0, end.ch, anno.title);
                    annoNode = this._displayLabel(anno, widget, 0, end.ch, true, false);
                }
                if (annoNode) nodes.push(annoNode);
            }
        }
        anno.nodes = nodes;
        this._eventsOnAnno(anno);
        for (var i = 0; i < nodes.length; i++)
            setData(nodes[i], { anno: anno });
    };
    // A label is a part of an annotation.
    Doc.prototype._displayLabel = function(anno, widget, fromCh, toCh, leftArrow, rightArrow) {
        var pxPos = this._getAnnoPos(widget.line.lineNo(), fromCh, toCh, anno.title);
        var annoNode = document.createElement('div');
        var lineNo = widget.line.lineNo();
        var indWidth = this._editor.getRange({ line: lineNo, ch: fromCh }, { line: lineNo, ch: toCh }).length * this._charWidth;
        annoNode.className = 'annotation';
        annoNode.style.left = pxPos.left + 'px';
        annoNode.style.width = pxPos.width + 'px';
        annoNode.innerHTML = [
            '', anno.title, '',
            '

'
        ].join('');
        annoNode.dataset.id = anno.id;
        widget.node.appendChild(annoNode);
        setData(annoNode, {
            widget: widget,
            fromCh: fromCh,
            toCh: toCh
        });
        // Add the multi-line arrows as needed.
        if (leftArrow) {
            var arrowDiv = document.createElement('div');
            arrowDiv.className = 'arrow left';
            arrowDiv.style['border-color'] = anno.color;
            annoNode.getElementsByClassName('indicator')[0].appendChild(arrowDiv);
        }
        if (rightArrow) {
            var arrowDiv = document.createElement('div');
            arrowDiv.className = 'arrow right';
            arrowDiv.style['border-color'] = anno.color;
            annoNode.getElementsByClassName('indicator')[0].appendChild(arrowDiv);
        }
        return annoNode;
    };
    Doc.prototype._getOrCreateLineWidget = function(line, fromCh, toCh, text) {
        var widget = false;
        var lineHandle = this._editor.getLineHandle(line);
        var widgets = lineHandle.widgets || [];
        for (var i = widgets.length - 1; i >= 0; i--) {
            var aWidget = widgets[i];
            var didIntersect = false;
            var nodes = aWidget.node.getElementsByClassName('annotation');
            for (var j = 0; j < nodes.length; j++) {
                var labelNode = nodes[j];
                labelNode.offsetRight = labelNode.offsetLeft + labelNode.offsetWidth;
                var newPos = this._getAnnoPos(lineHandle.lineNo(), fromCh, toCh, text);
                // Checks if the "from" and "to" fall within another label.
                var exactMatch = labelNode.data.fromCh === fromCh &&
                                 labelNode.data.toCh === toCh;
                var fromIntersect = parseInt(labelNode.offsetLeft) < parseInt(newPos.left) &&
                                    parseInt(newPos.left) < parseInt(labelNode.offsetRight);
                var toIntersect   = parseInt(labelNode.offsetLeft) < parseInt(newPos.right) &&
                                    parseInt(newPos.right) < parseInt(labelNode.offsetRight);
                // Checks if the new label "wraps" other labels.
                var subsetIntersect = (parseInt(newPos.left) <= parseInt(labelNode.offsetLeft) &&
                                       parseInt(labelNode.offsetRight) <= parseInt(newPos.right)) ||
                                       (parseInt(newPos.left) <= parseInt(labelNode.offsetLeft) &&
                                       parseInt(labelNode.offsetRight) <= parseInt(newPos.right))
                // If the "to" or "from" of the new label lies inside
                // another label then we don't want to use this lineWidget.
                if (exactMatch || fromIntersect || toIntersect || subsetIntersect) {
                    didIntersect = true; break;
                }
            }
            if (!didIntersect) { widget = aWidget; break; }
        }
        if (widget) return widget;
        var lineNode = document.createElement('div');
        lineNode.className = 'anno-line-widget';
        lineNode.innerHTML = ' ';
        return this._editor.addLineWidget(line, lineNode, {
            above: true,
            insertAt: 0
        });
    };
    Doc.prototype._getAnnoPos = function(line, fromCh, toCh, title) {
        var gutterCharWidth = this._gutterWidth + this._charWidth;
        var pos = {
            left:  this._editor.cursorCoords({ line: line, ch: fromCh }).left - gutterCharWidth,
            right: this._editor.cursorCoords({ line: line, ch: toCh   }).left - gutterCharWidth
        };
        pos.width = pos.right - pos.left;
        // Take the title into account.
        var titleWidth = title.length * this._charWidth;
        if (titleWidth > pos.width) {
            pos.left -= titleWidth / 2 - pos.width / 2;
            pos.width = titleWidth;
            pos.right = pos.left + pos.width;
        }
        return pos;
    };
    Doc.prototype._eventsOnAnno = function(anno) {
        var self = this;
        for (var i = 0; i < anno.nodes.length; i++) {
            var mark;
            anno.nodes[i].addEventListener('mouseover', function() {
                mark = self._editor.markText(self._editor.posFromIndex(anno.start),
                                             self._editor.posFromIndex(anno.end),
                                             { className: 'anno-mark-text' });
            });
            anno.nodes[i].addEventListener('mouseout', function() {
                if (mark) mark.clear();
            });
        }
    };
    return {
        fromTextArea: function(node, options) {
            return new Doc(node, options);
        }
    };
}));
<|endoftext|>


<commit_before>/**
 * Controller for handling basic camera events on a Landmarker.
 *
 * A landmarker in general has complex state - what landmarks are selected,
 * what mesh is being used, lighting arrangements, and so on. The camera's
 * behavior however is simple - in response to certain mouse and touch
 * interactions, the camera is rotated, zoomed, and panned around some sort of
 * target. This class encapsulates this behavior.
 *
 * Takes a camera object as it's first parameter, and optionally a domElement to
 * attach to (if none provided, the document is used).
 *
 * Hooks up the following callbacks to the domElement:
 *
 * - focus(target)  // refocus the camera on a new target
 * - pan(vector)  // pan the camera along a certain vector
 * - zoom(vector)  // zoom the camera along a certain vector
 * - rotate(delta)  // rotate the camera around the target
 *
 * Note that other more complex behaviors (selecting and repositioning landmarks
 * for instance) can disable the Controller temporarily with the enabled
 * property.
 */
var $ = require('jquery');
require('jquery-mousewheel')($);
var _ = require('underscore');
var Backbone = require('backbone');
var THREE = require('three');

var MOUSE_WHEEL_SENSITIVITY = 0.5;
var ROTATION_SENSITIVITY = 0.005;
var PIP_ZOOM_FACTOR = 20.0;

"use strict";

exports.CameraController = function (pCam, oCam, oCamZoom, domElement, IMAGE_MODE) {

    var controller = {};
    _.extend(controller, Backbone.Events);
    var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2 };
    var state = STATE.NONE;  // the current state of the Camera

    // internals
    var enabled = false; // note that we will enable on creation below!
    var tvec = new THREE.Vector3();  // a temporary vector for efficient maths
    var tinput = new THREE.Vector3(); // temp vec used for

    var target = new THREE.Vector3();  // where the camera is looking
    var normalMatrix = new THREE.Matrix3();

    // mouse tracking variables
    var mouseDownPosition = new THREE.Vector2();
    var mousePrevPosition = new THREE.Vector2();

    // Mouses position when in the middle of a click operation.
    var mouseCurrentPosition = new THREE.Vector2();

    // Mouses position hovering over the surface
    var mouseHoverPosition = new THREE.Vector2();
    var mouseMouseDelta = new THREE.Vector2();

    function focus(newTarget) {
        target.copy(newTarget);
        pCam.lookAt(target);
        controller.trigger('change');
    }

    function pan(distance) {
        // first, handle the pCam...
        var oDist = distance.clone();
        normalMatrix.getNormalMatrix(pCam.matrix);
        distance.applyMatrix3(normalMatrix);
        distance.multiplyScalar(distanceToTarget() * 0.001);
        pCam.position.add(distance);
        // TODO should the target change as this?!
        target.add(distance);

        // second, the othoCam
        var o = mousePositionInOrthographicView(oDist);
        // relative x movement * otho width = how much to change horiz
        var deltaH = o.xR * o.width;
        oCam.left += deltaH;
        oCam.right += deltaH;
        // relative y movement * otho height = how much to change vert
        var deltaV = o.yR * o.height;
        oCam.top += deltaV;
        oCam.bottom += deltaV;
        oCam.updateProjectionMatrix();
        controller.trigger('change');
    }

    function zoom(distance) {
        var scalar = distance.z * 0.0007;
        // First, handling the perspective matrix
        normalMatrix.getNormalMatrix(pCam.matrix);
        distance.applyMatrix3(normalMatrix);
        distance.multiplyScalar(distanceToTarget() * 0.001);
        pCam.position.add(distance);

        // Then, the orthographic. In general, we are just going to squeeze in
        // the bounds of the orthographic frustum to zoom.
        console.log(scalar);
        if (oCam.right - oCam.left < 0.001 && scalar < 0) {
            // trying to zoom in and we are already tight. return.
            return
        }

        // Difference must respect aspect ratio, otherwise we will distort
        var a = ((oCam.top - oCam.bottom)) / (oCam.right - oCam.left);

        // find out where the mouse currently is in the view.
        var oM = mousePositionInOrthographicView(mouseHoverPosition);

        // overall difference in height scale is scalar * 2, but we weight
        // where this comes off based on mouse position
        oCam.left   -= (scalar * oM.xR) / (a);
        oCam.right  += (scalar * (1 - oM.xR)) / (a);
        oCam.top    += scalar * oM.yR;
        oCam.bottom -= scalar * (1 - oM.yR);
        if (oCam.left > oCam.right) {
            oCam.left = oCam.right - 0.0001;
        }
        if (oCam.bottom > oCam.top) {
            oCam.bottom = oCam.top - (0.0001 * a);
        }
        oCam.updateProjectionMatrix();
        // call the mouse hover callback manually, he will trigger a change
        // for us. Little nasty, but we mock the event...
        onMouseMoveHover({pageX: mouseHoverPosition.x,
                          pageY: mouseHoverPosition.y});
        //controller.trigger('change');
    }

    function distanceToTarget() {
        return tvec.subVectors(target, pCam.position).length();
    }

    function rotateCamera(delta, camera) {
        var theta, phi, radius;
        var EPS = 0.000001;

        // vector = position - target
        tvec.copy(camera.position).sub(target);
        radius = tvec.length();

        theta = Math.atan2(tvec.x, tvec.z);
        phi = Math.atan2(Math.sqrt(tvec.x * tvec.x + tvec.z * tvec.z),
            tvec.y);
        theta += delta.x;
        phi += delta.y;
        phi = Math.max(EPS, Math.min(Math.PI - EPS, phi));

        // update the vector for the new theta/phi/radius
        tvec.x = radius * Math.sin(phi) * Math.sin(theta);
        tvec.y = radius * Math.cos(phi);
        tvec.z = radius * Math.sin(phi) * Math.cos(theta);

        camera.position.copy(target).add(tvec);
        camera.lookAt(target);
    }

    function rotate(delta) {
        rotateCamera(delta, pCam);
        rotateCamera(delta, oCam);
        rotateCamera(delta, oCamZoom);
        controller.trigger('change');
    }

    // mouse
    function onMouseDown(event) {
        console.log('camera: mousedown');
        if (!enabled) return;
        event.preventDefault();
        mouseDownPosition.set(event.pageX, event.pageY);
        mousePrevPosition.copy(mouseDownPosition);
        mouseCurrentPosition.copy(mousePrevPosition);
        switch (event.button) {
            case 0:
                if (IMAGE_MODE) {
                    state = STATE.PAN;
                } else {
                    state = STATE.ROTATE;
                }
                break;
            case 1:
                state = STATE.ZOOM;
                break;
            case 2:
                state = STATE.PAN;
                break;
        }
        $(document).on('mousemove.camera', onMouseMove);
        // listen once for the mouse up
        $(document).one('mouseup.camera', onMouseUp);
    }

    function onMouseMove(event) {
        event.preventDefault();
        mouseCurrentPosition.set(event.pageX, event.pageY);
        mouseMouseDelta.subVectors(mouseCurrentPosition, mousePrevPosition);

        switch (state) {
            case STATE.ROTATE:
                tinput.copy(mouseMouseDelta);
                tinput.z = 0;
                tinput.multiplyScalar(-ROTATION_SENSITIVITY);
                rotate(tinput);
                break;
            case STATE.ZOOM:
                tinput.set(0, 0, mouseMouseDelta.y);
                zoom(tinput);
                break;
            case STATE.PAN:
                tinput.set(-mouseMouseDelta.x, mouseMouseDelta.y, 0);
                pan(tinput);
                break;
        }

        // now work has been done update the previous position
        mousePrevPosition.copy(mouseCurrentPosition);
    }

    function mousePositionInOrthographicView(v) {
        // convert into relative coordinates (0-1)
        var x = v.x / domElement.width;
        var y = v.y / domElement.height;
        // get the current height and width of the orthographic
        var oWidth = oCam.right - oCam.left;
        var oHeight = oCam.top - oCam.bottom;

        // so in this coordinate ortho matrix is focused around
        var oX = oCam.left + x * oWidth;
        var oY = oCam.bottom + (1 - y) * oHeight;

        return {
            x: oX,
            y: oY,
            xR: x,
            yR: y,
            width: oWidth,
            height: oHeight
        };
    }

    function onMouseMoveHover(event) {
        mouseHoverPosition.set(event.pageX, event.pageY);
        var oM = mousePositionInOrthographicView(mouseHoverPosition);

        // and new bounds are
        var zH = oM.height / PIP_ZOOM_FACTOR;
        // TODO this assumes square PIP image
        var zV = zH;
        // reconstructing bounds is easy...
        oCamZoom.left = oM.x - (zH/2);
        oCamZoom.right = oM.x + (zH/2);
        oCamZoom.top = oM.y + (zV/2);
        oCamZoom.bottom = oM.y - (zV/2);
        oCamZoom.updateProjectionMatrix();
        controller.trigger('change');
    }

    function onMouseUp(event) {
        console.log('camera: up');
        event.preventDefault();
        $(document).off('mousemove.camera');
        state = STATE.NONE;
    }

    function onMouseWheel(event) {
        console.log('camera: mousewheel');
        if (!enabled) return;
        tinput.set(0, 0, (-event.deltaY * MOUSE_WHEEL_SENSITIVITY));
        console.log('wheel: ' + event.deltaY);
        zoom(tinput);
    }

    function disable() {
        console.log('camera: disable');
        enabled = false;
        $(domElement).off('mousedown.camera');
        $(domElement).off('mousewheel.camera');
        $(document).off('mousemove.camera');
    }

    function enable() {
        if (!enabled) {
            console.log('camera: enable');
            enabled = true;
            $(domElement).on('mousedown.camera', onMouseDown);
            $(domElement).on('mousewheel.camera', onMouseWheel);
        }
    }

//    // touch
//    var touch = new THREE.Vector3();
//    var prevTouch = new THREE.Vector3();
//    var prevDistance = null;
//
//    function touchStart(event) {
//        if (!enabled) return;
//        var touches = event.touches;
//        switch (touches.length) {
//            case 2:
//                var dx = touches[0].pageX - touches[1].pageX;
//                var dy = touches[0].pageY - touches[1].pageY;
//                prevDistance = Math.sqrt(dx * dx + dy * dy);
//                break;
//        }
//        prevTouch.set(touches[0].pageX, touches[0].pageY, 0);
//    }
//
//    function touchMove(event) {
//        if (!enabled) return;
//        event.preventDefault();
//        event.stopPropagation();
//        var touches = event.touches;
//        touch.set(touches[0].pageX, touches[0].pageY, 0);
//        switch (touches.length) {
//            case 1:
//                scope.rotate(touch.sub(prevTouch).multiplyScalar(-0.005));
//                break;
//            case 2:
//                var dx = touches[0].pageX - touches[1].pageX;
//                var dy = touches[0].pageY - touches[1].pageY;
//                var distance = Math.sqrt(dx * dx + dy * dy);
//                scope.zoom(new THREE.Vector3(0, 0, prevDistance - distance));
//                prevDistance = distance;
//                break;
//            case 3:
//                scope.pan(touch.sub(prevTouch).setX(-touch.x));
//                break;
//        }
//        prevTouch.set(touches[0].pageX, touches[0].pageY, 0);
//    }
//
//    // TODO renable touch
//    //domElement.addEventListener('touchstart', touchStart, false);
//    //domElement.addEventListener('touchmove', touchMove, false);

    // enable everything on creation
    enable();
    $(domElement).on('mousemove.pip', onMouseMoveHover);

    function resize (w, h) {
        var aspect = w / h;

        // 1. Update the orthographic camera
        if (aspect > 1) {
            // w > h
            oCam.left = -aspect;
            oCam.right = aspect;
            oCam.top = 1;
            oCam.bottom = -1;
        } else {
            // h > w
            oCam.left = -1;
            oCam.right = 1;
            oCam.top = 1 / aspect;
            oCam.bottom = -1 / aspect;
        }
        oCam.updateProjectionMatrix();

        // 2. Update the perceptive camera
        pCam.aspect = aspect;
        pCam.updateProjectionMatrix();
    }

    controller.enable = enable;
    controller.disable = disable;
    controller.resize = resize;

    return controller;
};
<commit_msg>mouse wheel now cross platform on chrome<commit_after>/**
 * Controller for handling basic camera events on a Landmarker.
 *
 * A landmarker in general has complex state - what landmarks are selected,
 * what mesh is being used, lighting arrangements, and so on. The camera's
 * behavior however is simple - in response to certain mouse and touch
 * interactions, the camera is rotated, zoomed, and panned around some sort of
 * target. This class encapsulates this behavior.
 *
 * Takes a camera object as it's first parameter, and optionally a domElement to
 * attach to (if none provided, the document is used).
 *
 * Hooks up the following callbacks to the domElement:
 *
 * - focus(target)  // refocus the camera on a new target
 * - pan(vector)  // pan the camera along a certain vector
 * - zoom(vector)  // zoom the camera along a certain vector
 * - rotate(delta)  // rotate the camera around the target
 *
 * Note that other more complex behaviors (selecting and repositioning landmarks
 * for instance) can disable the Controller temporarily with the enabled
 * property.
 */
var $ = require('jquery');
// Doesn't seem to be helping us...
//require('jquery-mousewheel')($);
var _ = require('underscore');
var Backbone = require('backbone');
var THREE = require('three');

var MOUSE_WHEEL_SENSITIVITY = 0.5;
var ROTATION_SENSITIVITY = 0.005;
var PIP_ZOOM_FACTOR = 20.0;

"use strict";

exports.CameraController = function (pCam, oCam, oCamZoom, domElement, IMAGE_MODE) {

    var controller = {};
    _.extend(controller, Backbone.Events);
    var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2 };
    var state = STATE.NONE;  // the current state of the Camera

    // internals
    var enabled = false; // note that we will enable on creation below!
    var tvec = new THREE.Vector3();  // a temporary vector for efficient maths
    var tinput = new THREE.Vector3(); // temp vec used for

    var target = new THREE.Vector3();  // where the camera is looking
    var normalMatrix = new THREE.Matrix3();

    // mouse tracking variables
    var mouseDownPosition = new THREE.Vector2();
    var mousePrevPosition = new THREE.Vector2();

    // Mouses position when in the middle of a click operation.
    var mouseCurrentPosition = new THREE.Vector2();

    // Mouses position hovering over the surface
    var mouseHoverPosition = new THREE.Vector2();
    var mouseMouseDelta = new THREE.Vector2();

    function focus(newTarget) {
        target.copy(newTarget);
        pCam.lookAt(target);
        controller.trigger('change');
    }

    function pan(distance) {
        // first, handle the pCam...
        var oDist = distance.clone();
        normalMatrix.getNormalMatrix(pCam.matrix);
        distance.applyMatrix3(normalMatrix);
        distance.multiplyScalar(distanceToTarget() * 0.001);
        pCam.position.add(distance);
        // TODO should the target change as this?!
        target.add(distance);

        // second, the othoCam
        var o = mousePositionInOrthographicView(oDist);
        // relative x movement * otho width = how much to change horiz
        var deltaH = o.xR * o.width;
        oCam.left += deltaH;
        oCam.right += deltaH;
        // relative y movement * otho height = how much to change vert
        var deltaV = o.yR * o.height;
        oCam.top += deltaV;
        oCam.bottom += deltaV;
        oCam.updateProjectionMatrix();
        controller.trigger('change');
    }

    function zoom(distance) {
        var scalar = distance.z * 0.0007;
        // First, handling the perspective matrix
        normalMatrix.getNormalMatrix(pCam.matrix);
        distance.applyMatrix3(normalMatrix);
        distance.multiplyScalar(distanceToTarget() * 0.001);
        pCam.position.add(distance);

        // Then, the orthographic. In general, we are just going to squeeze in
        // the bounds of the orthographic frustum to zoom.
        console.log(scalar);
        if (oCam.right - oCam.left < 0.001 && scalar < 0) {
            // trying to zoom in and we are already tight. return.
            return
        }

        // Difference must respect aspect ratio, otherwise we will distort
        var a = ((oCam.top - oCam.bottom)) / (oCam.right - oCam.left);

        // find out where the mouse currently is in the view.
        var oM = mousePositionInOrthographicView(mouseHoverPosition);

        // overall difference in height scale is scalar * 2, but we weight
        // where this comes off based on mouse position
        oCam.left   -= (scalar * oM.xR) / (a);
        oCam.right  += (scalar * (1 - oM.xR)) / (a);
        oCam.top    += scalar * oM.yR;
        oCam.bottom -= scalar * (1 - oM.yR);
        if (oCam.left > oCam.right) {
            oCam.left = oCam.right - 0.0001;
        }
        if (oCam.bottom > oCam.top) {
            oCam.bottom = oCam.top - (0.0001 * a);
        }
        oCam.updateProjectionMatrix();
        // call the mouse hover callback manually, he will trigger a change
        // for us. Little nasty, but we mock the event...
        onMouseMoveHover({pageX: mouseHoverPosition.x,
                          pageY: mouseHoverPosition.y});
        //controller.trigger('change');
    }

    function distanceToTarget() {
        return tvec.subVectors(target, pCam.position).length();
    }

    function rotateCamera(delta, camera) {
        var theta, phi, radius;
        var EPS = 0.000001;

        // vector = position - target
        tvec.copy(camera.position).sub(target);
        radius = tvec.length();

        theta = Math.atan2(tvec.x, tvec.z);
        phi = Math.atan2(Math.sqrt(tvec.x * tvec.x + tvec.z * tvec.z),
            tvec.y);
        theta += delta.x;
        phi += delta.y;
        phi = Math.max(EPS, Math.min(Math.PI - EPS, phi));

        // update the vector for the new theta/phi/radius
        tvec.x = radius * Math.sin(phi) * Math.sin(theta);
        tvec.y = radius * Math.cos(phi);
        tvec.z = radius * Math.sin(phi) * Math.cos(theta);

        camera.position.copy(target).add(tvec);
        camera.lookAt(target);
    }

    function rotate(delta) {
        rotateCamera(delta, pCam);
        rotateCamera(delta, oCam);
        rotateCamera(delta, oCamZoom);
        controller.trigger('change');
    }

    // mouse
    function onMouseDown(event) {
        console.log('camera: mousedown');
        if (!enabled) return;
        event.preventDefault();
        mouseDownPosition.set(event.pageX, event.pageY);
        mousePrevPosition.copy(mouseDownPosition);
        mouseCurrentPosition.copy(mousePrevPosition);
        switch (event.button) {
            case 0:
                if (IMAGE_MODE) {
                    state = STATE.PAN;
                } else {
                    state = STATE.ROTATE;
                }
                break;
            case 1:
                state = STATE.ZOOM;
                break;
            case 2:
                state = STATE.PAN;
                break;
        }
        $(document).on('mousemove.camera', onMouseMove);
        // listen once for the mouse up
        $(document).one('mouseup.camera', onMouseUp);
    }

    function onMouseMove(event) {
        event.preventDefault();
        mouseCurrentPosition.set(event.pageX, event.pageY);
        mouseMouseDelta.subVectors(mouseCurrentPosition, mousePrevPosition);

        switch (state) {
            case STATE.ROTATE:
                tinput.copy(mouseMouseDelta);
                tinput.z = 0;
                tinput.multiplyScalar(-ROTATION_SENSITIVITY);
                rotate(tinput);
                break;
            case STATE.ZOOM:
                tinput.set(0, 0, mouseMouseDelta.y);
                zoom(tinput);
                break;
            case STATE.PAN:
                tinput.set(-mouseMouseDelta.x, mouseMouseDelta.y, 0);
                pan(tinput);
                break;
        }

        // now work has been done update the previous position
        mousePrevPosition.copy(mouseCurrentPosition);
    }

    function mousePositionInOrthographicView(v) {
        // convert into relative coordinates (0-1)
        var x = v.x / domElement.width;
        var y = v.y / domElement.height;
        // get the current height and width of the orthographic
        var oWidth = oCam.right - oCam.left;
        var oHeight = oCam.top - oCam.bottom;

        // so in this coordinate ortho matrix is focused around
        var oX = oCam.left + x * oWidth;
        var oY = oCam.bottom + (1 - y) * oHeight;

        return {
            x: oX,
            y: oY,
            xR: x,
            yR: y,
            width: oWidth,
            height: oHeight
        };
    }

    function onMouseMoveHover(event) {
        mouseHoverPosition.set(event.pageX, event.pageY);
        var oM = mousePositionInOrthographicView(mouseHoverPosition);

        // and new bounds are
        var zH = oM.height / PIP_ZOOM_FACTOR;
        // TODO this assumes square PIP image
        var zV = zH;
        // reconstructing bounds is easy...
        oCamZoom.left = oM.x - (zH/2);
        oCamZoom.right = oM.x + (zH/2);
        oCamZoom.top = oM.y + (zV/2);
        oCamZoom.bottom = oM.y - (zV/2);
        oCamZoom.updateProjectionMatrix();
        controller.trigger('change');
    }

    function onMouseUp(event) {
        console.log('camera: up');
        event.preventDefault();
        $(document).off('mousemove.camera');
        state = STATE.NONE;
    }

    function onMouseWheel(event) {
        console.log('camera: mousewheel');
        if (!enabled) return;
        tinput.set(0, 0, (-event.originalEvent.deltaY * MOUSE_WHEEL_SENSITIVITY));
        zoom(tinput);
    }

    function disable() {
        console.log('camera: disable');
        enabled = false;
        $(domElement).off('mousedown.camera');
        $(domElement).off('wheel.camera');
        $(document).off('mousemove.camera');
    }

    function enable() {
        if (!enabled) {
            console.log('camera: enable');
            enabled = true;
            $(domElement).on('mousedown.camera', onMouseDown);
            $(domElement).on('wheel.camera', onMouseWheel);
        }
    }

//    // touch
//    var touch = new THREE.Vector3();
//    var prevTouch = new THREE.Vector3();
//    var prevDistance = null;
//
//    function touchStart(event) {
//        if (!enabled) return;
//        var touches = event.touches;
//        switch (touches.length) {
//            case 2:
//                var dx = touches[0].pageX - touches[1].pageX;
//                var dy = touches[0].pageY - touches[1].pageY;
//                prevDistance = Math.sqrt(dx * dx + dy * dy);
//                break;
//        }
//        prevTouch.set(touches[0].pageX, touches[0].pageY, 0);
//    }
//
//    function touchMove(event) {
//        if (!enabled) return;
//        event.preventDefault();
//        event.stopPropagation();
//        var touches = event.touches;
//        touch.set(touches[0].pageX, touches[0].pageY, 0);
//        switch (touches.length) {
//            case 1:
//                scope.rotate(touch.sub(prevTouch).multiplyScalar(-0.005));
//                break;
//            case 2:
//                var dx = touches[0].pageX - touches[1].pageX;
//                var dy = touches[0].pageY - touches[1].pageY;
//                var distance = Math.sqrt(dx * dx + dy * dy);
//                scope.zoom(new THREE.Vector3(0, 0, prevDistance - distance));
//                prevDistance = distance;
//                break;
//            case 3:
//                scope.pan(touch.sub(prevTouch).setX(-touch.x));
//                break;
//        }
//        prevTouch.set(touches[0].pageX, touches[0].pageY, 0);
//    }
//
//    // TODO renable touch
//    //domElement.addEventListener('touchstart', touchStart, false);
//    //domElement.addEventListener('touchmove', touchMove, false);

    // enable everything on creation
    enable();
    $(domElement).on('mousemove.pip', onMouseMoveHover);

    function resize (w, h) {
        var aspect = w / h;

        // 1. Update the orthographic camera
        if (aspect > 1) {
            // w > h
            oCam.left = -aspect;
            oCam.right = aspect;
            oCam.top = 1;
            oCam.bottom = -1;
        } else {
            // h > w
            oCam.left = -1;
            oCam.right = 1;
            oCam.top = 1 / aspect;
            oCam.bottom = -1 / aspect;
        }
        oCam.updateProjectionMatrix();

        // 2. Update the perceptive camera
        pCam.aspect = aspect;
        pCam.updateProjectionMatrix();
    }

    controller.enable = enable;
    controller.disable = disable;
    controller.resize = resize;

    return controller;
};
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Jesse van den Kieboom. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following disclaimer
 *      in the documentation and/or other materials provided with the
 *      distribution.
 *    * Neither the name of Google Inc. nor the names of its
 *      contributors may be used to endorse or promote products derived from
 *      this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

require('./glsl-mode');

var glsl = require('../glsl/glsl');
var esprima = require('../vendor/esprima');
var Signals = require('../signals/signals');

window.esprima = esprima;

function Editor(editor, ctx, type) {
    Signals.call(this);

    this.editor = editor;
    this.type = type;

    this._completionContext = {};

    this.options = {
        check_timeout: 300,
        history_size: 100
    };

    var keymap = {
        Tab: function(cm) {
            var spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
            cm.replaceSelection(spaces);
        },

        Backspace: function(cm) {
            var doc = cm.getDoc();
            var cur = doc.getCursor();

            if (!doc.somethingSelected() && cur.ch !== 0) {
                var line = doc.getLine(cur.line);

                var prefix = line.slice(0, cur.ch);

                for (var i = prefix.length - 1; i >= 0; i--) {
                    if (prefix[i] !== ' ') {
                        return CodeMirror.Pass;
                    }
                }

                var m = 0;

                if (cur.ch == line.length) {
                    m = line.length;
                } else {
                    var n = cm.getOption("indentUnit");

                    // all spaces, remove up to N indentUnit
                    m = prefix.length % n;

                    if (m == 0) {
                        m = n;
                    }
                }

                doc.replaceRange('', {line: cur.line, ch: cur.ch - m}, cur);

                if (cur.ch == line.length) {
                    CodeMirror.commands.delCharBefore(cm);
                    cur.line -= 1;
                }

                cm.indentLine(cur.line, null, true);

                return;
            }

            if (cur.ch === 0 && cur.line !== 0) {
                CodeMirror.commands.delCharBefore(cm);
                cm.indentLine(cur.line - 1, null, true);
                return;
            }

            return CodeMirror.Pass;
        },

        Enter: function(cm) {
            var doc = cm.getDoc();

            if (doc.somethingSelected()) {
                doc.replaceSelection('');
            }

            var cur = doc.getCursor();
            var line = doc.getLine(cur.line);

            for (var i = 0; i < line.length; i++) {
                if (line[i] != ' ') {
                    return CodeMirror.Pass;
                }
            }

            doc.replaceRange('', {line: cur.line, ch: 0}, {line: cur.line, ch: line.length});
            return CodeMirror.Pass;
        }
    };

    var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
    var mac = ios || /Mac/.test(navigator.platform);

    if (mac) {
        keymap['Cmd-Left'] = 'goLineLeftSmart';
    }

    if (type === glsl.source.VERTEX || type === glsl.source.FRAGMENT) {
        this._preprocessor_options = glsl.preprocessor.Preprocessor.options_from_context(ctx.gl);

        this.builtins = glsl.builtins.Builtins.create_for_context(ctx.gl, type);

        if (type === glsl.source.VERTEX) {
            this.editor.setOption('mode', 'glslv');
        } else {
            this.editor.setOption('mode', 'glslf');
        }
    } else {
        this.editor.setOption('mode', type);

        if (type === 'javascript') {
            keymap['.'] = (function(cm) {
                cm.replaceSelection('.');
                cm.indentLine(cm.getCursor().line);

                this._hint();
            }).bind(this);

            keymap['Ctrl-Space'] = (function(cm) {
                this._hint();
            }).bind(this);

            var mode = this.editor.getMode();
            var indentOrig = mode.indent;

            mode.indent = function(state, textAfter) {
                var ret = indentOrig.call(this, state, textAfter);

                if (textAfter[0] === '.') {
                    ret += editor.getOption('indentUnit');
                }

                return ret;
            };
        }
    }

    this._change_timeout = 0;

    this._internal_errors = {
        markers: [],
        errors: []
    };

    this._external_errors = {
        markers: [],
        errors: []
    };

    this._error_message = null;

    this.editor.on('change', this._on_change.bind(this));
    this.editor.on('cursorActivity', this._on_cursor_activity.bind(this));

    editor.addKeyMap(keymap);

    this.parsed = null;
    this._on_notify_parsed = this.register_signal('notify::parsed');
}

Editor.prototype = Object.create(Signals.prototype);
Editor.prototype.constructor = Editor;

Editor.prototype._hint = function() {
    this.editor.showHint({
        completeSingle: false,
        context: this._completionContext,
    });
}

Editor.prototype.completionContext = function(context) {
    if (!context) {
        return this._completionContext;
    }

    this._completionContext = context;
}

Editor.prototype._on_cursor_activity = function() {
    var doc = this.editor.getDoc();
    var cursor = this.cursor();
    var marks = doc.findMarksAt(cursor);

    var errs = [];

    for (var i = 0; i < marks.length; i++) {
        var m = marks[i];

        if (m.className === 'error') {
            errs.push(m.error);
        }
    }

    if (this._error_message !== null && (errs.length === 0 || this._error_message.errors[0] !== errs[0])) {
        this._error_message.widget.parentElement.removeChild(this._error_message.widget);
        this._error_message = null;
    }

    if (this._error_message === null && errs.length > 0) {
        var c = document.createElement('div');
        c.classList.add('error-message-container');

        var w = document.createElement('ul');
        w.classList.add('error-message');

        c.appendChild(w);

        for (var i = 0; i < errs.length; i++) {
            var li = document.createElement('li');
            li.textContent = errs[i].formatted_message();

            w.appendChild(li);
        }

        this._error_message = {
            errors: errs,
            widget: c
        };

        this.editor.addWidget({line: cursor.line, ch: 0}, this._error_message.widget, false, 'above');
        c.style.left = '';    }
}

Editor.prototype.focus = function() {
    this.editor.focus();
}

Editor.prototype.cursor = function(v) {
    if (typeof v === 'undefined') {
        return this.editor.getCursor();
    }

    this.editor.setCursor(v);
}

Editor.prototype.value = function(v) {
    var stripper = /[ \t]+$/gm;

    if (typeof v === 'undefined') {
        return this.editor.getValue().replace(stripper, '');
    }

    this.editor.setValue(v.replace(stripper, ''));

    if (this._change_timeout !== 0) {
        clearTimeout(this._change_timeout);
        this._change_timeout = 0;
    }

    this._on_change_timeout();
}

Editor.prototype.history = function(v) {
    if (typeof v === 'undefined') {
        var hist = this.editor.getHistory();

        var ret = {
            done: [],
            undone: []
        };

        if (hist.undone.length > this.options.history_size) {
            ret.undone = hist.undone.slice(0, this.options.history_size);
        } else {
            ret.undone = hist.undone;
        }

        if (ret.undone.length < this.options.history_size) {
            var rem = this.options.history_size - ret.undone.length;

            if (hist.done.length > rem) {
                ret.done = hist.done.slice(0, rem);
            } else {
                ret.done = hist.done;
            }
        }

        return ret;
    }

    this.editor.setHistory(v);
}

Editor.prototype._make_loc = function(l) {
    return {line: l.line - 1, ch: l.column - 1};
}

Editor.prototype._on_change_timeout = function() {
    this._change_timeout = 0;

    if (this.type === glsl.source.VERTEX || this.type === glsl.source.FRAGMENT) {
        this._on_change_timeout_glsl();
    } else {
        this._on_change_timeout_js();
    }
}

Editor.prototype._on_change_timeout_js = function() {
    try {
        esprima.parse(this.value());
    } catch (e) {
        this.runtime_error({
            message: e.description,
            location: {
                line: e.lineNumber,
                column: e.column
            }
        });

        return;
    }

    this._process_errors(this._internal_errors, []);
}

Editor.prototype._on_change_timeout_glsl = function() {
    this.parsed = new glsl.ast.Parser(this.value(), this.type, {
        preprocessor: this._preprocessor_options
    });

    glsl.sst.Annotate(this.parsed, {
        builtins: this.builtins
    });

    this._process_errors(this._internal_errors, this.parsed.errors());
    this._on_notify_parsed();
}

Editor.prototype.runtime_error = function(error) {
    var tok = this.editor.getTokenAt(CodeMirror.Pos(error.location.line - 1, error.location.column));

    var err = {
        location: {
            start: {
                line: error.location.line,
                column: tok.start + 1
            },
            end: {
                line: error.location.line,
                column: tok.end + 1
            }
        },

        message: error.message,

        formatted_message: function() {
            return error.location.line + '.' + (tok.start + 1) + '-' + error.location.line + '.' + (tok.end + 1) + ': ' + error.message;
        }
    };

    this._process_errors(this._internal_errors, [err]);
}

Editor.prototype.external_errors = function(errors) {
    this._process_errors(this._external_errors, errors);
}

Editor.prototype._process_errors = function(ctx, errors) {
    var doc = this.editor.getDoc();

    ctx.errors = errors.slice(0);

    for (var i = 0; i < ctx.markers.length; i++) {
        ctx.markers[i].clear();
    }

    ctx.markers = [];

    if (this._internal_errors.errors.length === 0 && this._external_errors.errors.length === 0) {
        this.editor.display.wrapper.classList.remove('error');
    } else {
        this.editor.display.wrapper.classList.add('error');
    }

    for (var i = 0; i < errors.length; i++) {
        var e = errors[i];
        var l = e.location.start.line - 1;

        var m = doc.markText(this._make_loc(e.location.start),
                             this._make_loc(e.location.end), {
                                className: 'error',
                                title: e.message,
                                inclusiveLeft: false,
                                inclusiveRight: true
                             });

        m.error = e;
        ctx.markers.push(m);
    }

    this._on_cursor_activity();
}

Editor.prototype._on_change = function(c, o) {
    if (this._change_timeout !== 0) {
        clearTimeout(this._change_timeout);
        this._change_timeout = 0;
    }

    this._change_timeout = setTimeout(this._on_change_timeout.bind(this), this.options.check_timeout);
}

module.exports = Editor;

// vi:ts=4:et
<commit_msg>Make Tab indent selection<commit_after>/*
 * Copyright (c) 2014 Jesse van den Kieboom. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following disclaimer
 *      in the documentation and/or other materials provided with the
 *      distribution.
 *    * Neither the name of Google Inc. nor the names of its
 *      contributors may be used to endorse or promote products derived from
 *      this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

require('./glsl-mode');

var glsl = require('../glsl/glsl');
var esprima = require('../vendor/esprima');
var Signals = require('../signals/signals');

window.esprima = esprima;

function Editor(editor, ctx, type) {
    Signals.call(this);

    this.editor = editor;
    this.type = type;

    this._completionContext = {};

    this.options = {
        check_timeout: 300,
        history_size: 100
    };

    var keymap = {
        Tab: function(cm) {
            var start = cm.getCursor('from');
            var end = cm.getCursor('to');

            for (var i = start.line; i <= end.line; i++) {
                cm.indentLine(i);
            }
        },

        Backspace: function(cm) {
            var doc = cm.getDoc();
            var cur = doc.getCursor();

            if (!doc.somethingSelected() && cur.ch !== 0) {
                var line = doc.getLine(cur.line);

                var prefix = line.slice(0, cur.ch);

                for (var i = prefix.length - 1; i >= 0; i--) {
                    if (prefix[i] !== ' ') {
                        return CodeMirror.Pass;
                    }
                }

                var m = 0;

                if (cur.ch == line.length) {
                    m = line.length;
                } else {
                    var n = cm.getOption("indentUnit");

                    // all spaces, remove up to N indentUnit
                    m = prefix.length % n;

                    if (m == 0) {
                        m = n;
                    }
                }

                doc.replaceRange('', {line: cur.line, ch: cur.ch - m}, cur);

                if (cur.ch == line.length) {
                    CodeMirror.commands.delCharBefore(cm);
                    cur.line -= 1;
                }

                cm.indentLine(cur.line, null, true);

                return;
            }

            if (cur.ch === 0 && cur.line !== 0) {
                CodeMirror.commands.delCharBefore(cm);
                cm.indentLine(cur.line - 1, null, true);
                return;
            }

            return CodeMirror.Pass;
        },

        Enter: function(cm) {
            var doc = cm.getDoc();

            if (doc.somethingSelected()) {
                doc.replaceSelection('');
            }

            var cur = doc.getCursor();
            var line = doc.getLine(cur.line);

            for (var i = 0; i < line.length; i++) {
                if (line[i] != ' ') {
                    return CodeMirror.Pass;
                }
            }

            doc.replaceRange('', {line: cur.line, ch: 0}, {line: cur.line, ch: line.length});
            return CodeMirror.Pass;
        }
    };

    var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
    var mac = ios || /Mac/.test(navigator.platform);

    if (mac) {
        keymap['Cmd-Left'] = 'goLineLeftSmart';
    }

    if (type === glsl.source.VERTEX || type === glsl.source.FRAGMENT) {
        this._preprocessor_options = glsl.preprocessor.Preprocessor.options_from_context(ctx.gl);

        this.builtins = glsl.builtins.Builtins.create_for_context(ctx.gl, type);

        if (type === glsl.source.VERTEX) {
            this.editor.setOption('mode', 'glslv');
        } else {
            this.editor.setOption('mode', 'glslf');
        }
    } else {
        this.editor.setOption('mode', type);

        if (type === 'javascript') {
            keymap['.'] = (function(cm) {
                cm.replaceSelection('.');
                cm.indentLine(cm.getCursor().line);

                this._hint();
            }).bind(this);

            keymap['Ctrl-Space'] = (function(cm) {
                this._hint();
            }).bind(this);

            var mode = this.editor.getMode();
            var indentOrig = mode.indent;

            mode.indent = function(state, textAfter) {
                var ret = indentOrig.call(this, state, textAfter);

                if (textAfter[0] === '.') {
                    ret += editor.getOption('indentUnit');
                }

                return ret;
            };
        }
    }

    this._change_timeout = 0;

    this._internal_errors = {
        markers: [],
        errors: []
    };

    this._external_errors = {
        markers: [],
        errors: []
    };

    this._error_message = null;

    this.editor.on('change', this._on_change.bind(this));
    this.editor.on('cursorActivity', this._on_cursor_activity.bind(this));

    editor.addKeyMap(keymap);

    this.parsed = null;
    this._on_notify_parsed = this.register_signal('notify::parsed');
}

Editor.prototype = Object.create(Signals.prototype);
Editor.prototype.constructor = Editor;

Editor.prototype._hint = function() {
    this.editor.showHint({
        completeSingle: false,
        context: this._completionContext,
    });
}

Editor.prototype.completionContext = function(context) {
    if (!context) {
        return this._completionContext;
    }

    this._completionContext = context;
}

Editor.prototype._on_cursor_activity = function() {
    var doc = this.editor.getDoc();
    var cursor = this.cursor();
    var marks = doc.findMarksAt(cursor);

    var errs = [];

    for (var i = 0; i < marks.length; i++) {
        var m = marks[i];

        if (m.className === 'error') {
            errs.push(m.error);
        }
    }

    if (this._error_message !== null && (errs.length === 0 || this._error_message.errors[0] !== errs[0])) {
        this._error_message.widget.parentElement.removeChild(this._error_message.widget);
        this._error_message = null;
    }

    if (this._error_message === null && errs.length > 0) {
        var c = document.createElement('div');
        c.classList.add('error-message-container');

        var w = document.createElement('ul');
        w.classList.add('error-message');

        c.appendChild(w);

        for (var i = 0; i < errs.length; i++) {
            var li = document.createElement('li');
            li.textContent = errs[i].formatted_message();

            w.appendChild(li);
        }

        this._error_message = {
            errors: errs,
            widget: c
        };

        this.editor.addWidget({line: cursor.line, ch: 0}, this._error_message.widget, false, 'above');
        c.style.left = '';    }
}

Editor.prototype.focus = function() {
    this.editor.focus();
}

Editor.prototype.cursor = function(v) {
    if (typeof v === 'undefined') {
        return this.editor.getCursor();
    }

    this.editor.setCursor(v);
}

Editor.prototype.value = function(v) {
    var stripper = /[ \t]+$/gm;

    if (typeof v === 'undefined') {
        return this.editor.getValue().replace(stripper, '');
    }

    this.editor.setValue(v.replace(stripper, ''));

    if (this._change_timeout !== 0) {
        clearTimeout(this._change_timeout);
        this._change_timeout = 0;
    }

    this._on_change_timeout();
}

Editor.prototype.history = function(v) {
    if (typeof v === 'undefined') {
        var hist = this.editor.getHistory();

        var ret = {
            done: [],
            undone: []
        };

        if (hist.undone.length > this.options.history_size) {
            ret.undone = hist.undone.slice(0, this.options.history_size);
        } else {
            ret.undone = hist.undone;
        }

        if (ret.undone.length < this.options.history_size) {
            var rem = this.options.history_size - ret.undone.length;

            if (hist.done.length > rem) {
                ret.done = hist.done.slice(0, rem);
            } else {
                ret.done = hist.done;
            }
        }

        return ret;
    }

    this.editor.setHistory(v);
}

Editor.prototype._make_loc = function(l) {
    return {line: l.line - 1, ch: l.column - 1};
}

Editor.prototype._on_change_timeout = function() {
    this._change_timeout = 0;

    if (this.type === glsl.source.VERTEX || this.type === glsl.source.FRAGMENT) {
        this._on_change_timeout_glsl();
    } else {
        this._on_change_timeout_js();
    }
}

Editor.prototype._on_change_timeout_js = function() {
    try {
        esprima.parse(this.value());
    } catch (e) {
        this.runtime_error({
            message: e.description,
            location: {
                line: e.lineNumber,
                column: e.column
            }
        });

        return;
    }

    this._process_errors(this._internal_errors, []);
}

Editor.prototype._on_change_timeout_glsl = function() {
    this.parsed = new glsl.ast.Parser(this.value(), this.type, {
        preprocessor: this._preprocessor_options
    });

    glsl.sst.Annotate(this.parsed, {
        builtins: this.builtins
    });

    this._process_errors(this._internal_errors, this.parsed.errors());
    this._on_notify_parsed();
}

Editor.prototype.runtime_error = function(error) {
    var tok = this.editor.getTokenAt(CodeMirror.Pos(error.location.line - 1, error.location.column));

    var err = {
        location: {
            start: {
                line: error.location.line,
                column: tok.start + 1
            },
            end: {
                line: error.location.line,
                column: tok.end + 1
            }
        },

        message: error.message,

        formatted_message: function() {
            return error.location.line + '.' + (tok.start + 1) + '-' + error.location.line + '.' + (tok.end + 1) + ': ' + error.message;
        }
    };

    this._process_errors(this._internal_errors, [err]);
}

Editor.prototype.external_errors = function(errors) {
    this._process_errors(this._external_errors, errors);
}

Editor.prototype._process_errors = function(ctx, errors) {
    var doc = this.editor.getDoc();

    ctx.errors = errors.slice(0);

    for (var i = 0; i < ctx.markers.length; i++) {
        ctx.markers[i].clear();
    }

    ctx.markers = [];

    if (this._internal_errors.errors.length === 0 && this._external_errors.errors.length === 0) {
        this.editor.display.wrapper.classList.remove('error');
    } else {
        this.editor.display.wrapper.classList.add('error');
    }

    for (var i = 0; i < errors.length; i++) {
        var e = errors[i];
        var l = e.location.start.line - 1;

        var m = doc.markText(this._make_loc(e.location.start),
                             this._make_loc(e.location.end), {
                                className: 'error',
                                title: e.message,
                                inclusiveLeft: false,
                                inclusiveRight: true
                             });

        m.error = e;
        ctx.markers.push(m);
    }

    this._on_cursor_activity();
}

Editor.prototype._on_change = function(c, o) {
    if (this._change_timeout !== 0) {
        clearTimeout(this._change_timeout);
        this._change_timeout = 0;
    }

    this._change_timeout = setTimeout(this._on_change_timeout.bind(this), this.options.check_timeout);
}

module.exports = Editor;

// vi:ts=4:et
<|endoftext|>


<commit_before>(function () {
  var app = angular.module('engine', ['ui.router', 'ui.bootstrap', 'chieffancypants.loadingBar', 'tableSort']);

  app.controller("HomeController", function($scope, $http) {

    var request = app.api + "version";

    $http.get( request )
      .success(function(data, status, header, config) {

      if (data.length > 0)
        $scope.version = data[0];
      else
        console.log("Error while retrieving database version.");
    })
      .error(function(data, status, header, config) {
      console.log("Error in VERSION $http.get request");
    });

    $scope.showItems = true;
    $scope.showNPCs = true;
    $scope.showQuests = true;
    $scope.showSpells = true;

    var itemPanel = angular.element(document.querySelector("#item-panel"));
    var npcPanel = angular.element(document.querySelector("#npc-panel"));
    var questPanel = angular.element(document.querySelector("#quest-panel"));
    var spellPanel = angular.element(document.querySelector("#spell-panel"));

    var noItemsFound = angular.element(document.querySelector("#no-items-found"));
    var noNPCsFound = angular.element(document.querySelector("#no-npcs-found"));
    var noQuestsFound = angular.element(document.querySelector("#no-quests-found"));
    var noSpellsFound = angular.element(document.querySelector("#no-spells-found"));

    $scope.search = function(searchstr) {

      if (typeof searchstr === 'undefined' || searchstr.length < 4) {
        alert("Please insert a string of at least 4 characters.");
        return;
      }

      itemPanel.addClass("hidden");
      npcPanel.addClass("hidden");
      questPanel.addClass("hidden");
      spellPanel.addClass("hidden");

      noItemsFound.addClass("hidden");
      noNPCsFound.addClass("hidden");
      noQuestsFound.addClass("hidden");
      noSpellsFound.addClass("hidden");

      /* looking for items... */
      request = app.api + "item/template/" + searchstr;

      $http.get( request )
        .success(function(data, status, header, config) {

        $scope.items = data;
        if ($scope.items.length > 0) { // we found items
          itemPanel.removeClass("hidden");
        } else {  // we didn't find any items
          noItemsFound.removeClass("hidden");
        }

      })
        .error(function(data, status, header, config) {
        console.log("Error in ITEM $http.get");
      });

      /* looking for NPCs... */
      var request = app.api + "creature/template/" + searchstr;

      $http.get( request )
        .success(function(data, status, header, config) {

        $scope.npcs = data;
        if ($scope.npcs.length > 0) { // we found npcs
          npcPanel.removeClass("hidden");
        } else {  // we didn't find any npcs
          noNPCsFound.removeClass("hidden");
        }

      })
        .error(function(data, status, header, config) {
        console.log("Error in NPC $http.get");
      });

      /* looking for Quests... */
      var request = app.api + "quest/template/" + searchstr;

      $http.get( request )
        .success(function(data, status, header, config) {

        $scope.quests = data;
        if ($scope.quests.length > 0) { // we found quests
          questPanel.removeClass("hidden");
        } else {  // we didn't find any quests
          noQuestsFound.removeClass("hidden");
        }

      })
        .error(function(data, status, header, config) {
        console.log("Error in QUEST $http.get");
      });

      /* looking for Spells... */
      if (app.spellsApi) {
        var request = app.spellsApi + "?search=" + searchstr;

        $http.get( request )
          .success(function(data, status, header, config) {

          $scope.spells = data;
          if ($scope.spells.length > 0) { // we found spells
            spellPanel.removeClass("hidden");
          } else {  // we didn't find any spells
            noSpellsFound.removeClass("hidden");
          }

        })
          .error(function(data, status, header, config) {
          console.log("Error in SPELL $http.get");
        });
      }

    };

  });

})();
<commit_msg>Added history to the search<commit_after>(function () {
  var app = angular.module('engine', ['ui.router', 'ui.bootstrap', 'chieffancypants.loadingBar', 'tableSort']);

  app.controller("HomeController", function($scope, $rootScope, $http) {

    var request = app.api + "version";

    $scope.searchstr = $rootScope.history;

    $http.get( request )
      .success(function(data, status, header, config) {

      if (data.length > 0)
        $scope.version = data[0];
      else
        console.log("Error while retrieving database version.");
    })
      .error(function(data, status, header, config) {
      console.log("Error in VERSION $http.get request");
    });

    $scope.showItems = true;
    $scope.showNPCs = true;
    $scope.showQuests = true;
    $scope.showSpells = true;

    var itemPanel = angular.element(document.querySelector("#item-panel"));
    var npcPanel = angular.element(document.querySelector("#npc-panel"));
    var questPanel = angular.element(document.querySelector("#quest-panel"));
    var spellPanel = angular.element(document.querySelector("#spell-panel"));

    var noItemsFound = angular.element(document.querySelector("#no-items-found"));
    var noNPCsFound = angular.element(document.querySelector("#no-npcs-found"));
    var noQuestsFound = angular.element(document.querySelector("#no-quests-found"));
    var noSpellsFound = angular.element(document.querySelector("#no-spells-found"));

    $scope.search = function(searchstr) {

      if (typeof searchstr === 'undefined' || searchstr.length < 4) {
        alert("Please insert a string of at least 4 characters.");
        return;
      }

      $rootScope.history = searchstr;

      itemPanel.addClass("hidden");
      npcPanel.addClass("hidden");
      questPanel.addClass("hidden");
      spellPanel.addClass("hidden");

      noItemsFound.addClass("hidden");
      noNPCsFound.addClass("hidden");
      noQuestsFound.addClass("hidden");
      noSpellsFound.addClass("hidden");

      /* looking for items... */
      request = app.api + "item/template/" + searchstr;

      $http.get( request )
        .success(function(data, status, header, config) {

        $scope.items = data;
        if ($scope.items.length > 0) { // we found items
          itemPanel.removeClass("hidden");
        } else {  // we didn't find any items
          noItemsFound.removeClass("hidden");
        }

      })
        .error(function(data, status, header, config) {
        console.log("Error in ITEM $http.get");
      });

      /* looking for NPCs... */
      var request = app.api + "creature/template/" + searchstr;

      $http.get( request )
        .success(function(data, status, header, config) {

        $scope.npcs = data;
        if ($scope.npcs.length > 0) { // we found npcs
          npcPanel.removeClass("hidden");
        } else {  // we didn't find any npcs
          noNPCsFound.removeClass("hidden");
        }

      })
        .error(function(data, status, header, config) {
        console.log("Error in NPC $http.get");
      });

      /* looking for Quests... */
      var request = app.api + "quest/template/" + searchstr;

      $http.get( request )
        .success(function(data, status, header, config) {

        $scope.quests = data;
        if ($scope.quests.length > 0) { // we found quests
          questPanel.removeClass("hidden");
        } else {  // we didn't find any quests
          noQuestsFound.removeClass("hidden");
        }

      })
        .error(function(data, status, header, config) {
        console.log("Error in QUEST $http.get");
      });

      /* looking for Spells... */
      if (app.spellsApi) {
        var request = app.spellsApi + "?search=" + searchstr;

        $http.get( request )
          .success(function(data, status, header, config) {

          $scope.spells = data;
          if ($scope.spells.length > 0) { // we found spells
            spellPanel.removeClass("hidden");
          } else {  // we didn't find any spells
            noSpellsFound.removeClass("hidden");
          }

        })
          .error(function(data, status, header, config) {
          console.log("Error in SPELL $http.get");
        });
      }

    };

  });

})();
<|endoftext|>


<commit_before>(function()
{
	var state = { }, api = APPDOTNET;

	// Standard one coming
	var vendorNamespace = 'net.app.mattflaschen.chess';

	jQuery.support.cors = true;

	function getToken()
	{
		var token, match = window.location.hash.match(/access_token=([^&]*)/);
		if(match)
		{
			token = match[1];
 			if(token)
			{
				return token;
			}
		}

		token = $.cookie('token');
		return token;
	}

	var boardCounter = 0;

	function renderGamePost($boardControlHolder, html, pgn)
	{
		$boardControlHolder.addClass('gamePost');
		var $boardHolder = $('
');
		$boardHolder.prop('id', 'board' + (boardCounter++));

		var beginning = $('', {'class': 'icon-backward', title: 'Beginning'});

		var previous =  $('', {'class': 'icon-step-backward', title: 'Previous'});

		var forward =  $('', {'class': 'icon-step-forward', title: 'Next'});

		function gotoEnd()
		{
			board.transitionTo(board.game.transitions.length);
		}

		var end = $('', {'class': 'icon-forward', title: 'End'});

		var controls = [beginning, previous, forward, end];
		var handlers =
		[
			function()
			{
				board.transitionTo(0);
			},
			function()
			{
				board.transitionBackward();
			},
			function()
			{
				board.transitionForward();
			},
			gotoEnd
		];

		for(var i = 0; i < controls.length; i++)
		{
			controls[i] = $('', {href: '#', 'class': 'btn'}).append(controls[i]).click(handlers[i]);
		}
		var $controlHolder = $('
', {'class': 'controls'}).append(controls);

		var $msg = $('
', {html: html});
		$('span[itemprop=hashtag]').each(function()
		{
			var $this = $(this);
			var hashtag = $this.data('hashtag-name');
			$this.html($('', {href: 'http://appeio.com/?tag=' + hashtag, text: $this.text()}));
		});

		$('span[itemprop=mention]').each(function()
		{
			var $this = $(this);
			var mention = $this.data('mention-name');
			$this.html($('', {href: 'http://appeio.com/' + mention, text: $this.text()}));
		});

		$boardControlHolder.html('').append($boardHolder, $controlHolder, $msg, $('
'));
		var board = $boardHolder.chess({pgn: pgn});
		gotoEnd();
	}

	$(function()
	{
		var token = getToken();
		if(!token)
		{
			var $connectLink = $('#connectContainer a');
			var connectUrl = 'https://alpha.app.net/oauth/authenticate?client_id=gpLxdRy8kwJEmdhHmfD3nfr6CJzXZWe6&response_type=token&redirect_uri=' +
				window.location.protocol + '//' + window.location.host + '&scope=stream%20write_post%20follow%20messages';
			$connectLink.attr('href', connectUrl);
			$('#connectContainer').removeClass('hide');

			return;
		}

		state.token = token;
		$.cookie('token', token);
		api.init(
		{
			access_token: token,
			debug: true,
			no_globals: true
		});

		$('#throbber').ajaxStart(function()
		{
			$(this).show();
		}).ajaxStop(function()
		{
			$(this).hide();
		});

		// https://github.com/appdotnet/api-spec/issues/154, please
		function fetchPosts(postsFetched, callback, isMore, minId)
		{
			if(postsFetched < 2000 && isMore)
			{
				api.stream({include_annotations: 1, count: 200, before_id: minId}).done(function(env)
				{
					console.log(env);
					postsFetched += env.data.length;
					for(var i = 0; i < env.data.length; i++)
					{
						for(var j = 0; j < env.data[i].annotations.length; j++)
						{
							if(env.data[i].annotations[j].type == vendorNamespace)
							{
								callback(env.data[i], env.data[i].annotations[j]);
							}
						}
					}
					fetchPosts(postsFetched, callback, env.meta.more, env.meta.min_id);
				});
			}
		}

		var $holder = $('#gamesFromStream');

		fetchPosts(0, function(post, annotation)
		{
			var $post = $('
');
			$holder.append($post);
			renderGamePost($post, post.html, annotation.value.pgn);
		}, true);

		$('body').removeClass('unauthorized').addClass('authorized');

		$('.modal').on('show', function()
		{
			$(':input', this).val('');
		});

		$('#postGameModal').on('show', function()
		{
			$('#postModalBoard').html('');
		});

		$('#previewGameBtn').click(function()
		{
			renderGamePost($('#postModalBoard'), $('#postModalMsg').val(), $('#postModalPgn').val());
		});

		$('#postGameBtn').click(function()
		{
			var btn = this;
			$(btn).button('loading');
			api.posts($('#postModalMsg').val(), null, true, [
			    {
				    type: vendorNamespace,
				    value:
				    {
				        is_active: $('#postModalBeingPlayed').is(':checked'),
					pgn: $('#postModalPgn').val()
				    }
			    }
			]).always(function()
			{
				$(btn).button('reset');

			});
		});
	});
})();<commit_msg>Fix linkification of hashtags and mentions.<commit_after>(function()
{
	var state = { }, api = APPDOTNET;

	// Standard one coming
	var vendorNamespace = 'net.app.mattflaschen.chess';

	jQuery.support.cors = true;

	function getToken()
	{
		var token, match = window.location.hash.match(/access_token=([^&]*)/);
		if(match)
		{
			token = match[1];
 			if(token)
			{
				return token;
			}
		}

		token = $.cookie('token');
		return token;
	}

	var boardCounter = 0;

	function renderGamePost($boardControlHolder, html, pgn)
	{
		$boardControlHolder.addClass('gamePost');
		var $boardHolder = $('
');
		$boardHolder.prop('id', 'board' + (boardCounter++));

		var beginning = $('', {'class': 'icon-backward', title: 'Beginning'});

		var previous =  $('', {'class': 'icon-step-backward', title: 'Previous'});

		var forward =  $('', {'class': 'icon-step-forward', title: 'Next'});

		function gotoEnd()
		{
			board.transitionTo(board.game.transitions.length);
		}

		var end = $('', {'class': 'icon-forward', title: 'End'});

		var controls = [beginning, previous, forward, end];
		var handlers =
		[
			function()
			{
				board.transitionTo(0);
			},
			function()
			{
				board.transitionBackward();
			},
			function()
			{
				board.transitionForward();
			},
			gotoEnd
		];

		for(var i = 0; i < controls.length; i++)
		{
			controls[i] = $('', {href: '#', 'class': 'btn'}).append(controls[i]).click(handlers[i]);
		}
		var $controlHolder = $('
', {'class': 'controls'}).append(controls);

		var $msg = $('
', {html: html});
		$('span[itemprop=hashtag]', $msg).each(function()
		{
			var $this = $(this);
			var hashtag = $this.data('hashtag-name');
			$this.html($('', {href: 'http://appeio.com/?tag=' + hashtag, text: $this.text()}));
		});

		$('span[itemprop=mention]', $msg).each(function()
		{
			var $this = $(this);
			var mention = $this.data('mention-name');
			$this.html($('', {href: 'http://appeio.com/' + mention, text: $this.text()}));
		});

		$boardControlHolder.html('').append($boardHolder, $controlHolder, $msg, $('
'));
		var board = $boardHolder.chess({pgn: pgn});
		gotoEnd();
	}

	$(function()
	{
		var token = getToken();
		if(!token)
		{
			var $connectLink = $('#connectContainer a');
			var connectUrl = 'https://alpha.app.net/oauth/authenticate?client_id=gpLxdRy8kwJEmdhHmfD3nfr6CJzXZWe6&response_type=token&redirect_uri=' +
				window.location.protocol + '//' + window.location.host + '&scope=stream%20write_post%20follow%20messages';
			$connectLink.attr('href', connectUrl);
			$('#connectContainer').removeClass('hide');

			return;
		}

		state.token = token;
		$.cookie('token', token);
		api.init(
		{
			access_token: token,
			debug: true,
			no_globals: true
		});

		$('#throbber').ajaxStart(function()
		{
			$(this).show();
		}).ajaxStop(function()
		{
			$(this).hide();
		});

		// https://github.com/appdotnet/api-spec/issues/154, please
		function fetchPosts(postsFetched, callback, isMore, minId)
		{
			if(postsFetched < 2000 && isMore)
			{
				api.stream({include_annotations: 1, count: 200, before_id: minId}).done(function(env)
				{
					console.log(env);
					postsFetched += env.data.length;
					for(var i = 0; i < env.data.length; i++)
					{
						for(var j = 0; j < env.data[i].annotations.length; j++)
						{
							if(env.data[i].annotations[j].type == vendorNamespace)
							{
								callback(env.data[i], env.data[i].annotations[j]);
							}
						}
					}
					fetchPosts(postsFetched, callback, env.meta.more, env.meta.min_id);
				});
			}
		}

		var $holder = $('#gamesFromStream');

		fetchPosts(0, function(post, annotation)
		{
			var $post = $('
');
			$holder.append($post);
			renderGamePost($post, post.html, annotation.value.pgn);
		}, true);

		$('body').removeClass('unauthorized').addClass('authorized');

		$('.modal').on('show', function()
		{
			$(':input', this).val('');
		});

		$('#postGameModal').on('show', function()
		{
			$('#postModalBoard').html('');
		});

		$('#previewGameBtn').click(function()
		{
			renderGamePost($('#postModalBoard'), $('#postModalMsg').val(), $('#postModalPgn').val());
		});

		$('#postGameBtn').click(function()
		{
			var btn = this;
			$(btn).button('loading');
			api.posts($('#postModalMsg').val(), null, true, [
			    {
				    type: vendorNamespace,
				    value:
				    {
				        is_active: $('#postModalBeingPlayed').is(':checked'),
					pgn: $('#postModalPgn').val()
				    }
			    }
			]).always(function()
			{
				$(btn).button('reset');

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


<commit_before>if (navigator.userAgent.search("Firefox")) {

  browser.browserAction.onClicked.addListener(function(tab) {
      browser.tabs.create({
          url: "index.html"
      });
  });

  browser.runtime.onInstalled.addListener(function (tab) {
    browser.tabs.create({
        url: "index.html"
    });
  });
}
else {
  chrome.browserAction.onClicked.addListener(function(tab) {
      window.open(chrome.runtime.getURL("index.html"));
  });

  chrome.runtime.onInstalled.addListener(function (tab) {
      window.open(chrome.runtime.getURL("index.html"));
  });
}
<commit_msg>search returns a number<commit_after>if (navigator.userAgent.search("Firefox") !== -1) {

  browser.browserAction.onClicked.addListener(function(tab) {
      browser.tabs.create({
          url: "index.html"
      });
  });

  browser.runtime.onInstalled.addListener(function (tab) {
    browser.tabs.create({
        url: "index.html"
    });
  });

} else {

  chrome.browserAction.onClicked.addListener(function(tab) {
      window.open(chrome.runtime.getURL("index.html"));
  });

  chrome.runtime.onInstalled.addListener(function (tab) {
      window.open(chrome.runtime.getURL("index.html"));
  });

}
<|endoftext|>