<commit_before>// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the License.txt file included with this file.

/** @scope modules */

// TODO: proper backwards search - implement our own component?
//     : implement our own highlighter?
//     : <ESC> should cancel search highlighting in 'incsearch' mode and jump
//       back to the presearch page location - can probably use the same
//       solution as marks
//     : 'linksearch' searches should highlight link matches only
//     : changing any search settings should also update the search state including highlighting
//     : incremental searches shouldn't permanently update search modifiers

// NOTE: (by Quicksaver) please don't do any of the above TODOs. Right now the findbar is
// not only per-tab, but it is also completely asynchronous to allow for proper behavior in e10s.
// Because of that, it's better to have Vimperator be more of a mediator between the user
// and the findbar (using its methods, keeping in sync with it, etc), than implementing any
// components itself.

/**
 * @instance finder
 */
var Finder = Module("finder", {
    requires: ["config"],

    init: function () {
        this._backwards = false;           // currently searching backwards
        this._searchPattern = "";          // current search string (includes modifiers)
        this._lastSearchPattern = "";      // the last searched pattern (includes modifiers)
    },

    get findbar () {
        return (config.name == "Muttator") ? document.getElementById('FindToolbar') : window.gFindBar;
    },

    get findbarInitialized () {
        return (config.name == "Muttator") || window.gFindBarInitialized;
    },

    // set searchString, searchPattern, caseSensitive, linksOnly
    _processUserPattern: function (pattern) {
        //// strip off pattern terminator and offset
        //if (backwards)
        //    pattern = pattern.replace(/\?.*/, "");
        //else
        //    pattern = pattern.replace(/\/.*/, "");

        this._searchPattern = pattern;
        let findbar = this.findbar;

        // links only search - \l wins if both modifiers specified
        let fm = findbar._findMode;
        if (/\\l/.test(pattern))
            fm = findbar.FIND_LINKS;
        else if (/\\L/.test(pattern))
            fm = findbar.FIND_NORMAL;
        else if (options["linksearch"])
            fm = findbar.FIND_LINKS;
        else
            fm = findbar.FIND_NORMAL;
        this.updateFindMode(fm);

        // strip links-only modifiers
        pattern = pattern.replace(/(\\)?\\[lL]/g, function ($0, $1) { return $1 ? $0 : ""; });

        // case sensitivity - \c wins if both modifiers specified
        let cs = findbar._typeAheadCaseSensitive;
        if (/\\c/.test(pattern))
            cs = false;
        else if (/\\C/.test(pattern))
            cs = true;
        else if (options["ignorecase"] && options["smartcase"] && /[A-Z]/.test(pattern))
            cs = true;
        else if (options["ignorecase"])
            cs = false;
        else
            cs = true;
        this.updateCaseSensitive(cs);

        // strip case-sensitive modifiers
        pattern = pattern.replace(/(\\)?\\[cC]/g, function ($0, $1) { return $1 ? $0 : ""; });

        // remove any modifier escape \
        pattern = pattern.replace(/\\(\\[cClL])/g, "$1");

        findbar._findField.value = pattern;
        this._lastSearchPattern = pattern;
    },

    /**
     * Called when the search dialog is requested.
     *
     * @param {number} mode The search mode, either modes.SEARCH_FORWARD or
     *     modes.SEARCH_BACKWARD.
     * @default modes.SEARCH_FORWARD
     */
    openPrompt: function (mode) {
        this._backwards = mode == modes.SEARCH_BACKWARD;
        //commandline.open(this._backwards ? "Find backwards" : "Find", "", mode);
        commandline.input(this._backwards ? "Find backwards" : "Find", this.closure.onSubmit, {
            onChange: function() {
                if (options["incsearch"] && !commandline._isIMEComposing) {
                    finder.find(commandline.command);
                }
            }
        });
        //modes.extended = mode;

        // TODO: focus the top of the currently visible screen
    },

    /**
     * Initialize some of the findbar's methods to play nice us.
     */
    setupFindbar: function() {
        let findbar = this.findbar;
        if (!findbar.vimperated) {
            findbar.vimperated = true;

            // Make sure we're listening for the result, so that we show it on the prompt later.
            findbar.browser.finder.addResultListener(this);

            // PDF.JS files are different, they use their own messages to communicate the results.
            // So we piggyback the end changes to the findbar when there are any.
            findbar._vimpbackup_updateControlState = findbar.updateControlState;
            findbar.updateControlState = function(aResult, aFindPrevious) {
                this._vimpbackup_updateControlState(aResult, aFindPrevious);
                finder.onFindResult({
                    searchString: this._findField.value,
                    result: aResult,
                    findBackwards: aFindPrevious
                });
            };

            // Normally the findbar appears to notify on failed results.
            // However, this shouldn't happen when we're finding through the command line,
            // even though that way we lose any kind of no matches notification until we
            // stop typing altogether; something to work on at a later time:
            // - show the quick findbar which will hide after a few seconds?
            // - or notify the user somehow in the command line itself?
            findbar._vimpbackup_open = findbar.open;
            findbar.open = function (aMode) {
                if (commandline._keepOpenForInput) { return false; }
                return this._vimpbackup_open(aMode);
            };
        }
    },

    /**
     * Searches the current buffer for str.
     *
     * @param {string} str The string to find.
     */
    find: function (str) {
        this.setupFindbar();
        this._processUserPattern(str);

        this.findbar._find();
    },

    /**
     * Searches the current buffer again for the most recently used search
     * string.
     *
     * @param {boolean} reverse Whether to search forwards or backwards.
     * @default false
     */
    findAgain: function (reverse) {
        // Nothing to find?
        if (!this._lastSearchPattern)
            return;

        this.setupFindbar();
        if (this._lastSearchPattern != this.findbar._findField.value)
	    this._processUserPattern(this._searchPattern);

        this.findbar.onFindAgainCommand(reverse);
    },

    /**
     * Updates the status line with the result from the find operation;
     * this is done aSync from the main (input) process.
     */
    onFindResult: function(aData) {
        if (aData.result == Ci.nsITypeAheadFind.FIND_NOTFOUND) {
            // Don't use aData.searchString, it may be a substring of the
            // user's actual input text and that can be confusing.
            // See https://github.com/vimperator/vimperator-labs/issues/401#issuecomment-180522987
            // for an example.
            let searchString = this.findbar._findField.value;
            liberator.echoerr("Pattern not found: " + searchString, commandline.FORCE_SINGLELINE);
        }
        else if (aData.result == Ci.nsITypeAheadFind.FIND_WRAPPED) {
            let msg = aData.findBackwards ? "Search hit TOP, continuing at BOTTOM" : "Search hit BOTTOM, continuing at TOP";
            commandline.echo(msg, commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES | commandline.FORCE_SINGLELINE);
        }
        else {
            liberator.echomsg("Found pattern: " + aData.searchString);
        }
    },

    /**
     * Called when the <Enter> key is pressed to trigger a search.
     *
     * @param {string} str The search string.
     * @param {boolean} forcedBackward Whether to search forwards or
     *     backwards. This overrides the direction set in
     *     (@link #openPrompt).
     * @default false
     */
    onSubmit: function (str, forcedBackward) {
        let findbar = this.findbar;

        if (typeof forcedBackward === "boolean")
            this._backwards = forcedBackward;

        let pattern;
        if (str)
            pattern = str;
        else {
            liberator.assert(this._lastSearchPattern, "No previous search pattern");
            pattern = this._lastSearchPattern;
        }

        // liberator.log('inc: ' + options["incsearch"] + ' sea:' + this._searchPattern + ' pat:' + pattern);
        if (!options["incsearch"] || this._searchPattern != pattern) {
            this.find(pattern);
        }

        // TODO: move to find() when reverse incremental searching is kludged in
        // need to find again for reverse searching
        if (this._backwards)
            this.findAgain(true);

        if (options["hlsearch"])
            this.highlight();
    },

    /**
     * Highlights all occurances of str in the buffer.
     */
    highlight: function () {
        this.setupFindbar();
        let findbar = this.findbar;

        let btn = findbar.getElement("highlight");
        btn.checked = true;

        findbar._setHighlightTimeout();
    },

    /**
     * Clears all search highlighting.
     */
    clear: function () {
        if (!this.findbarInitialized)
            return;

        this.setupFindbar();
        let findbar = this.findbar;

        let btn = findbar.getElement("highlight");
        btn.checked = false;

        findbar.toggleHighlight(false);
    },

    /**
     * Updates the case sensitivity parameter.
     */
    updateCaseSensitive: function (cs) {
        this.setupFindbar();
        let findbar = this.findbar;
        if (cs != findbar._typeAheadCaseSensitive) {
            findbar._setCaseSensitivity(cs);
        }
    },

    /**
     * Updates the find mode to show only matches in links or all matches.
     */
    updateFindMode: function (fm) {
        this.setupFindbar();
        let findbar = this.findbar;

        // We need to pretend like we're opening the findbar with a different mode,
        // but not actually do it.
        if (fm != findbar._findMode) {
            findbar._findMode = fm;
            findbar._findFailedString = null;
            if(!findbar.hidden) {
                findbar._updateFindUI();
            }
        }
    }
}, {
}, {
    commands: function () {
        // TODO: Remove in favor of :set nohlsearch?
        commands.add(["noh[lsearch]"],
            "Remove the search highlighting",
            function () { finder.clear(); },
            { argCount: "0" });
    },
    mappings: function () {
        var myModes = config.browserModes;
        myModes = myModes.concat([modes.CARET]);

        mappings.add(myModes,
            ["/"], "Search forward for a pattern",
            function () { finder.openPrompt(modes.SEARCH_FORWARD); });

        mappings.add(myModes,
            ["?"], "Search backwards for a pattern",
            function () { finder.openPrompt(modes.SEARCH_BACKWARD); });

        mappings.add(myModes,
            ["n"], "Find next",
            function () { finder.findAgain(false); });

        mappings.add(myModes,
            ["N"], "Find previous",
            function () { finder.findAgain(true); });

        mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["*"],
            "Find word under cursor",
            function () {
                finder.onSubmit(buffer.getCurrentWord(), false);
            });

        mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["#"],
            "Find word under cursor backwards",
            function () {
                finder.onSubmit(buffer.getCurrentWord(), true);
            });
    },
    options: function () {
        options.add(["hlsearch", "hls"],
            "Highlight previous search pattern matches",
            "boolean", true, {
                setter: function (value) {
                    try {
                        if (value)
                            finder.highlight();
                        else
                            finder.clear();
                    }
                    catch (e) {}

                    return value;
                }
            });

        options.add(["ignorecase", "ic"],
            "Ignore case in search patterns",
            "boolean", true, {
                setter: function (value) {
                    try {
                        finder.updateCaseSensitive(!value);
                    }
                    catch (e) {}

                    return value;
                }
            });

        options.add(["incsearch", "is"],
            "Show where the search pattern matches as it is typed",
            "boolean", true);

        options.add(["linksearch", "lks"],
            "Limit the search to hyperlink text",
            "boolean", false, {
                setter: function (value) {
                    try {
                        let findbar = finder.findbar;
                        let fm = (value) ? findbar.FIND_LINKS : findbar.FIND_NORMAL;
                        finder.updateFindMode(fm);
                    }
                    catch (e) {}

                    return value;
                }
            });

        options.add(["smartcase", "scs"],
            "Override the 'ignorecase' option if the pattern contains uppercase characters",
            "boolean", true);
    }
});

// vim: set fdm=marker sw=4 ts=4 et:
<commit_msg>re-fix showing only a substring in failed search notifications<commit_after>// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the License.txt file included with this file.

/** @scope modules */

// TODO: proper backwards search - implement our own component?
//     : implement our own highlighter?
//     : <ESC> should cancel search highlighting in 'incsearch' mode and jump
//       back to the presearch page location - can probably use the same
//       solution as marks
//     : 'linksearch' searches should highlight link matches only
//     : changing any search settings should also update the search state including highlighting
//     : incremental searches shouldn't permanently update search modifiers

// NOTE: (by Quicksaver) please don't do any of the above TODOs. Right now the findbar is
// not only per-tab, but it is also completely asynchronous to allow for proper behavior in e10s.
// Because of that, it's better to have Vimperator be more of a mediator between the user
// and the findbar (using its methods, keeping in sync with it, etc), than implementing any
// components itself.

/**
 * @instance finder
 */
var Finder = Module("finder", {
    requires: ["config"],

    init: function () {
        this._backwards = false;           // currently searching backwards
        this._searchPattern = "";          // current search string (includes modifiers)
        this._lastSearchPattern = "";      // the last searched pattern (includes modifiers)
    },

    get findbar () {
        return (config.name == "Muttator") ? document.getElementById('FindToolbar') : window.gFindBar;
    },

    get findbarInitialized () {
        return (config.name == "Muttator") || window.gFindBarInitialized;
    },

    // set searchString, searchPattern, caseSensitive, linksOnly
    _processUserPattern: function (pattern) {
        //// strip off pattern terminator and offset
        //if (backwards)
        //    pattern = pattern.replace(/\?.*/, "");
        //else
        //    pattern = pattern.replace(/\/.*/, "");

        this._searchPattern = pattern;
        let findbar = this.findbar;

        // links only search - \l wins if both modifiers specified
        let fm = findbar._findMode;
        if (/\\l/.test(pattern))
            fm = findbar.FIND_LINKS;
        else if (/\\L/.test(pattern))
            fm = findbar.FIND_NORMAL;
        else if (options["linksearch"])
            fm = findbar.FIND_LINKS;
        else
            fm = findbar.FIND_NORMAL;
        this.updateFindMode(fm);

        // strip links-only modifiers
        pattern = pattern.replace(/(\\)?\\[lL]/g, function ($0, $1) { return $1 ? $0 : ""; });

        // case sensitivity - \c wins if both modifiers specified
        let cs = findbar._typeAheadCaseSensitive;
        if (/\\c/.test(pattern))
            cs = false;
        else if (/\\C/.test(pattern))
            cs = true;
        else if (options["ignorecase"] && options["smartcase"] && /[A-Z]/.test(pattern))
            cs = true;
        else if (options["ignorecase"])
            cs = false;
        else
            cs = true;
        this.updateCaseSensitive(cs);

        // strip case-sensitive modifiers
        pattern = pattern.replace(/(\\)?\\[cC]/g, function ($0, $1) { return $1 ? $0 : ""; });

        // remove any modifier escape \
        pattern = pattern.replace(/\\(\\[cClL])/g, "$1");

        findbar._findField.value = pattern;
        this._lastSearchPattern = pattern;
    },

    /**
     * Called when the search dialog is requested.
     *
     * @param {number} mode The search mode, either modes.SEARCH_FORWARD or
     *     modes.SEARCH_BACKWARD.
     * @default modes.SEARCH_FORWARD
     */
    openPrompt: function (mode) {
        this._backwards = mode == modes.SEARCH_BACKWARD;
        //commandline.open(this._backwards ? "Find backwards" : "Find", "", mode);
        commandline.input(this._backwards ? "Find backwards" : "Find", this.closure.onSubmit, {
            onChange: function() {
                if (options["incsearch"] && !commandline._isIMEComposing) {
                    finder.find(commandline.command);
                }
            }
        });
        //modes.extended = mode;

        // TODO: focus the top of the currently visible screen
    },

    /**
     * Initialize some of the findbar's methods to play nice us.
     */
    setupFindbar: function() {
        let findbar = this.findbar;
        if (!findbar.vimperated) {
            findbar.vimperated = true;

            // Make sure we're listening for the result, so that we show it on the prompt later.
            findbar.browser.finder.addResultListener(this);

            // PDF.JS files are different, they use their own messages to communicate the results.
            // So we piggyback the end changes to the findbar when there are any.
            findbar._vimpbackup_updateControlState = findbar.updateControlState;
            findbar.updateControlState = function(aResult, aFindPrevious) {
                this._vimpbackup_updateControlState(aResult, aFindPrevious);
                finder.onFindResult({
                    searchString: this._findField.value,
                    result: aResult,
                    findBackwards: aFindPrevious
                });
            };

            // Normally the findbar appears to notify on failed results.
            // However, this shouldn't happen when we're finding through the command line,
            // even though that way we lose any kind of no matches notification until we
            // stop typing altogether; something to work on at a later time:
            // - show the quick findbar which will hide after a few seconds?
            // - or notify the user somehow in the command line itself?
            findbar._vimpbackup_open = findbar.open;
            findbar.open = function (aMode) {
                if (commandline._keepOpenForInput) { return false; }
                return this._vimpbackup_open(aMode);
            };
        }
    },

    /**
     * Searches the current buffer for str.
     *
     * @param {string} str The string to find.
     */
    find: function (str) {
        this.setupFindbar();
        this._processUserPattern(str);

        this.findbar._find();
    },

    /**
     * Searches the current buffer again for the most recently used search
     * string.
     *
     * @param {boolean} reverse Whether to search forwards or backwards.
     * @default false
     */
    findAgain: function (reverse) {
        // Nothing to find?
        if (!this._lastSearchPattern)
            return;

        this.setupFindbar();
        if (this._lastSearchPattern != this.findbar._findField.value)
	    this._processUserPattern(this._searchPattern);

        this.findbar.onFindAgainCommand(reverse);
    },

    /**
     * Updates the status line with the result from the find operation;
     * this is done aSync from the main (input) process.
     */
    onFindResult: function(aData) {
        if (aData.result == Ci.nsITypeAheadFind.FIND_NOTFOUND) {
            // Don't use aData.searchString, it may be a substring of the
            // user's actual input text and that can be confusing.
            // See https://github.com/vimperator/vimperator-labs/issues/401#issuecomment-180522987
            // for an example.
            let searchString = this.findbar._findField.value;
            liberator.echoerr("Pattern not found: " + searchString, commandline.FORCE_SINGLELINE);
        }
        else if (aData.result == Ci.nsITypeAheadFind.FIND_WRAPPED) {
            let msg = aData.findBackwards ? "Search hit TOP, continuing at BOTTOM" : "Search hit BOTTOM, continuing at TOP";
            commandline.echo(msg, commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES | commandline.FORCE_SINGLELINE);
        }
        else {
            liberator.echomsg("Found pattern: " + aData.searchString);
        }
    },

    /**
     * Called when the <Enter> key is pressed to trigger a search.
     *
     * @param {string} str The search string.
     * @param {boolean} forcedBackward Whether to search forwards or
     *     backwards. This overrides the direction set in
     *     (@link #openPrompt).
     * @default false
     */
    onSubmit: function (str, forcedBackward) {
        let findbar = this.findbar;

        if (typeof forcedBackward === "boolean")
            this._backwards = forcedBackward;

        let pattern;
        if (str)
            pattern = str;
        else {
            liberator.assert(this._lastSearchPattern, "No previous search pattern");
            pattern = this._lastSearchPattern;
        }

        // liberator.log('inc: ' + options["incsearch"] + ' sea:' + this._searchPattern + ' pat:' + pattern);
        if (!options["incsearch"] || this._searchPattern != pattern) {
            this.find(pattern);
        }

        // It won't send a message to find in this case, as it will assume the searched word doesn't exist,
        // so our onFindResult listener won't be triggered. Simulate it here.
        if (findbar._findFailedString && this._lastSearchPattern.startsWith(findbar._findFailedString)) {
            this.onFindResult({ result: Ci.nsITypeAheadFind.FIND_NOTFOUND });

            // There's also no point in continuing if there are no matches.
            return;
        }

        // TODO: move to find() when reverse incremental searching is kludged in
        // need to find again for reverse searching
        if (this._backwards)
            this.findAgain(true);

        if (options["hlsearch"])
            this.highlight();
    },

    /**
     * Highlights all occurances of str in the buffer.
     */
    highlight: function () {
        this.setupFindbar();
        let findbar = this.findbar;

        let btn = findbar.getElement("highlight");
        btn.checked = true;

        findbar._setHighlightTimeout();
    },

    /**
     * Clears all search highlighting.
     */
    clear: function () {
        if (!this.findbarInitialized)
            return;

        this.setupFindbar();
        let findbar = this.findbar;

        let btn = findbar.getElement("highlight");
        btn.checked = false;

        findbar.toggleHighlight(false);
    },

    /**
     * Updates the case sensitivity parameter.
     */
    updateCaseSensitive: function (cs) {
        this.setupFindbar();
        let findbar = this.findbar;
        if (cs != findbar._typeAheadCaseSensitive) {
            findbar._setCaseSensitivity(cs);
        }
    },

    /**
     * Updates the find mode to show only matches in links or all matches.
     */
    updateFindMode: function (fm) {
        this.setupFindbar();
        let findbar = this.findbar;

        // We need to pretend like we're opening the findbar with a different mode,
        // but not actually do it.
        if (fm != findbar._findMode) {
            findbar._findMode = fm;
            findbar._findFailedString = null;
            if(!findbar.hidden) {
                findbar._updateFindUI();
            }
        }
    }
}, {
}, {
    commands: function () {
        // TODO: Remove in favor of :set nohlsearch?
        commands.add(["noh[lsearch]"],
            "Remove the search highlighting",
            function () { finder.clear(); },
            { argCount: "0" });
    },
    mappings: function () {
        var myModes = config.browserModes;
        myModes = myModes.concat([modes.CARET]);

        mappings.add(myModes,
            ["/"], "Search forward for a pattern",
            function () { finder.openPrompt(modes.SEARCH_FORWARD); });

        mappings.add(myModes,
            ["?"], "Search backwards for a pattern",
            function () { finder.openPrompt(modes.SEARCH_BACKWARD); });

        mappings.add(myModes,
            ["n"], "Find next",
            function () { finder.findAgain(false); });

        mappings.add(myModes,
            ["N"], "Find previous",
            function () { finder.findAgain(true); });

        mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["*"],
            "Find word under cursor",
            function () {
                finder.onSubmit(buffer.getCurrentWord(), false);
            });

        mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["#"],
            "Find word under cursor backwards",
            function () {
                finder.onSubmit(buffer.getCurrentWord(), true);
            });
    },
    options: function () {
        options.add(["hlsearch", "hls"],
            "Highlight previous search pattern matches",
            "boolean", true, {
                setter: function (value) {
                    try {
                        if (value)
                            finder.highlight();
                        else
                            finder.clear();
                    }
                    catch (e) {}

                    return value;
                }
            });

        options.add(["ignorecase", "ic"],
            "Ignore case in search patterns",
            "boolean", true, {
                setter: function (value) {
                    try {
                        finder.updateCaseSensitive(!value);
                    }
                    catch (e) {}

                    return value;
                }
            });

        options.add(["incsearch", "is"],
            "Show where the search pattern matches as it is typed",
            "boolean", true);

        options.add(["linksearch", "lks"],
            "Limit the search to hyperlink text",
            "boolean", false, {
                setter: function (value) {
                    try {
                        let findbar = finder.findbar;
                        let fm = (value) ? findbar.FIND_LINKS : findbar.FIND_NORMAL;
                        finder.updateFindMode(fm);
                    }
                    catch (e) {}

                    return value;
                }
            });

        options.add(["smartcase", "scs"],
            "Override the 'ignorecase' option if the pattern contains uppercase characters",
            "boolean", true);
    }
});

// vim: set fdm=marker sw=4 ts=4 et:
<|endoftext|>


<commit_before>var
	ScrollMath = require('enyo/ScrollMath');

var
	Spotlight = require('spotlight');

module.exports = {
	// We make ourselves a Spotlight container so that 5-way
	// navigation stays within our bounds by default...
	spotlight: 'container',
	// But when focus enters us, we should spot the nearest
	// child, not whichever one was previously focused
	spotlightRememberFocus: false,

	handlers: {
		onRequestScrollIntoView: 'handleRequestScrollIntoView'
	},

	scrollMath: {kind: ScrollMath, kFrictionDamping: 0.93},

	/**
	* Responds to child components' requests to be scrolled into view.
	*
	* @private
	*/
	handleRequestScrollIntoView: function(sender, event) {
		var bubble = false,
			opts = {
				block: event.scrollFullPage ? 'farthest' : 'farthest'
			};
		if (!Spotlight.getPointerMode() || event.scrollInPointerMode === true) {
			if (this.canScrollX || this.canScrollY) {
				this.scrollToControl(event.originator, opts);
			} else {
				// If we don't need to scroll, bubble onRequestScrollIntoView so that
				// any scrollers above us in the control hierarchy can scroll as needed
				bubble = true;
			}
		}
		return !bubble;
	}
};<commit_msg>moon/Scrollable: Unspot when suppressing mouse events<commit_after>var
	ScrollMath = require('enyo/ScrollMath');

var
	Spotlight = require('spotlight');

module.exports = {
	// We make ourselves a Spotlight container so that 5-way
	// navigation stays within our bounds by default...
	spotlight: 'container',
	// But when focus enters us, we should spot the nearest
	// child, not whichever one was previously focused
	spotlightRememberFocus: false,

	handlers: {
		onRequestScrollIntoView: 'handleRequestScrollIntoView'
	},

	scrollMath: {kind: ScrollMath, kFrictionDamping: 0.93},

	/**
	* Responds to child components' requests to be scrolled into view.
	*
	* @private
	*/
	handleRequestScrollIntoView: function(sender, event) {
		var bubble = false,
			opts = {
				block: event.scrollFullPage ? 'farthest' : 'farthest'
			};
		if (!Spotlight.getPointerMode() || event.scrollInPointerMode === true) {
			if (this.canScrollX || this.canScrollY) {
				this.scrollToControl(event.originator, opts);
			} else {
				// If we don't need to scroll, bubble onRequestScrollIntoView so that
				// any scrollers above us in the control hierarchy can scroll as needed
				bubble = true;
			}
		}
		return !bubble;
	},

	suppressMouseEvents: enyo.inherit(function (sup) {
		return function () {
			sup.apply(this, arguments);
			if (enyo.Spotlight.getPointerMode()) {
				enyo.Spotlight.unspot();
			}
		};
	})

};
<|endoftext|>


<commit_before>/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, object-shorthand, comma-dangle, no-else-return, no-param-reassign, max-len */

import Cookies from 'js-cookie';

(function() {
  var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };

  this.Sidebar = (function() {
    function Sidebar(currentUser) {
      this.toggleTodo = bind(this.toggleTodo, this);
      this.sidebar = $('aside');
      this.removeListeners();
      this.addEventListeners();
    }

    Sidebar.prototype.removeListeners = function () {
      this.sidebar.off('click', '.sidebar-collapsed-icon');
      $('.dropdown').off('hidden.gl.dropdown');
      $('.dropdown').off('loading.gl.dropdown');
      $('.dropdown').off('loaded.gl.dropdown');
      $(document).off('click', '.js-sidebar-toggle');
    };

    Sidebar.prototype.addEventListeners = function() {
      const $document = $(document);
      const throttledSetSidebarHeight = _.throttle(this.setSidebarHeight, 10);

      this.sidebar.on('click', '.sidebar-collapsed-icon', this, this.sidebarCollapseClicked);
      $('.dropdown').on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden);
      $('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading);
      $('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded);
      $(window).on('resize', () => throttledSetSidebarHeight());
      $document.on('scroll', () => throttledSetSidebarHeight());
      $document.on('click', '.js-sidebar-toggle', function(e, triggered) {
        var $allGutterToggleIcons, $this, $thisIcon;
        e.preventDefault();
        $this = $(this);
        $thisIcon = $this.find('i');
        $allGutterToggleIcons = $('.js-sidebar-toggle i');
        if ($thisIcon.hasClass('fa-angle-double-right')) {
          $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left');
          $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
          $('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
        } else {
          $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right');
          $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
          $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
        }
        if (!triggered) {
          return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'));
        }
      });
      return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo);
    };

    Sidebar.prototype.toggleTodo = function(e) {
      var $btnText, $this, $todoLoading, ajaxType, url;
      $this = $(e.currentTarget);
      $todoLoading = $('.js-issuable-todo-loading');
      $btnText = $('.js-issuable-todo-text', $this);
      ajaxType = $this.attr('data-delete-path') ? 'DELETE' : 'POST';
      if ($this.attr('data-delete-path')) {
        url = "" + ($this.attr('data-delete-path'));
      } else {
        url = "" + ($this.data('url'));
      }
      return $.ajax({
        url: url,
        type: ajaxType,
        dataType: 'json',
        data: {
          issuable_id: $this.data('issuable-id'),
          issuable_type: $this.data('issuable-type')
        },
        beforeSend: (function(_this) {
          return function() {
            return _this.beforeTodoSend($this, $todoLoading);
          };
        })(this)
      }).done((function(_this) {
        return function(data) {
          return _this.todoUpdateDone(data, $this, $btnText, $todoLoading);
        };
      })(this));
    };

    Sidebar.prototype.beforeTodoSend = function($btn, $todoLoading) {
      $btn.disable();
      return $todoLoading.removeClass('hidden');
    };

    Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) {
      $(document).trigger('todo:toggle', data.count);

      $btn.enable();
      $todoLoading.addClass('hidden');

      if (data.delete_path != null) {
        $btn.attr('aria-label', $btn.data('mark-text')).attr('data-delete-path', data.delete_path);
        return $btnText.text($btn.data('mark-text'));
      } else {
        $btn.attr('aria-label', $btn.data('todo-text')).removeAttr('data-delete-path');
        return $btnText.text($btn.data('todo-text'));
      }
    };

    Sidebar.prototype.sidebarDropdownLoading = function(e) {
      var $loading, $sidebarCollapsedIcon, i, img;
      $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon');
      img = $sidebarCollapsedIcon.find('img');
      i = $sidebarCollapsedIcon.find('i');
      $loading = $('');
      if (img.length) {
        img.before($loading);
        return img.hide();
      } else if (i.length) {
        i.before($loading);
        return i.hide();
      }
    };

    Sidebar.prototype.sidebarDropdownLoaded = function(e) {
      var $sidebarCollapsedIcon, i, img;
      $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon');
      img = $sidebarCollapsedIcon.find('img');
      $sidebarCollapsedIcon.find('i.fa-spin').remove();
      i = $sidebarCollapsedIcon.find('i');
      if (img.length) {
        return img.show();
      } else {
        return i.show();
      }
    };

    Sidebar.prototype.sidebarCollapseClicked = function(e) {
      var $block, sidebar;
      if ($(e.currentTarget).hasClass('dont-change-state')) {
        return;
      }
      sidebar = e.data;
      e.preventDefault();
      $block = $(this).closest('.block');
      return sidebar.openDropdown($block);
    };

    Sidebar.prototype.openDropdown = function(blockOrName) {
      var $block;
      $block = _.isString(blockOrName) ? this.getBlock(blockOrName) : blockOrName;
      $block.find('.edit-link').trigger('click');
      if (!this.isOpen()) {
        this.setCollapseAfterUpdate($block);
        return this.toggleSidebar('open');
      }
    };

    Sidebar.prototype.setCollapseAfterUpdate = function($block) {
      $block.addClass('collapse-after-update');
      return $('.page-with-sidebar').addClass('with-overlay');
    };

    Sidebar.prototype.onSidebarDropdownHidden = function(e) {
      var $block, sidebar;
      sidebar = e.data;
      e.preventDefault();
      $block = $(this).closest('.block');
      return sidebar.sidebarDropdownHidden($block);
    };

    Sidebar.prototype.sidebarDropdownHidden = function($block) {
      if ($block.hasClass('collapse-after-update')) {
        $block.removeClass('collapse-after-update');
        $('.page-with-sidebar').removeClass('with-overlay');
        return this.toggleSidebar('hide');
      }
    };

    Sidebar.prototype.triggerOpenSidebar = function() {
      return this.sidebar.find('.js-sidebar-toggle').trigger('click');
    };

    Sidebar.prototype.toggleSidebar = function(action) {
      if (action == null) {
        action = 'toggle';
      }
      if (action === 'toggle') {
        this.triggerOpenSidebar();
      }
      if (action === 'open') {
        if (!this.isOpen()) {
          this.triggerOpenSidebar();
        }
      }
      if (action === 'hide') {
        if (this.isOpen()) {
          return this.triggerOpenSidebar();
        }
      }
    };

    Sidebar.prototype.setSidebarHeight = function() {
      const $navHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight();
      const $rightSidebar = $('.js-right-sidebar');
      const diff = $navHeight - $(window).scrollTop();
      if (diff > 0) {
        $rightSidebar.outerHeight($(window).height() - diff);
      } else {
        $rightSidebar.outerHeight('100%');
      }
    };

    Sidebar.prototype.isOpen = function() {
      return this.sidebar.is('.right-sidebar-expanded');
    };

    Sidebar.prototype.getBlock = function(name) {
      return this.sidebar.find(".block." + name);
    };

    return Sidebar;
  })();
}).call(window);
<commit_msg>Add subnav height to sidebar scroll calculation<commit_after>/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, object-shorthand, comma-dangle, no-else-return, no-param-reassign, max-len */

import Cookies from 'js-cookie';

(function() {
  var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };

  this.Sidebar = (function() {
    function Sidebar(currentUser) {
      this.toggleTodo = bind(this.toggleTodo, this);
      this.sidebar = $('aside');
      this.removeListeners();
      this.addEventListeners();
    }

    Sidebar.prototype.removeListeners = function () {
      this.sidebar.off('click', '.sidebar-collapsed-icon');
      $('.dropdown').off('hidden.gl.dropdown');
      $('.dropdown').off('loading.gl.dropdown');
      $('.dropdown').off('loaded.gl.dropdown');
      $(document).off('click', '.js-sidebar-toggle');
    };

    Sidebar.prototype.addEventListeners = function() {
      const $document = $(document);
      const throttledSetSidebarHeight = _.throttle(this.setSidebarHeight, 10);

      this.sidebar.on('click', '.sidebar-collapsed-icon', this, this.sidebarCollapseClicked);
      $('.dropdown').on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden);
      $('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading);
      $('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded);
      $(window).on('resize', () => throttledSetSidebarHeight());
      $document.on('scroll', () => throttledSetSidebarHeight());
      $document.on('click', '.js-sidebar-toggle', function(e, triggered) {
        var $allGutterToggleIcons, $this, $thisIcon;
        e.preventDefault();
        $this = $(this);
        $thisIcon = $this.find('i');
        $allGutterToggleIcons = $('.js-sidebar-toggle i');
        if ($thisIcon.hasClass('fa-angle-double-right')) {
          $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left');
          $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
          $('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
        } else {
          $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right');
          $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
          $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
        }
        if (!triggered) {
          return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'));
        }
      });
      return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo);
    };

    Sidebar.prototype.toggleTodo = function(e) {
      var $btnText, $this, $todoLoading, ajaxType, url;
      $this = $(e.currentTarget);
      $todoLoading = $('.js-issuable-todo-loading');
      $btnText = $('.js-issuable-todo-text', $this);
      ajaxType = $this.attr('data-delete-path') ? 'DELETE' : 'POST';
      if ($this.attr('data-delete-path')) {
        url = "" + ($this.attr('data-delete-path'));
      } else {
        url = "" + ($this.data('url'));
      }
      return $.ajax({
        url: url,
        type: ajaxType,
        dataType: 'json',
        data: {
          issuable_id: $this.data('issuable-id'),
          issuable_type: $this.data('issuable-type')
        },
        beforeSend: (function(_this) {
          return function() {
            return _this.beforeTodoSend($this, $todoLoading);
          };
        })(this)
      }).done((function(_this) {
        return function(data) {
          return _this.todoUpdateDone(data, $this, $btnText, $todoLoading);
        };
      })(this));
    };

    Sidebar.prototype.beforeTodoSend = function($btn, $todoLoading) {
      $btn.disable();
      return $todoLoading.removeClass('hidden');
    };

    Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) {
      $(document).trigger('todo:toggle', data.count);

      $btn.enable();
      $todoLoading.addClass('hidden');

      if (data.delete_path != null) {
        $btn.attr('aria-label', $btn.data('mark-text')).attr('data-delete-path', data.delete_path);
        return $btnText.text($btn.data('mark-text'));
      } else {
        $btn.attr('aria-label', $btn.data('todo-text')).removeAttr('data-delete-path');
        return $btnText.text($btn.data('todo-text'));
      }
    };

    Sidebar.prototype.sidebarDropdownLoading = function(e) {
      var $loading, $sidebarCollapsedIcon, i, img;
      $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon');
      img = $sidebarCollapsedIcon.find('img');
      i = $sidebarCollapsedIcon.find('i');
      $loading = $('');
      if (img.length) {
        img.before($loading);
        return img.hide();
      } else if (i.length) {
        i.before($loading);
        return i.hide();
      }
    };

    Sidebar.prototype.sidebarDropdownLoaded = function(e) {
      var $sidebarCollapsedIcon, i, img;
      $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon');
      img = $sidebarCollapsedIcon.find('img');
      $sidebarCollapsedIcon.find('i.fa-spin').remove();
      i = $sidebarCollapsedIcon.find('i');
      if (img.length) {
        return img.show();
      } else {
        return i.show();
      }
    };

    Sidebar.prototype.sidebarCollapseClicked = function(e) {
      var $block, sidebar;
      if ($(e.currentTarget).hasClass('dont-change-state')) {
        return;
      }
      sidebar = e.data;
      e.preventDefault();
      $block = $(this).closest('.block');
      return sidebar.openDropdown($block);
    };

    Sidebar.prototype.openDropdown = function(blockOrName) {
      var $block;
      $block = _.isString(blockOrName) ? this.getBlock(blockOrName) : blockOrName;
      $block.find('.edit-link').trigger('click');
      if (!this.isOpen()) {
        this.setCollapseAfterUpdate($block);
        return this.toggleSidebar('open');
      }
    };

    Sidebar.prototype.setCollapseAfterUpdate = function($block) {
      $block.addClass('collapse-after-update');
      return $('.page-with-sidebar').addClass('with-overlay');
    };

    Sidebar.prototype.onSidebarDropdownHidden = function(e) {
      var $block, sidebar;
      sidebar = e.data;
      e.preventDefault();
      $block = $(this).closest('.block');
      return sidebar.sidebarDropdownHidden($block);
    };

    Sidebar.prototype.sidebarDropdownHidden = function($block) {
      if ($block.hasClass('collapse-after-update')) {
        $block.removeClass('collapse-after-update');
        $('.page-with-sidebar').removeClass('with-overlay');
        return this.toggleSidebar('hide');
      }
    };

    Sidebar.prototype.triggerOpenSidebar = function() {
      return this.sidebar.find('.js-sidebar-toggle').trigger('click');
    };

    Sidebar.prototype.toggleSidebar = function(action) {
      if (action == null) {
        action = 'toggle';
      }
      if (action === 'toggle') {
        this.triggerOpenSidebar();
      }
      if (action === 'open') {
        if (!this.isOpen()) {
          this.triggerOpenSidebar();
        }
      }
      if (action === 'hide') {
        if (this.isOpen()) {
          return this.triggerOpenSidebar();
        }
      }
    };

    Sidebar.prototype.setSidebarHeight = function() {
      const $navHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight() + $('.sub-nav-scroll').outerHeight();
      const $rightSidebar = $('.js-right-sidebar');
      const diff = $navHeight - $(window).scrollTop();
      if (diff > 0) {
        $rightSidebar.outerHeight($(window).height() - diff);
      } else {
        $rightSidebar.outerHeight('100%');
      }
    };

    Sidebar.prototype.isOpen = function() {
      return this.sidebar.is('.right-sidebar-expanded');
    };

    Sidebar.prototype.getBlock = function(name) {
      return this.sidebar.find(".block." + name);
    };

    return Sidebar;
  })();
}).call(window);
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var moment = require('moment');
var async = require('async');

var Helper = require('../library/helper');
var UserModel = require('../model/user');
var StoryModel = require('../model/story');
var TaskModel = require('../model/task');
var TaskStatusModel = require('../model/task_status');
var TaskFollow = require('../model/task_follow');
var IterationModel = require('../model/iteration');
var VersionModel = require('../model/version');
var Msg91U = require('../library/msg91u');

router.get('/test', function(req, res) {
    //async.waterfall([
    //    function(callback) {
    //        TaskModel.getById(42, function(task) {
    //            callback(null, task);
    //        });
    //    },
    //    function(task, callback) {
    //        TaskStatusModel.getById(task.status_id, function(taskStatus) {
    //            task.task_status = taskStatus;
    //            callback(null, task);
    //        });
    //    }
    //], function(err, results) {
    //    res.json(results);
    //});

    //TaskModel.getListByIterationId(4, function(result) {
    //    res.json(result);
    //});

    UserModel.getAll(function(users) {
        async.each(users, function(user, callback) {
            var msg91u = new Msg91U(user.worker_num);
            msg91u.send('淡定淡定...');
            res.json({msg: 'xxxx'});
        }, function(err) {
            res.status(404);
            res.json(err);
        });
    });
});

router.get('/', checkIterationId);
router.get('/', function(req, res, next) {
    StoryModel.getListByIterationId(req.query.iteration_id, function(stories) {
        if (Helper.isEmptyArray(stories)) {
            res.json([]);
        } else {
            var storyIds = [];
            stories.forEach(function(story) {
                storyIds.push(story.id);
            });
            TaskModel.getListByStoryIds(storyIds, function(list) {
                //res.json(list);
                req.list = list;
                next();
            });
        }
    });
}, function(req, res) {
    req.list.forEach(function(task) {

    });
    res.json(req.list);
});

router.post('/', checkUserId);
router.post('/', checkStoryId);
router.post('/', checkPrevTaskIds);
router.post('/', checkTaskStausId);
router.post('/', function(req, res, next) {
    async.waterfall([
        // 获取迭代记录
        function(callback) {
            IterationModel.getById(req.storyInfo.iteration_id, function(iteration) {
                if (Helper.isEmptyObj(iteration)) {
                    res.status(404);
                    res.json({msg: '迭代记录不存在'});
                } else {
                    callback(null, iteration);
                }
            });
        },

        // 获取版本记录
        function(iteration, callback) {
            VersionModel.getById(iteration.version_id, function(version) {
                iteration.version = version;
                callback(null, iteration);
            });
        }
    ], function(err, iteration) {
        req.body.iteration_id = iteration.id;
        req.body.project_id = iteration.version.project_id;
        req.body.version_id = iteration.version.id;
        next();
    });
});
router.post('/', function(req, res, next) { // 添加任务
    var task = new TaskModel(req.body);
    task.save(function(id) {
        req.taskId = id;
        next();
    });
}, function(req, res) { // 前置任务添加
    req.prevTaskIds.forEach(function(prevTaskId) {
        var taskFollow = new TaskFollow({task_id: req.taskId, prev_task_id: prevTaskId});
        taskFollow.save();
    });
    res.json({id: req.taskId});
});

router.put('/:id', checkTaskId);
router.put('/:id', checkUserId);
router.put('/:id', checkStoryId);
router.put('/:id', checkPrevTaskIds);
router.put('/:id', checkTaskStausId);
router.put('/:id', function(req, res, next) {
    TaskModel.modiById(req.params.id, req.body);
    next();
}, function(req, res) {
    async.series([
        // 清除老的
        function(callback) {
            TaskFollow.deleteByTaskId(req.params.id);
            callback(null);
        },
        // 新增
        function() {
            req.prevTaskIds.forEach(function(prevTaskId) {
                var taskFollow = new TaskFollow({task_id: req.params.id, prev_task_id: prevTaskId});
                taskFollow.save();
            });
        }
    ]);
    res.json({id: req.params.id});
});

router.delete('/:id', checkTaskId);
router.delete('/:id', function(req, res, next) {
    TaskModel.deleteById(req.params.id, function() {
        res.json({msg: '删除成功'});
        next();
    });
});

router.put('/:id/status', checkTaskId);
router.put('/:id/status', checkTaskStausId);
router.put('/:id/status', function(req, res) {
    TaskModel.changeStatusById(req.params.id, req.body.task_status_id);
    res.json({id: req.params.id});
});

function checkUserId(req, res, next) {
    UserModel.getById(req.body.user_id, function(userInfo) {
        if (Helper.isEmptyObj(userInfo)) {
            res.status(404);
            res.json({msg: '用户不存在'});
        } else {
            res.userInfo = userInfo;
            next();
        }
    });
}
function checkStoryId(req, res, next) {
    StoryModel.getById(req.param('story_id'), function(storyInfo) {
        if (Helper.isEmptyObj(storyInfo)) {
            res.status(404);
            res.json({msg: '故事不存在'});
        } else {
            req.storyInfo = storyInfo;
            next();
        }
    });
}
function checkPrevTaskIds(req, res, next) {
    if (req.body.prev_task_ids === undefined) {
        req.prevTaskIds = [];
    } else {
        var prevTaskIds = req.body.prev_task_ids.toString();
        var prevTaskIdsSplit = prevTaskIds.split(',');
        if (Helper.isEmptyArray(prevTaskIdsSplit)) {
            req.prevTaskIds = [];
        } else {
            req.prevTaskIds = prevTaskIdsSplit;
        }
    }
    next();
}
function checkTaskStausId(req, res, next) {
    TaskStatusModel.getById(req.body.task_status_id, function(taskStatusInfo) {
        if (Helper.isEmptyObj(taskStatusInfo)) {
            res.status(404);
            res.json({msg: '请提供正确的状态'});
        } else {
            next();
        }
    });
}
function checkTaskId(req, res, next) {
    TaskModel.getById(req.params.id, function(taskInfo) {
        if (Helper.isEmptyObj(taskInfo)) {
            res.status(404);
            res.json({msg: '任务不存在'});
        } else {
            next();
        }
    });
}
function checkIterationId(req, res, next) {
    IterationModel.getById(req.param('iteration_id', 0), function(iterationInfo) {
        if (Helper.isEmptyObj(iterationInfo)) {
            res.status(404);
            res.json({msg: '迭代计划不存在'});
        } else {
            next();
        }
    });
}

module.exports = router;<commit_msg>* 部分重构后的任务代码<commit_after>var express = require('express');
var router = express.Router();
var moment = require('moment');
var async = require('async');

var Helper = require('../library/helper');
var UserModel = require('../model/user');
var StoryModel = require('../model/story');
var TaskModel = require('../model/task');
var TaskStatusModel = require('../model/task_status');
var TaskFollow = require('../model/task_follow');
var IterationModel = require('../model/iteration');
var VersionModel = require('../model/version');
var Msg91U = require('../library/msg91u');

var IterationModel2 = require('../model/iteration2');
var TaskModel2 = require('../model/task2');
var StoryModel2 = require('../model/story2');
var VersionModel2 = require('../model/version2');
var TaskFollow2 = require('../model/task_follow2');

router.get('/test', function(req, res) {
    UserModel.getAll(function(users) {
        async.each(users, function(user, callback) {
            var msg91u = new Msg91U(user.worker_num);
            msg91u.send('淡定淡定...');
            res.json({msg: 'xxxx'});
        }, function(err) {
            res.status(404);
            res.json(err);
        });
    });
});

router.get('/', checkIterationId);
router.get('/', function(req, res, next) {
    StoryModel.getListByIterationId(req.query.iteration_id, function(stories) {
        if (Helper.isEmptyArray(stories)) {
            res.json([]);
        } else {
            var storyIds = [];
            stories.forEach(function(story) {
                storyIds.push(story.id);
            });
            TaskModel.getListByStoryIds(storyIds, function(list) {
                //res.json(list);
                req.list = list;
                next();
            });
        }
    });
}, function(req, res) {
    req.list.forEach(function(task) {

    });
    res.json(req.list);
});

router.post('/', checkIterationId);
router.post('/', checkStoryId);
router.post('/', checkUserId);
router.post('/', checkPrevTaskIds);
router.post('/', checkTaskStausId);
router.post('/', function(req, res, next) {
  VersionModel2
    .find(req.iteration.version_id)
    .then(function(version) {
      if (version === null) {
        res.status(404);
        res.json({msg: '迭代对应的版本号记录不存在.'});
      } else {
        req.version = version;
        next();
      }
    });
});
router.post('/', function(req, res, next) { // 添加任务
  TaskModel2
    .build({
      project_id: req.version.project_id,
      version_id: req.version.id,
      iteration_id: req.iteration.id,
      story_id: req.story.id,
      user_id: req.body.user_id,
      desc: req.body.desc,
      is_new: req.body.is_new,
      is_challenging: req.body.is_challenging,
      priority: req.body.priority,
      estimated_time: req.body.estimated_time,
      status_id: req.body.status_id,
      start_time: req.body.start_time,
      create_time: req.body.create_time,
    })
    .save()
    .then(function(task) {
      res.json({id: task.id});
      req.taskId = task.id;
      next();
    })
    .catch(function(err) {
      res.status(500);
      res.json(err.errors);
    });

});
router.post('/', function(req) { // 前置任务添加
  req.prevTaskIds.forEach(function(prevTaskId) {
    TaskFollow2
      .build({
        task_id: req.taskId,
        prev_task_id: prevTaskId,
        create_time: moment().unix()
      })
      .save();
  });
});

router.put('/:id', checkIterationId);
router.put('/:id', checkTaskId);
router.put('/:id', checkUserId);
router.put('/:id', checkStoryId);
router.put('/:id', checkPrevTaskIds);
router.put('/:id', checkTaskStausId);
router.put('/:id', function(req, res, next) {
    TaskModel.modiById(req.params.id, req.body);
    next();
}, function(req, res) {
    async.series([
        // 清除老的
        function(callback) {
            TaskFollow.deleteByTaskId(req.params.id);
            callback(null);
        },
        // 新增
        function() {
            req.prevTaskIds.forEach(function(prevTaskId) {
                var taskFollow = new TaskFollow({task_id: req.params.id, prev_task_id: prevTaskId});
                taskFollow.save();
            });
        }
    ]);
    res.json({id: req.params.id});
});

router.delete('/:id', checkTaskId);
router.delete('/:id', function(req, res, next) {
    TaskModel.deleteById(req.params.id, function() {
        res.json({msg: '删除成功'});
        next();
    });
});

router.put('/:id/status', checkTaskId);
router.put('/:id/status', checkTaskStausId);
router.put('/:id/status', function(req, res) {
    TaskModel.changeStatusById(req.params.id, req.body.task_status_id);
    res.json({id: req.params.id});
});

function checkTaskId(req, res, next) {
  TaskModel2
    .find(req.params.id)
    .then(function(task) {
      if (task === null) {
        res.status(404);
        res.json({msg: '任务不存在'});
      } else {
        req.task = task;
        next();
      }
    });
}
function checkUserId(req, res, next) {
    UserModel.getById(req.body.user_id, function(userInfo) {
        if (Helper.isEmptyObj(userInfo)) {
            res.status(404);
            res.json({msg: '用户不存在'});
        } else {
            res.userInfo = userInfo;
            next();
        }
    });
}
function checkPrevTaskIds(req, res, next) {
    if (req.body.prev_task_ids === undefined) {
        req.prevTaskIds = [];
    } else {
        var prevTaskIds = req.body.prev_task_ids.toString();
        var prevTaskIdsSplit = prevTaskIds.split(',');
        if (Helper.isEmptyArray(prevTaskIdsSplit)) {
            req.prevTaskIds = [];
        } else {
            req.prevTaskIds = prevTaskIdsSplit;
        }
    }
    next();
}
function checkTaskStausId(req, res, next) {
    TaskStatusModel.getById(req.body.task_status_id, function(taskStatusInfo) {
        if (Helper.isEmptyObj(taskStatusInfo)) {
            res.status(404);
            res.json({msg: '请提供正确的状态'});
        } else {
            next();
        }
    });
}
function checkStoryId(req, res, next) {
  StoryModel2
    .find(req.param('story_id'))
    .then(function(story) {
      if (story === null) {
        res.status(404);
        res.json({msg: '故事不存在'});
      } else {
        req.story = story;
        next();
      }
    });
}
function checkIterationId(req, res, next) {
  IterationModel2
    .find(req.param('iteration_id'))
    .then(function(iteration) {
      if (iteration === null) {
        res.status(404);
        res.json({msg: '迭代计划不存在'});
      } else {
        if (iteration.isClosed()) {
          res.status(404);
          res.json({msg: '迭代计划已关闭,不能进行任何操作了.'});
        } else {
          req.iteration = iteration;
          next();
        }
      }
    });
}

module.exports = router;<|endoftext|>


<commit_before>/*
 * (c) Copyright Ascensio System SIA 2010-2019
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

"use strict";

(function (window, undefined)
{
    window['AscFonts'] = window['AscFonts'] || {};

    window['AscFonts'].isEngineReady = false;
    window['AscFonts'].api = null;
    window['AscFonts'].onSuccess = null;
    window['AscFonts'].onError = null;
    window['AscFonts'].maxLoadingIndex = 5; // engine, file, manager, wasm, sdk-manager
    window['AscFonts'].curLoadingIndex = 0;

    window['AscFonts'].allocate = function(size)
    {
        if (typeof(Uint8Array) != 'undefined' && !window.opera)
            return new Uint8Array(size);

        var arr = new Array(size);
        for (var i=0;i<size;i++)
            arr[i] = 0;
        return arr;
    };
    window['AscFonts'].allocateData = function(size)
    {
        return { data : window['AscFonts'].allocate(size) };
    };

    window['AscFonts'].onLoadModule = function()
	{
	    if (window['AscFonts'].isEngineReady)
	        return;

		++window['AscFonts'].curLoadingIndex;

		if (window['AscFonts'].curLoadingIndex == window['AscFonts'].maxLoadingIndex)
		{
			if (window['AscFonts'].api)
			{
                window['AscFonts'].isEngineReady = true;
                window['AscFonts'].onSuccess.call(window['AscFonts'].api);
			}

			delete window['AscFonts'].curLoadingIndex;
            delete window['AscFonts'].maxLoadingIndex;
            delete window['AscFonts'].api;
            delete window['AscFonts'].onSuccess;
            delete window['AscFonts'].onError;
		}
	};

    window['AscFonts'].load = function(api, onSuccess, onError)
    {
        window['AscFonts'].api = api;
        window['AscFonts'].onSuccess = onSuccess;
        window['AscFonts'].onError = onError;

        if (window["NATIVE_EDITOR_ENJINE"] === true || window["IS_NATIVE_EDITOR"] === true || window["Native"] !== undefined)
        {
            window['AscFonts'].isEngineReady = true;
            window['AscFonts'].onSuccess.call(window['AscFonts'].api);

            delete window['AscFonts'].curLoadingIndex;
            delete window['AscFonts'].maxLoadingIndex;
            delete window['AscFonts'].api;
            delete window['AscFonts'].onSuccess;
            delete window['AscFonts'].onError;
            return;
        }

        var url = "../../../../sdkjs/common/libfont";
        var useWasm = false;
        var webAsmObj = window["WebAssembly"];
        if (typeof webAsmObj === "object")
		{
            if (typeof webAsmObj["Memory"] === "function")
			{
				if ((typeof webAsmObj["instantiateStreaming"] === "function") || (typeof webAsmObj["instantiate"] === "function"))
					useWasm = true;
			}
		}

		useWasm ? (url += "/wasm") : (url += "/js");
		if (!useWasm)
            window['AscFonts'].onLoadModule();

		var _onSuccess = function(){
		};
		var _onError = function(){
            window['AscFonts'].onError();
		};

        if (window['AscNotLoadAllScript'])
        {
            AscCommon.loadScript(url + "/engine.js", _onSuccess, _onError);
            AscCommon.loadScript(url + "/file.js", _onSuccess, _onError);
            AscCommon.loadScript(url + "/manager.js", _onSuccess, _onError);
        }
        else
        {
            AscCommon.loadScript(url + "/fonts.js", _onSuccess, _onError);
        }
    };

    function FontStream(data, size)
    {
        this.data = data;
        this.size = size;
    }

    window['AscFonts'].FontStream = FontStream;

    window['AscFonts'].FT_Common = {
        UintToInt : function(v)
        {
            return (v>2147483647)?v-4294967296:v;
        },
        UShort_To_Short : function(v)
        {
            return (v>32767)?v-65536:v;
        },
        IntToUInt : function(v)
        {
            return (v<0)?v+4294967296:v;
        },
        Short_To_UShort : function(v)
        {
            return (v<0)?v+65536:v;
        },
        memset : function(d,v,s)
        {
            for (var i=0;i<s;i++)
                d[i]=v;
        }
    };

    function CPointer()
    {
        this.obj    = null;
        this.data   = null;
        this.pos    = 0;
    }

    function FT_Memory()
    {
        this.canvas = document.createElement('canvas');
        this.canvas.width = 1;
        this.canvas.height = 1;
        this.ctx    = this.canvas.getContext('2d');

        this.Alloc = function(size)
        {
            var p = new CPointer();
            p.obj = this.ctx.createImageData(1,parseInt((size + 3) / 4));
            p.data = p.obj.data;
            p.pos = 0;
            return p;
        };
        this.AllocHeap = function()
        {
            // TODO: нужно посмотреть, как эта память будет использоваться.
            // нужно ли здесь делать стек, либо все время от нуля делать??
        };
        this.CreateStream = function(size)
        {
            var _size = parseInt((size + 3) / 4);
            var obj = this.ctx.createImageData(1,_size);
            return new FontStream(obj.data,_size);
        };
    }

    window['AscFonts'].FT_Memory = FT_Memory;
    window['AscFonts'].g_memory = new FT_Memory();

    function CRasterMemory()
    {
        this.width = 0;
        this.height = 0;
        this.pitch = 0;

        this.m_oBuffer = null;
        this.CheckSize = function(w, h)
        {
            if (this.width < (w + 1) || this.height < (h + 1))
            {
                this.width = Math.max(this.width, w + 1);
                this.pitch = 4 * this.width;
                this.height = Math.max(this.height, h + 1);

                this.m_oBuffer = null;
                this.m_oBuffer = window['AscFonts'].g_memory.ctx.createImageData(this.width, this.height);
            }
        };
    }

    window['AscFonts'].raster_memory = new CRasterMemory();

    window['AscFonts'].registeredFontManagers = [];

	window['AscFonts'].getDefaultBlitting = function()
    {
		return (AscCommon.AscBrowser.isIE && !AscCommon.AscBrowser.isArm) ? true : false;
    };
	window['AscFonts'].setDefaultBlitting = function(value)
	{
	    var defaultValue = window['AscFonts'].getDefaultBlitting();
	    var newValue = value ? defaultValue : !defaultValue;
	    if (window['AscFonts'].use_map_blitting === newValue)
	        return;

        window['AscFonts'].use_map_blitting = newValue;
		var arrManagers = window['AscFonts'].registeredFontManagers;
	    for (var i = 0, count = arrManagers.length; i < count; i++)
        {
            arrManagers[i].ClearFontsRasterCache();
            arrManagers[i].InitializeRasterMemory();
        }
	};

    window['AscFonts'].use_map_blitting = window['AscFonts'].getDefaultBlitting();

})(window, undefined);
<commit_msg>Fix bug 52137<commit_after>/*
 * (c) Copyright Ascensio System SIA 2010-2019
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

"use strict";

(function (window, undefined)
{
    window['AscFonts'] = window['AscFonts'] || {};

    window['AscFonts'].isEngineReady = false;
    window['AscFonts'].api = null;
    window['AscFonts'].onSuccess = null;
    window['AscFonts'].onError = null;
    window['AscFonts'].maxLoadingIndex = 5; // engine, file, manager, wasm, sdk-manager
    window['AscFonts'].curLoadingIndex = 0;

    window['AscFonts'].allocate = function(size)
    {
        if (typeof(Uint8Array) != 'undefined' && !window.opera)
            return new Uint8Array(size);

        var arr = new Array(size);
        for (var i=0;i<size;i++)
            arr[i] = 0;
        return arr;
    };
    window['AscFonts'].allocateData = function(size)
    {
        return { data : window['AscFonts'].allocate(size) };
    };

    window['AscFonts'].onLoadModule = function()
	{
	    if (window['AscFonts'].isEngineReady)
	        return;

		++window['AscFonts'].curLoadingIndex;

		if (window['AscFonts'].curLoadingIndex == window['AscFonts'].maxLoadingIndex)
		{
			if (window['AscFonts'].api)
			{
                window['AscFonts'].isEngineReady = true;
                window['AscFonts'].onSuccess.call(window['AscFonts'].api);
			}

			delete window['AscFonts'].curLoadingIndex;
            delete window['AscFonts'].maxLoadingIndex;
            delete window['AscFonts'].api;
            delete window['AscFonts'].onSuccess;
            delete window['AscFonts'].onError;
		}
	};

    window['AscFonts'].load = function(api, onSuccess, onError)
    {
        window['AscFonts'].api = api;
        window['AscFonts'].onSuccess = onSuccess;
        window['AscFonts'].onError = onError;

        if (window["NATIVE_EDITOR_ENJINE"] === true || window["IS_NATIVE_EDITOR"] === true || window["Native"] !== undefined)
        {
            window['AscFonts'].isEngineReady = true;
            window['AscFonts'].onSuccess.call(window['AscFonts'].api);

            delete window['AscFonts'].curLoadingIndex;
            delete window['AscFonts'].maxLoadingIndex;
            delete window['AscFonts'].api;
            delete window['AscFonts'].onSuccess;
            delete window['AscFonts'].onError;
            return;
        }

        var url = "../../../../sdkjs/common/libfont";
        var useWasm = false;
        var webAsmObj = window["WebAssembly"];
        if (typeof webAsmObj === "object")
		{
            if (typeof webAsmObj["Memory"] === "function")
			{
				if ((typeof webAsmObj["instantiateStreaming"] === "function") || (typeof webAsmObj["instantiate"] === "function"))
					useWasm = true;
			}
		}

        // отключаем wasm для мобильных
        if (useWasm && (AscCommon.AscBrowser.isAppleDevices || AscCommon.AscBrowser.isAndroid))
            useWasm = false;

		useWasm ? (url += "/wasm") : (url += "/js");
		if (!useWasm)
            window['AscFonts'].onLoadModule();

		var _onSuccess = function(){
		};
		var _onError = function(){
            window['AscFonts'].onError();
		};

        if (window['AscNotLoadAllScript'])
        {
            AscCommon.loadScript(url + "/engine.js", _onSuccess, _onError);
            AscCommon.loadScript(url + "/file.js", _onSuccess, _onError);
            AscCommon.loadScript(url + "/manager.js", _onSuccess, _onError);
        }
        else
        {
            AscCommon.loadScript(url + "/fonts.js", _onSuccess, _onError);
        }
    };

    function FontStream(data, size)
    {
        this.data = data;
        this.size = size;
    }

    window['AscFonts'].FontStream = FontStream;

    window['AscFonts'].FT_Common = {
        UintToInt : function(v)
        {
            return (v>2147483647)?v-4294967296:v;
        },
        UShort_To_Short : function(v)
        {
            return (v>32767)?v-65536:v;
        },
        IntToUInt : function(v)
        {
            return (v<0)?v+4294967296:v;
        },
        Short_To_UShort : function(v)
        {
            return (v<0)?v+65536:v;
        },
        memset : function(d,v,s)
        {
            for (var i=0;i<s;i++)
                d[i]=v;
        }
    };

    function CPointer()
    {
        this.obj    = null;
        this.data   = null;
        this.pos    = 0;
    }

    function FT_Memory()
    {
        this.canvas = document.createElement('canvas');
        this.canvas.width = 1;
        this.canvas.height = 1;
        this.ctx    = this.canvas.getContext('2d');

        this.Alloc = function(size)
        {
            var p = new CPointer();
            p.obj = this.ctx.createImageData(1,parseInt((size + 3) / 4));
            p.data = p.obj.data;
            p.pos = 0;
            return p;
        };
        this.AllocHeap = function()
        {
            // TODO: нужно посмотреть, как эта память будет использоваться.
            // нужно ли здесь делать стек, либо все время от нуля делать??
        };
        this.CreateStream = function(size)
        {
            var _size = parseInt((size + 3) / 4);
            var obj = this.ctx.createImageData(1,_size);
            return new FontStream(obj.data,_size);
        };
    }

    window['AscFonts'].FT_Memory = FT_Memory;
    window['AscFonts'].g_memory = new FT_Memory();

    function CRasterMemory()
    {
        this.width = 0;
        this.height = 0;
        this.pitch = 0;

        this.m_oBuffer = null;
        this.CheckSize = function(w, h)
        {
            if (this.width < (w + 1) || this.height < (h + 1))
            {
                this.width = Math.max(this.width, w + 1);
                this.pitch = 4 * this.width;
                this.height = Math.max(this.height, h + 1);

                this.m_oBuffer = null;
                this.m_oBuffer = window['AscFonts'].g_memory.ctx.createImageData(this.width, this.height);
            }
        };
    }

    window['AscFonts'].raster_memory = new CRasterMemory();

    window['AscFonts'].registeredFontManagers = [];

	window['AscFonts'].getDefaultBlitting = function()
    {
		return (AscCommon.AscBrowser.isIE && !AscCommon.AscBrowser.isArm) ? true : false;
    };
	window['AscFonts'].setDefaultBlitting = function(value)
	{
	    var defaultValue = window['AscFonts'].getDefaultBlitting();
	    var newValue = value ? defaultValue : !defaultValue;
	    if (window['AscFonts'].use_map_blitting === newValue)
	        return;

        window['AscFonts'].use_map_blitting = newValue;
		var arrManagers = window['AscFonts'].registeredFontManagers;
	    for (var i = 0, count = arrManagers.length; i < count; i++)
        {
            arrManagers[i].ClearFontsRasterCache();
            arrManagers[i].InitializeRasterMemory();
        }
	};

    window['AscFonts'].use_map_blitting = window['AscFonts'].getDefaultBlitting();

})(window, undefined);
<|endoftext|>


<commit_before>var request = require('./base.js').request;
var clone = require('./base.js').clone;
var fs = require('fs');

var opts = {
	base: 'https://sms.teleapi.net',
	controller: '',
	endpoint: ''
};

function sms_send(source, destination, message, notify_url, callback) {
	if (typeof notifY_url == 'function') {
		callback = notify_url;
		notify_url = false;
	}
	callback = callback || function () {};
	var o = clone(opts);
	o.controller = 'sms';
	o.endpoint = 'send';
	var data = {
		source: source,
		destination: destination,
		message: message
	}
	if (notify_url) {
		data.notify_url = notify_url;
	}
	request(o, data, callback);
}

function mms_send(source, destination, full_file_path, notify_url, callback) {
	if (typeof notifY_url == 'function') {
		callback = notify_url;
		notify_url = false;
	}
	callback = callback || function () {};
	var o = clone(opts);
	o.controller = 'mms';
	o.endpoint = 'send';
	var data = {
		source: source,
		destination: destination
	}
	if (notify_url) {
		data.notify_url = notify_url;
	}
	fs.readFile(full_file_path, function (err, file_data) {
		if (err) {
			callback({ code: 400, status: 'error', data: 'Could not read file' });
			return false;
		}
		data.file_name = full_file_path.split('/').pop();
		data.file_data = new Buffer(file_data).toString('base64');
		request(o, data, callback);
	});
}

module.exports = {
	sms_send: sms_send,
	mms_send: mms_send
};
<commit_msg>Fixed a couple of bugs<commit_after>var request = require('./base.js').request;
var clone = require('./base.js').clone;
var fs = require('fs');

var opts = {
	base: 'https://sms.teleapi.net',
	controller: '',
	endpoint: ''
};

function sms_send(source, destination, message, notify_url, callback) {
	console.log('notify_url:', typeof notify_url);
	console.log('callback:', typeof callback);
	if (typeof notify_url == 'function') {
		callback = notify_url;
		notify_url = false;
	}
	console.log('notify_url:', typeof notify_url);
	console.log('callback:', typeof callback);
	callback = callback || function () {};
	var o = clone(opts);
	o.controller = 'sms';
	o.endpoint = 'send';
	var data = {
		source: source,
		destination: destination,
		message: message
	}
	if (notify_url) {
		data.notify_url = notify_url;
	}
	request(o, data, callback);
}

function mms_send(source, destination, full_file_path, notify_url, callback) {
	if (typeof notify_url == 'function') {
		callback = notify_url;
		notify_url = false;
	}
	callback = callback || function () {};
	var o = clone(opts);
	o.controller = 'mms';
	o.endpoint = 'send';
	var data = {
		source: source,
		destination: destination
	}
	if (notify_url) {
		data.notify_url = notify_url;
	}
	fs.readFile(full_file_path, function (err, file_data) {
		if (err) {
			callback({ code: 400, status: 'error', data: 'Could not read file' });
			return false;
		}
		data.file_name = full_file_path.split('/').pop();
		data.file_data = new Buffer(file_data).toString('base64');
		request(o, data, callback);
	});
}

module.exports = {
	sms_send: sms_send,
	mms_send: mms_send
};
<|endoftext|>


<commit_before>var url = require("url");
var scriptServingMiddleware = require("../script-serving-middleware");
var resource = require("./resource");
var scriptInjectionProcessor = require("./processors/script-injector");
var proxyMiddleware = require("../http-proxy");

module.exports = {
    contextPath: "",

    create: function (data) {
        var resourceSet = Object.create(this);

        resourceSet.resources = {};
        for (var key in data.resources) {
            resourceSet.resources[key] = resource.create(key, data.resources[key]);
        }

        resourceSet.load = data.load || [];
        resourceSet.rootResource = data.rootResource;
        if ("contextPath" in data) resourceSet.contextPath = data.contextPath;
        resourceSet.setUpRootResource();

        return resourceSet;
    },

    addResource: function (path, data) {
        this.resources[path] = resource.create(path, data);
    },

    setUpRootResource: function () {
        if (!("/" in this.resources)) {
            this.assignDefaultRootResource();
        }

        var p = Object.create(scriptInjectionProcessor);
        p.scripts = this.rootResourceScripts();
        this.resources["/"].addProcessor(p);
    },

    assignDefaultRootResource: function () {
        this.resources["/"] = resource.create("/", {
            content: "<!DOCTYPE html>",
            headers: {"Content-Type": "text/html"}
        });
    },

    /*
     * Takes the data for a resource set. Returns a string with an error message, or
     * nothing if there was no error.
     */
    validate: function (data) {
        if (!data.hasOwnProperty("resources")) {
            return "Missing property 'resources'.";
        }

        if (!data.hasOwnProperty("load")) {
            return "Missing property 'load'.";
        }

        for (var i = 0, ii = data.load.length; i < ii; i++) {
            var resourceFound = false;
            for (var resource in data.resources) {
                if (data.load[i] == resource) resourceFound = true;
            }

            if (!resourceFound) {
                return "'load' entry '" + data.load[i] + "' missing corresponding 'resources' entry.";
            }
        }

        for (var resource in data.resources) {
            if ("content" in data.resources[resource]) {
                if (data.resources[resource].content instanceof Buffer) {
                    continue;
                }

                if (typeof(data.resources[resource].content) == "string") {
                    continue;
                } else {
                    return "The resource '" + resource + "' was not a string."
                }
            }
        }
    },

    respond: function (req, res) {
        if (this.internalScriptServer.respond(req, res)) return true;

        var path, resource;

        for (path in this.resources) {
            resource = this.getResource(path);

            if (this.serveResourceContent(path, resource, req, res)) return true;
            if (this.serveProxyRequest(path, resource, req, res)) return true;
        }
    },

    serveResourceContent: function (path, resource, req, res) {
        if (this.resourceContextPath() + path != req.url) {
            return;
        }

        res.writeHead(200, resource.getHeaders());
        res.write(resource.getContent());
        res.end();
        return true;
    },

    serveProxyRequest: function (path, resource, req, res) {
        if (!resource.proxy ||
            req.url.indexOf(this.resourceContextPath() + path) != 0) {
            return;
        }

        resource.proxy.respond(req, res);
        return true;
    },

    rootResourceScripts: function () {
        var scripts = [];

        for (var i = 0, ii = this.internalScriptServer.scripts.length; i < ii; i++) {
            scripts.push(this.internalsContextPath() + this.internalScriptServer.scripts[i].path);
        }

        for (var i = 0, ii = this.load.length; i < ii; i++) {
            scripts.push(this.resourceContextPath() + this.load[i]);
        }

        return scripts;
    },

    get internalScriptServer() {
        return this._internalScriptServer || this.createInternalScriptServer();
    },

    createInternalScriptServer: function () {
        var self = this;
        var middleware = Object.create(scriptServingMiddleware);
        middleware.contextPath = this.internalsContextPath();
        middleware.requireFile(require.resolve("buster-core"));
        middleware.requireFile(require.resolve("./../browser/cross-frame"));
        middleware.requireFile(require.resolve("./../browser/client-frame-load"));

        return this._internalScriptServer = middleware;
    },

    getResource: function (path) {
        var resource = this.resources[path];

        if (resource.backend && !resource.proxy) {
            var parsed = url.parse(resource.backend);
            resource.proxy = proxyMiddleware.create(
                parsed.hostname, parsed.port, parsed.path);
            resource.proxy.proxyPath = this.resourceContextPath();
        }

        if (resource.combine && !resource.content) {
            resource.content = this.combineResources(resource.combine);
        }

        return resource;
    },

    combineResources: function (resources) {
        var content = "";

        for (var i = 0, l = resources.length; i < l; ++i) {
            content += this.getResource(resources[i]).content + "\n";
        }

        return content;
    },

    resourceContextPath: function () {
        return this.contextPath + "/res";
    },

    internalsContextPath: function () {
        return this.contextPath + "/_";
    }
};
<commit_msg>More sensible implementation of default resource set context path.<commit_after>var url = require("url");
var scriptServingMiddleware = require("../script-serving-middleware");
var resource = require("./resource");
var scriptInjectionProcessor = require("./processors/script-injector");
var proxyMiddleware = require("../http-proxy");

module.exports = {
    create: function (data) {
        var resourceSet = Object.create(this);

        resourceSet.resources = {};
        for (var key in data.resources) {
            resourceSet.resources[key] = resource.create(key, data.resources[key]);
        }

        resourceSet.load = data.load || [];
        resourceSet.rootResource = data.rootResource;
        resourceSet.contextPath = data.contextPath || "";
        resourceSet.setUpRootResource();

        return resourceSet;
    },

    addResource: function (path, data) {
        this.resources[path] = resource.create(path, data);
    },

    setUpRootResource: function () {
        if (!("/" in this.resources)) {
            this.assignDefaultRootResource();
        }

        var p = Object.create(scriptInjectionProcessor);
        p.scripts = this.rootResourceScripts();
        this.resources["/"].addProcessor(p);
    },

    assignDefaultRootResource: function () {
        this.resources["/"] = resource.create("/", {
            content: "<!DOCTYPE html>",
            headers: {"Content-Type": "text/html"}
        });
    },

    /*
     * Takes the data for a resource set. Returns a string with an error message, or
     * nothing if there was no error.
     */
    validate: function (data) {
        if (!data.hasOwnProperty("resources")) {
            return "Missing property 'resources'.";
        }

        if (!data.hasOwnProperty("load")) {
            return "Missing property 'load'.";
        }

        for (var i = 0, ii = data.load.length; i < ii; i++) {
            var resourceFound = false;
            for (var resource in data.resources) {
                if (data.load[i] == resource) resourceFound = true;
            }

            if (!resourceFound) {
                return "'load' entry '" + data.load[i] + "' missing corresponding 'resources' entry.";
            }
        }

        for (var resource in data.resources) {
            if ("content" in data.resources[resource]) {
                if (data.resources[resource].content instanceof Buffer) {
                    continue;
                }

                if (typeof(data.resources[resource].content) == "string") {
                    continue;
                } else {
                    return "The resource '" + resource + "' was not a string."
                }
            }
        }
    },

    respond: function (req, res) {
        if (this.internalScriptServer.respond(req, res)) return true;

        var path, resource;

        for (path in this.resources) {
            resource = this.getResource(path);

            if (this.serveResourceContent(path, resource, req, res)) return true;
            if (this.serveProxyRequest(path, resource, req, res)) return true;
        }
    },

    serveResourceContent: function (path, resource, req, res) {
        if (this.resourceContextPath() + path != req.url) {
            return;
        }

        res.writeHead(200, resource.getHeaders());
        res.write(resource.getContent());
        res.end();
        return true;
    },

    serveProxyRequest: function (path, resource, req, res) {
        if (!resource.proxy ||
            req.url.indexOf(this.resourceContextPath() + path) != 0) {
            return;
        }

        resource.proxy.respond(req, res);
        return true;
    },

    rootResourceScripts: function () {
        var scripts = [];

        for (var i = 0, ii = this.internalScriptServer.scripts.length; i < ii; i++) {
            scripts.push(this.internalsContextPath() + this.internalScriptServer.scripts[i].path);
        }

        for (var i = 0, ii = this.load.length; i < ii; i++) {
            scripts.push(this.resourceContextPath() + this.load[i]);
        }

        return scripts;
    },

    get internalScriptServer() {
        return this._internalScriptServer || this.createInternalScriptServer();
    },

    createInternalScriptServer: function () {
        var self = this;
        var middleware = Object.create(scriptServingMiddleware);
        middleware.contextPath = this.internalsContextPath();
        middleware.requireFile(require.resolve("buster-core"));
        middleware.requireFile(require.resolve("./../browser/cross-frame"));
        middleware.requireFile(require.resolve("./../browser/client-frame-load"));

        return this._internalScriptServer = middleware;
    },

    getResource: function (path) {
        var resource = this.resources[path];

        if (resource.backend && !resource.proxy) {
            var parsed = url.parse(resource.backend);
            resource.proxy = proxyMiddleware.create(
                parsed.hostname, parsed.port, parsed.path);
            resource.proxy.proxyPath = this.resourceContextPath();
        }

        if (resource.combine && !resource.content) {
            resource.content = this.combineResources(resource.combine);
        }

        return resource;
    },

    combineResources: function (resources) {
        var content = "";

        for (var i = 0, l = resources.length; i < l; ++i) {
            content += this.getResource(resources[i]).content + "\n";
        }

        return content;
    },

    resourceContextPath: function () {
        return this.contextPath + "/res";
    },

    internalsContextPath: function () {
        return this.contextPath + "/_";
    }
};
<|endoftext|>


<commit_before>'use strict';

var logger = require('./logger');

/*
 * Map of users registered in the system.
 */
function UserRegistry() {
  this.usersByName = {};
  this.usersBySessionId = {};
  this.sockets = {};
}

UserRegistry.prototype.addSocket = function (socket) {
  this.sockets[socket.id] = socket;
};

UserRegistry.prototype.register = function (userSession) {
  var userName = userSession.getUserName();
  logger.info('register user ' + userName);

  this.usersByName[userName] = userSession;
  this.usersBySessionId[userSession.getSessionId()] = userSession;
};

UserRegistry.prototype.getByName = function (userName) {
  return this.usersByName[userName];
};

UserRegistry.prototype.getBySessionId = function (sessionId) {
  return this.usersBySessionId[sessionId];
};

UserRegistry.prototype.removeBySession = function (sessionId) {
  var user = this.getBySessionId(sessionId);
  if (user) {
    delete this.usersByName[user.getUserName()];
    delete this.usersBySessionId[sessionId];
    return user;
  }
};

module.exports = UserRegistry;
<commit_msg>removed unused function from user-registry<commit_after>'use strict';

var logger = require('./logger');

/*
 * Map of users registered in the system.
 */
function UserRegistry() {
  this.usersByName = {};
  this.usersBySessionId = {};
}

UserRegistry.prototype.register = function (userSession) {
  var userName = userSession.getUserName();
  logger.info('register user ' + userName);

  this.usersByName[userName] = userSession;
  this.usersBySessionId[userSession.getSessionId()] = userSession;
};

UserRegistry.prototype.getByName = function (userName) {
  return this.usersByName[userName];
};

UserRegistry.prototype.getBySessionId = function (sessionId) {
  return this.usersBySessionId[sessionId];
};

UserRegistry.prototype.removeBySession = function (sessionId) {
  var user = this.getBySessionId(sessionId);
  if (user) {
    delete this.usersByName[user.getUserName()];
    delete this.usersBySessionId[sessionId];
    return user;
  }
};

module.exports = UserRegistry;
<|endoftext|>