<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);
        }

        // 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:
<commit_msg>failed findagain through cmdline shouldn't show findbar<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 || this._vimp_keepClosed) { 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);

        // We don't want to show the findbar on failed searches when using the commandline.
        this.findbar._vimp_keepClosed = true;

        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) {
        this.findbar._vimp_keepClosed = false;
        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>import React from "react";
import {Link} from "react-router";
import LectureItem from "../../components/LectureItem/LectureItem";
import Colors from "../../constants/ColorConstants";

/**
 * Thumbnail of course item
 */
class CourseItem extends React.Component {
  /**
   * constructor
   * @param {object} props
   */
  constructor(props) {
    super(props);
    /**
     * @type {object}
     * @property {integer} number of lectures
     */
    this.state = {
      numberOfLectures: Math.floor((window.innerWidth - 256) / 264) - 1
    };
    this.updateNumberOfLectures = this.updateNumberOfLectures.bind(this);
  }

  componentDidMount() {
    window.addEventListener("resize", this.updateNumberOfLectures);
  }

  componentWillUnmount() {
    window.removeEventListener("resize", this.updateNumberOfLectures);
  }

  toCoursePage() {
    this.context.router.push(`/courses/${this.props.course.id}`);
  }

  updateNumberOfLectures() {
    this.setState({
      numberOfLectures: Math.floor((window.innerWidth - 256) / 264) - 1
    });
  }

  render() {
    const colorList = [Colors.BLUE, Colors.PURPLE, Colors.YELLOW, Colors.GREEN];
    const lectures = Object.keys(this.props.lectures);
    return (

          <Link to={`/courses/${this.props.course.id}`}>
            {this.props.course.title}
          </Link>

          {
            lectures.reverse().slice(0, this.state.numberOfLectures).map(lectureId => {
              const course = this.props.course;
              return <LectureItem
                      compact={true}
                      key={course.id + lectureId}
                      courseId={course.id}
                      lectureId={lectureId}
                      title={lectureId}
                      date={course.lectures[lectureId].timestamp}
                      />;
            })
          }

            <button onClick={() => this.toCoursePage()}>See All ></button>

    );
  }
}

CourseItem.propTypes = {
  course: React.PropTypes.object.isRequired, // TODO: define shape
  lectures: React.PropTypes.object.isRequired
};

CourseItem.contextTypes = {
  router: React.PropTypes.object.isRequired
};

export default CourseItem;
<commit_msg>changes<commit_after>import React from "react";
import {Link} from "react-router";
import LectureItem from "../../components/LectureItem/LectureItem";
import Colors from "../../constants/ColorConstants";

/**
 * Represents a course as a row of lectures
 */
class CourseItem extends React.Component {
  /**
   * constructor
   * @param {object} props
   */
  constructor(props) {
    super(props);
    /**
     * @type {object}
     * @property {integer} numberOfLectures The number of lectures in a course
     */
    this.state = {
      numberOfLectures: Math.floor((window.innerWidth - 256) / 264) - 1
    };
    this.updateNumberOfLectures = this.updateNumberOfLectures.bind(this);
  }

  componentDidMount() {
    window.addEventListener("resize", this.updateNumberOfLectures);
  }

  componentWillUnmount() {
    window.removeEventListener("resize", this.updateNumberOfLectures);
  }

  toCoursePage() {
    this.context.router.push(`/courses/${this.props.course.id}`);
  }

  updateNumberOfLectures() {
    this.setState({
      numberOfLectures: Math.floor((window.innerWidth - 256) / 264) - 1
    });
  }

  render() {
    const colorList = [Colors.BLUE, Colors.PURPLE, Colors.YELLOW, Colors.GREEN];
    const lectures = Object.keys(this.props.lectures);
    return (

          <Link to={`/courses/${this.props.course.id}`}>
            {this.props.course.title}
          </Link>

          {
            lectures.reverse().slice(0, this.state.numberOfLectures).map(lectureId => {
              const course = this.props.course;
              return <LectureItem
                      compact={true}
                      key={course.id + lectureId}
                      courseId={course.id}
                      lectureId={lectureId}
                      title={lectureId}
                      date={course.lectures[lectureId].timestamp}
                      />;
            })
          }

            <button onClick={() => this.toCoursePage()}>See All ></button>

    );
  }
}

CourseItem.propTypes = {
  course: React.PropTypes.object.isRequired, // TODO: define shape
  lectures: React.PropTypes.object.isRequired
};

CourseItem.contextTypes = {
  router: React.PropTypes.object.isRequired
};

export default CourseItem;
<|endoftext|>


<commit_before>/**
 * This is where we load and initialize components of our app.
 */
define("app", function (require) {
  "use strict";

  var $ = require("jquery");
  var _ = require("lodash");

  // let's get going
  var Finder   = require("finder/Finder");
  var Donate   = require("donate/Donate");
  var Revealer = require("revealer/Revealer");
  var Swapper  = require("swapper/Swapper");
  var Reportback = require("reportback/Reportback");

  // Initialize modules on load
  require("neue");
  require("modal");
  require("validation");

  require("campaign/SchoolFinder");
  require("validators/auth");
  require("validators/reportback");
  require("validators/address");
  require("validators/donate");
  require("Analytics");
  require("tiles");

  $(document).ready(function() {
    // If a fixed-sticky element is on the page, hook it up.
    $(".js-fixedsticky").fixedsticky();

    var $body = $("body");

    var $campaignFinderForm = $(".js-finder-form");
    if( $campaignFinderForm.length ) {
      var $results = $(".js-campaign-results");
      var $blankSlate = $(".js-campaign-blankslate");
      Finder.init($campaignFinderForm, $results, $blankSlate);
    }

    var $donateForm = $("#modal--donate-form");
    if( $donateForm.length ) {
      Donate.init();
    }

    var $revealGalleries = $body.find('[data-show-more="true"]');
    /**
     * Show hide large galleries.
     */
    if ($revealGalleries.length > 0) {
      var galleryList = [];

      $revealGalleries.each(function (index, gallery) {
        var $gallery = $(gallery);
        var $tiles = $gallery.find("li");
        var isMosaicGallery = $gallery.hasClass('-mosaic');
        var initialItems = isMosaicGallery ? 5 : 6;
        var stepItems = isMosaicGallery ? 8 : 6;

        if ($tiles.length > initialItems) {
          galleryList[index] = new Revealer($gallery, $tiles, "gallery", initialItems, stepItems);
        }
      });
    }

    var $mosaicGalleries = $body.find(".gallery.-mosaic");

    /**
     * Swap low resolution images for feature tiles in mosaic gallery
     * with higher resolution version from tablet size up.
     */
    if ($mosaicGalleries.length && $mosaicGalleries.hasClass("-featured")) {
      var swappedImagesList = [];

      $(window).on("resize", _.debounce(function () {

        if (window.matchMedia("(min-width: 768px)").matches && !swappedImagesList.length) {
          $mosaicGalleries.each(function (index) {
            var $featureTile = $(this).find("li").first();
            swappedImagesList[index] = new Swapper($featureTile);
          });
        }
      }, 500));

    }

    /**
     * Initialize Reportback js.
     */
    var $reportback = $body.find("#reportback");

    if ($reportback.length) {
      Reportback.init($reportback);
    }

    // Open share links in a popup dialog.
    var windowOptions = 'scrollbars=yes,resizable=yes,toolbar=no,location=yes';
    var width         = 550;
    var height        = 420;
    var winHeight     = screen.height;
    var winWidth      = screen.width;

    $('body').on('click', 'a.js-share-link', function(event) {
      event.preventDefault();
      left = Math.round((winWidth / 2) - (width / 2));
      top = 0;

      if (winHeight > height) {
        top = Math.round((winHeight / 2) - (height / 2));
      }

      window.open(this.href, 'intent', windowOptions + ',width=' + width +
       ',height=' + height + ',left=' + left + ',top=' + top);
    });

    $("html").addClass("js-ready");

  });

});
<commit_msg>Changing reserved variable names to unique names<commit_after>/**
 * This is where we load and initialize components of our app.
 */
define("app", function (require) {
  "use strict";

  var $ = require("jquery");
  var _ = require("lodash");

  // let's get going
  var Finder   = require("finder/Finder");
  var Donate   = require("donate/Donate");
  var Revealer = require("revealer/Revealer");
  var Swapper  = require("swapper/Swapper");
  var Reportback = require("reportback/Reportback");

  // Initialize modules on load
  require("neue");
  require("modal");
  require("validation");

  require("campaign/SchoolFinder");
  require("validators/auth");
  require("validators/reportback");
  require("validators/address");
  require("validators/donate");
  require("Analytics");
  require("tiles");

  $(document).ready(function() {
    // If a fixed-sticky element is on the page, hook it up.
    $(".js-fixedsticky").fixedsticky();

    var $body = $("body");

    var $campaignFinderForm = $(".js-finder-form");
    if( $campaignFinderForm.length ) {
      var $results = $(".js-campaign-results");
      var $blankSlate = $(".js-campaign-blankslate");
      Finder.init($campaignFinderForm, $results, $blankSlate);
    }

    var $donateForm = $("#modal--donate-form");
    if( $donateForm.length ) {
      Donate.init();
    }

    var $revealGalleries = $body.find('[data-show-more="true"]');
    /**
     * Show hide large galleries.
     */
    if ($revealGalleries.length > 0) {
      var galleryList = [];

      $revealGalleries.each(function (index, gallery) {
        var $gallery = $(gallery);
        var $tiles = $gallery.find("li");
        var isMosaicGallery = $gallery.hasClass('-mosaic');
        var initialItems = isMosaicGallery ? 5 : 6;
        var stepItems = isMosaicGallery ? 8 : 6;

        if ($tiles.length > initialItems) {
          galleryList[index] = new Revealer($gallery, $tiles, "gallery", initialItems, stepItems);
        }
      });
    }

    var $mosaicGalleries = $body.find(".gallery.-mosaic");

    /**
     * Swap low resolution images for feature tiles in mosaic gallery
     * with higher resolution version from tablet size up.
     */
    if ($mosaicGalleries.length && $mosaicGalleries.hasClass("-featured")) {
      var swappedImagesList = [];

      $(window).on("resize", _.debounce(function () {

        if (window.matchMedia("(min-width: 768px)").matches && !swappedImagesList.length) {
          $mosaicGalleries.each(function (index) {
            var $featureTile = $(this).find("li").first();
            swappedImagesList[index] = new Swapper($featureTile);
          });
        }
      }, 500));

    }

    /**
     * Initialize Reportback js.
     */
    var $reportback = $body.find("#reportback");

    if ($reportback.length) {
      Reportback.init($reportback);
    }

    // Open share links in a popup dialog.
    var windowOptions = 'scrollbars=yes,resizable=yes,toolbar=no,location=yes';
    var width         = 550;
    var height        = 420;
    var winHeight     = screen.height;
    var winWidth      = screen.width;

    $('body').on('click', 'a.js-share-link', function(event) {
      event.preventDefault();
      var leftPos = Math.round((winWidth / 2) - (width / 2));
      var topPos = 0;

      if (winHeight > height) {
        topPos = Math.round((winHeight / 2) - (height / 2));
      }

      window.open(this.href, 'intent', windowOptions + ',width=' + width +
       ',height=' + height + ',left=' + leftPos + ',top=' + topPos);
    });

    $("html").addClass("js-ready");

  });

});
<|endoftext|>


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

'use strict';

var fsMgr = require('./fs-manager');
var Resource = fsMgr.Resource;
var pty = require('pty.js');
var path = require('path');
var _ = require('underscore');
var spawn = require('child_process').spawn;
//var express = require('express.io');
var express = require('express');
var shortid = require('shortid');

var authMgr = require('../../common/auth-manager');
var utils = require('../../common/utils');
var logger = require('../../common/log-manager');
var config = require('../../common/conf-manager').conf;

var ClientError = utils.ClientError;
var ServerError = utils.ServerError;

// Control Terminal Manager
/* termMgr = {
    processID1: {
        cid: clientID,
        term: child_object,
        status: 'RUN'/'STOP'
    },

    processID2: {
        ...
    }
}
*/
var termMgr = {};

exports.route = {
    // if occured io.emit('console:exec', data) event
    exec: function (req) {
        /*
            req.data = {
                cid: <client Id>,
                fsid: <fs Id>,
                cmd: <command>,
                args: <arguments>,
                opt: {
                    name: <name>,
                    cols: <columes>,
                    rows: <rows>,
                    cwd: <current working directory>,
                    env: <enviroments>
                }
            }
        */
        // TODO check security. Can caller access the fs(fsid)?
        // Make policy: Only fs owner can call console api?

        //logger.debug('console:exec', req.data, req.handshake);
        var cid = req.data.cid;
        var cmd = req.data.cmd;
        var args = req.data.args;

        if (!req.handshake.user) {
            return req.io.emit('error', {
                cid: cid,
                data: new Error('Unauthorized access')
            });
        }
        var rootPath = (new fsMgr.WebidaFS(req.data.fsid)).getRootPath();

        var name = req.data.opt.name || 'xterm-color';
        var cols = req.data.opt.cols || 80;
        var rows = req.data.opt.rows || 30;
        var cwd = path.resolve(rootPath, req.data.opt.cwd || '');

        var env = _.clone(process.env);
        for (var key in req.data.opt.env) {
            if (req.data.opt.env.hasOwnProperty(key)) {
                env[key] = req.data.opt.env[key];
            }
        }
        env.HOME = rootPath; // TODO rootPath is home?
        env.PATH = path.resolve(__dirname, '../bin') + ':' + env.PATH;

        var opt = {
            name: name,
            cols: cols,
            rows: rows,
            cwd: cwd,
            env: env
        };

        // execute console process
        var term = pty.spawn(cmd, args, opt);
        var pid = term.pid;

        // Add console's object info
        termMgr[pid] = {
            cid: cid,
            term: term,
            status: 'RUN'
        };

        // send to client about console's pid
        var resAck = {
            cid: cid,
            pid: pid
        };
        req.io.emit('ack', resAck);

        // register console's stdout event
        term.on('data', function (data) {
            // if commnad is invaild, occur error event.
            if (data.match(/^execvp\([0-9]*\)/)) {
                req.io.emit('error', {
                    cid: cid,
                    pid: pid,
                    data: cmd + ': command not found'
                });
            } else {
                req.io.emit('data', {
                    cid: cid,
                    pid: pid,
                    data: data
                });
            }
        });

        // register console's stderr event
        term.on('error', function (data) {
            // EIO, happens when someone closes our child
            // process: the only process in the terminal.

            // if close event occurs. execute self.kill() in pty.js
            if (data.code !== 'EIO') {
                req.io.emit('error', {
                    cid: cid,
                    pid: pid,
                    data: data
                });
            }
            logger.debug('console error', req, term, termMgr);
        });

        // register console's exit event
        term.on('close', function (data) {
            // if console program is ended, return ture / false.
            req.io.emit('close', {
                cid: cid,
                pid: pid,
                data: data
            });

            // Remove console's object info
            if (termMgr[pid]) {
                delete termMgr[pid];
            }
        });
    },
    // if occured io.emit('console:write', data) event
    write: function (req) {
        /*
            req.data = {
                pid: <process Id>,
                cmd: <user input>
            }
        */

        var pid = req.data.pid;
        var cmd = req.data.cmd;

        if (termMgr[pid]) {
            termMgr[pid].term.write(cmd);
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }
    },
    // if occured io.emit('console:kill', data) event
    kill: function (req) {
        /*
            req.data = {
                pid: <process Id>
            }
        */
        var pid = req.data.pid;

        if (termMgr[pid]) {
            termMgr[pid].term.kill();
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }
    },
    // if occured io.emit('console:pause', data) event
    pause: function (req) {
        /*
            req.data = {
                pid: <process Id>
            }
        */
        var pid = req.data.pid;

        if (termMgr[pid]) {
            termMgr[pid].status = 'STOP';
            termMgr[pid].term.pause();
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }
    },
    // if occured io.emit('console:resume', data) event
    resume: function (req) {
        /*
            req.data = {
                pid: <process Id>
            }
        */
        var pid = req.data.pid;
        if (termMgr[pid]) {
            termMgr[pid].status = 'RUN';
            termMgr[pid].term.resume();
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }
    },
    // if occured io.emit('console:setEncoding', data) event
    setEncoding: function (req) {
        /*
            req.data = {
                pid: <process Id>,
                encoding : <data encoding> (default: utf8)
            }
        */
        var pid = req.data.pid;
        var encoding = req.data.encoding || 'utf8';

        if (termMgr[pid]) {
            termMgr[pid].term.setEncoding(encoding);
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }
    },

    resize: function (req) {
        /*
            req.data = {
                pid: <process Id>,
                cols: <columns>,
                rows: <rows>
            }
        */
        var pid = req.data.pid;
        var cols = req.data.cols;
        var rows = req.data.rows;

        if (termMgr[pid]) {
            termMgr[pid].term.resize(cols, rows);
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }

    },

    getStatus: function (req) {
        /*
            req.data = {
                pid: <process Id>
            }
        */
        var pid = req.data.pid;
        if (termMgr[pid]) {
            req.io.emit('status', {
                cid: termMgr[pid].cid,
                pid: termMgr[pid].pid,
                data: termMgr[pid].status
            });
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }
    },

    disconnect: function () {
        for (var key in termMgr) {
            if (termMgr.hasOwnProperty(key)) {
                termMgr[key].term.kill();
            }
        }

        termMgr = {};
    }
};

var execTable = {};
var ipHostTable = {
    'privateNetworkReserved': '0.0.0',  // 10.0.0.0
    'broadcaseReserved': '255.255.255', // 10.255.255.255
    'gatewayReserved': '0.0.1' // 10.0.0.1
};
var ipHostLastUsed = '0.0.1';     // 0.0.2 ~ 255.255.254
function removeProc(proc) {
    logger.debug('Remove proc', proc.pid);
    proc.removeAllListeners();
    clearTimeout(proc._timeoutId);
    delete execTable[proc.pid];
    delete ipHostTable[proc.pid];
}
function addProc(proc, ipHostAddr) {
    logger.debug('Add proc', proc.pid);
    proc._stdout = '';
    proc._stderr = '';
    proc._timeoutId = setTimeout(function () {
        logger.debug('Timeout proc', proc.pid);
        proc.kill();
    }, config.services.fs.exec.timeoutSecs * 1000);
    execTable[proc.pid] = proc;
    ipHostTable[proc.pid] = ipHostAddr;
}
function startProc(cwdRsc, cmd, args, ipHostAddr, callback) {
    logger.debug('Exec start', cmd, args);

    // TODO env will be removed
    var env = _.clone(process.env);
    env.HOME = cwdRsc.wfs.getRootPath();
    env.PATH = path.resolve(__dirname, '../bin') + ':' + env.PATH;

    /* execute command */
    var proc = spawn(cmd, args, {
        cwd: cwdRsc.localPath,
        detached: false,
        env: env
    });

    addProc(proc, ipHostAddr);

    proc.stdout.on('data', function (data) {
        proc._stdout += data;
    });

    proc.stderr.on('data', function (data) {
        proc._stderr += data;
    });

    proc.on('exit', function (code) {
        logger.debug('Exec close', proc.pid, 'code:'+code, 'stdout:'+proc._stdout, 'stderr:'+proc._stderr);
        removeProc(proc);
        /*
        if (code === 0) {
            return callback(code, proc._stdout, proc._stderr);
        }
        */
        if (code === null) {
            return callback(new Error('Abnormal exit'), proc._stdout, proc._stderr);
        }
        callback(null, proc._stdout, proc._stderr, code);
    });

    proc.on('error', function (err) {
        logger.debug('Exec error', proc.pid, err, proc._stdout, proc._stderr);
        removeProc(proc);
        if (err.errno === 'EPERM') {
            callback(new ClientError('Exec timeout'), proc._stdout, proc._stderr);
        } else {
            callback(new ServerError('Exec failed'), proc._stdout, proc._stderr);
        }
    });
}
function exec(cwdUrl, cmdInfo, callback) {
    function escapeShellCmdComponent(cmd) {
        return '"' + cmd.replace(/(["$`\\])/g, '\\$1') + '"';
    }

    function getAvailableIPHostAddress(){
        function getNext(prevIpHost){
            var splitedIp = prevIpHost.split('.');
            for(var i = splitedIp.length-1, carried=true; i >= 0; i--){
                if(carried){
                    splitedIp[i]++;
                    carried = false;
                }
                if(splitedIp[i] > 255){
                    splitedIp[i] = 0;
                    carried = true;
                    if(i === 0){
                        splitedIp.fill(0);
                    }
                }
            }
            return splitedIp.join('.');
        }

        var usedIPHostAddr = _.values(ipHostTable);
        var next = getNext(ipHostLastUsed);
        while(usedIPHostAddr.indexOf(next) > -1){
            next = getNext(next);
        }
        ipHostLastUsed = next;
        return next;
    }

    var cwdRsc = new Resource(cwdUrl);
    /* request.info is command information
    * info.cmd : command
    * info.args : command arguments
    */
    var cmd = cmdInfo.cmd;
    if (!cmd) {
        return callback('Command is not specified.');
    }

    if (!config.services.fs.exec.validExecCommands.hasOwnProperty(cmd)) {
        return callback('Invalid command');
    }
    var subCmds = config.services.fs.exec.validExecCommands[cmd];
    if (subCmds && !_.contains(subCmds, cmdInfo.args[0])) {
        return callback('Invalid argument');
    }

    if (config.services.fs.lxc.useLxc) {
        var name = config.services.fs.lxc.containerNamePrefix + '-' + shortid.generate();
        var confPath = config.services.fs.lxc.confPath;
        var cwdLxcPath = path.join('/fs', cwdRsc.pathname);
        var cmdArgsStr = _.map(cmdInfo.args, escapeShellCmdComponent).join(' ');
        var cmdInLxc = 'cd "' + cwdLxcPath + '"; ' + cmd + ' ' + cmdArgsStr;

        var ipHostAddr = getAvailableIPHostAddress();
        var args = [
            '/usr/bin/lxc-execute',
            '-n', name,
            '-f', confPath,
            '-s', 'lxc.rootfs=' + config.services.fs.lxc.rootfsPath,
            '-s', 'lxc.mount.entry=' + cwdRsc.wfs.getRootPath() + ' fs none rw,bind 0 0',
            //'-s', 'lxc.network.ipv4=10.0.3.'+ ipHostAddr +'/24',
            '-s', 'lxc.network.ipv4=10.'+ ipHostAddr +'/8',
            '-s', 'lxc.network.ipv4.gateway=10.0.0.1',
            '--', 'su', config.services.fs.lxc.userid, '-c', cmdInLxc
        ];
        startProc(cwdRsc, 'sudo', args, ipHostAddr, callback);
    } else {
        startProc(cwdRsc, cmd, cmdInfo.args, null, callback);
    }
}
exports.exec = exec;

exports.router = new express.Router();

exports.router.post('/webida/api/fs/exec/:fsid/*',
    authMgr.verifyToken,
    function (req, res, next) {
        var rsc = 'fs:' + req.params.fsid + '/*';
        authMgr.checkAuthorize({uid:req.user.uid, action:'fs:exec', rsc:rsc}, res, next);
    },
    utils.keepConnect(),
    function (req, res) {
        var fsid = req.params.fsid;
        var cwdPath = path.join('/', req.params[0]);
        logger.info('exec path = ', cwdPath);
        var cwdUrl = 'wfs://' + fsid + cwdPath;
        var cmdInfo = JSON.parse(req.body.info);
        var sessionID = req.body.sessionID;
        var uid = req.user && req.user.uid;
        if (uid === undefined) {
            return res.sendfail(new ClientError('invalid uid'));
        }

        fsMgr.checkLock(fsid, cwdPath, cmdInfo, function(err) {
            if (err) {
                return res.sendfail(err);
            }
            exec(cwdUrl, cmdInfo, function (err, stdout, stderr, ret) {
                if (err) {
                    return res.sendfail(err);
                }
                fsMgr.updateByExec(cmdInfo,uid, fsid, cwdPath, cwdUrl, sessionID, function() {
                    return res.sendok({stdout: stdout, stderr: stderr, ret:ret});
                });
            });
        });
    }
);
<commit_msg>[TASK] Improve performance on issuing ip address for lxc-execute<commit_after>/*
 * Copyright (c) 2012-2015 S-Core Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

var fsMgr = require('./fs-manager');
var Resource = fsMgr.Resource;
var pty = require('pty.js');
var path = require('path');
var _ = require('underscore');
var spawn = require('child_process').spawn;
//var express = require('express.io');
var express = require('express');
var shortid = require('shortid');

var authMgr = require('../../common/auth-manager');
var utils = require('../../common/utils');
var logger = require('../../common/log-manager');
var config = require('../../common/conf-manager').conf;

var ClientError = utils.ClientError;
var ServerError = utils.ServerError;

// Control Terminal Manager
/* termMgr = {
    processID1: {
        cid: clientID,
        term: child_object,
        status: 'RUN'/'STOP'
    },

    processID2: {
        ...
    }
}
*/
var termMgr = {};

exports.route = {
    // if occured io.emit('console:exec', data) event
    exec: function (req) {
        /*
            req.data = {
                cid: <client Id>,
                fsid: <fs Id>,
                cmd: <command>,
                args: <arguments>,
                opt: {
                    name: <name>,
                    cols: <columes>,
                    rows: <rows>,
                    cwd: <current working directory>,
                    env: <enviroments>
                }
            }
        */
        // TODO check security. Can caller access the fs(fsid)?
        // Make policy: Only fs owner can call console api?

        //logger.debug('console:exec', req.data, req.handshake);
        var cid = req.data.cid;
        var cmd = req.data.cmd;
        var args = req.data.args;

        if (!req.handshake.user) {
            return req.io.emit('error', {
                cid: cid,
                data: new Error('Unauthorized access')
            });
        }
        var rootPath = (new fsMgr.WebidaFS(req.data.fsid)).getRootPath();

        var name = req.data.opt.name || 'xterm-color';
        var cols = req.data.opt.cols || 80;
        var rows = req.data.opt.rows || 30;
        var cwd = path.resolve(rootPath, req.data.opt.cwd || '');

        var env = _.clone(process.env);
        for (var key in req.data.opt.env) {
            if (req.data.opt.env.hasOwnProperty(key)) {
                env[key] = req.data.opt.env[key];
            }
        }
        env.HOME = rootPath; // TODO rootPath is home?
        env.PATH = path.resolve(__dirname, '../bin') + ':' + env.PATH;

        var opt = {
            name: name,
            cols: cols,
            rows: rows,
            cwd: cwd,
            env: env
        };

        // execute console process
        var term = pty.spawn(cmd, args, opt);
        var pid = term.pid;

        // Add console's object info
        termMgr[pid] = {
            cid: cid,
            term: term,
            status: 'RUN'
        };

        // send to client about console's pid
        var resAck = {
            cid: cid,
            pid: pid
        };
        req.io.emit('ack', resAck);

        // register console's stdout event
        term.on('data', function (data) {
            // if commnad is invaild, occur error event.
            if (data.match(/^execvp\([0-9]*\)/)) {
                req.io.emit('error', {
                    cid: cid,
                    pid: pid,
                    data: cmd + ': command not found'
                });
            } else {
                req.io.emit('data', {
                    cid: cid,
                    pid: pid,
                    data: data
                });
            }
        });

        // register console's stderr event
        term.on('error', function (data) {
            // EIO, happens when someone closes our child
            // process: the only process in the terminal.

            // if close event occurs. execute self.kill() in pty.js
            if (data.code !== 'EIO') {
                req.io.emit('error', {
                    cid: cid,
                    pid: pid,
                    data: data
                });
            }
            logger.debug('console error', req, term, termMgr);
        });

        // register console's exit event
        term.on('close', function (data) {
            // if console program is ended, return ture / false.
            req.io.emit('close', {
                cid: cid,
                pid: pid,
                data: data
            });

            // Remove console's object info
            if (termMgr[pid]) {
                delete termMgr[pid];
            }
        });
    },
    // if occured io.emit('console:write', data) event
    write: function (req) {
        /*
            req.data = {
                pid: <process Id>,
                cmd: <user input>
            }
        */

        var pid = req.data.pid;
        var cmd = req.data.cmd;

        if (termMgr[pid]) {
            termMgr[pid].term.write(cmd);
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }
    },
    // if occured io.emit('console:kill', data) event
    kill: function (req) {
        /*
            req.data = {
                pid: <process Id>
            }
        */
        var pid = req.data.pid;

        if (termMgr[pid]) {
            termMgr[pid].term.kill();
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }
    },
    // if occured io.emit('console:pause', data) event
    pause: function (req) {
        /*
            req.data = {
                pid: <process Id>
            }
        */
        var pid = req.data.pid;

        if (termMgr[pid]) {
            termMgr[pid].status = 'STOP';
            termMgr[pid].term.pause();
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }
    },
    // if occured io.emit('console:resume', data) event
    resume: function (req) {
        /*
            req.data = {
                pid: <process Id>
            }
        */
        var pid = req.data.pid;
        if (termMgr[pid]) {
            termMgr[pid].status = 'RUN';
            termMgr[pid].term.resume();
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }
    },
    // if occured io.emit('console:setEncoding', data) event
    setEncoding: function (req) {
        /*
            req.data = {
                pid: <process Id>,
                encoding : <data encoding> (default: utf8)
            }
        */
        var pid = req.data.pid;
        var encoding = req.data.encoding || 'utf8';

        if (termMgr[pid]) {
            termMgr[pid].term.setEncoding(encoding);
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }
    },

    resize: function (req) {
        /*
            req.data = {
                pid: <process Id>,
                cols: <columns>,
                rows: <rows>
            }
        */
        var pid = req.data.pid;
        var cols = req.data.cols;
        var rows = req.data.rows;

        if (termMgr[pid]) {
            termMgr[pid].term.resize(cols, rows);
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }

    },

    getStatus: function (req) {
        /*
            req.data = {
                pid: <process Id>
            }
        */
        var pid = req.data.pid;
        if (termMgr[pid]) {
            req.io.emit('status', {
                cid: termMgr[pid].cid,
                pid: termMgr[pid].pid,
                data: termMgr[pid].status
            });
        } else {
            req.io.emit('error', {
                pid: pid,
                data: pid + ': arguments must be process ID'
            });
        }
    },

    disconnect: function () {
        for (var key in termMgr) {
            if (termMgr.hasOwnProperty(key)) {
                termMgr[key].term.kill();
            }
        }

        termMgr = {};
    }
};

var execTable = {};
var ipHostTable = {
    'privateNetworkReserved': '0.0.0',  // 10.0.0.0
    'broadcastReserved': '255.255.255', // 10.255.255.255
    'gatewayReserved': '0.0.1' // 10.0.0.1
};
var usedIpHostAddr = {
    '0.0.0': null,
    '255.255.255': null,
    '0.0.1': null
};
var ipHostLastUsed = '0.0.1';     // 0.0.2 ~ 255.255.254
function removeProc(proc) {
    logger.debug('Remove proc', proc.pid);
    proc.removeAllListeners();
    clearTimeout(proc._timeoutId);
    delete execTable[proc.pid];
    delete usedIpHostAddr[ipHostTable[proc.pid]];
    delete ipHostTable[proc.pid];
}
function addProc(proc, ipHostAddr) {
    logger.debug('Add proc', proc.pid);
    proc._stdout = '';
    proc._stderr = '';
    proc._timeoutId = setTimeout(function () {
        logger.debug('Timeout proc', proc.pid);
        proc.kill();
    }, config.services.fs.exec.timeoutSecs * 1000);
    execTable[proc.pid] = proc;
    usedIpHostAddr[ipHostAddr] = null;
    ipHostTable[proc.pid] = ipHostAddr;
}
function startProc(cwdRsc, cmd, args, ipHostAddr, callback) {
    logger.debug('Exec start', cmd, args);

    // TODO env will be removed
    var env = _.clone(process.env);
    env.HOME = cwdRsc.wfs.getRootPath();
    env.PATH = path.resolve(__dirname, '../bin') + ':' + env.PATH;

    /* execute command */
    var proc = spawn(cmd, args, {
        cwd: cwdRsc.localPath,
        detached: false,
        env: env
    });

    addProc(proc, ipHostAddr);

    proc.stdout.on('data', function (data) {
        proc._stdout += data;
    });

    proc.stderr.on('data', function (data) {
        proc._stderr += data;
    });

    proc.on('exit', function (code) {
        logger.debug('Exec close', proc.pid, 'code:'+code, 'stdout:'+proc._stdout, 'stderr:'+proc._stderr);
        removeProc(proc);
        /*
        if (code === 0) {
            return callback(code, proc._stdout, proc._stderr);
        }
        */
        if (code === null) {
            return callback(new Error('Abnormal exit'), proc._stdout, proc._stderr);
        }
        callback(null, proc._stdout, proc._stderr, code);
    });

    proc.on('error', function (err) {
        logger.debug('Exec error', proc.pid, err, proc._stdout, proc._stderr);
        removeProc(proc);
        if (err.errno === 'EPERM') {
            callback(new ClientError('Exec timeout'), proc._stdout, proc._stderr);
        } else {
            callback(new ServerError('Exec failed'), proc._stdout, proc._stderr);
        }
    });
}
function exec(cwdUrl, cmdInfo, callback) {
    function escapeShellCmdComponent(cmd) {
        return '"' + cmd.replace(/(["$`\\])/g, '\\$1') + '"';
    }

    function getAvailableIPHostAddress() {
        function getNext(prevIpHost) {
            var splittedIp = prevIpHost.split('.');
            for (var i = splittedIp.length-1, carried=true; i >= 0; i--) {
                if (carried) {
                    splittedIp[i]++;
                    carried = false;
                }
                if (splittedIp[i] > 255) {
                    splittedIp[i] = 0;
                    carried = true;
                    if (i === 0) {
                        splittedIp.fill(0);
                    }
                }
            }
            return splittedIp.join('.');
        }

        //var usedIPHostAddr = _.values(ipHostTable);
        var next = getNext(ipHostLastUsed);
        while (next in usedIpHostAddr) {
            next = getNext(next);
        }
        ipHostLastUsed = next;
        return next;
    }

    var cwdRsc = new Resource(cwdUrl);
    /* request.info is command information
    * info.cmd : command
    * info.args : command arguments
    */
    var cmd = cmdInfo.cmd;
    if (!cmd) {
        return callback('Command is not specified.');
    }

    if (!config.services.fs.exec.validExecCommands.hasOwnProperty(cmd)) {
        return callback('Invalid command');
    }
    var subCmds = config.services.fs.exec.validExecCommands[cmd];
    if (subCmds && !_.contains(subCmds, cmdInfo.args[0])) {
        return callback('Invalid argument');
    }

    if (config.services.fs.lxc.useLxc) {
        var name = config.services.fs.lxc.containerNamePrefix + '-' + shortid.generate();
        var confPath = config.services.fs.lxc.confPath;
        var cwdLxcPath = path.join('/fs', cwdRsc.pathname);
        var cmdArgsStr = _.map(cmdInfo.args, escapeShellCmdComponent).join(' ');
        var cmdInLxc = 'cd "' + cwdLxcPath + '"; ' + cmd + ' ' + cmdArgsStr;

        var ipHostAddr = getAvailableIPHostAddress();
        var args = [
            '/usr/bin/lxc-execute',
            '-n', name,
            '-f', confPath,
            '-s', 'lxc.rootfs=' + config.services.fs.lxc.rootfsPath,
            '-s', 'lxc.mount.entry=' + cwdRsc.wfs.getRootPath() + ' fs none rw,bind 0 0',
            //'-s', 'lxc.network.ipv4=10.0.3.'+ ipHostAddr +'/24',
            '-s', 'lxc.network.ipv4=10.'+ ipHostAddr +'/8',
            '-s', 'lxc.network.ipv4.gateway=10.0.0.1',
            '--', 'su', config.services.fs.lxc.userid, '-c', cmdInLxc
        ];
        startProc(cwdRsc, 'sudo', args, ipHostAddr, callback);
    } else {
        startProc(cwdRsc, cmd, cmdInfo.args, null, callback);
    }
}
exports.exec = exec;

exports.router = new express.Router();

exports.router.post('/webida/api/fs/exec/:fsid/*',
    authMgr.verifyToken,
    function (req, res, next) {
        var rsc = 'fs:' + req.params.fsid + '/*';
        authMgr.checkAuthorize({uid:req.user.uid, action:'fs:exec', rsc:rsc}, res, next);
    },
    utils.keepConnect(),
    function (req, res) {
        var fsid = req.params.fsid;
        var cwdPath = path.join('/', req.params[0]);
        logger.info('exec path = ', cwdPath);
        var cwdUrl = 'wfs://' + fsid + cwdPath;
        var cmdInfo = JSON.parse(req.body.info);
        var sessionID = req.body.sessionID;
        var uid = req.user && req.user.uid;
        if (uid === undefined) {
            return res.sendfail(new ClientError('invalid uid'));
        }

        fsMgr.checkLock(fsid, cwdPath, cmdInfo, function(err) {
            if (err) {
                return res.sendfail(err);
            }
            exec(cwdUrl, cmdInfo, function (err, stdout, stderr, ret) {
                if (err) {
                    return res.sendfail(err);
                }
                fsMgr.updateByExec(cmdInfo,uid, fsid, cwdPath, cwdUrl, sessionID, function() {
                    return res.sendok({stdout: stdout, stderr: stderr, ret:ret});
                });
            });
        });
    }
);
<|endoftext|>


<commit_before>/*
 * Date Compare v0.1
 * jQuery Plugin to order a range of dates or validate if any date is greater than other
 * (c)2014 Rafael Maruta - rafaelmaruta@gmail.com - www.lotuscreative.com.br
 * Released under the MIT license
 *
 * It only accepts dates at numerical format like DD/MM/YYYY, MM/YYYY, YYYY-MM-DD or YYYY-MM.
 *
 * Accepts three parameters: type, arrayOrder and format.
 *
 * Use type:'asArray' if you wanna an ordering array return or 'just2' if you just wanna compare two dates.
 * If the first date in the selector is greater than the second, it will return false. It will consider the DOM ordering.
 *
 * Use arrayOrder:'ASC' if you wanna the array return to be at ascending order. The default value is 'DESC'.
 *
 * Use format:'-' if you wanna the YYYY-MM-DD or YYYY-MM format. The default value is '/' DD/MM/YYYY or MM/YYYY.
 *
 * Call examples:
 * alert($('#div1,#div2').dateCompare({type:'just2'}));
 * window.console.log($('#div1,#div2,#div3,#div4').dateCompare({arrayOrder:'ASC',format:'-'}));
 *
 * Any sugestions please contact me.
*/

(function()
{
	$.fn.dateCompare = function(options)
	{
		/* Setting options. They can be overwritten */
		var opts = $.extend({},$.fn.dateCompare.defaults,options);

		/* Array dates holder */
		var arr = [];

		/* Checks the tag name of each selection to hold its value and apply the checker push function */
		this.each(function()
		{
			switch ($(this).get(0).tagName)
			{
				case 'INPUT':
					chkfPush($(this).val());
				break;
				case 'TEXTAREA':
					chkfPush($(this).val());
				break;
				case 'SELECT':
					chkfPush($(this).val());
				break;
				default:
					chkfPush($(this).text());
				break;
			};
		});

		/* Checks if the chosen return is 'asArray' or if the array is holding more than 2 elements */
		if (opts.type == 'asArray' || arr.length > 2)
		{
			/* Checks the chosen ordering type */
			if (opts.arrayOrder == 'ASC')
			{
				arr.sort();
			}
			else
			{
				arr.sort().reverse();
			};

			/* Extends the Array native JavaScript object to apply a function to each index value */
			Array.prototype.map = function(x)
			{
				for (i = 0; i < this.length; i++)
				{
					this[i] = x(this[i]);
				};
			};

			/* Checks the requested format output, formats it and applies the function on the array */
			if (opts.format == '/')
			{
				arr.map(format);
			};

			/* Returns the ordering array */
			return arr;
		}
		/* Checks if the chosen type is 'just2' and the array length is equal to 2 */
		else if (opts.type == 'just2' && arr.length == 2)
		{
			/* If the first value is shorter, it returns true */
			if (arr[0] <= arr[1])
			{
				return true;
			}
			else
			{
				return false;
			};
		}
		/* If there's less than 2 elements in the selector */
		else
		{
			return 'Missing element.';
		};

		/* Private method which checks the date format, formats it and pushes in the array holder */
		function chkfPush(x,y)
		{
			if (x.indexOf('/') > -1)
			{
				arr.push(unformat(x));
			}
			else
			{
				arr.push(x);
			};
		};

		/* Private method which formats the date to DD/MM/YYYY */
		function format(x)
		{
			var x = x.split('-').reverse().join('/');
			return x;
		};

		/* Private method which formats the date to YYYY-MM-DD */
		function unformat(x)
		{
			var x = x.split('/').reverse().join('-');
			return x;
		};
	};

	/* Default properties values */
	$.fn.dateCompare.defaults = {
		type:'asArray',
		arrayOrder:'DESC',
		format:'/'
	};
}(jQuery));
<commit_msg>Delete jquery.date.compare-0.1.js<commit_after><|endoftext|>


<commit_before>var divlist = [];

function printspace(offset, htmltag) {
    for(var space=0; space<offset; space++) {
        htmltag.innerHTML+="&nbsp";
    }
}

function createbutton(innerhtml, weekobj){
    var buttontag = document.createElement("button");
    buttontag.setAttribute("type", "button");
    buttontag.onclick = weekobj.clearday();
    buttontag.className = "circlebutton";
    buttontag.innerHTML=innerhtml;
    return buttontag;
}
var parentdiv = document.createElement("div");

/* Days of Week module */
var weekcontainer = function(parentdiv) {
    this.divlist = [];
    this.tasklist = [];
    this.weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
    for(var i=0; i<this.weekdays.length;++i) {
        //Set day of week
        var weekdiv = document.createElement("div");
        weekdiv.setAttribute("style", "font-size:18px;color:black;font-family:monospace;");
        weekdiv.appendChild(createbutton("clear", this));
        weekdiv.innerHTML+=this.weekdays[i];
        this.divlist.push(weekdiv);
        parentdiv.appendChild(this.divlist[i]);
    }
}
weekcontainer.prototype.clearday = function() {
    for (var i=0; i < this.weekdays; ++i) {
        this.innerHTML = "";
        this.divlist[i] = "";
        this.divlist[i].appendChild(createbutton("clear"));
        this.divlist[i].innerHTML = this.weekdays[i];
    }
}
var weekobj = new weekcontainer(parentdiv);
document.body.appendChild(parentdiv);

document.getElementById("task-box").addEventListener("keyup", function(event) {
    event.preventDefault();
    if (event.keyCode == 13) {
        document.getElementById("task-enter").click();
    }
});

var displaycount = 0;
function hitbutton() {
    var divlist = weekobj.divlist;
    var tasklist = weekobj.tasklist;
    var val = document.getElementById("task-box").value;
    tasklist.push(val);
    var divindex = Math.floor(Math.random() * 6);
    if (displaycount == 7) {
        displaycount=0;
    }
    divindex = displaycount;
    displaycount += 1;
    var taskdiv = document.createElement("container");
    taskdiv.innerHTML+="
";
    printspace(5, taskdiv);
    taskdiv.innerHTML+= val;
    divlist[divindex].appendChild(taskdiv);
    for (var index=0; index < tasklist.length; index++) {
        console.log(tasklist[index]);
    }
}<commit_msg>preempt closure to make buttons work<commit_after>var divlist = [];
var allclearbtn = document.createElement("button");
allclearbtn.setAttribute("class", "circlebutton");
allclearbtn.innerHTML = "Clear ALL";
document.body.appendChild(allclearbtn);

function printspace(offset, htmltag) {
    for(var space=0; space<offset; space++) {
        htmltag.innerHTML+="&nbsp";
    }
}

function createbutton(innerhtml, id){
    var buttontag = document.createElement("button");
    buttontag.setAttribute("type", "button");
    buttontag.className = "circlebutton";
    buttontag.setAttribute("id", id);
    console.log(id);
    buttontag.innerHTML=innerhtml;
    return buttontag;
}
var parentdiv = document.createElement("div");
parentdiv.setAttribute("id", "root");

/* Days of Week module */
var weekcontainer = function(parentdiv) {
    this.divlist = []; //list of divs containing each day of the week and related properties
    this.tasklist = []; //list of possible tasks that were inputted
    this.taskcount = [];
    for (var i = 0; i < 7; ++i) {
        this.taskcount.push(i);
    }
    function clearday() {
        console.log("CALLED");
        for(var i=0; i<this.divlist.length; ++i) {
            //Set day of week
            console.log(this.divlist[i]);
        }
        console.log(this.divlist.length);
    }
    this.weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
    for(var i=0; i<this.weekdays.length;++i) {

        //div that holds dayofweek AND (later) list of tasks
        var weekdiv = document.createElement("div");
        //container with day of week as inner html
        var dayofweek = document.createElement("span");
        var buttontag = createbutton("clear", this.weekdays[i] + "btn");
        dayofweek.setAttribute("style", "font-size:18px;color:black;font-family:monospace;");
        dayofweek.appendChild(buttontag);
        dayofweek.innerHTML+=this.weekdays[i];
        weekdiv.setAttribute("id", this.weekdays[i]);
        weekdiv.appendChild(dayofweek);

        this.divlist.push(weekdiv);
        //console.log(this.divlist[i]);
        parentdiv.appendChild(this.divlist[i]);
    }

}
weekcontainer.prototype.clearday = function() {

}

var weekobj = new weekcontainer(parentdiv);
document.body.appendChild(parentdiv);

document.getElementById("task-box").addEventListener("keyup", function(event) {
    event.preventDefault();
    if (event.keyCode == 13) {
        document.getElementById("task-enter").click();
    }
});

var displaycount = 0;
function hitbutton() {
    var divlist = weekobj.divlist;
    var tasklist = weekobj.tasklist;
    var val = document.getElementById("task-box").value;
    tasklist.push(val);
    var divindex = Math.floor(Math.random() * 6);
    if (displaycount == 7) {
        displaycount=0;
    }
    divindex = displaycount;
    displaycount += 1;
    var taskdiv = document.createElement("container");
    taskdiv.innerHTML+="
";
    printspace(5, taskdiv);
    taskdiv.innerHTML+= val;
    divlist[divindex].appendChild(taskdiv);
    for (var index=0; index < tasklist.length; index++) {
        console.log(tasklist[index]);
    }
}

var createfn = function (i) {
    return function() {
      console.log(i + " IN LISTENER");
    };
}

for (var i=0; i < weekobj.weekdays.length; ++i) {
    console.log(weekobj.weekdays[i] + "btn");
    console.log(i + "before eventlistener");
    document.getElementById(weekobj.weekdays[i] + "btn").addEventListener("click", createfn(i));
}<|endoftext|>