<commit_before>/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2012, Ajax.org B.V.
 * 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 Ajax.org B.V. 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 AJAX.ORG B.V. 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.
 *
 * ***** END LICENSE BLOCK ******/

define(function(require, exports, module) {
"use strict";

var HashHandler = require("ace/keyboard/hash_handler").HashHandler;
var AcePopup = require("./ace_popup").AcePopup;
var util = require("ace/autocomplete/util");
var event = require("ace/lib/event");
var lang = require("ace/lib/lang");
var snippetManager = require("ace/snippets").snippetManager;

var Autocomplete = function() {
    this.autoInsert = true;
    this.autoSelect = true;
    this.keyboardHandler = new HashHandler();
    this.keyboardHandler.bindKeys(this.commands);

    this.blurListener = this.blurListener.bind(this);
    this.changeListener = this.changeListener.bind(this);
    this.mousedownListener = this.mousedownListener.bind(this);
    this.mousewheelListener = this.mousewheelListener.bind(this);

    this.changeTimer = lang.delayedCall(function() {
        this.updateCompletions(true);
    }.bind(this))
};

(function() {
    this.gatherCompletionsId = 0;

    this.$init = function() {
        this.popup = new AcePopup(document.body || document.documentElement);
        this.popup.on("click", function(e) {
            this.insertMatch();
            e.stop();
        }.bind(this));
        this.popup.focus = this.editor.focus.bind(this.editor);
    };

    this.openPopup = function(editor, prefix, keepPopupPosition) {
        if (!this.popup)
            this.$init();

        this.popup.setData(this.completions.filtered);

        var renderer = editor.renderer;
        this.popup.setRow(this.autoSelect ? 0 : -1);
        if (!keepPopupPosition) {
            this.popup.setTheme(editor.getTheme());
            this.popup.setFontSize(editor.getFontSize());

            var lineHeight = renderer.layerConfig.lineHeight;

            var pos = renderer.$cursorLayer.getPixelPosition(this.base, true);
            pos.left -= this.popup.getTextLeftOffset();
            pos.left -= 16;

            var rect = editor.container.getBoundingClientRect();
            pos.top += rect.top - renderer.layerConfig.offset;
            pos.left += rect.left - editor.renderer.scrollLeft;
            pos.left += renderer.$gutterLayer.gutterWidth;

            this.popup.show(pos, lineHeight);
        }
    };

    this.detach = function() {
        this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler);
        this.editor.off("changeSelection", this.changeListener);
        this.editor.off("blur", this.blurListener);
        this.editor.off("mousedown", this.mousedownListener);
        this.editor.off("mousewheel", this.mousewheelListener);
        this.changeTimer.cancel();

        if (this.popup && this.popup.isOpen) {
            this.gatherCompletionsId = this.gatherCompletionsId + 1;
        }

        if (this.popup)
            this.popup.hide();

        this.activated = false;
        this.completions = this.base = null;
    };

    this.changeListener = function(e) {
        var cursor = this.editor.selection.lead;
        if (cursor.row != this.base.row || cursor.column < this.base.column) {
            this.detach();
        }
        if (this.activated)
            this.changeTimer.schedule();
        else
            this.detach();
    };

    this.blurListener = function() {
        // we have to check if activeElement is a child of popup because
        // on IE preventDefault doesn't stop scrollbar from being focussed
        var el = document.activeElement;
        if (el != this.editor.textInput.getElement() && el.parentNode != this.popup.container)
            this.detach();
    };

    this.mousedownListener = function(e) {
        this.detach();
    };

    this.mousewheelListener = function(e) {
        this.detach();
    };

    this.goTo = function(where) {
        var row = this.popup.getRow();
        var max = this.popup.session.getLength() - 1;

        switch(where) {
            case "up": row = row <= 0 ? max : row - 1; break;
            case "down": row = row >= max ? -1 : row + 1; break;
            case "start": row = 0; break;
            case "end": row = max; break;
        }

        this.popup.setRow(row);
    };

    this.insertMatch = function(data) {
        if (!data)
            data = this.popup.getData(this.popup.getRow());
        if (!data)
            return false;

        if (data.completer && data.completer.insertMatch) {
            data.completer.insertMatch(this.editor);
        } else {
            if (this.completions.filterText) {
                var ranges = this.editor.selection.getAllRanges();
                for (var i = 0, range; range = ranges[i]; i++) {
                    range.start.column -= this.completions.filterText.length;
                    this.editor.session.remove(range);
                }
            }
            if (data.snippet)
                snippetManager.insertSnippet(this.editor, data.snippet);
            else
                this.editor.execCommand("insertstring", data.value || data);
        }
        this.detach();
    };

    this.commands = {
        "Up": function(editor) { editor.completer.goTo("up"); },
        "Down": function(editor) { editor.completer.goTo("down"); },
        "Ctrl-Up|Ctrl-Home": function(editor) { editor.completer.goTo("start"); },
        "Ctrl-Down|Ctrl-End": function(editor) { editor.completer.goTo("end"); },

        "Esc": function(editor) { editor.completer.detach(); },
        "Space": function(editor) { editor.completer.detach(); editor.insert(" ");},
        "Return": function(editor) { return editor.completer.insertMatch(); },
        "Shift-Return": function(editor) { editor.completer.insertMatch(true); },
        "Tab": function(editor) {
            var result = editor.completer.insertMatch();
            if (!result && !editor.tabstopManager)
                editor.completer.goTo("down");
            else
                return result;
        },

        "PageUp": function(editor) { editor.completer.popup.gotoPageUp(); },
        "PageDown": function(editor) { editor.completer.popup.gotoPageDown(); }
    };

    this.gatherCompletions = function(editor, callback) {
        var session = editor.getSession();
        var pos = editor.getCursorPosition();

        var line = session.getLine(pos.row);
        var prefix = util.retrievePrecedingIdentifier(line, pos.column);

        this.base = editor.getCursorPosition();
        this.base.column -= prefix.length;

        var matches = [];
        var total = editor.completers.length;
        editor.completers.forEach(function(completer, i) {
            completer.getCompletions(editor, session, pos, prefix, function(err, results) {
                if (!err)
                    matches = matches.concat(results);
                // Fetch prefix again, because they may have changed by now
                var pos = editor.getCursorPosition();
                var line = session.getLine(pos.row);
                callback(null, {
                    prefix: util.retrievePrecedingIdentifier(line, pos.column, results[0] && results[0].identifierRegex),
                    matches: matches,
                    finished: (--total === 0)
                });
            });
        });
        return true;
    };

    this.showPopup = function(editor) {
        if (this.editor)
            this.detach();

        this.activated = true;

        this.editor = editor;
        if (editor.completer != this) {
            if (editor.completer)
                editor.completer.detach();
            editor.completer = this;
        }

        editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
        editor.on("changeSelection", this.changeListener);
        editor.on("blur", this.blurListener);
        editor.on("mousedown", this.mousedownListener);
        editor.on("mousewheel", this.mousewheelListener);

        this.updateCompletions();
    };

    this.updateCompletions = function(keepPopupPosition) {
        var _id = this.gatherCompletionsId;
        this.gatherCompletions(this.editor, function(err, results) {
            // Only detach if result gathering is finished
            var doDetach = function() {
                if (!results.finished) return;
                return this.detach();
            }.bind(this);

            var prefix = results.prefix;
            var matches = results && results.matches;

            if (!matches || !matches.length)
                return doDetach();

            // Wrong prefix or wrong session -> ignore
            if (prefix.indexOf(results.prefix) != 0 || _id != this.gatherCompletionsId)
                return;

            this.completions = new FilteredList(matches);
            this.completions.setFilter(prefix);
            var filtered = this.completions.filtered;

            // No results
            if (!filtered.length)
                return doDetach();

            // One result equals to the prefix
            if (filtered.length == 1 && filtered[0].value == prefix && !filtered[0].snippet)
                return doDetach();

            // Autoinsert if one result
            if (this.autoInsert && filtered.length == 1 && !keepPopupPosition)
                return this.insertMatch(filtered[0]);

            this.openPopup(this.editor, prefix, keepPopupPosition);
        }.bind(this));
    };

    this.cancelContextMenu = function() {
        var stop = function(e) {
            this.editor.off("nativecontextmenu", stop);
            if (e && e.domEvent)
                event.stopEvent(e.domEvent);
        }.bind(this);
        setTimeout(stop, 10);
        this.editor.on("nativecontextmenu", stop);
    };

}).call(Autocomplete.prototype);

Autocomplete.startCommand = {
    name: "startAutocomplete",
    exec: function(editor) {
        if (!editor.completer)
            editor.completer = new Autocomplete();
        editor.completer.autoInsert =
        editor.completer.autoSelect = true;
        editor.completer.showPopup(editor);
        // needed for firefox on mac
        editor.completer.cancelContextMenu();
    },
    bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space"
};

var FilteredList = function(array, filterText) {
    this.all = array;
    this.filtered = array;
    this.filterText = filterText || "";
};
(function(){
    this.setFilter = function(str) {
        var matches;
        if (str.length > this.filterText && str.lastIndexOf(this.filterText, 0) === 0)
            matches = this.filtered;
        else
            matches = this.all;

        this.filterText = str;
        matches = this.filterCompletions(matches, this.filterText);
        matches = matches.sort(function(a, b) {
            return b.score - a.score;
        });

        // make unique
        var prev = null;
        matches = matches.filter(function(item){
            var caption = item.value || item.caption || item.snippet;
            if (caption === prev) return false;
            prev = caption;
            return true;
        });

        this.filtered = matches;
    };
    this.filterCompletions = function(items, needle) {
        var results = [];
        var upper = needle.toUpperCase();
        var lower = needle.toLowerCase();
        loop: for (var i = 0, item; item = items[i]; i++) {
            var caption = item.value || item.caption || item.snippet;
            if (!caption) continue;
            var lastIndex = -1;
            var matchMask = 0;
            var penalty = 0;
            var index, distance;
            // caption char iteration is faster in Chrome but slower in Firefox, so lets use indexOf
            for (var j = 0; j < needle.length; j++) {
                // TODO add penalty on case mismatch
                var i1 = caption.indexOf(lower[j], lastIndex + 1);
                var i2 = caption.indexOf(upper[j], lastIndex + 1);
                index = (i1 >= 0) ? ((i2 < 0 || i1 < i2) ? i1 : i2) : i2;
                if (index < 0)
                    continue loop;
                distance = index - lastIndex - 1;
                if (distance > 0) {
                    // first char mismatch should be more sensitive
                    if (lastIndex === -1)
                        penalty += 10;
                    penalty += distance;
                }
                matchMask = matchMask | (1 << index);
                lastIndex = index;
            }
            item.matchMask = matchMask;
            item.exactMatch = penalty ? 0 : 1;
            item.score = (item.score || 0) - penalty;
            results.push(item);
        }
        return results;
    };
}).call(FilteredList.prototype);

exports.Autocomplete = Autocomplete;
exports.FilteredList = FilteredList;

});
<commit_msg>Fix autocomplete after ACE update<commit_after>/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2012, Ajax.org B.V.
 * 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 Ajax.org B.V. 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 AJAX.ORG B.V. 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.
 *
 * ***** END LICENSE BLOCK ******/

define(function(require, exports, module) {
"use strict";

var ID_REGEX = /[a-zA-Z_0-9\$-]|[^\u0000-\u007F]/;

var HashHandler = require("ace/keyboard/hash_handler").HashHandler;
var AcePopup = require("./ace_popup").AcePopup;
var util = require("ace/autocomplete/util");
var event = require("ace/lib/event");
var lang = require("ace/lib/lang");
var snippetManager = require("ace/snippets").snippetManager;

var Autocomplete = function() {
    this.autoInsert = true;
    this.autoSelect = true;
    this.keyboardHandler = new HashHandler();
    this.keyboardHandler.bindKeys(this.commands);

    this.blurListener = this.blurListener.bind(this);
    this.changeListener = this.changeListener.bind(this);
    this.mousedownListener = this.mousedownListener.bind(this);
    this.mousewheelListener = this.mousewheelListener.bind(this);

    this.changeTimer = lang.delayedCall(function() {
        this.updateCompletions(true);
    }.bind(this))
};

(function() {
    this.gatherCompletionsId = 0;

    this.$init = function() {
        this.popup = new AcePopup(document.body || document.documentElement);
        this.popup.on("click", function(e) {
            this.insertMatch();
            e.stop();
        }.bind(this));
        this.popup.focus = this.editor.focus.bind(this.editor);
    };

    this.openPopup = function(editor, prefix, keepPopupPosition) {
        if (!this.popup)
            this.$init();

        this.popup.setData(this.completions.filtered);

        var renderer = editor.renderer;
        this.popup.setRow(this.autoSelect ? 0 : -1);
        if (!keepPopupPosition) {
            this.popup.setTheme(editor.getTheme());
            this.popup.setFontSize(editor.getFontSize());

            var lineHeight = renderer.layerConfig.lineHeight;

            var pos = renderer.$cursorLayer.getPixelPosition(this.base, true);
            pos.left -= this.popup.getTextLeftOffset();
            pos.left -= 16;

            var rect = editor.container.getBoundingClientRect();
            pos.top += rect.top - renderer.layerConfig.offset;
            pos.left += rect.left - editor.renderer.scrollLeft;
            pos.left += renderer.$gutterLayer.gutterWidth;

            this.popup.show(pos, lineHeight);
        }
    };

    this.detach = function() {
        this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler);
        this.editor.off("changeSelection", this.changeListener);
        this.editor.off("blur", this.blurListener);
        this.editor.off("mousedown", this.mousedownListener);
        this.editor.off("mousewheel", this.mousewheelListener);
        this.changeTimer.cancel();

        if (this.popup && this.popup.isOpen) {
            this.gatherCompletionsId = this.gatherCompletionsId + 1;
        }

        if (this.popup)
            this.popup.hide();

        this.activated = false;
        this.completions = this.base = null;
    };

    this.changeListener = function(e) {
        var cursor = this.editor.selection.lead;
        if (cursor.row != this.base.row || cursor.column < this.base.column) {
            this.detach();
        }
        if (this.activated)
            this.changeTimer.schedule();
        else
            this.detach();
    };

    this.blurListener = function() {
        // we have to check if activeElement is a child of popup because
        // on IE preventDefault doesn't stop scrollbar from being focussed
        var el = document.activeElement;
        if (el != this.editor.textInput.getElement() && el.parentNode != this.popup.container)
            this.detach();
    };

    this.mousedownListener = function(e) {
        this.detach();
    };

    this.mousewheelListener = function(e) {
        this.detach();
    };

    this.goTo = function(where) {
        var row = this.popup.getRow();
        var max = this.popup.session.getLength() - 1;

        switch(where) {
            case "up": row = row <= 0 ? max : row - 1; break;
            case "down": row = row >= max ? -1 : row + 1; break;
            case "start": row = 0; break;
            case "end": row = max; break;
        }

        this.popup.setRow(row);
    };

    this.insertMatch = function(data) {
        if (!data)
            data = this.popup.getData(this.popup.getRow());
        if (!data)
            return false;

        if (data.completer && data.completer.insertMatch) {
            data.completer.insertMatch(this.editor);
        } else {
            if (this.completions.filterText) {
                var ranges = this.editor.selection.getAllRanges();
                for (var i = 0, range; range = ranges[i]; i++) {
                    range.start.column -= this.completions.filterText.length;
                    this.editor.session.remove(range);
                }
            }
            if (data.snippet)
                snippetManager.insertSnippet(this.editor, data.snippet);
            else
                this.editor.execCommand("insertstring", data.value || data);
        }
        this.detach();
    };

    this.commands = {
        "Up": function(editor) { editor.completer.goTo("up"); },
        "Down": function(editor) { editor.completer.goTo("down"); },
        "Ctrl-Up|Ctrl-Home": function(editor) { editor.completer.goTo("start"); },
        "Ctrl-Down|Ctrl-End": function(editor) { editor.completer.goTo("end"); },

        "Esc": function(editor) { editor.completer.detach(); },
        "Space": function(editor) { editor.completer.detach(); editor.insert(" ");},
        "Return": function(editor) { return editor.completer.insertMatch(); },
        "Shift-Return": function(editor) { editor.completer.insertMatch(true); },
        "Tab": function(editor) {
            var result = editor.completer.insertMatch();
            if (!result && !editor.tabstopManager)
                editor.completer.goTo("down");
            else
                return result;
        },

        "PageUp": function(editor) { editor.completer.popup.gotoPageUp(); },
        "PageDown": function(editor) { editor.completer.popup.gotoPageDown(); }
    };

    this.gatherCompletions = function(editor, callback) {
        var session = editor.getSession();
        var pos = editor.getCursorPosition();

        var line = session.getLine(pos.row);
        var prefix = util.retrievePrecedingIdentifier(line, pos.column);

        this.base = editor.getCursorPosition();
        this.base.column -= prefix.length;

        var matches = [];
        var total = editor.completers.length;
        editor.completers.forEach(function(completer, i) {
            completer.getCompletions(editor, session, pos, prefix, function(err, results) {
                if (!err)
                    matches = matches.concat(results);
                // Fetch prefix again, because they may have changed by now
                var pos = editor.getCursorPosition();
                var line = session.getLine(pos.row);
                callback(null, {
                    prefix: util.retrievePrecedingIdentifier(line, pos.column, ID_REGEX),
                    matches: matches,
                    finished: (--total === 0)
                });
            });
        });
        return true;
    };

    this.showPopup = function(editor) {
        if (this.editor)
            this.detach();

        this.activated = true;

        this.editor = editor;
        if (editor.completer != this) {
            if (editor.completer)
                editor.completer.detach();
            editor.completer = this;
        }

        editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
        editor.on("changeSelection", this.changeListener);
        editor.on("blur", this.blurListener);
        editor.on("mousedown", this.mousedownListener);
        editor.on("mousewheel", this.mousewheelListener);

        this.updateCompletions();
    };

    this.updateCompletions = function(keepPopupPosition) {
        var _id = this.gatherCompletionsId;
        this.gatherCompletions(this.editor, function(err, results) {
            // Only detach if result gathering is finished
            var doDetach = function() {
                if (!results.finished) return;
                return this.detach();
            }.bind(this);

            var prefix = results.prefix;
            var matches = results && results.matches;

            if (!matches || !matches.length)
                return doDetach();

            // Wrong prefix or wrong session -> ignore
            if (prefix.indexOf(results.prefix) != 0 || _id != this.gatherCompletionsId)
                return;

            this.completions = new FilteredList(matches);
            this.completions.setFilter(prefix);
            var filtered = this.completions.filtered;

            // No results
            if (!filtered.length)
                return doDetach();

            // One result equals to the prefix
            if (filtered.length == 1 && filtered[0].value == prefix && !filtered[0].snippet)
                return doDetach();

            // Autoinsert if one result
            if (this.autoInsert && filtered.length == 1 && !keepPopupPosition)
                return this.insertMatch(filtered[0]);

            this.openPopup(this.editor, prefix, keepPopupPosition);
        }.bind(this));
    };

    this.cancelContextMenu = function() {
        var stop = function(e) {
            this.editor.off("nativecontextmenu", stop);
            if (e && e.domEvent)
                event.stopEvent(e.domEvent);
        }.bind(this);
        setTimeout(stop, 10);
        this.editor.on("nativecontextmenu", stop);
    };

}).call(Autocomplete.prototype);

Autocomplete.startCommand = {
    name: "startAutocomplete",
    exec: function(editor) {
        if (!editor.completer)
            editor.completer = new Autocomplete();
        editor.completer.autoInsert =
        editor.completer.autoSelect = true;
        editor.completer.showPopup(editor);
        // needed for firefox on mac
        editor.completer.cancelContextMenu();
    },
    bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space"
};

var FilteredList = function(array, filterText) {
    this.all = array;
    this.filtered = array;
    this.filterText = filterText || "";
};
(function(){
    this.setFilter = function(str) {
        var matches;
        if (str.length > this.filterText && str.lastIndexOf(this.filterText, 0) === 0)
            matches = this.filtered;
        else
            matches = this.all;

        this.filterText = str;
        matches = this.filterCompletions(matches, this.filterText);
        matches = matches.sort(function(a, b) {
            return b.score - a.score;
        });

        // make unique
        var prev = null;
        matches = matches.filter(function(item){
            var caption = item.value || item.caption || item.snippet;
            if (caption === prev) return false;
            prev = caption;
            return true;
        });

        this.filtered = matches;
    };
    this.filterCompletions = function(items, needle) {
        var results = [];
        var upper = needle.toUpperCase();
        var lower = needle.toLowerCase();
        loop: for (var i = 0, item; item = items[i]; i++) {
            var caption = item.value || item.caption || item.snippet;
            if (!caption) continue;
            var lastIndex = -1;
            var matchMask = 0;
            var penalty = 0;
            var index, distance;
            // caption char iteration is faster in Chrome but slower in Firefox, so lets use indexOf
            for (var j = 0; j < needle.length; j++) {
                // TODO add penalty on case mismatch
                var i1 = caption.indexOf(lower[j], lastIndex + 1);
                var i2 = caption.indexOf(upper[j], lastIndex + 1);
                index = (i1 >= 0) ? ((i2 < 0 || i1 < i2) ? i1 : i2) : i2;
                if (index < 0)
                    continue loop;
                distance = index - lastIndex - 1;
                if (distance > 0) {
                    // first char mismatch should be more sensitive
                    if (lastIndex === -1)
                        penalty += 10;
                    penalty += distance;
                }
                matchMask = matchMask | (1 << index);
                lastIndex = index;
            }
            item.matchMask = matchMask;
            item.exactMatch = penalty ? 0 : 1;
            item.score = (item.score || 0) - penalty;
            results.push(item);
        }
        return results;
    };
}).call(FilteredList.prototype);

exports.Autocomplete = Autocomplete;
exports.FilteredList = FilteredList;

});
<|endoftext|>


<commit_before>import AbstractModel from 'hospitalrun/models/abstract';
import CanEditRequested from 'hospitalrun/mixins/can-edit-requested';
import DS from 'ember-data';
import DateFormat from 'hospitalrun/mixins/date-format';
import Ember from 'ember';
import MedicationDetails from 'hospitalrun/mixins/medication-details';

export default AbstractModel.extend(CanEditRequested, DateFormat, MedicationDetails, {
  inventoryItem: DS.belongsTo('inventory', {
    async: true
  }),
  notes: DS.attr('string'),
  patient: DS.belongsTo('patient', {
    async: false
  }),
  prescription: DS.attr('string'),
  prescriptionDate: DS.attr('date'),
  quantity: DS.attr('number'),
  refills: DS.attr('number'),
  requestedDate: DS.attr('date'),
  requestedBy: DS.attr('string'),
  status: DS.attr('string'),
  visit: DS.belongsTo('visit', {
    async: false
  }),

  isRequested: function() {
    let status = this.get('status');
    return (status === 'Requested');
  }.property('status'),

  medicationName: function() {
    return this.getMedicationName('inventoryItem');
  }.property('medicationTitle', 'inventoryItem'),

  medicationPrice: function() {
    return this.getMedicationPrice('inventoryItem');
  }.property('priceOfMedication', 'inventoryItem'),

  prescriptionDateAsTime: function() {
    return this.dateToTime(this.get('prescriptionDate'));
  }.property('prescriptionDate'),

  requestedDateAsTime: function() {
    return this.dateToTime(this.get('requestedDate'));
  }.property('requestedDate'),

  validations: {
    prescription: {
      acceptance: {
        accept: true,
        if(object) {
          if (!object.get('hasDirtyAttributes') || object.get('isFulfilling')) {
            return false;
          }
          let prescription = object.get('prescription');
          let quantity = object.get('quantity');
          if (Ember.isEmpty(prescription) && Ember.isEmpty(quantity)) {
            // force validation to fail
            return true;
          } else {
            return false;
          }
        },
        message: 'Please enter a prescription or a quantity'
      }
    },

    inventoryItemTypeAhead: {
      acceptance: {
        accept: true,
        if(object) {
          if (!object.get('hasDirtyAttributes') || !object.get('isNew')) {
            return false;
          }
          let itemName = object.get('inventoryItem.name');
          let itemTypeAhead = object.get('inventoryItemTypeAhead');
          if (Ember.isEmpty(itemName) || Ember.isEmpty(itemTypeAhead)) {
            // force validation to fail
            return true;
          } else {
            let typeAheadName = itemTypeAhead.substr(0, itemName.length);
            if (itemName !== typeAheadName) {
              return true;
            }
          }
          // Inventory item is properly selected; don't do any further validation
          return false;
        },
        message: 'Please select a valid medication'
      }
    },

    patientTypeAhead: {
      presence: {
        if(object) {
          return (object.get('selectPatient'));
        }
      }
    },

    quantity: {
      numericality: {
        allowBlank: true,
        greaterThan: 0,
        messages: {
          greaterThan: 'must be greater than 0'
        }
      },
      presence: {
        if(object) {
          let isFulfilling = object.get('isFulfilling');
          return isFulfilling;
        }
      },
      acceptance: {
        accept: true,
        if(object) {
          let isFulfilling = object.get('isFulfilling');
          let requestQuantity = parseInt(object.get('quantity'));
          let quantityToCompare = null;
          if (!isFulfilling) {
            // no validation needed when not fulfilling
            return false;
          } else {
            quantityToCompare = object.get('inventoryItem.quantity');
          }
          if (requestQuantity > quantityToCompare) {
            // force validation to fail
            return true;
          } else {
            // There is enough quantity on hand.
            return false;
          }
        },
        message: 'The quantity must be less than or equal to the number of available medication.'
      }
    },

    refills: {
      numericality: {
        allowBlank: true
      }
    }
  }
});
<commit_msg>Modified medication model (#1065)<commit_after>import AbstractModel from 'hospitalrun/models/abstract';
import CanEditRequested from 'hospitalrun/mixins/can-edit-requested';
import DS from 'ember-data';
import DateFormat from 'hospitalrun/mixins/date-format';
import Ember from 'ember';
import MedicationDetails from 'hospitalrun/mixins/medication-details';

const { computed, get } = Ember;

export default AbstractModel.extend(CanEditRequested, DateFormat, MedicationDetails, {
  // Attributes
  notes: DS.attr('string'),
  prescription: DS.attr('string'),
  prescriptionDate: DS.attr('date'),
  quantity: DS.attr('number'),
  refills: DS.attr('number'),
  requestedDate: DS.attr('date'),
  requestedBy: DS.attr('string'),
  status: DS.attr('string'),

  // Associations
  inventoryItem: DS.belongsTo('inventory', { async: true }),
  patient: DS.belongsTo('patient', { async: false }),
  visit: DS.belongsTo('visit', { async: false }),

  isRequested: computed('status', function() {
    return get(this, 'status') === 'Requested';
  }),

  medicationName: computed('medicationTitle', 'inventoryItem', function() {
    return this.getMedicationName('inventoryItem');
  }),

  medicationPrice: computed('priceOfMedication', 'inventoryItem', function() {
    return this.getMedicationPrice('inventoryItem');
  }),

  prescriptionDateAsTime: computed('prescriptionDate', function() {
    return this.dateToTime(get(this, 'prescriptionDate'));
  }),

  requestedDateAsTime: computed('requestedDate', function() {
    return this.dateToTime(get(this, 'requestedDate'));
  }),

  validations: {
    prescription: {
      acceptance: {
        accept: true,
        if(object) {
          if (!get(object, 'hasDirtyAttributes') || get(object, 'isFulfilling')) {
            return false;
          }
          let prescription = get(object, 'prescription');
          let quantity = get(object, 'quantity');
          return Ember.isEmpty(prescription) && Ember.isEmpty(quantity);
        },
        message: 'Please enter a prescription or a quantity'
      }
    },

    inventoryItemTypeAhead: {
      acceptance: {
        accept: true,
        if(object) {
          if (!get(object, 'hasDirtyAttributes') || !get(object, 'isNew')) {
            return false;
          }
          let itemName = get(object, 'inventoryItem.name');
          let itemTypeAhead = get(object, 'inventoryItemTypeAhead');
          if (Ember.isEmpty(itemName) || Ember.isEmpty(itemTypeAhead)) {
            // force validation to fail
            return true;
          } else {
            let typeAheadName = itemTypeAhead.substr(0, itemName.length);
            if (itemName !== typeAheadName) {
              return true;
            }
          }
          // Inventory item is properly selected; don't do any further validation
          return false;
        },
        message: 'Please select a valid medication'
      }
    },

    patientTypeAhead: {
      presence: {
        if(object) {
          return get(object, 'selectPatient');
        }
      }
    },

    quantity: {
      numericality: {
        allowBlank: true,
        greaterThan: 0,
        messages: {
          greaterThan: 'must be greater than 0'
        }
      },
      presence: {
        if(object) {
          return get(object, 'isFulfilling');
        }
      },
      acceptance: {
        accept: true,
        if(object) {
          let isFulfilling = get(object, 'isFulfilling');
          let requestQuantity = parseInt(get(object, 'quantity'));
          let quantityToCompare = null;

          if (!isFulfilling) {
            // no validation needed when not fulfilling
            return false;
          } else {
            quantityToCompare = object.get('inventoryItem.quantity');
          }

          if (requestQuantity > quantityToCompare) {
            // force validation to fail
            return true;
          } else {
            // There is enough quantity on hand
            return false;
          }
        },
        message: 'The quantity must be less than or equal to the number of available medication.'
      }
    },

    refills: {
      numericality: {
        allowBlank: true
      }
    }
  }
});
<|endoftext|>


<commit_before>var MovableWindow = function(width,height,titleText) {

	width = width || 300;
	height = height || 200;

	var container = $(	'
' +
							'
' +
								'' +
								'' +
							'
' +
							'

' +
						'
')
							.appendTo( $(document.body) )
							.hide(),
		header = container.find('.movable-header'),
		title = container.find('.movable-header-title'),
		body = container.find('.movable-body'),
		close = container.find('.close-button'),
		win = $(window),
		doc = $(document),

		startWindowPosition = null,
		startMousePosition = null;

	header.on('mousedown', function(e) {
		doc
			.on('mousemove', move)
			.on('mouseup', mouseup);

		startWindowPosition = container.offset();
		startMousePosition = {x:e.clientX, y:e.clientY};
	});

	function mouseup() {
		doc
			.off('mousemove', move)
			.off('mouseup', mouseup);
	}

	function move(e) {
		// handle move
		//console.log(e);

		container.css({
			top: startWindowPosition.top - (startMousePosition.y - e.clientY),
			left: startWindowPosition.left - (startMousePosition.x - e.clientX)
		});
	}

	close.on('click', hide);

	function size(width, height) {

		body.width(width);
		body.height(height);

		return ext;
	}
	size(width, height);
	center();

	function show() {
		container.show();
		return ext;
	}
	function hide() {
		container.hide();
		return ext;
	}
	function center() {
		var
			infoWidth = container.outerWidth(),
			infoHeight = container.outerHeight();

		container.css({
			top: win.height()/2 - infoHeight/2,
			left: win.width()/2 - infoWidth/2
		});

		return ext;
	}

	var ext = {
		show: show,
		hide: hide,
		size: size,
		container: container,
		body: body,
		title: title,
		center: center
	};

	return ext;

};<commit_msg>fixed title<commit_after>var MovableWindow = function(width,height,titleText) {

	width = width || 300;
	height = height || 200;
	titleText = titleText || '';

	var container = $(	'
' +
							'
' +
								'' + titleText + '' +
								'' +
							'
' +
							'

' +
						'
')
							.appendTo( $(document.body) )
							.hide(),
		header = container.find('.movable-header'),
		title = container.find('.movable-header-title'),
		body = container.find('.movable-body'),
		close = container.find('.close-button'),
		win = $(window),
		doc = $(document),

		startWindowPosition = null,
		startMousePosition = null;

	header.on('mousedown', function(e) {
		doc
			.on('mousemove', move)
			.on('mouseup', mouseup);

		startWindowPosition = container.offset();
		startMousePosition = {x:e.clientX, y:e.clientY};
	});

	function mouseup() {
		doc
			.off('mousemove', move)
			.off('mouseup', mouseup);
	}

	function move(e) {
		// handle move
		//console.log(e);

		container.css({
			top: startWindowPosition.top - (startMousePosition.y - e.clientY),
			left: startWindowPosition.left - (startMousePosition.x - e.clientX)
		});
	}

	close.on('click', hide);

	function size(width, height) {

		body.width(width);
		body.height(height);

		return ext;
	}
	size(width, height);
	center();

	function show() {
		container.show();
		return ext;
	}
	function hide() {
		container.hide();
		return ext;
	}
	function center() {
		var
			infoWidth = container.outerWidth(),
			infoHeight = container.outerHeight();

		container.css({
			top: win.height()/2 - infoHeight/2,
			left: win.width()/2 - infoWidth/2
		});

		return ext;
	}

	var ext = {
		show: show,
		hide: hide,
		size: size,
		container: container,
		body: body,
		title: title,
		center: center
	};

	return ext;

};<|endoftext|>


<commit_before>'use strict';

/* Controllers */

var ermInitController = angular.module('ermInitController', ['facetsModel', 'facetsService']);

//angular.module('ermrestApp').controller('InitListCtrl', ['$scope', 'FacetsData',
ermInitController.controller('InitListCtrl', ['$sce', '$rootScope', '$scope', '$window', 'FacetsData', 'FacetsService', 'ermrest',
                                                      function($sce, $rootScope, $scope, $window, FacetsData, FacetsService, ermrest) {

	$scope.status = 0;
	$scope.errorMessage = null;

	if (chaiseConfig['customCSS'] !== undefined) {
		var fileref = document.createElement("link");
		fileref.setAttribute("rel", "stylesheet");
		fileref.setAttribute("type", "text/css");
		fileref.setAttribute("href", chaiseConfig['customCSS']);
		document.getElementsByTagName("head")[0].appendChild(fileref);
	}
	if (chaiseConfig['headTitle'] !== undefined) {
		document.getElementsByTagName("head")[0].getElementsByTagName("title")[0].innerHTML = chaiseConfig['headTitle'];
	}

	$('footer').hide();

	$('.panel-collapse').on('hide.bs.collapse', function () {
	      $(this).prev('.panel-heading').find('.glyphicon').removeClass('glyphicon-minus').addClass('glyphicon-plus');
	});
	$('.panel-collapse').on('show.bs.collapse', function () {
	      $(this).prev('.panel-heading').find('.glyphicon').removeClass('glyphicon-plus').addClass('glyphicon-minus');
	});

	$('#attrsort').click(function(){
		if ($('span').hasClass("glyphicon-sort-by-attributes")) {
		$("#attrsort span.glyphicon").removeClass("glyphicon-sort-by-attributes").addClass("glyphicon-sort-by-attributes-alt");
		}
		else {
		$("#attrsort span.glyphicon").removeClass("glyphicon-sort-by-attributes-alt").addClass("glyphicon-sort-by-attributes");
		}
	});

	$('.sidebar-overlay').click(function(event) {
    if ($('.sidebar-overlay').hasClass('active')) {
      $('#collectionsTree').removeClass('open');
      $('.sidebar-overlay').removeClass('active');
    }
	});

	$scope.FacetsData = FacetsData;

	$scope.displayError = function displayError(status, errorMessage) {
		$scope.status = status;
		$scope.errorMessage = errorMessage;
		$scope.FacetsData.error = true;
		$scope.FacetsData.progress = false;
		if (!$scope.$$phase) {
			$scope.$apply();
		}
		setTimeout(function() {
			$scope.FacetsData.error = true;
			$scope.FacetsData.progress = false;
			if (!$scope.$$phase) {
				$scope.$apply();
			}
		}, 1);
	};

	if (isRecordFilter(window.location.href)) {
		try {
			var newUrl = convertFilter(window.location.href);
			window.location.assign(newUrl);
		} catch(err) {
			loadApplicationFooter();
			$scope.errorMessage = err.message + '
' + window.location.href;
			$scope.displayError('', $scope.errorMessage);
		}
	}
	var searchQuery = getSearchQuery(window.location.href);
	if (searchQuery['entity'] != null) {
		var values = searchQuery['entity'].split(':');
		searchQuery['schema'] = decodeURIComponent(values[0]);
		searchQuery['table'] = decodeURIComponent(values[1]);
	}
	if (searchQuery['schema'] != null) {
		SCHEMA = searchQuery['schema'];
	} else if (SCHEMA == null) {
		//SCHEMA = 'legacy';
	}
	if (searchQuery['catalog'] != null) {
		CATALOG = searchQuery['catalog'];
	} else if (CATALOG == null) {
		if (chaiseConfig['catalog'] != null) {
			CATALOG = chaiseConfig['catalog'];
		} else {
			CATALOG = ermrest.catalog;
		}
	}

	if (chaiseConfig['facetPolicy'] != null) {
		facetPolicy = chaiseConfig['facetPolicy'];
	}

	FacetsService.initTable();

	if (searchQuery['table'] != null) {
		$scope.FacetsData.table = searchQuery['table'];
	} else {
		$scope.FacetsData.table = '';
	}

	if (searchQuery['page'] != null) {
		$scope.FacetsData.bookmarkPage = searchQuery['page'];
	} else {
		$scope.FacetsData.bookmarkPage = null;
	}

	if (searchQuery['facets'] != null) {
		try {
			$scope.FacetsData.filter = decodeFilter(searchQuery['facets']);
		} catch(err) {
			loadApplicationFooter();
			$scope.errorMessage = err.message + '
facets=' + searchQuery['facets'];
			$scope.displayError('', $scope.errorMessage);
		}
	} else {
		$scope.FacetsData.filter = null;
	}

	$window.addEventListener('popstate', function(event) {
    event.stopPropagation();
		event.preventDefault();
		$scope.FacetsData.isDetail = false;
		if (!$scope.$$phase) {
			$scope.$apply();
		}
	});

	$scope.FacetsData.view = ermrest.layout;
	if (searchQuery['layout'] != null) {
		$scope.FacetsData.view = searchQuery['layout'];
	} else if (chaiseConfig['layout'] != null) {
		$scope.FacetsData.view = chaiseConfig['layout'];
	}

	$scope.FacetsData.plotOptions.bookmarkParams = searchQuery['plot'];
	FacetsService.updatePlotFormatOptionsFromBookmarkParams();

	this.html = function (errorMessage) {
		return errorMessage != null ? $sce.trustAsHtml(errorMessage.replace(/\n/g, '
')) : '';
	};

	if ($scope.errorMessage == null) {
		initApplication($scope.FacetsData, $scope.displayError);
	}
	this.hideSpinner = function hideSpinner() {
		//return !$scope.FacetsData.progress;
		return true;
	};

	this.showSpinner = function showSpinner() {
		return $scope.FacetsData.progress && !$scope.FacetsData.error;
	};

	this.showError = function showError() {
		return $scope.FacetsData.error;
	};
}]);
<commit_msg>Reload the search page each time a menu item is selected from the navigation bar menu.<commit_after>'use strict';

/* Controllers */

var ermInitController = angular.module('ermInitController', ['facetsModel', 'facetsService']);

//angular.module('ermrestApp').controller('InitListCtrl', ['$scope', 'FacetsData',
ermInitController.controller('InitListCtrl', ['$sce', '$rootScope', '$scope', '$window', 'FacetsData', 'FacetsService', 'ermrest',
                                                      function($sce, $rootScope, $scope, $window, FacetsData, FacetsService, ermrest) {

	$scope.status = 0;
	$scope.errorMessage = null;

	if (chaiseConfig['customCSS'] !== undefined) {
		var fileref = document.createElement("link");
		fileref.setAttribute("rel", "stylesheet");
		fileref.setAttribute("type", "text/css");
		fileref.setAttribute("href", chaiseConfig['customCSS']);
		document.getElementsByTagName("head")[0].appendChild(fileref);
	}
	if (chaiseConfig['headTitle'] !== undefined) {
		document.getElementsByTagName("head")[0].getElementsByTagName("title")[0].innerHTML = chaiseConfig['headTitle'];
	}

	$('footer').hide();

	$('.panel-collapse').on('hide.bs.collapse', function () {
	      $(this).prev('.panel-heading').find('.glyphicon').removeClass('glyphicon-minus').addClass('glyphicon-plus');
	});
	$('.panel-collapse').on('show.bs.collapse', function () {
	      $(this).prev('.panel-heading').find('.glyphicon').removeClass('glyphicon-plus').addClass('glyphicon-minus');
	});

	$('#attrsort').click(function(){
		if ($('span').hasClass("glyphicon-sort-by-attributes")) {
		$("#attrsort span.glyphicon").removeClass("glyphicon-sort-by-attributes").addClass("glyphicon-sort-by-attributes-alt");
		}
		else {
		$("#attrsort span.glyphicon").removeClass("glyphicon-sort-by-attributes-alt").addClass("glyphicon-sort-by-attributes");
		}
	});

	$('.sidebar-overlay').click(function(event) {
    if ($('.sidebar-overlay').hasClass('active')) {
      $('#collectionsTree').removeClass('open');
      $('.sidebar-overlay').removeClass('active');
    }
	});

	$scope.FacetsData = FacetsData;

	$scope.displayError = function displayError(status, errorMessage) {
		$scope.status = status;
		$scope.errorMessage = errorMessage;
		$scope.FacetsData.error = true;
		$scope.FacetsData.progress = false;
		if (!$scope.$$phase) {
			$scope.$apply();
		}
		setTimeout(function() {
			$scope.FacetsData.error = true;
			$scope.FacetsData.progress = false;
			if (!$scope.$$phase) {
				$scope.$apply();
			}
		}, 1);
	};

	if (isRecordFilter(window.location.href)) {
		try {
			var newUrl = convertFilter(window.location.href);
			window.location.assign(newUrl);
		} catch(err) {
			loadApplicationFooter();
			$scope.errorMessage = err.message + '
' + window.location.href;
			$scope.displayError('', $scope.errorMessage);
		}
	}
	var searchQuery = getSearchQuery(window.location.href);
	if (searchQuery['entity'] != null) {
		var values = searchQuery['entity'].split(':');
		searchQuery['schema'] = decodeURIComponent(values[0]);
		searchQuery['table'] = decodeURIComponent(values[1]);
	}
	if (searchQuery['schema'] != null) {
		SCHEMA = searchQuery['schema'];
	} else if (SCHEMA == null) {
		//SCHEMA = 'legacy';
	}
	if (searchQuery['catalog'] != null) {
		CATALOG = searchQuery['catalog'];
	} else if (CATALOG == null) {
		if (chaiseConfig['catalog'] != null) {
			CATALOG = chaiseConfig['catalog'];
		} else {
			CATALOG = ermrest.catalog;
		}
	}

	if (chaiseConfig['facetPolicy'] != null) {
		facetPolicy = chaiseConfig['facetPolicy'];
	}

	FacetsService.initTable();

	if (searchQuery['table'] != null) {
		$scope.FacetsData.table = searchQuery['table'];
	} else {
		$scope.FacetsData.table = '';
	}

	if (searchQuery['page'] != null) {
		$scope.FacetsData.bookmarkPage = searchQuery['page'];
	} else {
		$scope.FacetsData.bookmarkPage = null;
	}

	if (searchQuery['facets'] != null) {
		try {
			$scope.FacetsData.filter = decodeFilter(searchQuery['facets']);
		} catch(err) {
			loadApplicationFooter();
			$scope.errorMessage = err.message + '
facets=' + searchQuery['facets'];
			$scope.displayError('', $scope.errorMessage);
		}
	} else {
		$scope.FacetsData.filter = null;
	}

	$window.addEventListener('popstate', function(event) {
    event.stopPropagation();
		event.preventDefault();
		if (assignBookmark == false) {
			window.location.reload(true);
		}
		$scope.FacetsData.isDetail = false;
		if (!$scope.$$phase) {
			$scope.$apply();
		}
	});

	$scope.FacetsData.view = ermrest.layout;
	if (searchQuery['layout'] != null) {
		$scope.FacetsData.view = searchQuery['layout'];
	} else if (chaiseConfig['layout'] != null) {
		$scope.FacetsData.view = chaiseConfig['layout'];
	}

	$scope.FacetsData.plotOptions.bookmarkParams = searchQuery['plot'];
	FacetsService.updatePlotFormatOptionsFromBookmarkParams();

	this.html = function (errorMessage) {
		return errorMessage != null ? $sce.trustAsHtml(errorMessage.replace(/\n/g, '
')) : '';
	};

	if ($scope.errorMessage == null) {
		initApplication($scope.FacetsData, $scope.displayError);
	}
	this.hideSpinner = function hideSpinner() {
		//return !$scope.FacetsData.progress;
		return true;
	};

	this.showSpinner = function showSpinner() {
		return $scope.FacetsData.progress && !$scope.FacetsData.error;
	};

	this.showError = function showError() {
		return $scope.FacetsData.error;
	};
}]);
<|endoftext|>


<commit_before>const jwt = require('jsonwebtoken');
require('dotenv').config();
require('dotenv').load();

module.exports = {
    getToken: (id, handle, callback) => {
        let user = {
            handle: handle,
            id: id
        }
        let token = jwt.sign(user, process.env.SECRET_KEY, {
            expiresIn: 100
        });
        callback({success: true, token: token});
    },
    authenticate: (req, res, next) => {
        let header = req.headers['authorization'];
        if(header){
            jwt.verify(header, process.env.SECRET_KEY, (err, data) => {
                if(err){
                    res.sendStatus(403);
                } else {
                    next();
                }
            })
        } else {
            res.status(400).send('no token');
        }
    }
}<commit_msg>changed auth expire time<commit_after>const jwt = require('jsonwebtoken');
require('dotenv').config();
require('dotenv').load();

module.exports = {
    getToken: (id, handle, callback) => {
        let user = {
            handle: handle,
            id: id
        }
        let token = jwt.sign(user, process.env.SECRET_KEY, {
            expiresIn: 60 * 60
        });
        callback({success: true, token: token});
    },
    authenticate: (req, res, next) => {
        let header = req.headers['authorization'];
        if(header){
            jwt.verify(header, process.env.SECRET_KEY, (err, data) => {
                if(err){
                    res.sendStatus(403);
                } else {
                    next();
                }
            })
        } else {
            res.status(400).send('no token');
        }
    }
}<|endoftext|>


<commit_before>var Wave = {};
Wave.Game = function(game) {};

Wave.Game.prototype = {
  init: function(){
    this.count = 0;
    this.waveTotalLength = 1600;
    this.numWaves = 10;
    this.waves = null;
    this.boat = null;
  },

  preload: function(){
    this.game.load.spritesheet('wave', 'wave.png', 160, 160);
    this.game.load.image('boat', 'boat.png');
  },

  create: function(){
    var length = this.waveTotalLength / this.numWaves;
    var x, y, wave;
    this.game.world.setBounds(0, 0, 3200, 600);
    this.game.physics.startSystem(Phaser.Physics.ARCADE);

    this.waves = this.game.add.group();
    this.waves.x = -320;
    this.waves.y = this.game.world.height - 50;
    this.waves.enableBody = true;
    this.waves.physicsBodyType = Phaser.Physics.ARCADE;

    for (var i = 0; i < this.numWaves; i++)
    {
      x = i*length;
      y = 0;
      wave = this.game.add.sprite(x, y, 'wave', 0);
      wave.anchor.set(0.5,0.5);
      this.waves.add(wave);
      wave.body.setCircle(this.game.rnd.between(80,140));
      wave.body.offset.set(0, 50);
      wave.body.immovable = true;
    }

    this.boat = this.game.add.sprite(0, 0, 'boat');
    this.game.physics.arcade.enable(this.boat);
    this.boat.body.gravity.y = 250;
    this.game.camera.follow(this.boat);
  },

  update: function(){
    this.game.physics.arcade.collide(this.boat, this.waves);
    this.count += 0.1;
    this.boat.body.velocity.x = 50; // Constantly move camera to the right

    for (var i = 0; i < this.numWaves; i++)
    {
      this.waves.children[i].y = Math.sin(i * 0.9 + this.count) * 10;
    }
  },

  render: function(){
    // this.game.debug.body(this.boat);
    // for(var i = 0; i < this.numWaves; i++) {
    //   this.game.debug.body(this.waves.children[i]);
    // }
  }
};<commit_msg>Give boat a circle physics body<commit_after>var Wave = {};
Wave.Game = function(game) {};

Wave.Game.prototype = {
  init: function(){
    this.count = 0;
    this.waveTotalLength = 1600;
    this.numWaves = 10;
    this.waves = null;
    this.boat = null;
  },

  preload: function(){
    this.game.load.spritesheet('wave', 'wave.png', 160, 160);
    this.game.load.image('boat', 'boat.png');
  },

  create: function(){
    var length = this.waveTotalLength / this.numWaves;
    var x, y, wave;
    this.game.world.setBounds(0, 0, 3200, 600);
    this.game.physics.startSystem(Phaser.Physics.ARCADE);

    this.waves = this.game.add.group();
    this.waves.x = -320;
    this.waves.y = this.game.world.height - 50;
    this.waves.enableBody = true;
    this.waves.physicsBodyType = Phaser.Physics.ARCADE;

    for (var i = 0; i < this.numWaves; i++)
    {
      x = i*length;
      y = 0;
      wave = this.game.add.sprite(x, y, 'wave', 0);
      wave.anchor.set(0.5,0.5);
      this.waves.add(wave);
      wave.body.setCircle(this.game.rnd.between(80,140));
      wave.body.offset.set(0, 50);
      wave.body.immovable = true;
    }

    this.boat = this.game.add.sprite(0, 0, 'boat');
    this.game.physics.arcade.enable(this.boat);
    this.boat.body.gravity.y = 250;
    this.boat.body.setCircle(32);
    this.game.camera.follow(this.boat);
  },

  update: function(){
    this.game.physics.arcade.collide(this.boat, this.waves);
    this.count += 0.1;
    this.boat.body.velocity.x = 50; // Constantly move camera to the right

    for (var i = 0; i < this.numWaves; i++)
    {
      this.waves.children[i].y = Math.sin(i * 0.9 + this.count) * 10;
    }
  },

  render: function(){
    // this.game.debug.body(this.boat);
    // for(var i = 0; i < this.numWaves; i++) {
    //   this.game.debug.body(this.waves.children[i]);
    // }
  }
};<|endoftext|>


<commit_before>'use strict';
const symbol = require('../utilities/symbol');

let experiments = {
  BUILD_INSTRUMENTATION: symbol('build-instrumentation'),
  INSTRUMENTATION: 'instrumentation',
  NPM_BLUEPRINTS: symbol('npm-blueprints'),
  BROWSER_TARGETS: symbol('browser-targets'),
};

Object.freeze(experiments);

module.exports = experiments;
<commit_msg>Add deprecation when accessing `experiments.INSTRUMENTATION`.<commit_after>'use strict';
const symbol = require('../utilities/symbol');
const getCallerFile = require('get-caller-file');
const deprecate = require('../utilities/deprecate');

let experiments = {
  BUILD_INSTRUMENTATION: symbol('build-instrumentation'),
  NPM_BLUEPRINTS: symbol('npm-blueprints'),
  BROWSER_TARGETS: symbol('browser-targets'),
  get INSTRUMENTATION() {
    deprecate(`\`experiments.INSTRUMENTATION\` is deprecated now that the feature has landed, use \`instrumentation\` method on your addon instead. Required from: \n${getCallerFile()}`, true);

    return 'instrumentation';
  },
};

Object.freeze(experiments);

module.exports = experiments;
<|endoftext|>


<commit_before>'use strict';

var compact      = require('es5-ext/array/#/compact')
  , flatten      = require('es5-ext/array/#/flatten')
  , isCallable   = require('es5-ext/object/is-callable')
  , isObservable = require('observable-value/is-observable-value')
  , isAttr       = require('../../attr/is-attr')
  , element      = require('../valid-element')

  , isArray = Array.isArray, map = Array.prototype.map
  , resolveObservable = function (val) { return isObservable(val) ? val.value : val; };

var resolveArray = function (element, name, arr) {
	var onUpdate;
	arr = compact.call(arr);
	if (!arr.some(function (el) { return isObservable(el); })) {
		element.setAttribute(name, compact.call(arr).map(String).join(" "));
		return;
	}
	onUpdate = function () {
		element.setAttribute(name,
			compact.call(flatten.call(map.call(arr, resolveObservable))).map(String).join(" "));
	};
	arr.forEach(function (value) {
		if (isObservable(value)) value.on('change', onUpdate);
	});
	onUpdate();
};

module.exports = function (name, value) {
	element(this);
	if (value == null) {
		this[name] = null;
		this.removeAttribute(name);
	} else if (typeof value === 'string') {
		this.setAttribute(name, value);
	} else if (isCallable(value)) {
		this.setAttribute(name, name);
		this[name] = value;
	} else if (typeof value === 'boolean') {
		if (value) {
			this.setAttribute(name, name);
			this[name] = true;
		} else {
			this[name] = false;
			this.removeAttribute(name);
		}
	} else if (isCallable(value.toDOMAttr)) {
		value.toDOMAttr(this, name);
	} else if (isAttr(value)) {
		// Deprecated
		this.setAttributeNode(value);
	} else if (isArray(value)) {
		resolveArray(this, name, value);
	} else {
		this.setAttribute(name, value);
	}
};
<commit_msg>Flatten array of attibutes for non observable attrs<commit_after>'use strict';

var compact      = require('es5-ext/array/#/compact')
  , flatten      = require('es5-ext/array/#/flatten')
  , isCallable   = require('es5-ext/object/is-callable')
  , isObservable = require('observable-value/is-observable-value')
  , isAttr       = require('../../attr/is-attr')
  , element      = require('../valid-element')

  , isArray = Array.isArray, map = Array.prototype.map
  , resolveObservable = function (val) { return isObservable(val) ? val.value : val; };

var resolveArray = function (element, name, arr) {
	var onUpdate;
	arr = compact.call(arr);
	if (!arr.some(function (el) { return isObservable(el); })) {
		element.setAttribute(name, compact.call(flatten.call(arr)).map(String).join(" "));
		return;
	}
	onUpdate = function () {
		element.setAttribute(name,
			compact.call(flatten.call(map.call(arr, resolveObservable))).map(String).join(" "));
	};
	arr.forEach(function (value) {
		if (isObservable(value)) value.on('change', onUpdate);
	});
	onUpdate();
};

module.exports = function (name, value) {
	element(this);
	if (value == null) {
		this[name] = null;
		this.removeAttribute(name);
	} else if (typeof value === 'string') {
		this.setAttribute(name, value);
	} else if (isCallable(value)) {
		this.setAttribute(name, name);
		this[name] = value;
	} else if (typeof value === 'boolean') {
		if (value) {
			this.setAttribute(name, name);
			this[name] = true;
		} else {
			this[name] = false;
			this.removeAttribute(name);
		}
	} else if (isCallable(value.toDOMAttr)) {
		value.toDOMAttr(this, name);
	} else if (isAttr(value)) {
		// Deprecated
		this.setAttributeNode(value);
	} else if (isArray(value)) {
		resolveArray(this, name, value);
	} else {
		this.setAttribute(name, value);
	}
};
<|endoftext|>


<commit_before>/******************\
|    Mock Trial    |
|    Clerk Tool    |
| @author Anthony  |
| @version 0.1     |
| @date 2015/01/08 |
| @edit 2015/01/08 |
\******************/

var MTClerkTool = (function() {
    /**********
     * config */
    var calibTime = 3; //seconds spent calibrating
    var timerDefns = [
        [4*60, 'Def-Pret Stmt'],
        [4*60, 'Pro-Pret Stmt'],
        [2*60, 'Def-Pret Rbtl'],
        [2*60, 'Pro-Pret Rbtl'],

        [9*60, 'Pro-Opng/Clsn'],
        [9*60, 'Def-Opng/Clsn'],

        [14*60, 'Pro-Direct'],
        [14*60, 'Def-Cross'],
        [14*60, 'Def-Direct'],
        [14*60, 'Pro-Cross'],

        [1*60, 'Pro-Clsn Rbtl'],
        [1*60, 'Def-Clsn Rbtl']
    ];
    var gapLen = 3000; //ms gap inserted halfway when testing the timer

    /*************
     * constants */

    /*********************
     * working variables */
    var timers;
    var calibratedDelay;

    /******************
     * work functions */
    function initMTClerkTool() {
        //populate the select
        for (var ti = 0; ti < timerDefns.length; ti++) {
            var option = document.createElement('option');
            option.value = ti;
            option.innerHTML = timerDefns[ti][1];
            $s('#section').appendChild(option);
        }

        //calibrate the timers
        testTimer(calibTime, function(g, a) {
            var errPerSec = (a-g)/g;
            calibratedDelay = Math.floor(1000*(1-errPerSec));

            //clear "calibrating..."
            $s('#controls').removeChild($s('#calib-notice'));

            //init timers
            timers = [];
            for (var ti = 0; ti < timerDefns.length; ti++) {
                //the html
                var div = document.createElement('div');
                div.id = 'timer'+ti;
                div.className = 'timer';
                div.innerHTML = '
'+timerDefns[ti][1]+'
'+
                    '<input id="time'+ti+
                    '" type="button" value="00:00">';
                $s('#container').appendChild(div);

                //the js
                timers.push(
                    new Timer(timerDefns[ti][0], calibratedDelay,
                        (function(idx) {
                            return function(self) {
                                $s('#time'+idx).value = self.format();
                            };
                        })(ti)
                    )
                );

                //user control
                $s('#time'+ti).addEventListener('click', (function(idx) {
                    return function() {
                        var cns = $s('#time'+idx).className;
                        if (cns.indexOf('unlocked') !== -1) {
                            $s('#time'+idx).className = 'locked';
                        } else {
                            $s('#time'+idx).className = 'unlocked';
                        }
                        timers[idx].startStop();
                    };
                })(ti));
            }

            //timer control
            $s('#set-time-btn').addEventListener('click', function() {
                var numSec = parseInt($s('#min').value || 0)*60;
                numSec += parseInt($s('#sec').value || 0);
                if (numSec < 0 || numSec === undefined) numSec = 0;
                var idx = parseInt($s('#section').value);
                timers[idx].timeLeft = numSec;
                $s('#time'+idx).value = timers[idx].format();
            });

            //tests
            testTimer(5);
            testTimer(10);
            testTimer(60);
        });
    }

    function testTimer(g, callback) { //g is goal in seconds
        callback = callback || false;

        var start = +new Date();
        var tmr = new Timer(g, 1000, (function(s) {
            return function(self) {
                if (self.timeLeft === 0) {
                    var actual = ((+new Date())-s)/1000 - gapLen/1000;
                    if (callback) callback(g, actual);
                    else {
                        console.log(
                            'Goal: '+g+'s; '+'Actual: '+actual
                        );
                    }
                }
            };
        })(start));
        tmr.startStop();

        setTimeout(function() {
            tmr.startStop();
            setTimeout(function() {
                tmr.startStop();
            }, gapLen);
        }, g/(2+0.3*Math.random()));
    }

    /***********
     * objects */
    function Timer(t, d, f) {
        this.timeLeft = t;
        this.delay = d || 1000;
        this.func = f || false;
        this.locked = true;
        this.secBeganAt = -1;
        this.lockedAt = -1;

        var self = this;
        this.func(self); //first function call
    }
    Timer.prototype.startStop = function() {
        var self = this;
        if (this.locked) { //stopped? then start it
            this.timeLeft += 1;
            this.locked = false;
            self.countDown(this.delay - (this.lockedAt - this.secBeganAt));
        } else { //currently counting down? stop it
            this.locked = true;
            this.lockedAt = +new Date();
        }
    };
    Timer.prototype.countDown = function(delay) {
        delay = delay || this.delay;

        var self = this;
        if (this.timeLeft > 0 && !this.locked) {
            this.secBeganAt = +new Date();
            this.timeLeft -= 1;
            var funStart = +new Date();
            if (this.func) this.func(self);
            var funEnd = +new Date();
            setTimeout(function() {
                self.countDown();
            }, delay - (funEnd - funStart));
        }
    };
    Timer.prototype.format = function() {
        var m = Math.floor(this.timeLeft/60);
        var s = this.timeLeft-60*m;
        return forceLen(m+'', '0', 2, true)+':'+forceLen(s+'', '0', 2, true);
    };

    /********************
     * helper functions */
    function $s(id) { //for convenience
        if (id.charAt(0) !== '#') return false;
        return document.getElementById(id.substring(1));
    }
    function getRandInt(low, high) { //output is in [low, high)
        return Math.floor(low + Math.random()*(high-low));
    }
    function forceLen(str, padding, len, padFront) {
        if (str.length > len) return;
        var ret = str;
        for (var ai = 0; ai < len - str.length; ai++) {
            if (padFront) ret = padding+ret;
            else ret = ret+padding;
        }
        return ret;
    }
    function round(n, places) {
        var mult = Math.pow(10, places);
        return Math.round(mult*n)/mult;
    }

    return {
        init: initMTClerkTool
    };
})();

window.addEventListener('load', function() {
    MTClerkTool.init();
});<commit_msg>Implements a secret reset code to automatically reset all the times.<commit_after>/******************\
|    Mock Trial    |
|    Clerk Tool    |
| @author Anthony  |
| @version 0.1     |
| @date 2015/01/08 |
| @edit 2015/01/08 |
\******************/

var MTClerkTool = (function() {
    /**********
     * config */
    var calibTime = 5; //seconds spent calibrating
    var timerDefns = [
        [4*60, 'Def-Pret Stmt'],
        [4*60, 'Pro-Pret Stmt'],
        [2*60, 'Def-Pret Rbtl'],
        [2*60, 'Pro-Pret Rbtl'],

        [9*60, 'Pro-Opng/Clsn'],
        [9*60, 'Def-Opng/Clsn'],

        [14*60, 'Pro-Direct'],
        [14*60, 'Def-Cross'],
        [14*60, 'Def-Direct'],
        [14*60, 'Pro-Cross'],

        [1*60, 'Pro-Clsn Rbtl'],
        [1*60, 'Def-Clsn Rbtl']
    ];
    var gapLen = 3000; //ms gap inserted halfway when testing the timer

    /*************
     * constants */

    /*********************
     * working variables */
    var timers;
    var calibratedDelay;

    /******************
     * work functions */
    function initMTClerkTool() {
        //populate the select
        for (var ti = 0; ti < timerDefns.length; ti++) {
            var option = document.createElement('option');
            option.value = ti;
            option.innerHTML = timerDefns[ti][1];
            $s('#section').appendChild(option);
        }

        //calibrate the timers
        testTimer(calibTime, function(g, a) {
            var errPerSec = (a-g)/g;
            calibratedDelay = Math.floor(1000*(1-errPerSec));

            //clear "calibrating..."
            $s('#controls').removeChild($s('#calib-notice'));

            //init timers
            timers = [];
            for (var ti = 0; ti < timerDefns.length; ti++) {
                //the html
                var div = document.createElement('div');
                div.id = 'timer'+ti;
                div.className = 'timer';
                div.innerHTML = '
'+timerDefns[ti][1]+'
'+
                    '<input id="time'+ti+
                    '" type="button" value="00:00">';
                $s('#container').appendChild(div);

                //the js
                timers.push(
                    new Timer(timerDefns[ti][0], calibratedDelay,
                        (function(idx) {
                            return function(self) {
                                $s('#time'+idx).value = self.format();
                            };
                        })(ti)
                    )
                );

                //user control
                $s('#time'+ti).addEventListener('click', (function(idx) {
                    return function() {
                        var cns = $s('#time'+idx).className;
                        if (cns.indexOf('unlocked') !== -1) {
                            $s('#time'+idx).className = 'locked';
                        } else {
                            $s('#time'+idx).className = 'unlocked';
                        }
                        timers[idx].startStop();
                    };
                })(ti));
            }

            //timer control
            $s('#set-time-btn').addEventListener('click', function() {
                var m = parseInt($s('#min').value || 0);
                if (m >= 100) { //reset condition
                    //reset all the times
                    for (var ti = 0; ti < timerDefns.length; ti++) {
                        timers[ti].timeLeft = timerDefns[ti][0];
                        $s('#time'+ti).value = timers[ti].format();
                    }
                } else {
                    var s = parseInt($s('#sec').value || 0);
                    var numSec = 60*m + s;
                    if (numSec < 0 || numSec === undefined) numSec = 0;
                    var idx = parseInt($s('#section').value);
                    timers[idx].timeLeft = numSec;
                    $s('#time'+idx).value = timers[idx].format();
                }
            });

            //tests
            testTimer(5);
            testTimer(10);
            testTimer(60);
        });
    }

    function testTimer(g, callback) { //g is goal in seconds
        callback = callback || false;

        var start = +new Date();
        var tmr = new Timer(g, 1000, (function(s) {
            return function(self) {
                if (self.timeLeft === 0) {
                    var actual = ((+new Date())-s)/1000 - gapLen/1000;
                    if (callback) callback(g, actual);
                    else {
                        console.log(
                            'Goal: '+g+'s; '+'Actual: '+actual
                        );
                    }
                }
            };
        })(start));
        tmr.startStop();

        setTimeout(function() {
            tmr.startStop();
            setTimeout(function() {
                tmr.startStop();
            }, gapLen);
        }, g/(2+0.3*Math.random()));
    }

    /***********
     * objects */
    function Timer(t, d, f) {
        this.timeLeft = t;
        this.delay = d || 1000;
        this.func = f || false;
        this.locked = true;
        this.secBeganAt = -1;
        this.lockedAt = -1;

        var self = this;
        this.func(self); //first function call
    }
    Timer.prototype.startStop = function() {
        var self = this;
        if (this.locked) { //stopped? then start it
            this.timeLeft += 1;
            this.locked = false;
            self.countDown(this.delay - (this.lockedAt - this.secBeganAt));
        } else { //currently counting down? stop it
            this.locked = true;
            this.lockedAt = +new Date();
        }
    };
    Timer.prototype.countDown = function(delay) {
        delay = delay || this.delay;

        var self = this;
        if (this.timeLeft > 0 && !this.locked) {
            this.secBeganAt = +new Date();
            this.timeLeft -= 1;
            var funStart = +new Date();
            if (this.func) this.func(self);
            var funEnd = +new Date();
            setTimeout(function() {
                self.countDown();
            }, delay - (funEnd - funStart));
        }
    };
    Timer.prototype.format = function() {
        var m = Math.floor(this.timeLeft/60);
        var s = this.timeLeft-60*m;
        return forceLen(m+'', '0', 2, true)+':'+forceLen(s+'', '0', 2, true);
    };

    /********************
     * helper functions */
    function $s(id) { //for convenience
        if (id.charAt(0) !== '#') return false;
        return document.getElementById(id.substring(1));
    }
    function getRandInt(low, high) { //output is in [low, high)
        return Math.floor(low + Math.random()*(high-low));
    }
    function forceLen(str, padding, len, padFront) {
        if (str.length > len) return;
        var ret = str;
        for (var ai = 0; ai < len - str.length; ai++) {
            if (padFront) ret = padding+ret;
            else ret = ret+padding;
        }
        return ret;
    }
    function round(n, places) {
        var mult = Math.pow(10, places);
        return Math.round(mult*n)/mult;
    }

    return {
        init: initMTClerkTool
    };
})();

window.addEventListener('load', function() {
    MTClerkTool.init();
});<|endoftext|>


<commit_before>var gmailbuttons = {
  onLoad: function() {
    // initialization code
    this.initialized = true;
    this.strings = document.getElementById("gmailbuttons-strings");
	// add support for preferences
	this.prefs = Components.classes["@mozilla.org/preferences-service;1"]
         .getService(Components.interfaces.nsIPrefService)
         .getBranch("extensions.gmailbuttons.");
     this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
	 this.prefs.addObserver("", this, false);
  },

  onUnload: function() {
    // cleanup preferences
    this.prefs.removeObserver("", this);
  },

  observe: function(subject, topic, data) {
    if (topic != "nsPref:changed") {
      return; // only need to act on pref change
    }
    // process change
    switch(data) {
      case "showDeleteButton":
         this.updateJunkSpamButtons();
         break;
      }
  },

  GetMessageFolder: function() {
    // get current message
	var hdr = gFolderDisplay.selectedMessage;
    // give up if no message selected or this is a dummy header
    if (!hdr || gMessageDisplay.isDummy)
      return;
	// get folder that contains message
	var fldr = hdr.folder;
	if (!fldr) // message not in folder somehow?
		return;
	return fldr;
  },

  GetMessageServer: function() {
    var fldr = this.GetMessageFolder();
	if (!fldr) // message not in folder somehow?
		return;
	// get server that hosts folder
	var svr = fldr.server;
	if (!svr) // folder does not have server?
		return;
	return svr;
  },

  // returns true if message is in Gmail imap
  IsServerGmailIMAP: function(svr) {
    // check that svr parameter is valid
    if (!(svr instanceof Ci.nsIImapIncomingServer))
	  return;
	// check to see if it is a Gmail server
	return (svr.type == "imap" &&
	    svr.QueryInterface(Ci.nsIImapIncomingServer).isGMailServer);
  },

  IsSpecialFolder: function(fldr, flag) {
    // make sure this is a valid folder
    if (!(fldr instanceof Ci.nsMessageFolder))
	  return;
	// check if folder has special folder flag
	return fldr.isSpecialFolder(flag);
  },

  updateJunkSpamButtons: function() {

    /* get message-specific header buttons*/
    var deleteButton = document.getElementById("hdrTrashButton");
    var trashButton = document.getElementById("gmailbuttons-trash-button");
    var junkButton = document.getElementById("hdrJunkButton");
    var spamButton = document.getElementById("gmailbuttons-spam-button");

    if (this.IsServerGmailIMAP(this.GetMessageServer())) {
      // this is a Gmail imap account

      /* get actual folder names from server  */
      try {
        var svr = this.GetMessageServer();
        var svrRootFolder = svr.rootFolder;
        var trashFolder = this.getSpecialFolder(svrRootFolder, nsMsgFolderFlags.Trash);
        var spamFolder = this.getSpecialFolder(svrRootFolder, nsMsgFolderFlags.Junk);
      } catch(ex) {
        // don't need to do anything here
        //alert(ex);
      }
      // get label text
      var trashLabel = trashFolder ? trashFolder.prettiestName :
          this.strings.getString("gmailbuttons.error");
      var spamLabel = spamFolder ? spamFolder.prettiestName :
          this.strings.getString("gmailbuttons.error");
      // get tooltip text
      var trashTooltip = trashFolder ?
          this.strings.getFormattedString("gmailbuttons.moveButton.tooltip",
          [trashFolder.URI.replace(svrRootFolder.URI, "").substr(1)], 1) :
          this.strings.getString("gmailbuttons.error");
      var spamTooltip = spamFolder ?
          this.strings.getFormattedString("gmailbuttons.moveButton.tooltip",
          [spamFolder.URI.replace(svrRootFolder.URI, "").substr(1)], 1) :
          this.strings.getString("gmailbuttons.error");

      if (deleteButton) {
        // save the original tooltip - this only runs once
        if (!deleteButton.oldTooltipText)
          deleteButton.oldTooltipText = deleteButton.tooltipText;
        // apply new tooltip
        deleteButton.tooltipText = this.strings.getString("gmailbuttons.deleteButton.tooltip");
      try {
        var showDelete = this.prefs.getBoolPref("showDeleteButton")
        deleteButton.hidden = !showDelete;
        } catch(ex) {
        // preference does not exist - do nothing
      }
        }
      if (trashButton) {
        trashButton.hidden = false;
        trashButton.label = trashLabel;
        trashButton.tooltipText = trashTooltip;
      }
      if (junkButton)
        junkButton.hidden = true;
      if (spamButton)	{
        spamButton.hidden = false;
        spamButton.label = spamLabel;
        spamButton.tooltipText = spamTooltip;
      }
    } else {
      // this is not a GMail account

      if (deleteButton) {
        if (deleteButton.oldTooltipText)
          deleteButton.tooltipText = deleteButton.oldTooltipText;
      deleteButton.hidden = false;
        }
      if (trashButton)
          // if (!IsSpecialFolder(this.getMessageFolder(), Ci.nsMsgFolderFlags.Trash))
        trashButton.hidden = true; // TODO hide trash button if we are in the [Gmail]/Trash folder
      if (junkButton)
        junkButton.hidden = false;
      if (spamButton)
        spamButton.hidden = true;
    }
  },

  // unhides all buttons - used during customization of toolbar
  showAllButtons: function() {

	// get message-specific header buttons
	var deleteButton = document.getElementById("hdrTrashButton");
	var trashButton = document.getElementById("gmailbuttons-trash-button");
	var junkButton = document.getElementById("hdrJunkButton");
	var spamButton = document.getElementById("gmailbuttons-spam-button");

	// show all buttons
	if (deleteButton)
      deleteButton.hidden = false;
	if (trashButton)
	  trashButton.hidden = false;
	if (junkButton)
	  junkButton.hidden = false;
	if (spamButton)
	  spamButton.hidden = false;
  },

  // handle message header load events
  messageListener: {
    onStartHeaders: function() {
      // do nothing
    },

    onEndHeaders: function() {
      gmailbuttons.updateJunkSpamButtons();
    },

    onEndAttachments: function() {
      // do nothing
    }
  },

  folderDisplayListener: {
     onMessagesLoaded: function(aAll) {
       try {
        var hideJunkStatusCol = gmailbuttons.prefs.getBoolPref("hideJunkStatusCol")
        // don't need to do anything if pref doesn't exist or is false
        if(!hideJunkStatusCol)
         return;
        // get the server from the selected folder
        var svr = aAll.displayedFolder.server;
        if (!svr)
          return;
        if (gmailbuttons.IsServerGmailIMAP(svr)) {
          // hide junk status column
          var junkStatusColumn = document.getElementById("junkStatusCol");
          if (junkStatusColumn) {
            junkStatusColumn.hidden = true;
          }
        }
      } catch(ex) {
        // preference does not exist - do nothing
        //alert(ex);
      }
    },
  },

  onBeforeCustomization: function(e) {
	if (e.target.id == "header-view-toolbox")
	  gmailbuttons.showAllButtons();
  },

  onAfterCustomization: function(e) {
	if (e.target.id == "header-view-toolbox")
	  gmailbuttons.updateJunkSpamButtons();
  },

  // search for folder flagged as a special folder. i.e. Trash and Spam folders
  getSpecialFolder: function(fldr, flag) {
    /* TODO would be nice if we could do this directly using XPATH */
	/* for now, we use recurstion to search folders instead */

	// make sure we have a folder
	if (!(fldr instanceof Ci.nsIMsgFolder))
	  return;
	// if folder is flagged as trash folder, retun the folder
    if (fldr.isSpecialFolder(flag))
		return fldr;
	// otherwise, search recursivly
	if (fldr.hasSubFolders) {
	  var subfldrs = fldr.subFolders;
	  while (subfldrs.hasMoreElements()) {
	    var subfldr = subfldrs.getNext();
	    var result = this.getSpecialFolder(subfldr, flag);
  	    if (result)
	  	  return result;
	  }
	}
	// no trash folders were found
	return;
  },

  // moves the selected message to a special folder. i.e. [Gmail]/Trash
  MoveToSpecialFolder: function(flag, e) {
    var svr = this.GetMessageServer();
    if (this.IsServerGmailIMAP(svr)) { // mesage is on Gmail imap server
	  var specialFolder = this.getSpecialFolder(svr.rootFolder, flag);
      if (specialFolder) {
		gFolderDisplay.hintAboutToDeleteMessages();
        gDBView.doCommandWithFolder(nsMsgViewCommandType.moveMessages, specialFolder);
		//return; // otherwise show error mesage below
	  }
    } // trash button should not be visivle if not a Gmail imap message
	// TODO may want error message here
  }
};

// listen for initial window load event
window.addEventListener("load", function () { gmailbuttons.onLoad(); }, false);
// listen for window unload event
window.addEventListener("unload", function () { gmailbuttons.onUnload(); }, false);
// listen for customization events
window.addEventListener("beforecustomization", function (e) { gmailbuttons.onBeforeCustomization(e); }, false);
window.addEventListener("aftercustomization", function (e) { gmailbuttons.onAfterCustomization(e); }, false);
// listen for messages loading
gMessageListeners.push(gmailbuttons.messageListener);
// listen for folder selection
FolderDisplayListenerManager.registerListener(gmailbuttons.folderDisplayListener);<commit_msg>fix indentation<commit_after>var gmailbuttons = {
  onLoad: function() {
    // initialization code
    this.initialized = true;
    this.strings = document.getElementById("gmailbuttons-strings");
    // add support for preferences
    this.prefs = Components.classes["@mozilla.org/preferences-service;1"]
        .getService(Components.interfaces.nsIPrefService)
        .getBranch("extensions.gmailbuttons.");
    this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
    this.prefs.addObserver("", this, false);
  },

  onUnload: function() {
    // cleanup preferences
    this.prefs.removeObserver("", this);
  },

  observe: function(subject, topic, data) {
    if (topic != "nsPref:changed") {
      return; // only need to act on pref change
    }
    // process change
    switch(data) {
      case "showDeleteButton":
         this.updateJunkSpamButtons();
         break;
      }
  },

  GetMessageFolder: function() {
    // get current message
    var hdr = gFolderDisplay.selectedMessage;
    // give up if no message selected or this is a dummy header
    if (!hdr || gMessageDisplay.isDummy)
      return;
    // get folder that contains message
    var fldr = hdr.folder;
    if (!fldr) // message not in folder somehow?
      return;
    return fldr;
  },

  GetMessageServer: function() {
    var fldr = this.GetMessageFolder();
    if (!fldr) // message not in folder somehow?
      return;
    // get server that hosts folder
    var svr = fldr.server;
    if (!svr) // folder does not have server?
      return;
    return svr;
  },

  // returns true if message is in Gmail imap
  IsServerGmailIMAP: function(svr) {
    // check that svr parameter is valid
    if (!(svr instanceof Ci.nsIImapIncomingServer))
      return;
    // check to see if it is imap and Gmail server
    return (svr.type == "imap" && svr.isGMailServer);
  },

  IsSpecialFolder: function(fldr, flag) {
    // make sure this is a valid folder
    if (!(fldr instanceof Ci.nsMessageFolder))
	  return;
    // check if folder has special folder flag
    return fldr.isSpecialFolder(flag);
  },

  updateJunkSpamButtons: function() {

    /* get message-specific header buttons */
    var deleteButton = document.getElementById("hdrTrashButton");
    var trashButton = document.getElementById("gmailbuttons-trash-button");
    var junkButton = document.getElementById("hdrJunkButton");
    var spamButton = document.getElementById("gmailbuttons-spam-button");

    if (this.IsServerGmailIMAP(this.GetMessageServer())) {
      // this is a Gmail imap account

      /* get actual folder names from server  */
      try {
        var svr = this.GetMessageServer();
        var svrRootFolder = svr.rootFolder;
        var trashFolder = this.getSpecialFolder(svrRootFolder, nsMsgFolderFlags.Trash);
        var spamFolder = this.getSpecialFolder(svrRootFolder, nsMsgFolderFlags.Junk);
      } catch(ex) {
        // don't need to do anything here
        //alert(ex);
      }
      // get label text
      var trashLabel = trashFolder ? trashFolder.prettiestName :
          this.strings.getString("gmailbuttons.error");
      var spamLabel = spamFolder ? spamFolder.prettiestName :
          this.strings.getString("gmailbuttons.error");
      // get tooltip text
      var trashTooltip = trashFolder ?
          this.strings.getFormattedString("gmailbuttons.moveButton.tooltip",
          [trashFolder.URI.replace(svrRootFolder.URI, "").substr(1)], 1) :
          this.strings.getString("gmailbuttons.error");
      var spamTooltip = spamFolder ?
          this.strings.getFormattedString("gmailbuttons.moveButton.tooltip",
          [spamFolder.URI.replace(svrRootFolder.URI, "").substr(1)], 1) :
          this.strings.getString("gmailbuttons.error");

      if (deleteButton) {
        // save the original tooltip - this only runs once
        if (!deleteButton.oldTooltipText)
          deleteButton.oldTooltipText = deleteButton.tooltipText;
        // apply new tooltip
        deleteButton.tooltipText = this.strings.getString("gmailbuttons.deleteButton.tooltip");
      try {
        var showDelete = this.prefs.getBoolPref("showDeleteButton")
        deleteButton.hidden = !showDelete;
        } catch(ex) {
        // preference does not exist - do nothing
      }
        }
      if (trashButton) {
        trashButton.hidden = false;
        trashButton.label = trashLabel;
        trashButton.tooltipText = trashTooltip;
      }
      if (junkButton)
        junkButton.hidden = true;
      if (spamButton)	{
        spamButton.hidden = false;
        spamButton.label = spamLabel;
        spamButton.tooltipText = spamTooltip;
      }
    } else {
      // this is not a GMail account

      if (deleteButton) {
        if (deleteButton.oldTooltipText)
          deleteButton.tooltipText = deleteButton.oldTooltipText;
      deleteButton.hidden = false;
        }
      if (trashButton)
          // if (!IsSpecialFolder(this.getMessageFolder(), Ci.nsMsgFolderFlags.Trash))
        trashButton.hidden = true; // TODO hide trash button if we are in the [Gmail]/Trash folder
      if (junkButton)
        junkButton.hidden = false;
      if (spamButton)
        spamButton.hidden = true;
    }
  },

  // unhides all buttons - used during customization of toolbar
  showAllButtons: function() {

	// get message-specific header buttons
	var deleteButton = document.getElementById("hdrTrashButton");
	var trashButton = document.getElementById("gmailbuttons-trash-button");
	var junkButton = document.getElementById("hdrJunkButton");
	var spamButton = document.getElementById("gmailbuttons-spam-button");

	// show all buttons
	if (deleteButton)
      deleteButton.hidden = false;
	if (trashButton)
	  trashButton.hidden = false;
	if (junkButton)
	  junkButton.hidden = false;
	if (spamButton)
	  spamButton.hidden = false;
  },

  // handle message header load events
  messageListener: {
    onStartHeaders: function() {
      // do nothing
    },

    onEndHeaders: function() {
      gmailbuttons.updateJunkSpamButtons();
    },

    onEndAttachments: function() {
      // do nothing
    }
  },

  folderDisplayListener: {
     onMessagesLoaded: function(aAll) {
       try {
        var hideJunkStatusCol = gmailbuttons.prefs.getBoolPref("hideJunkStatusCol")
        // don't need to do anything if pref doesn't exist or is false
        if(!hideJunkStatusCol)
         return;
        // get the server from the selected folder
        var svr = aAll.displayedFolder.server;
        if (!svr)
          return;
        if (gmailbuttons.IsServerGmailIMAP(svr)) {
          // hide junk status column
          var junkStatusColumn = document.getElementById("junkStatusCol");
          if (junkStatusColumn) {
            junkStatusColumn.hidden = true;
          }
        }
      } catch(ex) {
        // preference does not exist - do nothing
        //alert(ex);
      }
    },
  },

  onBeforeCustomization: function(e) {
    if (e.target.id == "header-view-toolbox")
      gmailbuttons.showAllButtons();
  },

  onAfterCustomization: function(e) {
    if (e.target.id == "header-view-toolbox")
      gmailbuttons.updateJunkSpamButtons();
  },

  // search for folder flagged as a special folder. i.e. Trash and Spam folders
  getSpecialFolder: function(fldr, flag) {
    /* TODO would be nice if we could do this directly using XPATH */

    /* for now, we use recurstion to search folders instead */

    // make sure we have a folder
    if (!(fldr instanceof Ci.nsIMsgFolder))
      return;
    // if folder is flagged as trash folder, retun the folder
    if (fldr.isSpecialFolder(flag))
      return fldr;
    // otherwise, search recursivly
    if (fldr.hasSubFolders) {
      var subfldrs = fldr.subFolders;
      while (subfldrs.hasMoreElements()) {
        var subfldr = subfldrs.getNext();
        var result = this.getSpecialFolder(subfldr, flag);
          if (result)
            return result;
      }
    }
    // no trash folders were found
    return;
  },

  // moves the selected message to a special folder. i.e. [Gmail]/Trash
  MoveToSpecialFolder: function(flag, e) {
    var svr = this.GetMessageServer();
    if (this.IsServerGmailIMAP(svr)) { // mesage is on Gmail imap server
      var specialFolder = this.getSpecialFolder(svr.rootFolder, flag);
        if (specialFolder) {
      gFolderDisplay.hintAboutToDeleteMessages();
          gDBView.doCommandWithFolder(nsMsgViewCommandType.moveMessages, specialFolder);
      //return; // otherwise show error mesage below
      }
    } // trash button should not be visible if not a Gmail imap message
	// TODO may want error message here
  }
};

// listen for initial window load event
window.addEventListener("load", function () { gmailbuttons.onLoad(); }, false);
// listen for window unload event
window.addEventListener("unload", function () { gmailbuttons.onUnload(); }, false);
// listen for customization events
window.addEventListener("beforecustomization", function (e) { gmailbuttons.onBeforeCustomization(e); }, false);
window.addEventListener("aftercustomization", function (e) { gmailbuttons.onAfterCustomization(e); }, false);
// listen for messages loading
gMessageListeners.push(gmailbuttons.messageListener);
// listen for folder selection
FolderDisplayListenerManager.registerListener(gmailbuttons.folderDisplayListener);<|endoftext|>


<commit_before>// UI for the Navigation Bar in the sidebar

NavigationBar = {
  detectSelectedSection: function() {
    var link = $$('.nav_links a').detect(function(e) {
      return e.getAttribute('href') == window.location.pathname
    })
    if(link) return link.up('.el')
  },

  showContainers: function(current) {
    var container = current.up('.contained')
    if (container) {
      container.show()
      prev_container = container.up('.contained')
      prev_container && prev_container.show()
    }
  },

  toggleElement: function(el, effect) {
    var contained = el.next()
    // if next element is an expanded area..
    if (contained && contained.hasClassName('contained')) {
      if (el.hasClassName('expanded')) {
        // contract it if it's open
        el.removeClassName('expanded')
        contained.hide()
      } else {
        // contract others if open
        el.up().select('.contained').invoke('hide')
        el.up().select('.el').invoke('removeClassName', 'expanded')
        // expand the selected one
        el.addClassName('expanded')
        effect ? contained.blindDown({ duration: 0.2 }) : contained.show()
      }
      // Stop the event and don't follow the link
      return true
    }
    // Stop the event if it's selected (don't follow the link)
    return el.hasClassName('selected')
  },

  selectElement: function(el) {
    $$('.nav_links .el.selected').invoke('removeClassName', 'selected')
    el.addClassName('selected')
  }
}

document.on("dom:loaded", function() {
  $$('.nav_links .contained').invoke('hide')

  // Select and expand the current element
  var current = NavigationBar.detectSelectedSection()
  if (current) {
    current.addClassName('selected')
    NavigationBar.toggleElement(current)
    NavigationBar.showContainers(current)
  }
  // If we're on All Projects, then expand the recent projects list
  var elements = $$('.nav_links .el')
  if (elements[0].hasClassName('selected')) {
    NavigationBar.toggleElement(elements[1])
  }
})

document.on('click', '.nav_links .el', function(e,el) {
  if (e.isMiddleClick()) return
  NavigationBar.toggleElement(el, true) && e.stop()
})
<commit_msg>Nicer animations for the navigation bar<commit_after>// UI for the Navigation Bar in the sidebar

NavigationBar = {
  detectSelectedSection: function() {
    var link = $$('.nav_links a').detect(function(e) {
      return e.getAttribute('href') == window.location.pathname
    })
    if(link) return link.up('.el')
  },

  showContainers: function(current) {
    var container = current.up('.contained')
    if (container) {
      container.show()
      prev_container = container.up('.contained')
      prev_container && prev_container.show()
    }
  },

  toggleElement: function(el, effect) {
    var contained = el.next()
    // if next element is an expanded area..
    if (contained && contained.hasClassName('contained')) {
      if (el.hasClassName('expanded')) {
        // contract it if it's open
        el.removeClassName('expanded')
        contained.blindUp({ duration: 0.2 })
      } else {
        // contract others if open
        var visible_containers = el.up().select('.contained').select( function(e) { return e.visible() })
        effect ? visible_containers.invoke("blindUp", { duration: 0.2 }) : visible_containers.invoke('hide')
        el.up().select('.el').invoke('removeClassName', 'expanded')
        // expand the selected one
        el.addClassName('expanded')
        effect ? contained.blindDown({ duration: 0.2 }) : contained.show()
      }
      // Stop the event and don't follow the link
      return true
    }
    // Stop the event if it's selected (don't follow the link)
    return el.hasClassName('selected')
  },

  selectElement: function(el) {
    $$('.nav_links .el.selected').invoke('removeClassName', 'selected')
    el.addClassName('selected')
  }
}

document.on("dom:loaded", function() {
  $$('.nav_links .contained').invoke('hide')

  // Select and expand the current element
  var current = NavigationBar.detectSelectedSection()
  if (current) {
    current.addClassName('selected')
    NavigationBar.toggleElement(current)
    NavigationBar.showContainers(current)
  }
  // If we're on All Projects, then expand the recent projects list
  var elements = $$('.nav_links .el')
  if (elements[0].hasClassName('selected')) {
    NavigationBar.toggleElement(elements[1])
  }
})

document.on('click', '.nav_links .el', function(e,el) {
  if (e.isMiddleClick()) return
  NavigationBar.toggleElement(el, true) && e.stop()
})
<|endoftext|>