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

export const PRIVATE = Symbol('@@webxr-polyfill/XRRenderState');

export const XRRenderStateInit = Object.freeze({
  depthNear: 0.1,
  depthFar: 1000.0,
  inlineVerticalFieldOfView: null,
  baseLayer: null
});

// TODO: inlineVerticalFieldOfView must be PI * 0.5 for non-immersive sessions.
export default class XRRenderState {
  /**
   * @param {Object?} stateInit
   */
  constructor(stateInit = {}) {
    const config = Object.assign({}, XRRenderStateInit, stateInit);
    this[PRIVATE] = { config };
  }

  /**
   * @return {number}
   */
  get depthNear() { return this[PRIVATE].depthNear; }

  /**
   * @return {number}
   */
  get depthFar() { return this[PRIVATE].depthFar; }

  /**
   * @return {number?}
   */
  get inlineVerticalFieldOfView() { return this[PRIVATE].inlineVerticalFieldOfView; }

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

export const PRIVATE = Symbol('@@webxr-polyfill/XRRenderState');

export const XRRenderStateInit = Object.freeze({
  depthNear: 0.1,
  depthFar: 1000.0,
  inlineVerticalFieldOfView: null,
  baseLayer: null
});

// TODO: inlineVerticalFieldOfView must be PI * 0.5 for non-immersive sessions.
export default class XRRenderState {
  /**
   * @param {Object?} stateInit
   */
  constructor(stateInit = {}) {
    const config = Object.assign({}, XRRenderStateInit, stateInit);
    this[PRIVATE] = { config };
  }

  /**
   * @return {number}
   */
  get depthNear() { return this[PRIVATE].config.depthNear; }

  /**
   * @return {number}
   */
  get depthFar() { return this[PRIVATE].config.depthFar; }

  /**
   * @return {number?}
   */
  get inlineVerticalFieldOfView() { return this[PRIVATE].config.inlineVerticalFieldOfView; }

  /**
   * @return {XRWebGLLayer}
   */
  get baseLayer() { return this[PRIVATE].config.baseLayer; }
}
<|endoftext|>


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

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, window, brackets, Mustache */

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

    // Load dependent modules
    var Menus             = require("command/Menus"),
        StringUtils       = require("utils/StringUtils"),
        PopUpManager      = require("widgets/PopUpManager"),
        ViewUtils         = require("utils/ViewUtils"),
        KeyBindingManager = require("command/KeyBindingManager"),
        KeyEvent          = require("utils/KeyEvent");

    var CodeHintListHTML  = require("text!htmlContent/code-hint-list.html");

    /**
     * Displays a popup list of hints for a given editor context.
     *
     * @constructor
     * @param {Editor} editor
     */
    function CodeHintList(editor) {

        /**
         * The list of hints to display
         *
         * @type {Array.<string|jQueryObject>}
         */
        this.hints = [];

        /**
         * The selected position in the list; otherwise -1.
         *
         * @type {number}
         */
        this.selectedIndex = -1;

        /**
         * The maximum number of hints to display
         *
         * @type {number}
         */
        this.maxResults = 999;

        /**
         * Is the list currently open?
         *
         * @type {boolean}
         */
        this.opened = false;

        /**
         * The editor context
         *
         * @type {Editor}
         */
        this.editor = editor;

        /**
         * The hint selection callback function
         *
         * @type {Function}
         */
        this.handleSelect = null;

        /**
         * The hint list closure callback function
         *
         * @type {Function}
         */
        this.handleClose = null;

        /**
         * The hint list menu object
         *
         * @type {jQuery.Object}
         */
        this.$hintMenu =
            $("

")
                .append($("")
                        .hide())
                .append("

");

        this._keydownHook = this._keydownHook.bind(this);
    }

    /**
     * Select the item in the hint list at the specified index, or remove the
     * selection if index < 0.
     *
     * @private
     * @param {number} index
     */
    CodeHintList.prototype._setSelectedIndex = function (index) {
        var items = this.$hintMenu.find("li");

        // Range check
        index = Math.max(-1, Math.min(index, items.length - 1));

        // Clear old highlight
        if (this.selectedIndex !== -1) {
            $(items[this.selectedIndex]).find("a").removeClass("highlight");
        }

        this.selectedIndex = index;

        // Highlight the new selected item, if necessary
        if (this.selectedIndex !== -1) {
            var $item = $(items[this.selectedIndex]);
            var $view = this.$hintMenu.find("ul.dropdown-menu");

            ViewUtils.scrollElementIntoView($view, $item, false);
            $item.find("a").addClass("highlight");
        }
    };

    /**
     * Rebuilds the list items for the hint list.
     *
     * @private
     */
    CodeHintList.prototype._buildListView = function (hintObj) {
        var self            = this,
            match           = hintObj.match,
            selectInitial   = hintObj.selectInitial,
            view            = { hints: [] },
            _addHint;

        this.hints = hintObj.hints;
        this.hints.handleWideResults = hintObj.handleWideResults;

        // if there is no match, assume name is already a formatted jQuery
        // object; otherwise, use match to format name for display.
        if (match) {
            _addHint = function (name) {
                var displayName = name.replace(
                    new RegExp(StringUtils.regexEscape(match), "i"),
                    "$&"
                );

                view.hints.push({ formattedHint: "" + displayName + "" });
            };
        } else {
            _addHint = function (hint) {
                view.hints.push({ formattedHint: (hint instanceof $) ? "" : hint });
            };
        }

        // clear the list
        this.$hintMenu.find("li").remove();

        // if there are no hints then close the list; otherwise add them and
        // set the selection
        if (this.hints.length === 0) {
            if (this.handleClose) {
                this.handleClose();
            }
        } else {
            this.hints.some(function (item, index) {
                if (index > self.maxResults) {
                    return true;
                }

                _addHint(item);
            });

            // render code hint list
            var $ul = this.$hintMenu.find("ul.dropdown-menu"),
                $parent = $ul.parent();

            // remove list temporarily to save rendering time
            $ul.remove().append(Mustache.render(CodeHintListHTML, view));

            $ul.children("li").each(function (index, element) {
                var hint        = self.hints[index],
                    $element    = $(element);

                // store hint on each list item
                $element.data("hint", hint);

                // insert jQuery hint objects after the template is rendered
                if (hint instanceof $) {
                    $element.find(".codehint-item").append(hint);
                }
            });

            // delegate list item events to the top-level ul list element
            $ul.on("click", "li", function (e) {
                // Don't let the click propagate upward (otherwise it will
                // hit the close handler in bootstrap-dropdown).
                e.stopPropagation();
                if (self.handleSelect) {
                    self.handleSelect($(this).data("hint"));
                }
            });

            // Lists with wide results require different formatting
            if (this.hints.handleWideResults) {
                $ul.find("li a").addClass("wide-result");
            }

            // attach to DOM
            $parent.append($ul);

            this._setSelectedIndex(selectInitial ? 0 : -1);
        }
    };

    /**
     * Computes top left location for hint list so that the list is not clipped by the window.
     * Also computes the largest available width.
     *
     * @private
     * @return {{left: number, top: number, width: number}}
     */
    CodeHintList.prototype._calcHintListLocation = function () {
        var cursor      = this.editor._codeMirror.cursorCoords(),
            posTop      = cursor.bottom,
            posLeft     = cursor.left,
            textHeight  = this.editor.getTextHeight(),
            $window     = $(window),
            $menuWindow = this.$hintMenu.children("ul"),
            menuHeight  = $menuWindow.outerHeight();

        // TODO Ty: factor out menu repositioning logic so code hints and Context menus share code
        // adjust positioning so menu is not clipped off bottom or right
        var bottomOverhang = posTop + menuHeight - $window.height();
        if (bottomOverhang > 0) {
            posTop -= (textHeight + 2 + menuHeight);
        }

        posTop -= 30;   // shift top for hidden parent element

        var menuWidth = $menuWindow.width();
        var availableWidth = menuWidth;
        var rightOverhang = posLeft + menuWidth - $window.width();
        if (rightOverhang > 0) {
            posLeft = Math.max(0, posLeft - rightOverhang);
        } else if (this.hints.handleWideResults) {
            // Right overhang is negative
            availableWidth = menuWidth + Math.abs(rightOverhang);
        }

        return {left: posLeft, top: posTop, width: availableWidth};
    };

    /**
     * Check whether keyCode is one of the keys that we handle or not.
     *
     * @param {number} keyCode
     */
    CodeHintList.prototype.isHandlingKeyCode = function (keyCode) {
        return (keyCode === KeyEvent.DOM_VK_UP || keyCode === KeyEvent.DOM_VK_DOWN ||
                keyCode === KeyEvent.DOM_VK_PAGE_UP || keyCode === KeyEvent.DOM_VK_PAGE_DOWN ||
                keyCode === KeyEvent.DOM_VK_RETURN || keyCode === KeyEvent.DOM_VK_TAB);

    };

    /**
     * Convert keydown events into hint list navigation actions.
     *
     * @param {KeyBoardEvent} keyEvent
     */
    CodeHintList.prototype._keydownHook = function (event) {
        var keyCode,
            self = this;

        // positive distance rotates down; negative distance rotates up
        function _rotateSelection(distance) {
            var len = Math.min(self.hints.length, self.maxResults),
                pos;

            if (self.selectedIndex < 0) {
                // set the initial selection
                pos = (distance > 0) ? distance - 1 : len - 1;

            } else {
                // adjust current selection
                pos = self.selectedIndex;

                // Don't "rotate" until all items have been shown
                if (distance > 0) {
                    if (pos === (len - 1)) {
                        pos = 0;  // wrap
                    } else {
                        pos = Math.min(pos + distance, len - 1);
                    }
                } else {
                    if (pos === 0) {
                        pos = (len - 1);  // wrap
                    } else {
                        pos = Math.max(pos + distance, 0);
                    }
                }
            }

            self._setSelectedIndex(pos);
        }

        // Calculate the number of items per scroll page.
        function _itemsPerPage() {
            var itemsPerPage = 1,
                $items = self.$hintMenu.find("li"),
                $view = self.$hintMenu.find("ul.dropdown-menu"),
                itemHeight;

            if ($items.length !== 0) {
                itemHeight = $($items[0]).height();
                if (itemHeight) {
                    // round down to integer value
                    itemsPerPage = Math.floor($view.height() / itemHeight);
                    itemsPerPage = Math.max(1, Math.min(itemsPerPage, $items.length));
                }
            }

            return itemsPerPage;
        }

        // If we're no longer visible, skip handling the key and end the session.
        if (!this.isOpen()) {
            this.handleClose();
            return false;
        }

        // (page) up, (page) down, enter and tab key are handled by the list
        if (event.type === "keydown" && this.isHandlingKeyCode(event.keyCode)) {
            keyCode = event.keyCode;

            if (event.shiftKey &&
                    (event.keyCode === KeyEvent.DOM_VK_UP ||
                     event.keyCode === KeyEvent.DOM_VK_DOWN ||
                     event.keyCode === KeyEvent.DOM_VK_PAGE_UP ||
                     event.keyCode === KeyEvent.DOM_VK_PAGE_DOWN)) {
                this.handleClose();

                // Let the event bubble.
                return false;
            } else if (keyCode === KeyEvent.DOM_VK_UP) {
                _rotateSelection.call(this, -1);
            } else if (keyCode === KeyEvent.DOM_VK_DOWN) {
                _rotateSelection.call(this, 1);
            } else if (keyCode === KeyEvent.DOM_VK_PAGE_UP) {
                _rotateSelection.call(this, -_itemsPerPage());
            } else if (keyCode === KeyEvent.DOM_VK_PAGE_DOWN) {
                _rotateSelection.call(this, _itemsPerPage());
            } else if (this.selectedIndex !== -1 &&
                    (keyCode === KeyEvent.DOM_VK_RETURN || keyCode === KeyEvent.DOM_VK_TAB)) {
                // Trigger a click handler to commmit the selected item
                $(this.$hintMenu.find("li")[this.selectedIndex]).trigger("click");
            } else {
                // Let the event bubble.
                return false;
            }

            event.stopImmediatePropagation();
            event.preventDefault();
            return true;
        }

        // If we didn't handle it, let other global keydown hooks handle it.
        return false;
    };

    /**
     * Is the CodeHintList open?
     *
     * @return {boolean}
     */
    CodeHintList.prototype.isOpen = function () {
        // We don't get a notification when the dropdown closes. The best
        // we can do is keep an "opened" flag and check to see if we
        // still have the "open" class applied.
        if (this.opened && !this.$hintMenu.hasClass("open")) {
            this.opened = false;
        }

        return this.opened;
    };

    /**
     * Displays the hint list at the current cursor position
     *
     * @param {{hints: Array.<string|jQueryObject>, match: string,
     *          selectInitial: boolean}} hintObj
     */
    CodeHintList.prototype.open = function (hintObj) {
        Menus.closeAll();
        this._buildListView(hintObj);

        if (this.hints.length) {
            // Need to add the menu to the DOM before trying to calculate its ideal location.
            $("#codehint-menu-bar > ul").append(this.$hintMenu);

            var hintPos = this._calcHintListLocation();

            this.$hintMenu.addClass("open")
                .css({"left": hintPos.left, "top": hintPos.top, "width": hintPos.width + "px"});
            this.opened = true;

            PopUpManager.addPopUp(this.$hintMenu, this.handleClose, true);

            KeyBindingManager.addGlobalKeydownHook(this._keydownHook);
        }
    };

    /**
     * Updates the (already open) hint list window with new hints
     *
     * @param {{hints: Array.<string|jQueryObject>, match: string,
     *          selectInitial: boolean}} hintObj
     */
    CodeHintList.prototype.update = function (hintObj) {
        this.$hintMenu.addClass("apply-transition");
        this._buildListView(hintObj);

        // Update the CodeHintList location
        if (this.hints.length) {
            var hintPos = this._calcHintListLocation();
            this.$hintMenu.css({"left": hintPos.left, "top": hintPos.top,
                                "width": hintPos.width + "px"});
        }
    };

    /**
     * Closes the hint list
     */
    CodeHintList.prototype.close = function () {
        this.$hintMenu.removeClass("open");
        this.opened = false;

        PopUpManager.removePopUp(this.$hintMenu);
        this.$hintMenu.remove();

        KeyBindingManager.removeGlobalKeydownHook(this._keydownHook);
    };

    /**
     * Set the hint list selection callback function
     *
     * @param {Function} callback
     */
    CodeHintList.prototype.onSelect = function (callback) {
        this.handleSelect = callback;
    };

    /**
     * Set the hint list closure callback function
     *
     * @param {Function} callback
     */
    CodeHintList.prototype.onClose = function (callback) {
        // TODO: Due to #1381, this won't get called if the user clicks out of
        // the code hint menu. That's (sort of) okay right now since it doesn't
        // really matter if a single old invisible code hint list is lying
        // around (it will ignore keydown events, and it'll get closed the next
        // time the user pops up a code hint). Once #1381 is fixed this issue
        // should go away.
        this.handleClose = callback;
    };

    // Define public API
    exports.CodeHintList = CodeHintList;
});
<commit_msg>Don't commit code hints when tab key is pressed.<commit_after>/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, window, brackets, Mustache */

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

    // Load dependent modules
    var Menus             = require("command/Menus"),
        StringUtils       = require("utils/StringUtils"),
        PopUpManager      = require("widgets/PopUpManager"),
        ViewUtils         = require("utils/ViewUtils"),
        KeyBindingManager = require("command/KeyBindingManager"),
        KeyEvent          = require("utils/KeyEvent");

    var CodeHintListHTML  = require("text!htmlContent/code-hint-list.html");

    /**
     * Displays a popup list of hints for a given editor context.
     *
     * @constructor
     * @param {Editor} editor
     */
    function CodeHintList(editor) {

        /**
         * The list of hints to display
         *
         * @type {Array.<string|jQueryObject>}
         */
        this.hints = [];

        /**
         * The selected position in the list; otherwise -1.
         *
         * @type {number}
         */
        this.selectedIndex = -1;

        /**
         * The maximum number of hints to display
         *
         * @type {number}
         */
        this.maxResults = 999;

        /**
         * Is the list currently open?
         *
         * @type {boolean}
         */
        this.opened = false;

        /**
         * The editor context
         *
         * @type {Editor}
         */
        this.editor = editor;

        /**
         * The hint selection callback function
         *
         * @type {Function}
         */
        this.handleSelect = null;

        /**
         * The hint list closure callback function
         *
         * @type {Function}
         */
        this.handleClose = null;

        /**
         * The hint list menu object
         *
         * @type {jQuery.Object}
         */
        this.$hintMenu =
            $("

")
                .append($("")
                        .hide())
                .append("

");

        this._keydownHook = this._keydownHook.bind(this);
    }

    /**
     * Select the item in the hint list at the specified index, or remove the
     * selection if index < 0.
     *
     * @private
     * @param {number} index
     */
    CodeHintList.prototype._setSelectedIndex = function (index) {
        var items = this.$hintMenu.find("li");

        // Range check
        index = Math.max(-1, Math.min(index, items.length - 1));

        // Clear old highlight
        if (this.selectedIndex !== -1) {
            $(items[this.selectedIndex]).find("a").removeClass("highlight");
        }

        this.selectedIndex = index;

        // Highlight the new selected item, if necessary
        if (this.selectedIndex !== -1) {
            var $item = $(items[this.selectedIndex]);
            var $view = this.$hintMenu.find("ul.dropdown-menu");

            ViewUtils.scrollElementIntoView($view, $item, false);
            $item.find("a").addClass("highlight");
        }
    };

    /**
     * Rebuilds the list items for the hint list.
     *
     * @private
     */
    CodeHintList.prototype._buildListView = function (hintObj) {
        var self            = this,
            match           = hintObj.match,
            selectInitial   = hintObj.selectInitial,
            view            = { hints: [] },
            _addHint;

        this.hints = hintObj.hints;
        this.hints.handleWideResults = hintObj.handleWideResults;

        // if there is no match, assume name is already a formatted jQuery
        // object; otherwise, use match to format name for display.
        if (match) {
            _addHint = function (name) {
                var displayName = name.replace(
                    new RegExp(StringUtils.regexEscape(match), "i"),
                    "$&"
                );

                view.hints.push({ formattedHint: "" + displayName + "" });
            };
        } else {
            _addHint = function (hint) {
                view.hints.push({ formattedHint: (hint instanceof $) ? "" : hint });
            };
        }

        // clear the list
        this.$hintMenu.find("li").remove();

        // if there are no hints then close the list; otherwise add them and
        // set the selection
        if (this.hints.length === 0) {
            if (this.handleClose) {
                this.handleClose();
            }
        } else {
            this.hints.some(function (item, index) {
                if (index > self.maxResults) {
                    return true;
                }

                _addHint(item);
            });

            // render code hint list
            var $ul = this.$hintMenu.find("ul.dropdown-menu"),
                $parent = $ul.parent();

            // remove list temporarily to save rendering time
            $ul.remove().append(Mustache.render(CodeHintListHTML, view));

            $ul.children("li").each(function (index, element) {
                var hint        = self.hints[index],
                    $element    = $(element);

                // store hint on each list item
                $element.data("hint", hint);

                // insert jQuery hint objects after the template is rendered
                if (hint instanceof $) {
                    $element.find(".codehint-item").append(hint);
                }
            });

            // delegate list item events to the top-level ul list element
            $ul.on("click", "li", function (e) {
                // Don't let the click propagate upward (otherwise it will
                // hit the close handler in bootstrap-dropdown).
                e.stopPropagation();
                if (self.handleSelect) {
                    self.handleSelect($(this).data("hint"));
                }
            });

            // Lists with wide results require different formatting
            if (this.hints.handleWideResults) {
                $ul.find("li a").addClass("wide-result");
            }

            // attach to DOM
            $parent.append($ul);

            this._setSelectedIndex(selectInitial ? 0 : -1);
        }
    };

    /**
     * Computes top left location for hint list so that the list is not clipped by the window.
     * Also computes the largest available width.
     *
     * @private
     * @return {{left: number, top: number, width: number}}
     */
    CodeHintList.prototype._calcHintListLocation = function () {
        var cursor      = this.editor._codeMirror.cursorCoords(),
            posTop      = cursor.bottom,
            posLeft     = cursor.left,
            textHeight  = this.editor.getTextHeight(),
            $window     = $(window),
            $menuWindow = this.$hintMenu.children("ul"),
            menuHeight  = $menuWindow.outerHeight();

        // TODO Ty: factor out menu repositioning logic so code hints and Context menus share code
        // adjust positioning so menu is not clipped off bottom or right
        var bottomOverhang = posTop + menuHeight - $window.height();
        if (bottomOverhang > 0) {
            posTop -= (textHeight + 2 + menuHeight);
        }

        posTop -= 30;   // shift top for hidden parent element

        var menuWidth = $menuWindow.width();
        var availableWidth = menuWidth;
        var rightOverhang = posLeft + menuWidth - $window.width();
        if (rightOverhang > 0) {
            posLeft = Math.max(0, posLeft - rightOverhang);
        } else if (this.hints.handleWideResults) {
            // Right overhang is negative
            availableWidth = menuWidth + Math.abs(rightOverhang);
        }

        return {left: posLeft, top: posTop, width: availableWidth};
    };

    /**
     * Check whether keyCode is one of the keys that we handle or not.
     *
     * @param {number} keyCode
     */
    CodeHintList.prototype.isHandlingKeyCode = function (keyCode) {
        return (keyCode === KeyEvent.DOM_VK_UP || keyCode === KeyEvent.DOM_VK_DOWN ||
                keyCode === KeyEvent.DOM_VK_PAGE_UP || keyCode === KeyEvent.DOM_VK_PAGE_DOWN ||
                keyCode === KeyEvent.DOM_VK_RETURN || keyCode === KeyEvent.DOM_VK_TAB);

    };

    /**
     * Convert keydown events into hint list navigation actions.
     *
     * @param {KeyBoardEvent} keyEvent
     */
    CodeHintList.prototype._keydownHook = function (event) {
        var keyCode,
            self = this;

        // positive distance rotates down; negative distance rotates up
        function _rotateSelection(distance) {
            var len = Math.min(self.hints.length, self.maxResults),
                pos;

            if (self.selectedIndex < 0) {
                // set the initial selection
                pos = (distance > 0) ? distance - 1 : len - 1;

            } else {
                // adjust current selection
                pos = self.selectedIndex;

                // Don't "rotate" until all items have been shown
                if (distance > 0) {
                    if (pos === (len - 1)) {
                        pos = 0;  // wrap
                    } else {
                        pos = Math.min(pos + distance, len - 1);
                    }
                } else {
                    if (pos === 0) {
                        pos = (len - 1);  // wrap
                    } else {
                        pos = Math.max(pos + distance, 0);
                    }
                }
            }

            self._setSelectedIndex(pos);
        }

        // Calculate the number of items per scroll page.
        function _itemsPerPage() {
            var itemsPerPage = 1,
                $items = self.$hintMenu.find("li"),
                $view = self.$hintMenu.find("ul.dropdown-menu"),
                itemHeight;

            if ($items.length !== 0) {
                itemHeight = $($items[0]).height();
                if (itemHeight) {
                    // round down to integer value
                    itemsPerPage = Math.floor($view.height() / itemHeight);
                    itemsPerPage = Math.max(1, Math.min(itemsPerPage, $items.length));
                }
            }

            return itemsPerPage;
        }

        // If we're no longer visible, skip handling the key and end the session.
        if (!this.isOpen()) {
            this.handleClose();
            return false;
        }

        // (page) up, (page) down, enter and tab key are handled by the list
        if (event.type === "keydown" && this.isHandlingKeyCode(event.keyCode)) {
            keyCode = event.keyCode;

            if (event.shiftKey &&
                    (event.keyCode === KeyEvent.DOM_VK_UP ||
                     event.keyCode === KeyEvent.DOM_VK_DOWN ||
                     event.keyCode === KeyEvent.DOM_VK_PAGE_UP ||
                     event.keyCode === KeyEvent.DOM_VK_PAGE_DOWN)) {
                this.handleClose();

                // Let the event bubble.
                return false;
            } else if (keyCode === KeyEvent.DOM_VK_UP) {
                _rotateSelection.call(this, -1);
            } else if (keyCode === KeyEvent.DOM_VK_DOWN) {
                _rotateSelection.call(this, 1);
            } else if (keyCode === KeyEvent.DOM_VK_PAGE_UP) {
                _rotateSelection.call(this, -_itemsPerPage());
            } else if (keyCode === KeyEvent.DOM_VK_PAGE_DOWN) {
                _rotateSelection.call(this, _itemsPerPage());
            } else if (this.selectedIndex !== -1 &&
                    (keyCode === KeyEvent.DOM_VK_RETURN)) {
                // Trigger a click handler to commmit the selected item
                $(this.$hintMenu.find("li")[this.selectedIndex]).trigger("click");
            } else {
                // Let the event bubble.
                return false;
            }

            event.stopImmediatePropagation();
            event.preventDefault();
            return true;
        }

        // If we didn't handle it, let other global keydown hooks handle it.
        return false;
    };

    /**
     * Is the CodeHintList open?
     *
     * @return {boolean}
     */
    CodeHintList.prototype.isOpen = function () {
        // We don't get a notification when the dropdown closes. The best
        // we can do is keep an "opened" flag and check to see if we
        // still have the "open" class applied.
        if (this.opened && !this.$hintMenu.hasClass("open")) {
            this.opened = false;
        }

        return this.opened;
    };

    /**
     * Displays the hint list at the current cursor position
     *
     * @param {{hints: Array.<string|jQueryObject>, match: string,
     *          selectInitial: boolean}} hintObj
     */
    CodeHintList.prototype.open = function (hintObj) {
        Menus.closeAll();
        this._buildListView(hintObj);

        if (this.hints.length) {
            // Need to add the menu to the DOM before trying to calculate its ideal location.
            $("#codehint-menu-bar > ul").append(this.$hintMenu);

            var hintPos = this._calcHintListLocation();

            this.$hintMenu.addClass("open")
                .css({"left": hintPos.left, "top": hintPos.top, "width": hintPos.width + "px"});
            this.opened = true;

            PopUpManager.addPopUp(this.$hintMenu, this.handleClose, true);

            KeyBindingManager.addGlobalKeydownHook(this._keydownHook);
        }
    };

    /**
     * Updates the (already open) hint list window with new hints
     *
     * @param {{hints: Array.<string|jQueryObject>, match: string,
     *          selectInitial: boolean}} hintObj
     */
    CodeHintList.prototype.update = function (hintObj) {
        this.$hintMenu.addClass("apply-transition");
        this._buildListView(hintObj);

        // Update the CodeHintList location
        if (this.hints.length) {
            var hintPos = this._calcHintListLocation();
            this.$hintMenu.css({"left": hintPos.left, "top": hintPos.top,
                                "width": hintPos.width + "px"});
        }
    };

    /**
     * Closes the hint list
     */
    CodeHintList.prototype.close = function () {
        this.$hintMenu.removeClass("open");
        this.opened = false;

        PopUpManager.removePopUp(this.$hintMenu);
        this.$hintMenu.remove();

        KeyBindingManager.removeGlobalKeydownHook(this._keydownHook);
    };

    /**
     * Set the hint list selection callback function
     *
     * @param {Function} callback
     */
    CodeHintList.prototype.onSelect = function (callback) {
        this.handleSelect = callback;
    };

    /**
     * Set the hint list closure callback function
     *
     * @param {Function} callback
     */
    CodeHintList.prototype.onClose = function (callback) {
        // TODO: Due to #1381, this won't get called if the user clicks out of
        // the code hint menu. That's (sort of) okay right now since it doesn't
        // really matter if a single old invisible code hint list is lying
        // around (it will ignore keydown events, and it'll get closed the next
        // time the user pops up a code hint). Once #1381 is fixed this issue
        // should go away.
        this.handleClose = callback;
    };

    // Define public API
    exports.CodeHintList = CodeHintList;
});
<|endoftext|>


<commit_before>/**
 * Module Name: Command
 * Description: An essential module which maps PRIVMSG input to an appropriate
 * command and then runs that command, given the user isn't banned from or
 * ignoring that command.
 */
var command = function(dbot) {
    /**
     * Is user banned from using command?
     */
    var isBanned = function(user, command) {
        var banned = false;
        if(dbot.db.bans.hasOwnProperty(command)) {
            if(dbot.db.bans[command].include(user) || dbot.db.bans['*'].include(user)) {
                banned = true;
            }
        }
        return banned;
    };

    /**
     * Does the user have the correct access level to use the command?
     */
    var hasAccess = function(user, command) {
        var access = true;
        var accessNeeded = dbot.commands[command].access;

        if(accessNeeded == 'admin') {
            if(!dbot.config.admins.include(user)) {
                access = false;
            }
        } else if(accessNeeded == 'moderator') {
            if(!dbot.config.moderators.include(user) &&
                    !dbot.config.admins.include(user)) {
                access = false;
            }
        }
        console.log(accessNeeded);
        console.log(user);

        return access;
    };

    /**
     * Is user ignoring command?
     */
    var isIgnoring = function(user, command) {
        var module = dbot.commandMap[command];
        var ignoring = false;
        if(dbot.db.ignores.hasOwnProperty(user) && dbot.db.ignores[user].include(module)) {
            ignoring = true;
        }
        return ignoring;
    };

    /**
     * Apply Regex to event message, store result. Return false if it doesn't
     * apply.
     */
    var applyRegex = function(commandName, event) {
        var applies = false;
        if(dbot.commands[commandName].hasOwnProperty('regex')) {
            var cRegex = dbot.commands[commandName].regex;
            var q = event.message.valMatch(cRegex[0], cRegex[1]);
            if(q) {
                applies = true;
                event.input = q;
            }
        } else {
            applies = true;
        }
        return applies;
    };

    return {
        'name': 'command',
        'ignorable': false,

        'commands': {
            '~usage': function(event) {
                var commandName = event.params[1];
                if(dbot.usage.hasOwnProperty(commandName)) {
                    event.reply(dbot.t('usage', {
                        'command': commandName,
                        'usage': dbot.usage[commandName]
                    }));
                } else {
                    event.reply(dbot.t('no_usage_info', {
                        'command': commandName
                    }));
                }
            },

            '~help': function(event) {
                var moduleName = event.params[1];
                if(!dbot.modules.hasOwnProperty(moduleName)) {
                    var moduleName = dbot.commandMap[moduleName];
                }

                if(moduleName && dbot.config[moduleName].hasOwnProperty('help')) {
                    var help = dbot.config[moduleName].help;
                    event.reply(dbot.t('help_link', {
                        'module': moduleName,
                        'link': help
                    }));
                } else {
                    if(!moduleName) {
                        moduleName = event.params[1];
                    }
                    event.reply(dbot.t('no_help', { 'module': moduleName }))
                }
            }
        },

        /**
         * Run the appropriate command given the input.
         */
        'listener': function(event) {
            var commandName = event.params[0];
            if(!dbot.commands.hasOwnProperty(commandName)) {
                commandName = '~';
            }

            if(isBanned(event.user, commandName)) {
                event.reply(dbot.t('command_ban', {'user': event.user}));
            } else {
                if(!isIgnoring(event.user, commandName) &&
                        hasAccess(event.user, commandName) &&
                        dbot.commands[commandName].disabled !== true) {
                    if(applyRegex(commandName, event)) {
                        try {
                            dbot.commands[commandName](event);
                        } catch(err) {
                            if(dbot.config.debugMode == true) {
                                event.reply('- Error in ' + commandName + ':');
                                event.reply('- Message: ' + err);
                                event.reply('- Top of stack: ' + err.stack.split('\n')[1].trim());
                            }
                        }
                        dbot.save();
                    } else {
                        if(commandName !== '~') {
                            if(dbot.usage.hasOwnProperty(commandName)){
                                event.reply('Usage: ' + dbot.usage[commandName]);
                            } else {
                                event.reply(dbot.t('syntax_error'));
                            }
                        }
                    }
                }
            }
        },
        'on': 'PRIVMSG'
    };
};

exports.fetch = function(dbot) {
    return command(dbot);
};

<commit_msg>Remove some debug output<commit_after>/**
 * Module Name: Command
 * Description: An essential module which maps PRIVMSG input to an appropriate
 * command and then runs that command, given the user isn't banned from or
 * ignoring that command.
 */
var command = function(dbot) {
    /**
     * Is user banned from using command?
     */
    var isBanned = function(user, command) {
        var banned = false;
        if(dbot.db.bans.hasOwnProperty(command)) {
            if(dbot.db.bans[command].include(user) || dbot.db.bans['*'].include(user)) {
                banned = true;
            }
        }
        return banned;
    };

    /**
     * Does the user have the correct access level to use the command?
     */
    var hasAccess = function(user, command) {
        var access = true;
        var accessNeeded = dbot.commands[command].access;

        if(accessNeeded == 'admin') {
            if(!dbot.config.admins.include(user)) {
                access = false;
            }
        } else if(accessNeeded == 'moderator') {
            if(!dbot.config.moderators.include(user) &&
                    !dbot.config.admins.include(user)) {
                access = false;
            }
        }

        return access;
    };

    /**
     * Is user ignoring command?
     */
    var isIgnoring = function(user, command) {
        var module = dbot.commandMap[command];
        var ignoring = false;
        if(dbot.db.ignores.hasOwnProperty(user) && dbot.db.ignores[user].include(module)) {
            ignoring = true;
        }
        return ignoring;
    };

    /**
     * Apply Regex to event message, store result. Return false if it doesn't
     * apply.
     */
    var applyRegex = function(commandName, event) {
        var applies = false;
        if(dbot.commands[commandName].hasOwnProperty('regex')) {
            var cRegex = dbot.commands[commandName].regex;
            var q = event.message.valMatch(cRegex[0], cRegex[1]);
            if(q) {
                applies = true;
                event.input = q;
            }
        } else {
            applies = true;
        }
        return applies;
    };

    return {
        'name': 'command',
        'ignorable': false,

        'commands': {
            '~usage': function(event) {
                var commandName = event.params[1];
                if(dbot.usage.hasOwnProperty(commandName)) {
                    event.reply(dbot.t('usage', {
                        'command': commandName,
                        'usage': dbot.usage[commandName]
                    }));
                } else {
                    event.reply(dbot.t('no_usage_info', {
                        'command': commandName
                    }));
                }
            },

            '~help': function(event) {
                var moduleName = event.params[1];
                if(!dbot.modules.hasOwnProperty(moduleName)) {
                    var moduleName = dbot.commandMap[moduleName];
                }

                if(moduleName && dbot.config[moduleName].hasOwnProperty('help')) {
                    var help = dbot.config[moduleName].help;
                    event.reply(dbot.t('help_link', {
                        'module': moduleName,
                        'link': help
                    }));
                } else {
                    if(!moduleName) {
                        moduleName = event.params[1];
                    }
                    event.reply(dbot.t('no_help', { 'module': moduleName }))
                }
            }
        },

        /**
         * Run the appropriate command given the input.
         */
        'listener': function(event) {
            var commandName = event.params[0];
            if(!dbot.commands.hasOwnProperty(commandName)) {
                commandName = '~';
            }

            if(isBanned(event.user, commandName)) {
                event.reply(dbot.t('command_ban', {'user': event.user}));
            } else {
                if(!isIgnoring(event.user, commandName) &&
                        hasAccess(event.user, commandName) &&
                        dbot.commands[commandName].disabled !== true) {
                    if(applyRegex(commandName, event)) {
                        try {
                            dbot.commands[commandName](event);
                        } catch(err) {
                            if(dbot.config.debugMode == true) {
                                event.reply('- Error in ' + commandName + ':');
                                event.reply('- Message: ' + err);
                                event.reply('- Top of stack: ' + err.stack.split('\n')[1].trim());
                            }
                        }
                        dbot.save();
                    } else {
                        if(commandName !== '~') {
                            if(dbot.usage.hasOwnProperty(commandName)){
                                event.reply('Usage: ' + dbot.usage[commandName]);
                            } else {
                                event.reply(dbot.t('syntax_error'));
                            }
                        }
                    }
                }
            }
        },
        'on': 'PRIVMSG'
    };
};

exports.fetch = function(dbot) {
    return command(dbot);
};

<|endoftext|>


<commit_before>'use strict';
var angular = require('angular');
var _ = require('lodash'); //@todo eliminate

/**
 * @ngInject
 */
var Resource = function($resource, $location, $log, npolarApiConfig, NpolarApiSecurity) {

  // @return Array of path segments "under" the current request URI
  let pathSegments = function() {
    // Split request URI into parts and remove hostname & appname from array [via the slice(2)]
    return $location.absUrl().split("//")[1].split("?")[0].split("/").filter(s => { return s !== "";}).slice(2);
  };

  // Get href for id [warn:] relative to current application /path/
  // @return href
  this.href = function(id) {
    // Add .json if id contains dots, otherwise the API barfs
    if ((/[.]/).test(id)) {
      id += ".json";
    }
    let segments = pathSegments();

    // For apps at /something, we just need to link to the id
    if (segments.length === 0) {
      return id;

    // For /cat app with children like /cat/lynx we need to link to `lynx/${id}`
    } else {
      return segments.join("/")+'/'+id;
    }
  };

  this.editHref = function() {
    return pathSegments().join('/')+'/edit';
  };

  this.newHref = function() {
    return pathSegments().join('/')+'/__new/edit';
  };

  this.base = function(service) {
    return (angular.isString(service.base)) ? service.base : npolarApiConfig.base;
  };

  /**
   * Generic error handler
   *
   *  MyResource.feed(angular.extend({ limit: 10 }, $location.search()), function(response) {
   *    $scope.feed = response.feed;
   *  }, function(error) {
   *    $scope.error = NpolarApiResource.error(error);
   *  });
   */
  this.error = function(error) {
  // response: {data: null, status: 0, headers: function, config: Object, statusText: ''}
  if (error.status >= 400) {
    return error;
  } else {
    var status = (error.status === undefined) ? 500 : error.status ;
    return { status: status, statusText: 'Npolar API error, failed accessing '+npolarApiConfig.base, data: 'Please inform data@npolar.no if this problem persists' };
  }
  };

  // NpolarApiResource factory
  // @param service e.g. { path: '/dataset', 'resource': 'Dataset'}
  // @return NpolarApiResource - extended ngResource
  // @todo service.get == null|GET|JSONP
  // @todo make extending ngResource optional
  // @todo Support user-supplied extending
  // @todo Support non-search engine query/array/fetch
  this.resource = function(service) {

    var base = this.base(service);

    // Default parameters
    var params = { id:null, limit: 100, format: 'json', q: '', variant: 'atom' };

    //var fields_feed = (angular.isString(service.fields)) ? service.fields : null ;
    var fields_query = (angular.isString(service.fields)) ? service.fields : 'id,title,name,code,titles,links,created,updated' ;

    //var params_feed = angular.extend({}, params, { fields: fields_feed });
    var params_query = angular.extend({}, params, { variant: 'array', limit: 1000, fields: fields_query });

    var resource = $resource(base+service.path+'/:id', {  }, {
      feed: { method: 'GET', params: params, headers: { Accept:'application/json, application/vnd.geo+json' }, cache: true },
      query: { method: 'GET', params: params_query, isArray: true, cache: true},
      array: { method: 'GET', params: params_query, isArray: true, cache: true },
      fetch: { method: 'GET', params: { }, headers: { Accept:'application/json' }, cache: true },
      //delete: { method:'DELETE', params: {  }, headers: { Accept:'application/json', Authorization: NpolarApiSecurity.authorization() } },
      update: { method:'PUT', params: { id: '@id' }, headers: { Accept:'application/json' } }
    });

    resource.path = base+service.path;

    resource.href = this.href;
    resource.newHref = this.newHref;
    resource.editHref = this.editHref;

    // Extend Npolar API resources (individual documents)
    angular.extend(resource.prototype, {

    // Usage: var parameter = timeseries._link({rel: 'parameter', type: 'application/json'});
    _link: function(link) {
      return _.find(this.links, link);
    },
    _links: function(link) {
      return _.select(this.links, link);
    },
    // @todo injectable resoiurce dereferencing...
    // @tods lambda functions on link/links
    _filters: function() {
      var filters = [];
      angular.forEach($location.search(), function(v,k) {
      if (k.match(/^filter-\w+/)) {
        var f = {};
        f[k]=v;
        filters.push(f);
      }
      });
      return filters;

    }

    });
    return resource;

  };
};

module.exports = Resource;<commit_msg>Removing angular resource caching<commit_after>'use strict';
var angular = require('angular');
var _ = require('lodash'); //@todo eliminate

/**
 * @ngInject
 */
var Resource = function($resource, $location, $log, npolarApiConfig, NpolarApiSecurity) {

  // @return Array of path segments "under" the current request URI
  let pathSegments = function() {
    // Split request URI into parts and remove hostname & appname from array [via the slice(2)]
    let segments = $location.absUrl().split("//")[1].split("?")[0].split("/").filter(s => { return s !== "";});
    return segments.slice(2);
  };

  // Get href for id [warn:] relative to current application /path/
  // @return href
  this.href = function(id) {
    // Add .json if id contains dots, otherwise the API barfs
    if ((/[.]/).test(id)) {
      id += ".json";
    }
    let segments = pathSegments();

    // For apps at /something, we just need to link to the id
    if (segments.length === 0) {
      return id;

    // For /cat app with children like /cat/lynx we need to link to `lynx/${id}`
    } else {
      return segments.join("/")+'/'+id;
    }
  };

  this.editHref = function(id) {
    // @todo test that provided id is last segment before edit
    // @warn now only works if id is in current request URI
    return pathSegments().join('/')+'/edit';
  };

  this.newHref = function() {
    console.debug(pathSegments());
    return pathSegments().join('/')+'/__new/edit';
  };

  this.base = function(service) {
    return (angular.isString(service.base)) ? service.base : npolarApiConfig.base;
  };

  /**
   * Generic error handler
   *
   *  MyResource.feed(angular.extend({ limit: 10 }, $location.search()), function(response) {
   *    $scope.feed = response.feed;
   *  }, function(error) {
   *    $scope.error = NpolarApiResource.error(error);
   *  });
   */
  this.error = function(error) {
  // response: {data: null, status: 0, headers: function, config: Object, statusText: ''}
  if (error.status >= 400) {
    return error;
  } else {
    var status = (error.status === undefined) ? 500 : error.status ;
    return { status: status, statusText: 'Npolar API error, failed accessing '+npolarApiConfig.base, data: 'Please inform data@npolar.no if this problem persists' };
  }
  };

  // NpolarApiResource factory
  // @param service e.g. { path: '/dataset', 'resource': 'Dataset'}
  // @return NpolarApiResource - extended ngResource
  // @todo service.get == null|GET|JSONP
  // @todo make extending ngResource optional
  // @todo Support user-supplied extending
  // @todo Support non-search engine query/array/fetch
  this.resource = function(service) {

    var base = this.base(service);

    // Default parameters
    var params = { id:null, limit: 100, format: 'json', q: '', variant: 'atom' };

    //var fields_feed = (angular.isString(service.fields)) ? service.fields : null ;
    var fields_query = (angular.isString(service.fields)) ? service.fields : 'id,title,name,code,titles,links,created,updated' ;

    //var params_feed = angular.extend({}, params, { fields: fields_feed });
    var params_query = angular.extend({}, params, { variant: 'array', limit: 1000, fields: fields_query });

    var resource = $resource(base+service.path+'/:id', {  }, {
      feed: { method: 'GET', params: params, headers: { Accept:'application/json, application/vnd.geo+json' }, cache: false },
      query: { method: 'GET', params: params_query, isArray: true, cache: false},
      array: { method: 'GET', params: params_query, isArray: true, cache: false },
      fetch: { method: 'GET', params: { }, headers: { Accept:'application/json' }, cache: false },
      //delete: { method:'DELETE', params: {  }, headers: { Accept:'application/json', Authorization: NpolarApiSecurity.authorization() } },
      update: { method:'PUT', params: { id: '@id' }, headers: { Accept:'application/json' } }
    });

    resource.path = base+service.path;

    resource.href = this.href;
    resource.newHref = this.newHref;
    resource.editHref = this.editHref;

    // Extend Npolar API resources (individual documents)
    angular.extend(resource.prototype, {

    // Usage: var parameter = timeseries._link({rel: 'parameter', type: 'application/json'});
    _link: function(link) {
      return _.find(this.links, link);
    },
    _links: function(link) {
      return _.select(this.links, link);
    },
    // @todo injectable resoiurce dereferencing...
    // @tods lambda functions on link/links
    _filters: function() {
      var filters = [];
      angular.forEach($location.search(), function(v,k) {
      if (k.match(/^filter-\w+/)) {
        var f = {};
        f[k]=v;
        filters.push(f);
      }
      });
      return filters;

    }

    });
    return resource;

  };
};

module.exports = Resource;<|endoftext|>


<commit_before>class HttpRequestError extends Error {
    constructor(message, status, statusCode) {
        super(message);
        this.status = status;
        this.statusCode = statusCode;
        // workaround for handleCatchOnSave in geFormRenderer
        // delete when/if we refactor handleCatchOnSave
        this.body = {
            message: `{"errorMessage": "${message}"}`
        }
    }

    toJSON() {
        return {
            error: {
                name: this.name,
                message: this.message,
                stacktrace: this.stack,
                status: this.status,
                statusCode: this.statusCode,
                body: {
                    message: `{"errorMessage": "${this.message}"}`
                }
            }
        }
    }
}

export default HttpRequestError;<commit_msg>remove unnecessary custom error workaround for handleCatchOnSave method<commit_after>class HttpRequestError extends Error {
    constructor(message, status, statusCode) {
        super(message);
        this.status = status;
        this.statusCode = statusCode;
    }

    toJSON() {
        return {
            error: {
                name: this.name,
                message: this.message,
                stacktrace: this.stack,
                status: this.status,
                statusCode: this.statusCode
            }
        }
    }
}

export default HttpRequestError;<|endoftext|>


<commit_before>JSONEditor.defaults.editors.multiselect = JSONEditor.AbstractEditor.extend({
  preBuild: function() {
    this._super();

    this.select_options = {};
    this.select_values = {};

    var items_schema = this.jsoneditor.expandRefs(this.schema.items || {});

    var e = items_schema.enum || [];
    this.option_keys = [];
    for(i=0; i<e.length; i++) {
      // If the sanitized value is different from the enum value, don't include it
      if(this.sanitize(e[i]) !== e[i]) continue;

      this.option_keys.push(e[i]+"");
      this.select_values[e[i]+""] = e[i];
    }
  },
  build: function() {
    var self = this, i;
    if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
    if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);

    if((!this.schema.format && this.option_keys.length < 8) || this.schema.format === "checkbox") {
      this.input_type = 'checkboxes';

      this.inputs = {};
      this.controls = {};
      for(i=0; i<this.option_keys.length; i++) {
        this.inputs[this.option_keys[i]] = this.theme.getCheckbox();
        this.select_options[this.option_keys[i]] = this.inputs[this.option_keys[i]];
        var label = this.theme.getCheckboxLabel(this.option_keys[i]);
        this.controls[this.option_keys[i]] = this.theme.getFormControl(label, this.inputs[this.option_keys[i]]);
      }

      this.control = this.theme.getMultiCheckboxHolder(this.controls,this.label,this.description);
    }
    else {
      this.input_type = 'select';
      this.input = this.theme.getSelectInput(this.option_keys);
      this.input.multiple = true;
      this.input.size = Math.min(10,this.option_keys.length);

      for(i=0; i<this.option_keys.length; i++) {
        this.select_options[this.option_keys[i]] = this.input.children[i];
      }

      if(this.schema.readOnly || this.schema.readonly) {
        this.always_disabled = true;
        this.input.disabled = true;
      }

      this.control = this.theme.getFormControl(this.label, this.input, this.description);
    }

    this.container.appendChild(this.control);
    this.control.addEventListener('change',function(e) {
      e.preventDefault();
      e.stopPropagation();

      var new_value = [];
      for(i = 0; i<self.option_keys.length; i++) {
        if(self.select_options[self.option_keys[i]].selected || self.select_options[self.option_keys[i]].checked) new_value.push(self.select_values[self.option_keys[i]]);
      }

      self.updateValue(new_value);
      self.onChange(true);
    });
  },
  setValue: function(value, initial) {
    var i;
    value = value || [];
    if(typeof value !== "object") value = [value];
    else if(!(Array.isArray(value))) value = [];

    // Make sure we are dealing with an array of strings so we can check for strict equality
    for(i=0; i<value.length; i++) {
      if(typeof value[i] !== "string") value[i] += "";
    }

    // Update selected status of options
    for(i in this.select_options) {
      if(!this.select_options.hasOwnProperty(i)) continue;

      this.select_options[i][this.input_type === "select"? "selected" : "checked"] = (value.indexOf(i) !== -1);
    }

    this.updateValue(value);
    this.onChange();
  },
  register: function() {
    this._super();
    if(!this.input) return;
    this.input.setAttribute('name',this.formname);
  },
  unregister: function() {
    this._super();
    if(!this.input) return;
    this.input.removeAttribute('name');
  },
  getNumColumns: function() {
    var longest_text = this.getTitle().length;
    for(var i in this.select_values) {
      if(!this.select_values.hasOwnProperty(i)) continue;
      longest_text = Math.max(longest_text,(this.select_values[i]+"").length+4);
    }

    return Math.min(12,Math.max(longest_text/7,2));
  },
  updateValue: function(value) {
    var changed = false;
    var new_value = [];
    for(var i=0; i<value.length; i++) {
      if(!this.select_options[value[i]+""]) {
        changed = true;
        continue;
      }
      var sanitized = this.sanitize(this.select_values[value[i]]);
      new_value.push(sanitized);
      if(sanitized !== value[i]) changed = true;
    }
    this.value = new_value;
    return changed;
  },
  sanitize: function(value) {
    if(this.schema.items.type === "number") {
      return 1*value;
    }
    else if(this.schema.items.type === "integer") {
      return Math.floor(value*1);
    }
    else {
      return ""+value;
    }
  },
  enable: function() {
    if(!this.always_disabled) {
      if(this.input) {
        this.input.disabled = false;
      }
      else if(this.inputs) {
        for(var i in this.inputs) {
          if(!this.inputs.hasOwnProperty(i)) continue;
          this.inputs[i].disabled = false;
        }
      }
    }
    this._super();
  },
  disable: function() {
    if(this.input) {
      this.input.disabled = true;
    }
    else if(this.inputs) {
      for(var i in this.inputs) {
        if(!this.inputs.hasOwnProperty(i)) continue;
        this.inputs[i].disabled = true;
      }
    }
    this._super();
  }
});
<commit_msg>Update multiselect.js<commit_after>JSONEditor.defaults.editors.multiselect = JSONEditor.AbstractEditor.extend({
  preBuild: function() {
    this._super();

    this.select_options = {};
    this.select_values = {};

    var items_schema = this.jsoneditor.expandRefs(this.schema.items || {});

    var e = items_schema.enum || [];
    this.option_keys = [];
    for(i=0; i<e.length; i++) {
      // If the sanitized value is different from the enum value, don't include it
      if(this.sanitize(e[i]) !== e[i]) continue;

      this.option_keys.push(e[i]+"");
      this.select_values[e[i]+""] = e[i];
    }
  },
  build: function() {
    var self = this, i;
    if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
    if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);

    if((!this.schema.format && this.option_keys.length < 8) || this.schema.format === "checkbox") {
      this.input_type = 'checkboxes';

      this.inputs = {};
      this.controls = {};
      for(i=0; i<this.option_keys.length; i++) {
        this.inputs[this.option_keys[i]] = this.theme.getCheckbox();
        this.select_options[this.option_keys[i]] = this.inputs[this.option_keys[i]];
        var label = this.theme.getCheckboxLabel(this.option_keys[i]);
        this.controls[this.option_keys[i]] = this.theme.getFormControl(label, this.inputs[this.option_keys[i]]);
      }

      this.control = this.theme.getMultiCheckboxHolder(this.controls,this.label,this.description);
    }
    else {
      this.input_type = 'select';
      this.input = this.theme.getSelectInput(this.option_keys);
      this.input.multiple = true;
      this.input.size = Math.min(10,this.option_keys.length);

      for(i=0; i<this.option_keys.length; i++) {
        this.select_options[this.option_keys[i]] = this.input.children[i];
      }

      if(this.schema.readOnly || this.schema.readonly) {
        this.always_disabled = true;
        this.input.disabled = true;
      }

      this.control = this.theme.getFormControl(this.label, this.input, this.description);
    }

    this.container.appendChild(this.control);
    this.control.addEventListener('change',function(e) {
      e.preventDefault();
      e.stopPropagation();

      var new_value = [];
      for(i = 0; i<self.option_keys.length; i++) {
        if(self.select_options[self.option_keys[i]].selected || self.select_options[self.option_keys[i]].checked) new_value.push(self.select_values[self.option_keys[i]]);
      }

      self.updateValue(new_value);
      self.onChange(true);
    });
  },
  setValue: function(value, initial) {
    var i;
    value = value || [];
    if(typeof value !== "object") value = [value];
    else if(!(Array.isArray(value))) value = [];

    // Make sure we are dealing with an array of strings so we can check for strict equality
    for(i=0; i<value.length; i++) {
      if(typeof value[i] !== "string") value[i] += "";
    }

    // Update selected status of options
    for(i in this.select_options) {
      if(!this.select_options.hasOwnProperty(i)) continue;

      this.select_options[i][this.input_type === "select"? "selected" : "checked"] = (value.indexOf(i) !== -1);
    }

    this.updateValue(value);
    this.onChange();
  },
  setupSelect2: function() {
    if(window.jQuery && window.jQuery.fn && window.jQuery.fn.select2) {
        var options = $.extend({},JSONEditor.plugins.select2);
        if(this.schema.options && this.schema.options.select2_options) options = $extend(options,this.schema.options.select2_options);
        this.select2 = window.jQuery(this.input).select2(options);
        var self = this;
        this.select2.on('select2-blur',function() {
            var val =self.select2.select2('val');
            self.value = val;
            self.onChange(true);
        });
    }
    else {
        this.select2 = null;
    }
  },
  onInputChange: function() {
      this.value = this.input.value;
      this.onChange(true);
  },
  postBuild: function() {
      this._super();
      this.setupSelect2();
  },
  register: function() {
    this._super();
    if(!this.input) return;
    this.input.setAttribute('name',this.formname);
  },
  unregister: function() {
    this._super();
    if(!this.input) return;
    this.input.removeAttribute('name');
  },
  getNumColumns: function() {
    var longest_text = this.getTitle().length;
    for(var i in this.select_values) {
      if(!this.select_values.hasOwnProperty(i)) continue;
      longest_text = Math.max(longest_text,(this.select_values[i]+"").length+4);
    }

    return Math.min(12,Math.max(longest_text/7,2));
  },
  updateValue: function(value) {
    var changed = false;
    var new_value = [];
    for(var i=0; i<value.length; i++) {
      if(!this.select_options[value[i]+""]) {
        changed = true;
        continue;
      }
      var sanitized = this.sanitize(this.select_values[value[i]]);
      new_value.push(sanitized);
      if(sanitized !== value[i]) changed = true;
    }
    this.value = new_value;
    if(this.select2) this.select2.select2('val',this.value);
    return changed;
  },
  sanitize: function(value) {
    if(this.schema.items.type === "number") {
      return 1*value;
    }
    else if(this.schema.items.type === "integer") {
      return Math.floor(value*1);
    }
    else {
      return ""+value;
    }
  },
  enable: function() {
    if(!this.always_disabled) {
      if(this.input) {
        this.input.disabled = false;
      }
      else if(this.inputs) {
        for(var i in this.inputs) {
          if(!this.inputs.hasOwnProperty(i)) continue;
          this.inputs[i].disabled = false;
        }
      }
      if(this.select2) this.select2.select2("enable",true);
    }
    this._super();
  },
  disable: function() {
    if(this.input) {
      this.input.disabled = true;
    }
    else if(this.inputs) {
      for(var i in this.inputs) {
        if(!this.inputs.hasOwnProperty(i)) continue;
        this.inputs[i].disabled = true;
      }
    }
    if(this.select2) this.select2.select2("enable",false);
    this._super();
  },
  destroy: function() {
    if(this.select2) {
        this.select2.select2('destroy');
        this.select2 = null;
    }
    this._super();
  }
});
<|endoftext|>


<commit_before>var React = require('react');
var classSet = require('react/lib/cx');
var assign = require('react/lib/Object.assign');
var PropTypes = require('../PropTypes');

function isLeftClickEvent(event) {
  return event.button === 0;
}

function isModifiedEvent(event) {
  return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
}

/**
 * <Link> components are used to create an <a> element that links to a route.
 * When that route is active, the link gets an "active" class name (or the
 * value of its `activeClassName` prop).
 *
 * For example, assuming you have the following route:
 *
 *   <Route name="showPost" path="/posts/:postID" handler={Post}/>
 *
 * You could use the following component to link to that route:
 *
 *   <Link to="showPost" params={{ postID: "123" }} />
 *
 * In addition to params, links may pass along query string parameters
 * using the `query` prop.
 *
 *   <Link to="showPost" params={{ postID: "123" }} query={{ show:true }}/>
 */
class Link extends React.Component {

  handleClick(event) {
    var allowTransition = true;
    var clickResult;

    if (this.props.onClick)
      clickResult = this.props.onClick(event);

    if (isModifiedEvent(event) || !isLeftClickEvent(event))
      return;

    if (clickResult === false || event.defaultPrevented === true)
      allowTransition = false;

    event.preventDefault();

    if (allowTransition)
      this.context.router.transitionTo(this.props.to, this.props.params, this.props.query);
  }

  /**
   * Returns the value of the "href" attribute to use on the DOM element.
   */
  getHref() {
    return this.context.router.makeHref(this.props.to, this.props.params, this.props.query);
  }

  /**
   * Returns the value of the "class" attribute to use on the DOM element, which contains
   * the value of the activeClassName property when this <Link> is active.
   */
  getClassName() {
    var classNames = {};

    if (this.props.className)
      classNames[this.props.className] = true;

    if (this.getActiveState())
      classNames[this.props.activeClassName] = true;

    return classSet(classNames);
  }

  getActiveState() {
    return this.context.router.isActive(this.props.to, this.props.params, this.props.query);
  }

  render() {
    var props = assign({}, this.props, {
      href: this.getHref(),
      className: this.getClassName(),
      onClick: this.handleClick
    });

    if (props.activeStyle && this.getActiveState())
      props.style = props.activeStyle;

    return React.DOM.a(props, this.props.children);
  }

}

// TODO: Include these in the above class definition
// once we can use ES7 property initializers.
// https://github.com/babel/babel/issues/619

Link.contextTypes = {
  router: PropTypes.router.isRequired
};

Link.propTypes = {
  activeClassName: PropTypes.string.isRequired,
  to: PropTypes.oneOfType([ PropTypes.string, PropTypes.route ]).isRequired,
  params: PropTypes.object,
  query: PropTypes.object,
  activeStyle: PropTypes.object,
  onClick: PropTypes.func
};

Link.defaultProps = {
  activeClassName: 'active'
};

module.exports = Link;
<commit_msg>Bind handleClick to component instance<commit_after>var React = require('react');
var classSet = require('react/lib/cx');
var assign = require('react/lib/Object.assign');
var PropTypes = require('../PropTypes');

function isLeftClickEvent(event) {
  return event.button === 0;
}

function isModifiedEvent(event) {
  return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
}

/**
 * <Link> components are used to create an <a> element that links to a route.
 * When that route is active, the link gets an "active" class name (or the
 * value of its `activeClassName` prop).
 *
 * For example, assuming you have the following route:
 *
 *   <Route name="showPost" path="/posts/:postID" handler={Post}/>
 *
 * You could use the following component to link to that route:
 *
 *   <Link to="showPost" params={{ postID: "123" }} />
 *
 * In addition to params, links may pass along query string parameters
 * using the `query` prop.
 *
 *   <Link to="showPost" params={{ postID: "123" }} query={{ show:true }}/>
 */
class Link extends React.Component {

  handleClick(event) {
    var allowTransition = true;
    var clickResult;

    if (this.props.onClick)
      clickResult = this.props.onClick(event);

    if (isModifiedEvent(event) || !isLeftClickEvent(event))
      return;

    if (clickResult === false || event.defaultPrevented === true)
      allowTransition = false;

    event.preventDefault();

    if (allowTransition)
      this.context.router.transitionTo(this.props.to, this.props.params, this.props.query);
  }

  /**
   * Returns the value of the "href" attribute to use on the DOM element.
   */
  getHref() {
    return this.context.router.makeHref(this.props.to, this.props.params, this.props.query);
  }

  /**
   * Returns the value of the "class" attribute to use on the DOM element, which contains
   * the value of the activeClassName property when this <Link> is active.
   */
  getClassName() {
    var classNames = {};

    if (this.props.className)
      classNames[this.props.className] = true;

    if (this.getActiveState())
      classNames[this.props.activeClassName] = true;

    return classSet(classNames);
  }

  getActiveState() {
    return this.context.router.isActive(this.props.to, this.props.params, this.props.query);
  }

  render() {
    var props = assign({}, this.props, {
      href: this.getHref(),
      className: this.getClassName(),
      onClick: this.handleClick.bind(this)
    });

    if (props.activeStyle && this.getActiveState())
      props.style = props.activeStyle;

    return React.DOM.a(props, this.props.children);
  }

}

// TODO: Include these in the above class definition
// once we can use ES7 property initializers.
// https://github.com/babel/babel/issues/619

Link.contextTypes = {
  router: PropTypes.router.isRequired
};

Link.propTypes = {
  activeClassName: PropTypes.string.isRequired,
  to: PropTypes.oneOfType([ PropTypes.string, PropTypes.route ]).isRequired,
  params: PropTypes.object,
  query: PropTypes.object,
  activeStyle: PropTypes.object,
  onClick: PropTypes.func
};

Link.defaultProps = {
  activeClassName: 'active'
};

module.exports = Link;
<|endoftext|>


<commit_before>/**
Generic template helper definitions for HackMyResume / FluentCV.
@license MIT. See LICENSE.md for details.
@module generic-helpers.js
*/

(function() {

  var MD = require('marked')
    , H2W = require('../utils/html-to-wpml')
    , XML = require('xml-escape')
    , moment = require('moment')
    , LO = require('lodash')
    , _ = require('underscore')
    , unused = require('../utils/string');

  /**
  Generic template helper function definitions.
  @class GenericHelpers
  */
  var GenericHelpers = module.exports = {

    /**
    Convert the input date to a specified format through Moment.js.
    If date is invalid, will return the time provided by the user,
    or default to the fallback param or 'Present' if that is set to true
    @method formatDate
    */
    formatDate: function(datetime, format, fallback) {
      if (moment) {
        var momentDate = moment( datetime );

        if (momentDate.isValid()) {
          return moment.format(format);
        }
      }

      return datetime || (typeof fallback == 'string' ? fallback : (fallback === true ? 'Present' : null));
    },

    /**
    Format a from/to date range.
    @method dateRange
    */
    dateRange: function( obj, fmt, sep, options ) {
      fmt = (fmt && String.is(fmt) && fmt) || 'YYYY-MM';
      sep = (sep && String.is(sep) && sep) || ' — ';
      if( obj.safe ) {
        var dateA = (obj.safe.start && obj.safe.start.format(fmt)) || '';
        var dateB = (obj.safe.end && obj.safe.end.format(fmt)) || '';
        if( obj.safe.start && obj.safe.end ) {
          return dateA + sep + dateB ;
        }
        else if( obj.safe.start || obj.safe.end ) {
          return dateA || dateB;
        }
      }
      return '';
    },

    /**
    Return true if the section is present on the resume and has at least one
    element.
    @method section
    */
    section: function( title, options ) {
      title = title.trim().toLowerCase();
      var obj = LO.get( this.r, title );
      if( _.isArray( obj ) ) {
        return obj.length ? options.fn(this) : undefined;
      }
      else if( _.isObject( obj )) {
        return ( (obj.history && obj.history.length) ||
            ( obj.sets && obj.sets.length ) ) ?
            options.fn(this) : undefined;
      }
    },

    /**
    Capitalize the first letter of the word.
    @method section
    */
    camelCase: function(val) {
      val = (val && val.trim()) || '';
      return val ? (val.charAt(0).toUpperCase() + val.slice(1)) : val;
    },

    /**
    Return true if the context has the property or subpropery.
    @method has
    */
    has: function( title, options ) {
      title = title && title.trim().toLowerCase();
      if( LO.get( this.r, title ) ) {
        return options.fn(this);
      }
    },

    /**
    Generic template helper function to display a user-overridable section
    title for a FRESH resume theme. Use this in lieue of hard-coding section
    titles.

    Usage:

        {{sectionTitle "sectionName"}}
        {{sectionTitle "sectionName" "sectionTitle"}}

    Example:

        {{sectionTitle "Education"}}
        {{sectionTitle "Employment" "Project History"}}

    @param sect_name The name of the section being title. Must be one of the
    top-level FRESH resume sections ("info", "education", "employment", etc.).
    @param sect_title The theme-specified section title. May be replaced by the
    user.
    @method sectionTitle
    */
    sectionTitle: function( sname, stitle ) {

      // If not provided by the user, stitle should default to sname. ps.
      // Handlebars silently passes in the options object to the last param,
      // where in Underscore stitle will be null/undefined, so we check both.
      stitle = (stitle && String.is(stitle) && stitle) || sname;

      // If there's a section title override, use it.
      return ( this.opts.stitles &&
               this.opts.stitles[ sname.toLowerCase().trim() ] ) ||
               stitle;
    },

    /**
    Convert inline Markdown to inline WordProcessingML.
    @method wpml
    */
    wpml: function( txt, inline ) {
      if(!txt) return '';
      inline = (inline && !inline.hash) || false;
      txt = XML(txt.trim());
      txt = inline ?
        MD(txt).replace(/^\s*
|<\/p>\s*$/gi, '') :
        MD(txt);
      txt = H2W( txt );
      return txt;
    },

    /**
    Emit a conditional link.
    @method link
    */
    link: function( text, url ) {
      return url && url.trim() ?
        ('' + text + '') : text;
    },

    /**
    Return the last word of the specified text.
    @method lastWord
    */
    lastWord: function( txt ) {
      return txt && txt.trim() ? _.last( txt.split(' ') ) : '';
    },

    /**
    Convert a skill level to an RGB color triplet. TODO: refactor
    @method skillColor
    @param lvl Input skill level. Skill level can be expressed as a string
    ("beginner", "intermediate", etc.), as an integer (1,5,etc), as a string
    integer ("1", "5", etc.), or as an RRGGBB color triplet ('#C00000',
    '#FFFFAA').
    */
    skillColor: function( lvl ) {
      var idx = skillLevelToIndex( lvl );
      var skillColors = (this.theme && this.theme.palette &&
        this.theme.palette.skillLevels) ||
        [ '#FFFFFF', '#5CB85C', '#F1C40F', '#428BCA', '#C00000' ];
      return skillColors[idx];
    },

    /**
    Return an appropriate height. TODO: refactor
    @method lastWord
    */
    skillHeight: function( lvl ) {
      var idx = skillLevelToIndex( lvl );
      return ['38.25', '30', '16', '8', '0'][idx];
    },

    /**
    Return all but the last word of the input text.
    @method initialWords
    */
    initialWords: function( txt ) {
      return txt && txt.trim() ? _.initial( txt.split(' ') ).join(' ') : '';
    },

    /**
    Trim the protocol (http or https) from a URL/
    @method trimURL
    */
    trimURL: function( url ) {
      return url && url.trim() ? url.trim().replace(/^https?:\/\//i, '') : '';
    },

    /**
    Convert text to lowercase.
    @method toLower
    */
    toLower: function( txt ) {
      return txt && txt.trim() ? txt.toLowerCase() : '';
    },

    /**
    Return true if either value is truthy.
    @method either
    */
    either: function( lhs, rhs, options ) {
      if (lhs || rhs) return options.fn(this);
    },

    /**
    Conditional stylesheet link. Either display the link or embed the stylesheet
    via  tag.
    */
    styleSheet: function( file, options ) {
      var styles = ( this.opts.css === 'link') ?
        '<link href="' + file + '" rel="stylesheet" type="text/css">' :
        '';
      if( this.opts.themeObj.inherits &&
          this.opts.themeObj.inherits.html &&
          this.format === 'html' ) {
        styles += (this.opts.css === 'link') ?
          '<link href="' + this.opts.themeObj.overrides.path + '" rel="stylesheet" type="text/css">' :
          '';
      }
      return styles;
    },

    /**
    Perform a generic comparison.
    See: http://doginthehat.com.au/2012/02/comparison-block-helper-for-handlebars-templates
    @method compare
    */
    compare: function(lvalue, rvalue, options) {
      if (arguments.length < 3)
        throw new Error("Handlerbars Helper 'compare' needs 2 parameters");
      var operator = options.hash.operator || "==";
      var operators = {
          '==':       function(l,r) { return l == r; },
          '===':      function(l,r) { return l === r; },
          '!=':       function(l,r) { return l != r; },
          '<':        function(l,r) { return l < r; },
          '>':        function(l,r) { return l > r; },
          '<=':       function(l,r) { return l <= r; },
          '>=':       function(l,r) { return l >= r; },
          'typeof':   function(l,r) { return typeof l == r; }
      };
      if (!operators[operator])
        throw new Error("Handlerbars Helper 'compare' doesn't know the operator "+operator);
      var result = operators[operator](lvalue,rvalue);
      return result ? options.fn(this) : options.inverse(this);
    }

  };

  function skillLevelToIndex( lvl ) {
    var idx = 0;
    if( String.is( lvl ) ) {
      lvl = lvl.trim().toLowerCase();
      var intVal = parseInt( lvl );
      if( isNaN( intVal ) ) {
        switch( lvl ) {
          case 'beginner': idx = 1; break;
          case 'intermediate': idx = 2; break;
          case 'advanced': idx = 3; break;
          case 'master': idx = 4; break;
        }
      }
      else {
        idx = Math.min( intVal / 2, 4 );
        idx = Math.max( 0, idx );
      }
    }
    else {
      idx = Math.min( lvl / 2, 4 );
      idx = Math.max( 0, idx );
    }
    return idx;
  }

}());
<commit_msg>Fix: formatDate helper references the moment method, not the momentDate object<commit_after>/**
Generic template helper definitions for HackMyResume / FluentCV.
@license MIT. See LICENSE.md for details.
@module generic-helpers.js
*/

(function() {

  var MD = require('marked')
    , H2W = require('../utils/html-to-wpml')
    , XML = require('xml-escape')
    , moment = require('moment')
    , LO = require('lodash')
    , _ = require('underscore')
    , unused = require('../utils/string');

  /**
  Generic template helper function definitions.
  @class GenericHelpers
  */
  var GenericHelpers = module.exports = {

    /**
    Convert the input date to a specified format through Moment.js.
    If date is invalid, will return the time provided by the user,
    or default to the fallback param or 'Present' if that is set to true
    @method formatDate
    */
    formatDate: function(datetime, format, fallback) {
      if (moment) {
        var momentDate = moment( datetime );
        if (momentDate.isValid()) return momentDate.format(format);
      }

      return datetime || (typeof fallback == 'string' ? fallback : (fallback === true ? 'Present' : null));
    },

    /**
    Format a from/to date range.
    @method dateRange
    */
    dateRange: function( obj, fmt, sep, options ) {
      fmt = (fmt && String.is(fmt) && fmt) || 'YYYY-MM';
      sep = (sep && String.is(sep) && sep) || ' — ';
      if( obj.safe ) {
        var dateA = (obj.safe.start && obj.safe.start.format(fmt)) || '';
        var dateB = (obj.safe.end && obj.safe.end.format(fmt)) || '';
        if( obj.safe.start && obj.safe.end ) {
          return dateA + sep + dateB ;
        }
        else if( obj.safe.start || obj.safe.end ) {
          return dateA || dateB;
        }
      }
      return '';
    },

    /**
    Return true if the section is present on the resume and has at least one
    element.
    @method section
    */
    section: function( title, options ) {
      title = title.trim().toLowerCase();
      var obj = LO.get( this.r, title );
      if( _.isArray( obj ) ) {
        return obj.length ? options.fn(this) : undefined;
      }
      else if( _.isObject( obj )) {
        return ( (obj.history && obj.history.length) ||
            ( obj.sets && obj.sets.length ) ) ?
            options.fn(this) : undefined;
      }
    },

    /**
    Capitalize the first letter of the word.
    @method section
    */
    camelCase: function(val) {
      val = (val && val.trim()) || '';
      return val ? (val.charAt(0).toUpperCase() + val.slice(1)) : val;
    },

    /**
    Return true if the context has the property or subpropery.
    @method has
    */
    has: function( title, options ) {
      title = title && title.trim().toLowerCase();
      if( LO.get( this.r, title ) ) {
        return options.fn(this);
      }
    },

    /**
    Generic template helper function to display a user-overridable section
    title for a FRESH resume theme. Use this in lieue of hard-coding section
    titles.

    Usage:

        {{sectionTitle "sectionName"}}
        {{sectionTitle "sectionName" "sectionTitle"}}

    Example:

        {{sectionTitle "Education"}}
        {{sectionTitle "Employment" "Project History"}}

    @param sect_name The name of the section being title. Must be one of the
    top-level FRESH resume sections ("info", "education", "employment", etc.).
    @param sect_title The theme-specified section title. May be replaced by the
    user.
    @method sectionTitle
    */
    sectionTitle: function( sname, stitle ) {

      // If not provided by the user, stitle should default to sname. ps.
      // Handlebars silently passes in the options object to the last param,
      // where in Underscore stitle will be null/undefined, so we check both.
      stitle = (stitle && String.is(stitle) && stitle) || sname;

      // If there's a section title override, use it.
      return ( this.opts.stitles &&
               this.opts.stitles[ sname.toLowerCase().trim() ] ) ||
               stitle;
    },

    /**
    Convert inline Markdown to inline WordProcessingML.
    @method wpml
    */
    wpml: function( txt, inline ) {
      if(!txt) return '';
      inline = (inline && !inline.hash) || false;
      txt = XML(txt.trim());
      txt = inline ?
        MD(txt).replace(/^\s*
|<\/p>\s*$/gi, '') :
        MD(txt);
      txt = H2W( txt );
      return txt;
    },

    /**
    Emit a conditional link.
    @method link
    */
    link: function( text, url ) {
      return url && url.trim() ?
        ('' + text + '') : text;
    },

    /**
    Return the last word of the specified text.
    @method lastWord
    */
    lastWord: function( txt ) {
      return txt && txt.trim() ? _.last( txt.split(' ') ) : '';
    },

    /**
    Convert a skill level to an RGB color triplet. TODO: refactor
    @method skillColor
    @param lvl Input skill level. Skill level can be expressed as a string
    ("beginner", "intermediate", etc.), as an integer (1,5,etc), as a string
    integer ("1", "5", etc.), or as an RRGGBB color triplet ('#C00000',
    '#FFFFAA').
    */
    skillColor: function( lvl ) {
      var idx = skillLevelToIndex( lvl );
      var skillColors = (this.theme && this.theme.palette &&
        this.theme.palette.skillLevels) ||
        [ '#FFFFFF', '#5CB85C', '#F1C40F', '#428BCA', '#C00000' ];
      return skillColors[idx];
    },

    /**
    Return an appropriate height. TODO: refactor
    @method lastWord
    */
    skillHeight: function( lvl ) {
      var idx = skillLevelToIndex( lvl );
      return ['38.25', '30', '16', '8', '0'][idx];
    },

    /**
    Return all but the last word of the input text.
    @method initialWords
    */
    initialWords: function( txt ) {
      return txt && txt.trim() ? _.initial( txt.split(' ') ).join(' ') : '';
    },

    /**
    Trim the protocol (http or https) from a URL/
    @method trimURL
    */
    trimURL: function( url ) {
      return url && url.trim() ? url.trim().replace(/^https?:\/\//i, '') : '';
    },

    /**
    Convert text to lowercase.
    @method toLower
    */
    toLower: function( txt ) {
      return txt && txt.trim() ? txt.toLowerCase() : '';
    },

    /**
    Return true if either value is truthy.
    @method either
    */
    either: function( lhs, rhs, options ) {
      if (lhs || rhs) return options.fn(this);
    },

    /**
    Conditional stylesheet link. Either display the link or embed the stylesheet
    via  tag.
    */
    styleSheet: function( file, options ) {
      var styles = ( this.opts.css === 'link') ?
        '<link href="' + file + '" rel="stylesheet" type="text/css">' :
        '';
      if( this.opts.themeObj.inherits &&
          this.opts.themeObj.inherits.html &&
          this.format === 'html' ) {
        styles += (this.opts.css === 'link') ?
          '<link href="' + this.opts.themeObj.overrides.path + '" rel="stylesheet" type="text/css">' :
          '';
      }
      return styles;
    },

    /**
    Perform a generic comparison.
    See: http://doginthehat.com.au/2012/02/comparison-block-helper-for-handlebars-templates
    @method compare
    */
    compare: function(lvalue, rvalue, options) {
      if (arguments.length < 3)
        throw new Error("Handlerbars Helper 'compare' needs 2 parameters");
      var operator = options.hash.operator || "==";
      var operators = {
          '==':       function(l,r) { return l == r; },
          '===':      function(l,r) { return l === r; },
          '!=':       function(l,r) { return l != r; },
          '<':        function(l,r) { return l < r; },
          '>':        function(l,r) { return l > r; },
          '<=':       function(l,r) { return l <= r; },
          '>=':       function(l,r) { return l >= r; },
          'typeof':   function(l,r) { return typeof l == r; }
      };
      if (!operators[operator])
        throw new Error("Handlerbars Helper 'compare' doesn't know the operator "+operator);
      var result = operators[operator](lvalue,rvalue);
      return result ? options.fn(this) : options.inverse(this);
    }

  };

  function skillLevelToIndex( lvl ) {
    var idx = 0;
    if( String.is( lvl ) ) {
      lvl = lvl.trim().toLowerCase();
      var intVal = parseInt( lvl );
      if( isNaN( intVal ) ) {
        switch( lvl ) {
          case 'beginner': idx = 1; break;
          case 'intermediate': idx = 2; break;
          case 'advanced': idx = 3; break;
          case 'master': idx = 4; break;
        }
      }
      else {
        idx = Math.min( intVal / 2, 4 );
        idx = Math.max( 0, idx );
      }
    }
    else {
      idx = Math.min( lvl / 2, 4 );
      idx = Math.max( 0, idx );
    }
    return idx;
  }

}());
<|endoftext|>


<commit_before>export default (url, callback)=> {
  var httpRequest = new XMLHttpRequest()
    httpRequest.onreadystatechange = function() {
        if (httpRequest.readyState === 4) {
            if (httpRequest.status === 200) {
                var data = JSON.parse(httpRequest.responseText)
                if (callback) callback(data)
            }
        }
    }
    httpRequest.open('GET', url)
    httpRequest.send()
}<commit_msg>explicitly accept json in ajax requests<commit_after>export default (url, callback)=> {
  var httpRequest = new XMLHttpRequest()
    httpRequest.onreadystatechange = function() {
        if (httpRequest.readyState === 4) {
            if (httpRequest.status === 200) {
                var data = JSON.parse(httpRequest.responseText)
                if (callback) callback(data)
            }
        }
    }
    httpRequest.setRequestHeader("Accept", "application/json")
    httpRequest.open('GET', url)
    httpRequest.send()
}
<|endoftext|>