<commit_before>/*global $: false, kanso: true*/

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

/**
 * Implementation of widget actions. These are procedures
 * that can be referenced by widgets to present/collect information,
 * manipulate the DOM, or otherwise affect the application state
 * when a widget is acted upon.
 *
 * @module
 */

/**
 * Module dependencies
 */

var widgets = require('./widgets'),
    sanitize = require('./sanitize'),
    _ = require('./underscore')._;

var h = sanitize.escapeHtml;

/**
 * Create a closure that loads {module}, and invokes {method}
 * on it. The {options} parameter is prepenced to the method's
 * argument list. This separate function silences a lint warning.
 * See the parse method for details.
 */
exports.make_action_handler = function (module, method, options) {
    return function () {
        var args = [ options ].concat(
            Array.prototype.slice.apply(arguments)
        );
        return require(module)[method].apply(null, args);
    };
};

/**
 * Convert an object containing several [ module, callback ] or
 * { module: x, callback: y } items in to an object containing
 * several native javascript functions, by using require.
 *
 * @param actions An object, containing items describing a
 *          function that can be obtained via require().
 */
exports.parse = function (actions) {
    var rv = {};
    for (var k in actions) {
        var module, callback, options;
        var action = actions[k];
        if (_.isArray(action)) {
            module = action[0];
            callback = action[1];
            options = action[2];
        } else if (_.isFunction(action)) {
            rv[k] = action;
            continue;
        } else if (typeof(action) === 'object') {
            module = action.module;
            callback = action.callback;
            options = action.options;
        } else {
            throw new Error(
                'Action `' + k + '` is of type `' + typeof(action) + '`, ' +
                    "which this function doesn't know how to interpret"
            );
        }
        /* Resolve function description to actual function */
        rv[k] = exports.make_action_handler(module, callback, options);
    }
    return rv;
};

/**
 * An action that produces a modal dialog box, with buttons along
 * its bottom. The contents of the dialog can be controlled by
 * setting either options.widget or options.type. If both are
 * specified, the widget will be used. If only type is specified,
 * this function transparently instansiates an embedForm widget,
 * which does the actual form rendering and presentation.
 */

exports.modalDialog = function (action_options, action_name,
                                type_name, field, path, value,
                                raw, errors, options, callback) {
    options = (options || {});
    action_options = (action_options || {});

    var div = $('
');
    var widget = action_options.widget;
    var name = sanitize.generateDomName.apply(null, path);
    var path_extra = (options.path_extra || []).concat([ 'modal' ]);
    var widget_options = { path_extra: path_extra };

    /* Resolve widget */
    if (!widget && action_options.type) {
        widget = widgets.embedForm(
            _.defaults(action_options.options || {}, {
                type: action_options.type
            })
        );
    }
    if (!widget) {
        throw new Error(
            'modalDialog: Unable to determine the widget to' +
            ' use for the field named `' + path.join('.') + '`;' +
            ' widget or field type was not correctly specified'
        );
    }

    /* Generate strings for content */
    var cancel_label = 'Cancel';
    var type_label = utils.titleize(type_name);
    var action_label = utils.titleize(action_name);

    /* Generate inner elements */
    var title_elt = $(
        '
' + [ action_label, type_label ].join(' ') + '
'
    );
    var ok_elt = $(
        '<input type="button" value="' + h(action_label) + '" />'
    );
    var cancel_elt = $(
        '<input type="button" value="' + h(cancel_label) + '" />'
    );
    var actions_elt = $(
        '
'
    );

    /* Add dialog title */
    div.append(title_elt);

    /* Draw widget */
    div.append(
        widget.toHTML(
            name, value, raw, field, widget_options
        )
    );

    ok_elt.click(function () {

        /* Validate widget:
            This usually defers to a form type's implementation.
            Most simple widgets just return true for this method. */

        errors = widget.validate(div, path, widget_options);

        if (errors.length > 0) {

            /* Repost dialog box:
                This will replace the current dialog box.
                The modal dialog returns to the event loop before
                actually removing its elements, so we do the same. */

            $.modal.close();

            setTimeout(function () {
                exports.modalDialog(
                    action_options, action_name, type_name, field,
                        path, value, raw, errors, options, callback
                );
            }, 0);

        } else {

            /* Close the dialog box:
                Again, note that the dialog box won't actually disappear
                until we've unwound and returned to the main event
                loop. If you depend upon closure, use setTimeout(). */

            callback(
                true, widget.getValue(div, path, widget_options)
            );

            /* Order matters:
                The callback may refer to elements inside of the modal
                dialog, so don't destroy it until after it returns, and
                has had a chance to register any callbacks / timeouts. */

            $.modal.close();
        }
    });

    cancel_elt.click(function () {
        callback(
            false, widget.getValue(div, path, widget_options)
        );
        $.modal.close();
    });

    div.submit(function (ev) {
        ev.preventDefault();
        ok_elt.click();
        return false;
    });

    /* Insert content */
    actions_elt.append(ok_elt);
    actions_elt.append(cancel_elt);
    div.append(actions_elt);

    /* Launch */
    div.modal();

    /* Initialize widget */
    widget.clientInit(
        field, path, value, raw, errors, widget_options
    );

};

<commit_msg>Resolves issue #122, in actions.js@modalDialog -- find the form we just rendered, and make its default action the modalDialog's 'ok' button.<commit_after>/*global $: false, kanso: true*/

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

/**
 * Implementation of widget actions. These are procedures
 * that can be referenced by widgets to present/collect information,
 * manipulate the DOM, or otherwise affect the application state
 * when a widget is acted upon.
 *
 * @module
 */

/**
 * Module dependencies
 */

var widgets = require('./widgets'),
    sanitize = require('./sanitize'),
    _ = require('./underscore')._;

var h = sanitize.escapeHtml;

/**
 * Create a closure that loads {module}, and invokes {method}
 * on it. The {options} parameter is prepenced to the method's
 * argument list. This separate function silences a lint warning.
 * See the parse method for details.
 */
exports.make_action_handler = function (module, method, options) {
    return function () {
        var args = [ options ].concat(
            Array.prototype.slice.apply(arguments)
        );
        return require(module)[method].apply(null, args);
    };
};

/**
 * Convert an object containing several [ module, callback ] or
 * { module: x, callback: y } items in to an object containing
 * several native javascript functions, by using require.
 *
 * @param actions An object, containing items describing a
 *          function that can be obtained via require().
 */
exports.parse = function (actions) {
    var rv = {};
    for (var k in actions) {
        var module, callback, options;
        var action = actions[k];
        if (_.isArray(action)) {
            module = action[0];
            callback = action[1];
            options = action[2];
        } else if (_.isFunction(action)) {
            rv[k] = action;
            continue;
        } else if (typeof(action) === 'object') {
            module = action.module;
            callback = action.callback;
            options = action.options;
        } else {
            throw new Error(
                'Action `' + k + '` is of type `' + typeof(action) + '`, ' +
                    "which this function doesn't know how to interpret"
            );
        }
        /* Resolve function description to actual function */
        rv[k] = exports.make_action_handler(module, callback, options);
    }
    return rv;
};

/**
 * An action that produces a modal dialog box, with buttons along
 * its bottom. The contents of the dialog can be controlled by
 * setting either options.widget or options.type. If both are
 * specified, the widget will be used. If only type is specified,
 * this function transparently instansiates an embedForm widget,
 * which does the actual form rendering and presentation.
 */

exports.modalDialog = function (action_options, action_name,
                                type_name, field, path, value,
                                raw, errors, options, callback) {
    options = (options || {});
    action_options = (action_options || {});

    var div = $('
');
    var widget = action_options.widget;
    var name = sanitize.generateDomName.apply(null, path);
    var path_extra = (options.path_extra || []).concat([ 'modal' ]);
    var widget_options = { path_extra: path_extra };

    /* Resolve widget */
    if (!widget && action_options.type) {
        widget = widgets.embedForm(
            _.defaults(action_options.options || {}, {
                type: action_options.type
            })
        );
    }
    if (!widget) {
        throw new Error(
            'modalDialog: Unable to determine the widget to' +
            ' use for the field named `' + path.join('.') + '`;' +
            ' widget or field type was not correctly specified'
        );
    }

    /* Generate strings for content */
    var cancel_label = 'Cancel';
    var type_label = utils.titleize(type_name);
    var action_label = utils.titleize(action_name);

    /* Generate inner elements */
    var title_elt = $(
        '
' + [ action_label, type_label ].join(' ') + '
'
    );
    var ok_elt = $(
        '<input type="submit" value="' + h(action_label) + '" />'
    );
    var cancel_elt = $(
        '<input type="button" value="' + h(cancel_label) + '" />'
    );
    var actions_elt = $(
        '
'
    );

    /* Add dialog title */
    div.append(title_elt);

    /* Draw widget */
    div.append(
        widget.toHTML(
            name, value, raw, field, widget_options
        )
    );

    /* Find the form element:
        This is created by the call to widget.toHTML, above. */

    var form_elt = div.closestChild('form');

    /* Make default action 'ok' */
    form_elt.submit(function (ev) {
        ev.preventDefault();
        ok_elt.click();
        return false;
    });

    /* Handle success */
    ok_elt.click(function (ev) {

        /* Validate widget:
            This usually defers to a form type's implementation.
            Most simple widgets just return true for this method. */

        errors = widget.validate(div, path, widget_options);

        if (errors.length > 0) {

            /* Repost dialog box:
                This will replace the current dialog box.
                The modal dialog returns to the event loop before
                actually removing its elements, so we do the same. */

            $.modal.close();

            setTimeout(function () {
                exports.modalDialog(
                    action_options, action_name, type_name, field,
                        path, value, raw, errors, options, callback
                );
            }, 0);

        } else {

            /* Close the dialog box:
                Again, note that the dialog box won't actually disappear
                until we've unwound and returned to the main event
                loop. If you depend upon closure, use setTimeout(). */

            callback(
                true, widget.getValue(div, path, widget_options)
            );

            /* Order matters:
                The callback may refer to elements inside of the modal
                dialog, so don't destroy it until after it returns, and
                has had a chance to register any callbacks / timeouts. */

            $.modal.close();
        }

        ev.preventDefault();
    });

    /* Handle failure */
    cancel_elt.click(function () {
        callback(
            false, widget.getValue(div, path, widget_options)
        );
        $.modal.close();
    });

    /* Insert dialog-managed elements */
    actions_elt.append(ok_elt);
    actions_elt.append(cancel_elt);
    form.append(actions_elt);

    /* Launch dialog */
    div.modal();

    /* Initialize widget:
        We do this last -- this makes sure all elements are present
        and initialized prior to client-side widget initialization. */

    widget.clientInit(
        field, path, value, raw, errors, widget_options
    );

};

<|endoftext|>


<commit_before>"use strict";

define(['logManager',
    'js/Constants',
    'js/Utils/GMEConcepts',
    'js/NodePropertyNames'], function (logManager,
                             CONSTANTS,
                             GMEConcepts,
                             nodePropertyNames) {

    var PartBrowserControl,
        WIDGET_NAME = 'PartBrowser',
        DEFAULT_DECORATOR = "ModelDecorator";

    PartBrowserControl = function (myClient, myPartBrowserView) {
        this._client = myClient;
        this._partBrowserView = myPartBrowserView;

        //the ID of the node whose valid children types should be displayed
        this._containerNodeId = null;

        //the ID of the valid children types of the container node
        this._validChildrenTypeIDs = [];

        //decorators can use it to ask for notifications about their registered sub IDs
        this._componentIDPartIDMap = {};

        this._initDragDropFeatures();

        this._logger = logManager.create("PartBrowserControl");
        this._logger.debug("Created");
    };

    PartBrowserControl.prototype.selectedObjectChanged = function (nodeId) {
        this._logger.debug("SELECTEDOBJECT_CHANGED nodeId '" + nodeId + "'");

        //remove current territory patterns
        if (this._containerNodeId) {
            this._client.removeUI(this._territoryId);
            this._partBrowserView.clear();
        }

        this._containerNodeId = nodeId;
        this._validChildrenTypeIDs = [];

        if (this._containerNodeId) {
            //put new node's info into territory rules
            this._selfPatterns = {};
            this._selfPatterns[nodeId] = { "children": 1 };

            this._territoryId = this._client.addUI(this, true);
            //update the territory
            this._logger.debug('UPDATING TERRITORY: selectedObjectChanged' + JSON.stringify(this._selfPatterns));
            this._client.updateTerritory(this._territoryId, this._selfPatterns);
        }
    };

    PartBrowserControl.prototype._getObjectDescriptor = function (nodeId) {
        var nodeObj = this._client.getNode(nodeId),
            objDescriptor;

        if (nodeObj) {
            objDescriptor = {};

            objDescriptor.id = nodeObj.getId();
            objDescriptor.decorator = nodeObj.getRegistry(nodePropertyNames.Registry.decorator) || DEFAULT_DECORATOR;
            objDescriptor.name = nodeObj.getAttribute(nodePropertyNames.Attributes.name);
        }

        return objDescriptor;
    };

    PartBrowserControl.prototype.onOneEvent = function (events) {
        var i = events ? events.length : 0,
            e;

        this._logger.debug("onOneEvent '" + i + "' items, events: " + JSON.stringify(events));

        while (i--) {
            e = events[i];
            switch (e.etype) {
                case CONSTANTS.TERRITORY_EVENT_LOAD:
                    this._onLoad(e.eid);
                    break;
                case CONSTANTS.TERRITORY_EVENT_UPDATE:
                    this._onUpdate(e.eid);
                    break;
                case CONSTANTS.TERRITORY_EVENT_UNLOAD:
                    this._onUnload(e.eid);
                    break;
            }
        }

        this._updateValidChildrenTypeDecorators();

        this._logger.debug("onOneEvent '" + events.length + "' items - DONE");
    };

    // PUBLIC METHODS
    PartBrowserControl.prototype._onLoad = function (gmeID) {
        if (this._containerNodeId === gmeID) {
            this._processContainerNode(gmeID);
        }
    };

    PartBrowserControl.prototype._onUpdate = function (gmeID) {
        if (this._containerNodeId === gmeID) {
            this._processContainerNode(gmeID)
        }
    };

    PartBrowserControl.prototype._onUnload = function (gmeID) {
        if (this._containerNodeId === gmeID) {
            this._logger.warning('Container node got unloaded...');
            this._client.removeUI(this._territoryId);
            this._partBrowserView.clear();
        }
    };

    PartBrowserControl.prototype._processContainerNode = function (gmeID) {
        var node = this._client.getNode(gmeID),
            validChildrenTypes = [],
            oValidChildrenTypes = this._validChildrenTypeIDs.slice(0),
            len,
            diff,
            id,
            territoryChanged = false;

        if (node) {
            //get possible targets from MetaDescriptor
            validChildrenTypes = this._client.getValidChildrenTypes(gmeID);

            //the deleted ones
            diff = _.difference(oValidChildrenTypes, validChildrenTypes);
            len = diff.length;
            while (len--) {
                id = diff[len];
                this._removePart(id);

                //remove it from the territory
                //only if not itself, then we need to keep it in the territory
                if (id !== gmeID) {
                    delete this._selfPatterns[id];
                    territoryChanged = true;
                }
            }

            //check the added ones
            diff = _.difference(validChildrenTypes, oValidChildrenTypes);
            len = diff.length;
            while (len--) {
                id = diff[len];
                if (this._validChildrenTypeIDs.indexOf(id) === -1) {
                    this._validChildrenTypeIDs.push(id);

                    //add to the territory
                    //only if not self, because it's already in the territory
                    if (id !== gmeID) {
                        this._selfPatterns[id] = { "children": 0 };
                        territoryChanged = true;
                    }
                }
            }

            //update the territory
            if (territoryChanged) {
                this._doUpdateTerritory(true);
            }
        }
    };

    PartBrowserControl.prototype._doUpdateTerritory = function (async) {
        var territoryId = this._territoryId,
            patterns = this._selfPatterns,
            client = this._client,
            logger = this._logger;

        if (async === true) {
            setTimeout(function () {
                logger.debug('Updating territory with rules: ' + JSON.stringify(patterns));
                client.updateTerritory(territoryId, patterns);
            }, 10);
        } else {
            logger.debug('Updating territory with rules: ' + JSON.stringify(patterns));
            client.updateTerritory(territoryId, patterns);
        }
    };

    PartBrowserControl.prototype._getItemDecorator = function (decorator) {
        var result;

        result = this._client.decoratorManager.getDecoratorForWidget(decorator, WIDGET_NAME);
        if (!result) {
            result = this._client.decoratorManager.getDecoratorForWidget(DEFAULT_DECORATOR, WIDGET_NAME);
        }

        return result;
    };

    PartBrowserControl.prototype._getPartDescriptor = function (id) {
        var desc = this._getObjectDescriptor(id);

        desc.decoratorClass = this._getItemDecorator(desc.decorator);
        desc.control = this;
        desc.metaInfo = {};
        desc.metaInfo[CONSTANTS.GME_ID] = id;

        return desc;
    };

    PartBrowserControl.prototype.registerComponentIDForPartID = function (componentID, partId) {
        this._componentIDPartIDMap[componentID] = this._componentIDPartIDMap[componentID] || [];
        if (this._componentIDPartIDMap[componentID].indexOf(partId) === -1) {
            this._componentIDPartIDMap[componentID].push(partId);
        }
    };

    PartBrowserControl.prototype.unregisterComponentIDFromPartID = function (componentID, partId) {
        var idx;

        if (this._componentIDPartIDMap && this._componentIDPartIDMap[componentID]) {
           idx = this._componentIDPartIDMap[componentID].indexOf(partId);
            if (idx !== -1) {
                this._componentIDPartIDMap[componentID].splice(idx, 1);
            }
        }
    };

    PartBrowserControl.prototype._initDragDropFeatures = function () {
        var dragEffects = this._partBrowserView.DRAG_EFFECTS;

        this._partBrowserView.getDragEffects = function (/*el*/) {
            return [dragEffects.DRAG_CREATE_INSTANCE];
        };

        this._partBrowserView.getDragItems = function (el) {
            return [el.attr('id')];
        };
    };

    PartBrowserControl.prototype._removePart = function (id) {
        var idx;

        //remove from the UI
        this._partBrowserView.removePart(id);

        //fix accounting
        idx = this._validChildrenTypeIDs.indexOf(id);
        this._validChildrenTypeIDs.splice(idx, 1);
    };

    PartBrowserControl.prototype._updateValidChildrenTypeDecorators = function () {
        var len = this._validChildrenTypeIDs.length,
            decorators = [],
            self = this;

        while (len--) {
            decorators.push(this._getObjectDescriptor(this._validChildrenTypeIDs[len]).decorator);
        }

        if (decorators.length > 0) {
            this._client.decoratorManager.download(decorators, WIDGET_NAME, function () {
                self._refreshPartList();
            });
        }
    };

    PartBrowserControl.prototype._refreshPartList = function () {
        var childrenTypeToDisplay = [],
            i,
            id,
            names = [],
            mapNameID = {},
            objDesc,
            childrenWithName,
            decoratorInstance,
            j,
            getDecoratorTerritoryQueries,
            territoryChanged = false,
            _selfPatterns = this._selfPatterns,
            partEnabled;

        getDecoratorTerritoryQueries = function (decorator) {
            var query,
                entry;

            if (decorator) {
                query = decorator.getTerritoryQuery();

                if (query) {
                    for (entry in query) {
                        if (query.hasOwnProperty(entry)) {
                            _selfPatterns[entry] = query[entry];
                            territoryChanged = true;
                        }
                    }
                }
            }
        };

        this._logger.debug('_refreshPartList this._validChildrenTypeIDs: ' + this._validChildrenTypeIDs);

        //clear view
        this._partBrowserView.clear();

        //filter out the types that doesn't need to be displayed for whatever reason:
        // - don't display validConnectionTypes
        i = this._validChildrenTypeIDs.length;
        while (i--) {
            id = this._validChildrenTypeIDs[i];
            if (GMEConcepts.isConnectionType(id) === false) {
                childrenTypeToDisplay.push(id);

                objDesc = this._getObjectDescriptor(id);
                if (names.indexOf(objDesc.name) === -1) {
                    names.push(objDesc.name);
                    mapNameID[objDesc.name] = [id];
                } else {
                    mapNameID[objDesc.name].push(id);
                }
            }
        }

        this._logger.debug('_refreshPartList childrenTypeToDisplay: ' + childrenTypeToDisplay);

        //sort the parts by name
        names.sort();
        names.reverse();

        //display the parts in the order of their names
        i = names.length;
        while (i--) {
            childrenWithName = mapNameID[names[i]];
            childrenWithName.sort();
            childrenWithName.reverse();
            this._logger.debug(names[i] + ':  ' + childrenWithName);

            j = childrenWithName.length;
            while (j--) {
                id = childrenWithName[j];
                decoratorInstance = this._partBrowserView.addPart(id, this._getPartDescriptor(id));
                getDecoratorTerritoryQueries(decoratorInstance);
            }
        }

        //update child creation possibility
        i = this._validChildrenTypeIDs.length;
        while (i--) {
            id = this._validChildrenTypeIDs[i];
            partEnabled = GMEConcepts.canCreateChild(this._containerNodeId, id) &&
                        !GMEConcepts.isProjectPROJECTBASE(this._containerNodeId);
            this._partBrowserView.setEnabled(id, partEnabled);
        }

        if (territoryChanged) {
            this._doUpdateTerritory(true);
        }
    };

    return PartBrowserControl;
});<commit_msg>bugfix: make sure to download default decorator just in case the object's decorator does not support PartBrowser<commit_after>"use strict";

define(['logManager',
    'js/Constants',
    'js/Utils/GMEConcepts',
    'js/NodePropertyNames'], function (logManager,
                             CONSTANTS,
                             GMEConcepts,
                             nodePropertyNames) {

    var PartBrowserControl,
        WIDGET_NAME = 'PartBrowser',
        DEFAULT_DECORATOR = "ModelDecorator";

    PartBrowserControl = function (myClient, myPartBrowserView) {
        this._client = myClient;
        this._partBrowserView = myPartBrowserView;

        //the ID of the node whose valid children types should be displayed
        this._containerNodeId = null;

        //the ID of the valid children types of the container node
        this._validChildrenTypeIDs = [];

        //decorators can use it to ask for notifications about their registered sub IDs
        this._componentIDPartIDMap = {};

        this._initDragDropFeatures();

        this._logger = logManager.create("PartBrowserControl");
        this._logger.debug("Created");
    };

    PartBrowserControl.prototype.selectedObjectChanged = function (nodeId) {
        this._logger.debug("SELECTEDOBJECT_CHANGED nodeId '" + nodeId + "'");

        //remove current territory patterns
        if (this._containerNodeId) {
            this._client.removeUI(this._territoryId);
            this._partBrowserView.clear();
        }

        this._containerNodeId = nodeId;
        this._validChildrenTypeIDs = [];

        if (this._containerNodeId) {
            //put new node's info into territory rules
            this._selfPatterns = {};
            this._selfPatterns[nodeId] = { "children": 1 };

            this._territoryId = this._client.addUI(this, true);
            //update the territory
            this._logger.debug('UPDATING TERRITORY: selectedObjectChanged' + JSON.stringify(this._selfPatterns));
            this._client.updateTerritory(this._territoryId, this._selfPatterns);
        }
    };

    PartBrowserControl.prototype._getObjectDescriptor = function (nodeId) {
        var nodeObj = this._client.getNode(nodeId),
            objDescriptor;

        if (nodeObj) {
            objDescriptor = {};

            objDescriptor.id = nodeObj.getId();
            objDescriptor.decorator = nodeObj.getRegistry(nodePropertyNames.Registry.decorator) || DEFAULT_DECORATOR;
            objDescriptor.name = nodeObj.getAttribute(nodePropertyNames.Attributes.name);
        }

        return objDescriptor;
    };

    PartBrowserControl.prototype.onOneEvent = function (events) {
        var i = events ? events.length : 0,
            e;

        this._logger.debug("onOneEvent '" + i + "' items, events: " + JSON.stringify(events));

        while (i--) {
            e = events[i];
            switch (e.etype) {
                case CONSTANTS.TERRITORY_EVENT_LOAD:
                    this._onLoad(e.eid);
                    break;
                case CONSTANTS.TERRITORY_EVENT_UPDATE:
                    this._onUpdate(e.eid);
                    break;
                case CONSTANTS.TERRITORY_EVENT_UNLOAD:
                    this._onUnload(e.eid);
                    break;
            }
        }

        this._updateValidChildrenTypeDecorators();

        this._logger.debug("onOneEvent '" + events.length + "' items - DONE");
    };

    // PUBLIC METHODS
    PartBrowserControl.prototype._onLoad = function (gmeID) {
        if (this._containerNodeId === gmeID) {
            this._processContainerNode(gmeID);
        }
    };

    PartBrowserControl.prototype._onUpdate = function (gmeID) {
        if (this._containerNodeId === gmeID) {
            this._processContainerNode(gmeID)
        }
    };

    PartBrowserControl.prototype._onUnload = function (gmeID) {
        if (this._containerNodeId === gmeID) {
            this._logger.warning('Container node got unloaded...');
            this._client.removeUI(this._territoryId);
            this._partBrowserView.clear();
        }
    };

    PartBrowserControl.prototype._processContainerNode = function (gmeID) {
        var node = this._client.getNode(gmeID),
            validChildrenTypes = [],
            oValidChildrenTypes = this._validChildrenTypeIDs.slice(0),
            len,
            diff,
            id,
            territoryChanged = false;

        if (node) {
            //get possible targets from MetaDescriptor
            validChildrenTypes = this._client.getValidChildrenTypes(gmeID);

            //the deleted ones
            diff = _.difference(oValidChildrenTypes, validChildrenTypes);
            len = diff.length;
            while (len--) {
                id = diff[len];
                this._removePart(id);

                //remove it from the territory
                //only if not itself, then we need to keep it in the territory
                if (id !== gmeID) {
                    delete this._selfPatterns[id];
                    territoryChanged = true;
                }
            }

            //check the added ones
            diff = _.difference(validChildrenTypes, oValidChildrenTypes);
            len = diff.length;
            while (len--) {
                id = diff[len];
                if (this._validChildrenTypeIDs.indexOf(id) === -1) {
                    this._validChildrenTypeIDs.push(id);

                    //add to the territory
                    //only if not self, because it's already in the territory
                    if (id !== gmeID) {
                        this._selfPatterns[id] = { "children": 0 };
                        territoryChanged = true;
                    }
                }
            }

            //update the territory
            if (territoryChanged) {
                this._doUpdateTerritory(true);
            }
        }
    };

    PartBrowserControl.prototype._doUpdateTerritory = function (async) {
        var territoryId = this._territoryId,
            patterns = this._selfPatterns,
            client = this._client,
            logger = this._logger;

        if (async === true) {
            setTimeout(function () {
                logger.debug('Updating territory with rules: ' + JSON.stringify(patterns));
                client.updateTerritory(territoryId, patterns);
            }, 10);
        } else {
            logger.debug('Updating territory with rules: ' + JSON.stringify(patterns));
            client.updateTerritory(territoryId, patterns);
        }
    };

    PartBrowserControl.prototype._getItemDecorator = function (decorator) {
        var result;

        result = this._client.decoratorManager.getDecoratorForWidget(decorator, WIDGET_NAME);
        if (!result) {
            result = this._client.decoratorManager.getDecoratorForWidget(DEFAULT_DECORATOR, WIDGET_NAME);
        }

        return result;
    };

    PartBrowserControl.prototype._getPartDescriptor = function (id) {
        var desc = this._getObjectDescriptor(id);

        desc.decoratorClass = this._getItemDecorator(desc.decorator);
        desc.control = this;
        desc.metaInfo = {};
        desc.metaInfo[CONSTANTS.GME_ID] = id;

        return desc;
    };

    PartBrowserControl.prototype.registerComponentIDForPartID = function (componentID, partId) {
        this._componentIDPartIDMap[componentID] = this._componentIDPartIDMap[componentID] || [];
        if (this._componentIDPartIDMap[componentID].indexOf(partId) === -1) {
            this._componentIDPartIDMap[componentID].push(partId);
        }
    };

    PartBrowserControl.prototype.unregisterComponentIDFromPartID = function (componentID, partId) {
        var idx;

        if (this._componentIDPartIDMap && this._componentIDPartIDMap[componentID]) {
           idx = this._componentIDPartIDMap[componentID].indexOf(partId);
            if (idx !== -1) {
                this._componentIDPartIDMap[componentID].splice(idx, 1);
            }
        }
    };

    PartBrowserControl.prototype._initDragDropFeatures = function () {
        var dragEffects = this._partBrowserView.DRAG_EFFECTS;

        this._partBrowserView.getDragEffects = function (/*el*/) {
            return [dragEffects.DRAG_CREATE_INSTANCE];
        };

        this._partBrowserView.getDragItems = function (el) {
            return [el.attr('id')];
        };
    };

    PartBrowserControl.prototype._removePart = function (id) {
        var idx;

        //remove from the UI
        this._partBrowserView.removePart(id);

        //fix accounting
        idx = this._validChildrenTypeIDs.indexOf(id);
        this._validChildrenTypeIDs.splice(idx, 1);
    };

    PartBrowserControl.prototype._updateValidChildrenTypeDecorators = function () {
        var len = this._validChildrenTypeIDs.length,
            decorators = [DEFAULT_DECORATOR],
            self = this,
            dec;

        while (len--) {
            dec = this._getObjectDescriptor(this._validChildrenTypeIDs[len]).decorator;
            if (decorators.indexOf(dec) === -1) {
                decorators.push(dec);
            }
        }

        if (decorators.length > 0) {
            this._client.decoratorManager.download(decorators, WIDGET_NAME, function () {
                self._refreshPartList();
            });
        }
    };

    PartBrowserControl.prototype._refreshPartList = function () {
        var childrenTypeToDisplay = [],
            i,
            id,
            names = [],
            mapNameID = {},
            objDesc,
            childrenWithName,
            decoratorInstance,
            j,
            getDecoratorTerritoryQueries,
            territoryChanged = false,
            _selfPatterns = this._selfPatterns,
            partEnabled;

        getDecoratorTerritoryQueries = function (decorator) {
            var query,
                entry;

            if (decorator) {
                query = decorator.getTerritoryQuery();

                if (query) {
                    for (entry in query) {
                        if (query.hasOwnProperty(entry)) {
                            _selfPatterns[entry] = query[entry];
                            territoryChanged = true;
                        }
                    }
                }
            }
        };

        this._logger.debug('_refreshPartList this._validChildrenTypeIDs: ' + this._validChildrenTypeIDs);

        //clear view
        this._partBrowserView.clear();

        //filter out the types that doesn't need to be displayed for whatever reason:
        // - don't display validConnectionTypes
        i = this._validChildrenTypeIDs.length;
        while (i--) {
            id = this._validChildrenTypeIDs[i];
            if (GMEConcepts.isConnectionType(id) === false) {
                childrenTypeToDisplay.push(id);

                objDesc = this._getObjectDescriptor(id);
                if (names.indexOf(objDesc.name) === -1) {
                    names.push(objDesc.name);
                    mapNameID[objDesc.name] = [id];
                } else {
                    mapNameID[objDesc.name].push(id);
                }
            }
        }

        this._logger.debug('_refreshPartList childrenTypeToDisplay: ' + childrenTypeToDisplay);

        //sort the parts by name
        names.sort();
        names.reverse();

        //display the parts in the order of their names
        i = names.length;
        while (i--) {
            childrenWithName = mapNameID[names[i]];
            childrenWithName.sort();
            childrenWithName.reverse();
            this._logger.debug(names[i] + ':  ' + childrenWithName);

            j = childrenWithName.length;
            while (j--) {
                id = childrenWithName[j];
                decoratorInstance = this._partBrowserView.addPart(id, this._getPartDescriptor(id));
                getDecoratorTerritoryQueries(decoratorInstance);
            }
        }

        //update child creation possibility
        i = this._validChildrenTypeIDs.length;
        while (i--) {
            id = this._validChildrenTypeIDs[i];
            partEnabled = GMEConcepts.canCreateChild(this._containerNodeId, id) &&
                        !GMEConcepts.isProjectPROJECTBASE(this._containerNodeId);
            this._partBrowserView.setEnabled(id, partEnabled);
        }

        if (territoryChanged) {
            this._doUpdateTerritory(true);
        }
    };

    return PartBrowserControl;
});<|endoftext|>


<commit_before>var React = require('react-native');
var FeedbackView = require('./display_feedback/feedbackView');
var api = require('./../../utils/api');
var NavBar = require('./../shared/navbar');
var Button = require('./../shared/button');

var {
  View,
  Text,
  StyleSheet,
  Navigator,
  TouchableOpacity,
  TouchableHighlight,
  Modal,
  Alert,
  TextInput,
  Dimensions,
  Image,
} = React;

class RequestFeedbackView extends React.Component {
  constructor(props) {
    super(props);
    var {height, width} = Dimensions.get('window');
    this.state = {
      lessonId: this.props.route.lessonId,
      classId: this.props.route.classId,
      socket: this.props.route.socket,
      lesson: '',
      modal: false,
      modalQuestion: false,
      height: height,
      width: width,
      feedbackOptions: [
        {
          type: 'thumbs',
          name: 'Thumbs Check',
          /*options:{for preset answers?}*/
        },
        {
          type: 'multiChoice',
          name: 'Multiple Choice'
        }
      ],
      raisedHandList: [],
      raisedHandCount: 0,
      count: 0,
      questionLists:[],
      polls:[],
      pollModal: false,
    };
    //populate feedbackOptions with anything custom from lesson
    this.state.socket.on('studentRaisedHand', function(data){
      var raisedHandCount = this.state.raisedHandCount;
      var raisedHandList = this.state.raisedHandList.slice();
      if(raisedHandList.length === 0) {
        raisedHandCount += 1;
        if(data.user.firstName) {
          raisedHandList.unshift({id: data.user.uid, student: data.user.firstName + ' ' + data.user.lastName , active: true});
        } else {
          raisedHandList.unshift({id: data.user.uid, student: data.user.uid, active: true});
        }
      } else {
        for(var i = 0; i < raisedHandList.length; i++){
          if(data.user.uid === raisedHandList[i].id && raisedHandList[i].active === false) {
            raisedHandCount += 1;
            raisedHandList[i].active = true;
          }
          if(raisedHandList.length < 5 && data.user.uid !== raisedHandList[i].id) {
            raisedHandCount += 1;
            if(data.user.firstName) {
              raisedHandList.unshift({id: data.user.uid, student: data.user.firstName + ' ' + data.user.lastName , active: true});
            } else {
              raisedHandList.unshift({id: data.user.uid, student: data.user.uid, active: true});
            }
          }
        }
      }
      this.setState({
        raisedHandList: raisedHandList,
        raisedHandCount: raisedHandCount,
      });
    }.bind(this))

    this.state.socket.on('studentQuestions', function(data){
      var questionLists = this.state.questionLists.slice();
      if(data.student.firstName) {
        var student = data.student.firstName + ' ' + data.student.lastName;
      } else {
        var student = data.student.uid
      }
      questionLists.push({question: data.question, student: student});
      this.setState({
        questionLists: questionLists,
      })
    }.bind(this))
  }

  componentWillMount(){
    var lesson = this.state.lessonId;
    if( lesson === 'Quick Class') {
      this.setState({
        lesson: 'Quick Class',
      })
    } else {
      var that = this;
      api.getCurrentLesson(lesson)
      .then(function(resp){
        if(resp.status === 500) {
          console.error('Error for getting lesson data')
        } else if(resp.status === 200) {
          var lessons = JSON.parse(resp._bodyInit);
          that.setState({
            lesson: lessons.name,
          })
        }
      })
      .catch(function(err){
        console.error(err);
      })
      api.getLessonPolls(lesson)
      .then(function(resp){
        if(resp.status === 500) {
          console.error('Error for getting poll data');
        } else if(resp.status === 200) {
          var polls = JSON.parse(resp._bodyInit).filter(function(polls) {
            return polls.sent === false
          });
          that.setState({
            polls: polls,
          })
        }
      })
    }
  }

  answeredQuestion() {
    var questionLists = this.state.questionLists.slice();
    questionLists.shift();
    this.setState({
      questionLists: questionLists,
    })
  }

  clickQuestion() {
    this.setState({
      modalQuestion: !this.state.modalQuestion,
    })
  }

  clickRaisedHand() {
    var count = this.state.count + 1;
    this.setState({
      modal: !this.state.modal,
      count: count,
      raisedHandCount: 0,
    })
    if(this.state.modal === false) {
      this.setState({
        count: 0,
      })
    }
  }

  dismissClass() {
    // emit socket dismissClass
    this.state.socket.emit('dismiss');
    // if quickClass, need to disconnect from socket
    if(this.state.lessonId = 'Quick Class') {
      this.state.socket.disconnect();
    }
    this.props.navigator.pop();
  }

  beforeLogout() {
    this.state.socket.emit('teacherLoggingOut');
  }

  listQuestion(list) {
    if( list.length === 0 ) {
      return(
        <Text>No Question yet</Text>
      )
    }
    return (
      <View>
        <Text>Question from {list[0].student}</Text>
        <Text>{list[0].question}</Text>
      </View>
    )
  }

  listStudent(list) {
    if(list.length === 0) {
      return (
        <Text>No one raised hand yet</Text>
      )
    }
    return list.map((student, index) => {
      if(!this.state.modal && this.state.count > 0) {
        student.active = false
      }
      if(student.active){
        return (
          <View key={index}>
            <Text style={styles.studentName}>
              {student.student}
            </Text>
          </View>
        )
      } else {
        return (
          <View key={index}>
            <Text style={styles.studentNameChange}>
              {student.student}
            </Text>
          </View>
        )
      }
    })
  }

  clearList() {
    this.setState({
      raisedHandList: [],
      count: 0,
      raisedHandCount: 0,
    })
  }

  callOnStudent() {
    var studentsObj = this.props.route.getActiveStudents();
    var keys = Object.keys(studentsObj);
    var index = Math.floor(Math.random() * keys.length);
    var randomKey = keys[index];
    var student = studentsObj[randomKey];
    console.log('Calling on:', student);
    this.state.socket.emit('callOnStudent', student);
    if(!student || studentsObj === undefined) {
      Alert.alert('No student has yet signed in');
    } else {
      if(student.firstName) {
        Alert.alert('Selected', student.firstName + ' ' + student.lastName);
      } else {
        Alert.alert('Selected student', student.uid);
      }
    }
  }

  selectFeedbackOption(feedbackOption) {
    api.startPoll(feedbackOption, this.state.lessonId, this.state.classId)
    .then((response) => {
      if(response.status === 500) {
        console.error('Server error', response);
      } else if(response.status === 201) {
        var pollId = JSON.parse(response._bodyText).pollId;
        this.props.navigator.push({
          component: FeedbackView,
          lessonId: this.state.lessonId,
          pollId: pollId,
          feedbackOption: feedbackOption,
          socket: this.state.socket,
          sceneConfig: {
            ...Navigator.SceneConfigs.HorizontalSwipeJump,
            gestures: {}
          }
        });
      } else {
        console.error('Error getting poll data', response);
      }
      this.setState({
        pollModal: false
      })

    })
    .catch((err) => {
      console.error('Error starting poll', err);
    });
  }

  renderFeedbackOptions(feedbackOptions) {
    return feedbackOptions.map((feedbackOption, index) => {
      return (
        <Button key={index} onPress={this.selectFeedbackOption.bind(this, feedbackOption)} text={feedbackOption.name} />
      )
    })
  }

  presetPoll() {
    this.setState({
      pollModal: !this.state.pollModal,
    })
  }

  listOfPoll (list) {
    return list.map((poll, index) => {
      return (
        <Button key={index} text={poll.name} onPress={this.selectFeedbackOption.bind(this, poll)}/>
      )
    })
  }

  previousSection() {
    this.props.navigator.pop();
  }

  render() {
    return (
      <View style={{flex: 1, backgroundColor: '#ededed'}}>
        <NavBar navi={this.props.navigator} beforeLogout={this.beforeLogout.bind(this)}
          onBack={this.previousSection.bind(this)}>
          Request Feedback
        </NavBar>
        <View style={styles.viewContainer}>
          <Button onPress={this.dismissClass.bind(this)} text={'Dismiss Class'}/>
          {this.renderFeedbackOptions(this.state.feedbackOptions)}
          <Button onPress={this.callOnStudent.bind(this)} text={'Call On Student'}/>
          <Button onPress={this.presetPoll.bind(this)} text={'Preset Poll'} />
        </View>
        <View style={styles.studentResponse}>
          <TouchableOpacity onPress={this.clickQuestion.bind(this)} style={styles.questionBox}>
            <View style={styles.imageBox}>
              <Image source={require('../../image/question.png')}/>
              <Text style={styles.textSize}>
                : {this.state.questionLists.length}
              </Text>
            </View>
          </TouchableOpacity>
          <View style={[styles.currentLessonBox, {width: this.state.width - 205}]}>
            <Text style={styles.currentLesson}>{this.state.lesson}</Text>
          </View>
          <TouchableOpacity onPress={this.clickRaisedHand.bind(this)} style={styles.raiseHandBox}>
            <View style={styles.imageBox}>
              <Image source={require('../../image/raiseHand.png')}/>
              <Text style={styles.textSize}>
                : {this.state.raisedHandCount}
              </Text>
            </View>
          </TouchableOpacity>
        </View>

        <Modal visible={this.state.modal} transparent={true} animated={true}>
          <View style={styles.modal}>
            <View style={{height:this.state.height * 0.8, width:this.state.width * 0.85}}>
              <View style={styles.modalBox}>
                <Text style={styles.textSizeModal}> Raised hand student: </Text>
                {this.listStudent(this.state.raisedHandList)}
              </View>
              <Button onPress={this.clickRaisedHand.bind(this)} text={'Close'}/>
              <Button onPress={this.clearList.bind(this)} text={'clear'}/>
            </View>
          </View>
        </Modal>

        <Modal visible={this.state.modalQuestion} transparent={true} animated={true}>
          <View style={styles.modal}>
            <View style={{height:this.state.height * 0.8, width:this.state.width * 0.85}}>
              <View style={styles.modalBox}>
                <Text style={styles.textSizeModal}> Question </Text>
                {this.listQuestion(this.state.questionLists)}
              </View>
              <Button onPress={this.answeredQuestion.bind(this)} text={'answered'} />
              <Button onPress={this.clickQuestion.bind(this)} text={'Close'}/>
            </View>
          </View>
        </Modal>

        <Modal visible={this.state.pollModal} transparent={true} animated={false}>
          <View style={styles.modal}>
            <View style={{height:this.state.height * 0.8, width:this.state.width * 0.85}}>
              <View style={styles.modalBox}>
                {this.listOfPoll(this.state.polls)}
              </View>
              <Button onPress={this.presetPoll.bind(this)} text={'Close'}/>
            </View>
          </View>
        </Modal>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  viewContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },

  modal: {
    flex: 1,
    backgroundColor: 'rgba(0,0,0,0.8)',
    justifyContent: 'center',
    alignItems: 'center',
  },

  modalBox: {
    flex: 1,
    borderRadius: 10,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'white'
  },

  textSizeModal: {
    fontSize : 20,
    fontWeight: 'bold',
  },

  studentName: {
    fontSize: 35,
    fontWeight: 'bold',
  },

  studentNameChange:{
    fontSize: 35,
    fontWeight: 'bold',
    color: '#C6C7C3',
  },

  textSize: {
    fontSize : 25,
    fontWeight: 'bold',
    color: '#fafafa'
  },

  studentResponse: {
    flexDirection: 'row',
    height:60,
    width: null,
    justifyContent: 'space-between',
  },

  questionBox: {
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor:'#01579b',
    height: 60,
    width: 100,
  },

  raiseHandBox: {
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor:'#01579b',
    height: 60,
    width: 100,
  },
  imageBox: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  currentLessonBox: {
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#01579b',
  },
  currentLesson: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#fafafa',
  }
});

module.exports = RequestFeedbackView;
<commit_msg>rebase it<commit_after>var React = require('react-native');
var FeedbackView = require('./display_feedback/feedbackView');
var api = require('./../../utils/api');
var NavBar = require('./../shared/navbar');
var Button = require('./../shared/button');

var {
  View,
  Text,
  StyleSheet,
  Navigator,
  TouchableOpacity,
  TouchableHighlight,
  Modal,
  Alert,
  TextInput,
  Dimensions,
  Image,
} = React;

class RequestFeedbackView extends React.Component {
  constructor(props) {
    super(props);
    var {height, width} = Dimensions.get('window');
    this.state = {
      lessonId: this.props.route.lessonId,
      classId: this.props.route.classId,
      socket: this.props.route.socket,
      lesson: '',
      modal: false,
      modalQuestion: false,
      height: height,
      width: width,
      feedbackOptions: [
        {
          type: 'thumbs',
          name: 'Thumbs Check',
          /*options:{for preset answers?}*/
        },
        {
          type: 'multiChoice',
          name: 'Multiple Choice'
        }
      ],
      raisedHandList: [],
      raisedHandCount: 0,
      count: 0,
      questionLists:[],
      polls:[],
      pollModal: false,
    };
    //populate feedbackOptions with anything custom from lesson
    this.state.socket.on('studentRaisedHand', function(data){
      var raisedHandCount = this.state.raisedHandCount;
      var raisedHandList = this.state.raisedHandList.slice();
      if(raisedHandList.length === 0) {
        raisedHandCount += 1;
        if(data.user.firstName) {
          raisedHandList.unshift({id: data.user.uid, student: data.user.firstName + ' ' + data.user.lastName , active: true});
        } else {
          raisedHandList.unshift({id: data.user.uid, student: data.user.uid, active: true});
        }
      } else {
        for(var i = 0; i < raisedHandList.length; i++){
          if(data.user.uid === raisedHandList[i].id && raisedHandList[i].active === false) {
            raisedHandCount += 1;
            raisedHandList[i].active = true;
          }
          if(raisedHandList.length < 5 && data.user.uid !== raisedHandList[i].id) {
            raisedHandCount += 1;
            if(data.user.firstName) {
              raisedHandList.unshift({id: data.user.uid, student: data.user.firstName + ' ' + data.user.lastName , active: true});
            } else {
              raisedHandList.unshift({id: data.user.uid, student: data.user.uid, active: true});
            }
          }
        }
      }
      this.setState({
        raisedHandList: raisedHandList,
        raisedHandCount: raisedHandCount,
      });
    }.bind(this))

    this.state.socket.on('studentQuestions', function(data){
      var questionLists = this.state.questionLists.slice();
      if(data.student.firstName) {
        var student = data.student.firstName + ' ' + data.student.lastName;
      } else {
        var student = data.student.uid
      }
      questionLists.push({question: data.question, student: student});
      this.setState({
        questionLists: questionLists,
      })
    }.bind(this))
  }

  componentWillMount(){
    var lesson = this.state.lessonId;
    if( lesson === 'Quick Class') {
      this.setState({
        lesson: 'Quick Class',
      })
    } else {
      var that = this;
      api.getCurrentLesson(lesson)
      .then(function(resp){
        if(resp.status === 500) {
          console.error('Error for getting lesson data')
        } else if(resp.status === 200) {
          var lessons = JSON.parse(resp._bodyInit);
          that.setState({
            lesson: lessons.name,
          })
        }
      })
      .catch(function(err){
        console.error(err);
      })
      api.getLessonPolls(lesson)
      .then(function(resp){
        if(resp.status === 500) {
          console.error('Error for getting poll data');
        } else if(resp.status === 200) {
          var polls = JSON.parse(resp._bodyInit).filter(function(polls) {
            return polls.sent === false
          });
          that.setState({
            polls: polls,
          })
        }
      })
    }
  }

  answeredQuestion() {
    var questionLists = this.state.questionLists.slice();
    questionLists.shift();
    this.setState({
      questionLists: questionLists,
    })
  }

  clickQuestion() {
    this.setState({
      modalQuestion: !this.state.modalQuestion,
    })
  }

  clickRaisedHand() {
    var count = this.state.count + 1;
    this.setState({
      modal: !this.state.modal,
      count: count,
      raisedHandCount: 0,
    })
    if(this.state.modal === false) {
      this.setState({
        count: 0,
      })
    }
  }

  dismissClass() {
    // emit socket dismissClass
    this.state.socket.emit('dismiss');
    // if quickClass, need to disconnect from socket
    if(this.state.lessonId = 'Quick Class') {
      this.state.socket.disconnect();
    }
    this.props.navigator.pop();
  }

  beforeLogout() {
    this.state.socket.emit('teacherLoggingOut');
  }

  listQuestion(list) {
    if( list.length === 0 ) {
      return(
        <Text>No Question yet</Text>
      )
    }
    return (
      <View>
        <Text>Question from {list[0].student}</Text>
        <Text>{list[0].question}</Text>
      </View>
    )
  }

  listStudent(list) {
    if(list.length === 0) {
      return (
        <Text>No one raised hand yet</Text>
      )
    }
    return list.map((student, index) => {
      if(!this.state.modal && this.state.count > 0) {
        student.active = false
      }
      if(student.active){
        return (
          <View key={index}>
            <Text style={styles.studentName}>
              {student.student}
            </Text>
          </View>
        )
      } else {
        return (
          <View key={index}>
            <Text style={styles.studentNameChange}>
              {student.student}
            </Text>
          </View>
        )
      }
    })
  }

  clearList() {
    this.setState({
      raisedHandList: [],
      count: 0,
      raisedHandCount: 0,
    })
  }

  callOnStudent() {
    var studentsObj = this.props.route.getActiveStudents();
    var keys = Object.keys(studentsObj);
    var index = Math.floor(Math.random() * keys.length);
    var randomKey = keys[index];
    var student = studentsObj[randomKey];
    console.log('Calling on:', student);
    this.state.socket.emit('callOnStudent', student);
    if(!student || studentsObj === undefined) {
      Alert.alert('No student has yet signed in');
    } else {
      if(student.firstName) {
        Alert.alert('Selected', student.firstName + ' ' + student.lastName);
      } else {
        Alert.alert('Selected student', student.uid);
      }
    }
  }

  selectFeedbackOption(feedbackOption) {
    api.startPoll(feedbackOption, this.state.lessonId, this.state.classId)
    .then((response) => {
      if(response.status === 500) {
        console.error('Server error', response);
      } else if(response.status === 201) {
        var pollId = JSON.parse(response._bodyText).pollId;
        this.props.navigator.push({
          component: FeedbackView,
          lessonId: this.state.lessonId,
          pollId: pollId,
          feedbackOption: feedbackOption,
          socket: this.state.socket,
          sceneConfig: {
            ...Navigator.SceneConfigs.HorizontalSwipeJump,
            gestures: {}
          }
        });
      } else {
        console.error('Error getting poll data', response);
      }
      this.setState({
        pollModal: false
      })

    })
    .catch((err) => {
      console.error('Error starting poll', err);
    });
  }

  renderFeedbackOptions(feedbackOptions) {
    return feedbackOptions.map((feedbackOption, index) => {
      return (
        <Button key={index} onPress={this.selectFeedbackOption.bind(this, feedbackOption)} text={feedbackOption.name} />
      )
    })
  }

  presetPoll() {
    this.setState({
      pollModal: !this.state.pollModal,
    })
  }

  listOfPoll (list) {
    return list.map((poll, index) => {
      return (
        <Button key={index} text={poll.name} onPress={this.selectFeedbackOption.bind(this, poll)}/>
      )
    })
  }

  previousSection() {
    this.props.navigator.pop();
  }

  render() {
    return (
      <View style={{flex: 1, backgroundColor: '#ededed'}}>
        <NavBar navi={this.props.navigator} beforeLogout={this.beforeLogout.bind(this)}
          onBack={this.previousSection.bind(this)}>
          Request Feedback
        </NavBar>
        <View style={styles.viewContainer}>
          <Button onPress={this.dismissClass.bind(this)} text={'Dismiss Class'}/>
          {this.renderFeedbackOptions(this.state.feedbackOptions)}
          <Button onPress={this.callOnStudent.bind(this)} text={'Call On Student'}/>
          <Button onPress={this.presetPoll.bind(this)} text={'Preset Poll'} />
        </View>
        <View style={styles.studentResponse}>
          <TouchableOpacity onPress={this.clickQuestion.bind(this)} style={styles.questionBox}>
            <View style={styles.imageBox}>
              <Image source={require('../../image/question.png')}/>
              <Text style={styles.textSize}>
                : {this.state.questionLists.length}
              </Text>
            </View>
          </TouchableOpacity>
          <View style={[styles.currentLessonBox, {width: this.state.width - 205}]}>
            <Text style={styles.currentLesson}>{this.state.lesson}</Text>
          </View>
          <TouchableOpacity onPress={this.clickRaisedHand.bind(this)} style={styles.raiseHandBox}>
            <View style={styles.imageBox}>
              <Image source={require('../../image/raiseHand.png')}/>
              <Text style={styles.textSize}>
                : {this.state.raisedHandCount}
              </Text>
            </View>
          </TouchableOpacity>
        </View>

        <Modal visible={this.state.modal} transparent={true} animated={true}>
          <View style={styles.modal}>
            <View style={{height:this.state.height * 0.8, width:this.state.width * 0.85}}>
              <View style={styles.modalBox}>
                <Text style={styles.textSizeModal}> Raised hand student: </Text>
                {this.listStudent(this.state.raisedHandList)}
              </View>
              <Button onPress={this.clickRaisedHand.bind(this)} text={'Close'}/>
              <Button onPress={this.clearList.bind(this)} text={'clear'}/>
            </View>
          </View>
        </Modal>

        <Modal visible={this.state.modalQuestion} transparent={true} animated={true}>
          <View style={styles.modal}>
            <View style={{height:this.state.height * 0.8, width:this.state.width * 0.85}}>
              <View style={styles.modalBox}>
                <Text style={styles.textSizeModal}> Question </Text>
                {this.listQuestion(this.state.questionLists)}
              </View>
              <Button onPress={this.answeredQuestion.bind(this)} text={'answered'} />
              <Button onPress={this.clickQuestion.bind(this)} text={'Close'}/>
            </View>
          </View>
        </Modal>

        <Modal visible={this.state.pollModal} transparent={true} animated={false}>
          <View style={styles.modal}>
            <View style={{height:this.state.height * 0.8, width:this.state.width * 0.85}}>
              <View style={styles.modalBox}>
                {this.listOfPoll(this.state.polls)}
              </View>
              <Button onPress={this.presetPoll.bind(this)} text={'Close'}/>
            </View>
          </View>
        </Modal>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  viewContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },

  modal: {
    flex: 1,
    backgroundColor: 'rgba(0,0,0,0.8)',
    justifyContent: 'center',
    alignItems: 'center',
  },

  modalBox: {
    flex: 1,
    borderRadius: 10,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'white'
  },

  textSizeModal: {
    fontSize : 20,
    fontWeight: 'bold',
  },

  studentName: {
    fontSize: 35,
    fontWeight: 'bold',
  },

  studentNameChange:{
    fontSize: 35,
    fontWeight: 'bold',
    color: '#C6C7C3',
  },

  textSize: {
    fontSize : 25,
    fontWeight: 'bold',
    color: '#fafafa'
  },

  studentResponse: {
    flexDirection: 'row',
    height:60,
    width: null,
    justifyContent: 'space-between',
  },

  questionBox: {
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor:'#01579b',
    height: 60,
    width: 100,
  },

  raiseHandBox: {
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor:'#01579b',
    height: 60,
    width: 100,
  },
  imageBox: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  currentLessonBox: {
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#01579b',
  },
  currentLesson: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#fafafa',
  },
});

module.exports = RequestFeedbackView;
<|endoftext|>


<commit_before>!function(t){var e;if(window.jQuery)e=function(t){t(jQuery)};else{var n=document.createElement("script");n.type="text/javascript",n.src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js",document.getElementsByTagName("head")[0].appendChild(n),e=function(t){window.jQuery?(jQuery.noConflict(),t(jQuery)):window.setTimeout(function(){e(t)},100)}}e(function(e){var n=e("head");n.append('<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">');var i=e('
');if(e(function(){var t=e("body");t.append(i);var n='';e("head").append(n),t.append(e('

'))}),sessionStorage.alreadyLoaded)i.remove();else{var a={search:0,facebook:1,twitter:1,googleplus:1};window.tealditLocalConfig!==t&&(a=e.extend(a,tealditLocalConfig));var o=e('
'),l='
\n

\n    \n

\n

\n    \n    \n

\n

Tweet\n    \n

\r\n
\r\n        \r\n
\r\n    \r\n    \r\n    \r\n    \r\n
\n    \n    \n
\n    t\n
\n\n\n';o.append(l),i.html(o)}})}();<commit_msg>updated search term of referrer<commit_after>!function(t){var e;if(window.jQuery)e=function(t){t(jQuery)};else{var n=document.createElement("script");n.type="text/javascript",n.src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js",document.getElementsByTagName("head")[0].appendChild(n),e=function(t){window.jQuery?(jQuery.noConflict(),t(jQuery)):window.setTimeout(function(){e(t)},100)}}e(function(e){var n=e("head");n.append('<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">');var i=e('
');if(e(function(){var t=e("body");t.append(i);var n='';e("head").append(n),t.append(e('

'))}),sessionStorage.alreadyLoaded)i.remove();else{var a={search:0,facebook:1,twitter:1,googleplus:1};window.tealditLocalConfig!==t&&(a=e.extend(a,tealditLocalConfig));var o=e('
'),l='
\n

\n    \n

\n

\n    \n    \n

\n

Tweet\n    \n

\r\n
\r\n        \r\n
\r\n    \r\n    \r\n    \r\n    \r\n
\n    \n    \n
\n    t\n
\n\n\n';o.append(l),i.html(o)}})}();<|endoftext|>


<commit_before>$(function() {
  var $main = $("#ajax-content");
  var $title = document.title;
  var $body = $("body");

  var loadPage = function(href) {
    $body.addClass('ajax-loading');
    setTimeout(function() {
      $main.load(href + " #ajax-content > *", function(response, status, xhr) {
        if (status === 'success') {
          $title = response.match(/<title>(.*?)<\/title>/)[1]
        } else {
          loadPage('/404.html');
        }
        $body.removeClass('ajax-loading');
        document.title = $title;
        $('a.ajax-link').each(function() {
          $(this).removeClass('active');
          if ($(this).attr('href') === href) {
            $(this).addClass('active');
          }
        });
      })
    }, 300);
  }

  $(document).on("click", "a.ajax-link", function() {
    var href = $(this).attr("href");
    console.log(location);
    if (href.indexOf('://') === -1 && href.indexOf('#') === -1 && href !== location.pathname) {
      history.pushState({}, '', href);
      loadPage(href);
    }
    return false;
  });

  $(window).on("popstate", function(e) {
    // if (e.originalEvent.state !== null) {
    //   loadPage(location.href);
    // }
    loadPage(location.pathname);
  });

});<commit_msg>remove debug line<commit_after>$(function() {
  var $main = $("#ajax-content");
  var $title = document.title;
  var $body = $("body");

  var loadPage = function(href) {
    $body.addClass('ajax-loading');
    setTimeout(function() {
      $main.load(href + " #ajax-content > *", function(response, status, xhr) {
        if (status === 'success') {
          $title = response.match(/<title>(.*?)<\/title>/)[1]
        } else {
          loadPage('/404.html');
        }
        $body.removeClass('ajax-loading');
        document.title = $title;
        $('a.ajax-link').each(function() {
          $(this).removeClass('active');
          if ($(this).attr('href') === href) {
            $(this).addClass('active');
          }
        });
      })
    }, 300);
  }

  $(document).on("click", "a.ajax-link", function() {
    var href = $(this).attr("href");
    // console.log(location);
    if (href.indexOf('://') === -1 && href.indexOf('#') === -1 && href !== location.pathname) {
      history.pushState({}, '', href);
      loadPage(href);
    }
    return false;
  });

  $(window).on("popstate", function(e) {
    // if (e.originalEvent.state !== null) {
    //   loadPage(location.href);
    // }
    loadPage(location.pathname);
  });

});<|endoftext|>


<commit_before>define(['mobileDetect', 'canvasBg','angular-animate'], function(mobileDetect, canvasBg) {
    var myAdmin = angular.module('view', ['ui.router', 'ngResource', 'ngAnimate', 'myService', 'ngDialog','pascalprecht.translate']);
    myAdmin.run(["$window", '$rootScope', "PageStorage", "LocalSetting", "GetOneBg",
        function($window, $rootScope, PageStorage, LocalSetting, GetOneBg) {
            if (mobileDetect()) { // detect the platform whether is mobile
                $window.sessionStorage['isMobile'] = 'true';
            } else {
                $window.sessionStorage['isMobile'] = 'false';
            }
            var a = document.createElement('a'); //detect browser whether supoort download attribute
            if ((typeof a.download) != "undefined") {
                LocalSetting.setSetting('support', 'true');
            }
            for (key in $window.localStorage) {
                LocalSetting.setSetting(key, $window.localStorage[key]);
            }
            if (!$window.localStorage.hasOwnProperty('isSafe')) { //默认开启安全模式
                LocalSetting.setSetting('isSafe', 'true');
            }
            if (!$window.localStorage.hasOwnProperty('isRememberPage')) { //默认关闭记录页数
                LocalSetting.setSetting('isRememberPage', 'false');
            }
            if ($window.sessionStorage["current"]) {
                PageStorage.setCurrentPage(Number($window.sessionStorage["current"]));
            } else if ($window.localStorage['isRememberPage'] == "true") {
                PageStorage.setCurrentPage(Number($window.localStorage["localCurrent"]));
            } else {
                PageStorage.setCurrentPage(null);
            }
            if (LocalSetting.getSetting('cover')) { //header 和footer 的backgroundImg 设置
                setBgImg(LocalSetting.getSetting('cover'))
            } else {
                setBgImg('../images/bg.jpg');
            }

            function canvasParentSizeChange(selector,place) { //判断canvas的父元素窗口大小是否发生改变
                var canvasParent = document.querySelector(selector);
                var nowWith = canvasParent.clientWidth;
                var nowHeight = canvasParent.clientHeight;
                if (place=="header") {
                    var width = "headerWidth";
                    var height = "headerHeight";
                }
                if (place=="content") {
                    var width = "contentWidth";
                    var height = "contentHeight";
                }
                if ($rootScope[width] == undefined && $rootScope[height] == undefined) {
                    $rootScope[width] = nowWith;
                    $rootScope[height] = nowHeight;
                    return true;
                } else if ($rootScope[width] == nowWith && $rootScope[height] == nowHeight) {
                    return false;
                } else {
                    $rootScope[width] = nowWith;
                    $rootScope[height] = nowHeight;
                    return true;
                }
            }

            function renderHeaderCanvasBg() {
                var can1 = new canvasBg('body header canvas');
                can1.renderAnimateRandomFillStroke('rect', "transparent", can1.getShape('doubleRect', {
                    w: 20,
                    h: 20,
                    sw: 10,
                    sh: 10
                }), 5);

            }

            function setBgImg(url) {
                var covers = document.querySelectorAll('.cover');
                [].forEach.call(covers, function(elem) {
                    elem.style.backgroundImage = "url(" + url + ")";
                })
            }

            function renderCover() { //远程获取一张图片当作header和footer的背景，并存入localstorage中
                GetOneBg.get().then(function(data) {
                    setBgImg(data);
                    LocalSetting.setSetting('cover', data);
                })
            }
            if ($window.sessionStorage['isMobile'] !== "true") {
                var w = angular.element($window);
                var handler;
                w.bind('resize', function() { //当窗口resize时，重新绘制背景
                    if (handler) {
                        clearTimeout(handler);
                    }
                    handler = setTimeout(function() {
                        var isHeaderChange = canvasParentSizeChange('body>header','header');
                        var isContentChange = canvasParentSizeChange('.inner','content')
                        if (isHeaderChange) {
                            renderHeaderCanvasBg();
                        }
                        if (isContentChange) {
                            $rootScope.$broadcast('contentRender');
                        }
                    }, 1000);
                });
                renderHeaderCanvasBg();
            }
            // setTimeout(renderCover, 5000); //放入记时器中，异步执行。
        }
    ]);
    return myAdmin;
});<commit_msg>forgot something<commit_after>define(['mobileDetect', 'canvasBg','angular-animate'], function(mobileDetect, canvasBg) {
    var myAdmin = angular.module('view', ['ui.router', 'ngResource', 'ngAnimate', 'myService', 'ngDialog','pascalprecht.translate']);
    myAdmin.run(["$window", '$rootScope', "PageStorage", "LocalSetting", "GetOneBg",
        function($window, $rootScope, PageStorage, LocalSetting, GetOneBg) {
            if (mobileDetect()) { // detect the platform whether is mobile
                $window.sessionStorage['isMobile'] = 'true';
            } else {
                $window.sessionStorage['isMobile'] = 'false';
            }
            var a = document.createElement('a'); //detect browser whether supoort download attribute
            if ((typeof a.download) != "undefined") {
                LocalSetting.setSetting('support', 'true');
            }
            for (key in $window.localStorage) {
                LocalSetting.setSetting(key, $window.localStorage[key]);
            }
            if (!$window.localStorage.hasOwnProperty('isSafe')) { //默认开启安全模式
                LocalSetting.setSetting('isSafe', 'true');
            }
            if (!$window.localStorage.hasOwnProperty('isRememberPage')) { //默认关闭记录页数
                LocalSetting.setSetting('isRememberPage', 'false');
            }
            if ($window.sessionStorage["current"]) {
                PageStorage.setCurrentPage(Number($window.sessionStorage["current"]));
            } else if ($window.localStorage['isRememberPage'] == "true") {
                PageStorage.setCurrentPage(Number($window.localStorage["localCurrent"]));
            } else {
                PageStorage.setCurrentPage(null);
            }
            if (LocalSetting.getSetting('cover')) { //header 和footer 的backgroundImg 设置
                setBgImg(LocalSetting.getSetting('cover'))
            } else {
                setBgImg('../images/bg.jpg');
            }

            function canvasParentSizeChange(selector,place) { //判断canvas的父元素窗口大小是否发生改变
                var canvasParent = document.querySelector(selector);
                var nowWith = canvasParent.clientWidth;
                var nowHeight = canvasParent.clientHeight;
                if (place=="header") {
                    var width = "headerWidth";
                    var height = "headerHeight";
                }
                if (place=="content") {
                    var width = "contentWidth";
                    var height = "contentHeight";
                }
                if ($rootScope[width] == undefined && $rootScope[height] == undefined) {
                    $rootScope[width] = nowWith;
                    $rootScope[height] = nowHeight;
                    return true;
                } else if ($rootScope[width] == nowWith && $rootScope[height] == nowHeight) {
                    return false;
                } else {
                    $rootScope[width] = nowWith;
                    $rootScope[height] = nowHeight;
                    return true;
                }
            }

            function renderHeaderCanvasBg() {
                var can1 = new canvasBg('body header canvas');
                can1.renderAnimateRandomFillStroke('rect', "transparent", can1.getShape('doubleRect', {
                    w: 20,
                    h: 20,
                    sw: 10,
                    sh: 10
                }), 5);

            }

            function setBgImg(url) {
                var covers = document.querySelectorAll('.cover');
                [].forEach.call(covers, function(elem) {
                    elem.style.backgroundImage = "url(" + url + ")";
                })
            }

            function renderCover() { //远程获取一张图片当作header和footer的背景，并存入localstorage中
                GetOneBg.get().then(function(data) {
                    setBgImg(data);
                    LocalSetting.setSetting('cover', data);
                })
            }
            if ($window.sessionStorage['isMobile'] !== "true") {
                var w = angular.element($window);
                var handler;
                w.bind('resize', function() { //当窗口resize时，重新绘制背景
                    if (handler) {
                        clearTimeout(handler);
                    }
                    handler = setTimeout(function() {
                        var isHeaderChange = canvasParentSizeChange('body>header','header');
                        var isContentChange = canvasParentSizeChange('.inner','content')
                        if (isHeaderChange) {
                            renderHeaderCanvasBg();
                        }
                        if (isContentChange) {
                            $rootScope.$broadcast('contentRender');
                        }
                    }, 1000);
                });
                renderHeaderCanvasBg();
            }
            setTimeout(renderCover, 5000); //放入记时器中，异步执行。
        }
    ]);
    return myAdmin;
});<|endoftext|>


<commit_before>/**
 * Copyright 2016, Google, Inc.
 * 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';

// [START functions_pubsub_setup]
const {PubSub} = require('@google-cloud/pubsub');

// Instantiates a client
const pubsub = new PubSub();
// [END functions_pubsub_setup]

const {Buffer} = require('safe-buffer');

// [START functions_pubsub_publish]
/**
 * Publishes a message to a Cloud Pub/Sub Topic.
 *
 * @example
 * gcloud functions call publish --data '{"topic":"[YOUR_TOPIC_NAME]","message":"Hello, world!"}'
 *
 *   - Replace `[YOUR_TOPIC_NAME]` with your Cloud Pub/Sub topic name.
 *
 * @param {object} req Cloud Function request context.
 * @param {object} req.body The request body.
 * @param {string} req.body.topic Topic name on which to publish.
 * @param {string} req.body.message Message to publish.
 * @param {object} res Cloud Function response context.
 */
exports.publish = async (req, res) => {
  if (!req.body.topic || !req.body.message) {
    res
      .status(500)
      .send(
        'Missing parameter(s); include "topic" and "subscription" properties in your request.'
      );
    return;
  }

  console.log(`Publishing message to topic ${req.body.topic}`);

  // References an existing topic
  const topic = pubsub.topic(req.body.topic);

  const messageObject = {
    data: {
      message: req.body.message,
    },
  };
  const messageBuffer = Buffer.from(JSON.stringify(messageObject), 'base64');

  // Publishes a message
  try {
    await topic.publish(messageBuffer);
    res.status(200).send('Message published.');
  } catch (err) {
    console.error(err);
    res.status(500).send(err);
    return Promise.reject(err);
  }
};
// [END functions_pubsub_publish]

// [START functions_pubsub_subscribe]
/**
 * Triggered from a message on a Cloud Pub/Sub topic.
 *
 * @param {object} pubsubMessage The Cloud Pub/Sub Message object.
 * @param {string} pubsubMessage.data The "data" property of the Cloud Pub/Sub Message.
 */
exports.subscribe = pubsubMessage => {
  // Print out the data from Pub/Sub, to prove that it worked
  console.log(Buffer.from(pubsubMessage.data, 'base64').toString());
};
// [END functions_pubsub_subscribe]
<commit_msg>Update index.js (#1380)<commit_after>/**
 * Copyright 2016, Google, Inc.
 * 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';

// [START functions_pubsub_setup]
const {PubSub} = require('@google-cloud/pubsub');

// Instantiates a client
const pubsub = new PubSub();
// [END functions_pubsub_setup]

const {Buffer} = require('safe-buffer');

// [START functions_pubsub_publish]
/**
 * Publishes a message to a Cloud Pub/Sub Topic.
 *
 * @example
 * gcloud functions call publish --data '{"topic":"[YOUR_TOPIC_NAME]","message":"Hello, world!"}'
 *
 *   - Replace `[YOUR_TOPIC_NAME]` with your Cloud Pub/Sub topic name.
 *
 * @param {object} req Cloud Function request context.
 * @param {object} req.body The request body.
 * @param {string} req.body.topic Topic name on which to publish.
 * @param {string} req.body.message Message to publish.
 * @param {object} res Cloud Function response context.
 */
exports.publish = async (req, res) => {
  if (!req.body.topic || !req.body.message) {
    res
      .status(500)
      .send(
        'Missing parameter(s); include "topic" and "subscription" properties in your request.'
      );
    return;
  }

  console.log(`Publishing message to topic ${req.body.topic}`);

  // References an existing topic
  const topic = pubsub.topic(req.body.topic);

  const messageObject = {
    data: {
      message: req.body.message,
    },
  };
  const messageBuffer = Buffer.from(JSON.stringify(messageObject), 'utf8');

  // Publishes a message
  try {
    await topic.publish(messageBuffer);
    res.status(200).send('Message published.');
  } catch (err) {
    console.error(err);
    res.status(500).send(err);
    return Promise.reject(err);
  }
};
// [END functions_pubsub_publish]

// [START functions_pubsub_subscribe]
/**
 * Triggered from a message on a Cloud Pub/Sub topic.
 *
 * @param {object} pubsubMessage The Cloud Pub/Sub Message object.
 * @param {string} pubsubMessage.data The "data" property of the Cloud Pub/Sub Message.
 */
exports.subscribe = pubsubMessage => {
  // Print out the data from Pub/Sub, to prove that it worked
  console.log(Buffer.from(pubsubMessage.data, 'base64').toString());
};
// [END functions_pubsub_subscribe]
<|endoftext|>


<commit_before>'use strict';

jest.unmock('../js/components/game-over');
jest.unmock('material-ui/lib/raised-button');

import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import GameOver from '../js/components/game-over';

navigator.__defineGetter__('userAgent', function() {
  return 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2454.85 Safari/537.36'
});

describe('In the game, after the user submits a word...', () => {
  it('if this is the last word available to be submitted we display the component', () => {
    const onStartNewGameMockFunc = jest.fn();
    const onStartSameGameMockFunc = jest.fn();
    // Render a gameover component in the document
    const gameOver = TestUtils.renderIntoDocument(
      <GameOver status='Game Finished' numberCorrect="5" onStartNewGame={onStartNewGameMockFunc} onStartSameGame={onStartSameGameMockFunc}/>
    );

    const gameOverNode = ReactDOM.findDOMNode(gameOver);
    expect(gameOverNode.style.display).toEqual('flex');
    const titleNode = gameOverNode.children[0].children[0];
    expect(titleNode.textContent).toEqual('You got 5 correct!');

  });

  it('if this is not the last word available to spell, then keep the component hidden', () => {
    const onStartNewGameMockFunc = jest.fn();
    const onStartSameGameMockFunc = jest.fn();
    // Render a gameover component in the document
    const gameOver = TestUtils.renderIntoDocument(
      <GameOver status='Playing'  onStartNewGame={onStartNewGameMockFunc} onStartSameGame={onStartSameGameMockFunc}/>
    );

    const gameOverNode = ReactDOM.findDOMNode(gameOver);
    expect(gameOverNode.style.display).toEqual('none');
  });
    // Render a gameover component in the document
    const gameOver = TestUtils.renderIntoDocument(
      <GameOver status='Playing'/>
    );

    const gameOverNode = ReactDOM.findDOMNode(gameOver);
    expect(gameOverNode.style.display).toEqual('none');
  });

});<commit_msg>added click testing and label comparisons for game over buttons<commit_after>'use strict';

jest.unmock('../js/components/game-over');
jest.unmock('material-ui/lib/raised-button');

import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import GameOver from '../js/components/game-over';

navigator.__defineGetter__('userAgent', function() {
  return 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2454.85 Safari/537.36'
});

describe('In the game, after the user submits a word...', () => {
  it('if this is the last word available to be submitted we display the component', () => {
    const onStartNewGameMockFunc = jest.fn();
    const onStartSameGameMockFunc = jest.fn();
    // Render a gameover component in the document
    const gameOver = TestUtils.renderIntoDocument(
      <GameOver status='Game Finished' numberCorrect="5" onStartNewGame={onStartNewGameMockFunc} onStartSameGame={onStartSameGameMockFunc}/>
    );

    const gameOverNode = ReactDOM.findDOMNode(gameOver);
    expect(gameOverNode.style.display).toEqual('flex');
    const titleNode = gameOverNode.children[0].children[0];
    expect(titleNode.textContent).toEqual('You got 5 correct!');

  });

  it('if this is not the last word available to spell, then keep the component hidden', () => {
    const onStartNewGameMockFunc = jest.fn();
    const onStartSameGameMockFunc = jest.fn();
    // Render a gameover component in the document
    const gameOver = TestUtils.renderIntoDocument(
      <GameOver status='Playing'  onStartNewGame={onStartNewGameMockFunc} onStartSameGame={onStartSameGameMockFunc}/>
    );

    const gameOverNode = ReactDOM.findDOMNode(gameOver);
    expect(gameOverNode.style.display).toEqual('none');
  });

  it('clicking same game button calls the correct function', () => {
    const onStartNewGameMockFunc = jest.fn();
    const onStartSameGameMockFunc = jest.fn();
    // Render a gameover component in the document
    const gameOver = TestUtils.renderIntoDocument(
      <GameOver status='Playing'  onStartNewGame={onStartNewGameMockFunc} onStartSameGame={onStartSameGameMockFunc}/>
    );

    const gameOverNode = ReactDOM.findDOMNode(gameOver);
    expect(gameOverNode.style.display).toEqual('none');
    let startSameButton = gameOverNode.children[0].children[1].children[0];
    expect(startSameButton.textContent).toEqual('Start Again?');
    TestUtils.Simulate.click(startSameButton);
    expect(onStartSameGameMockFunc).not.toBeCalled();
  });

  it('clicking new game button calls the correct function', () => {
    const onStartNewGameMockFunc = jest.fn();
    const onStartSameGameMockFunc = jest.fn();
    // Render a gameover component in the document
    const gameOver = TestUtils.renderIntoDocument(
      <GameOver status='Playing'  onStartNewGame={onStartNewGameMockFunc} onStartSameGame={onStartSameGameMockFunc}/>
    );

    const gameOverNode = ReactDOM.findDOMNode(gameOver);
    expect(gameOverNode.style.display).toEqual('none');
    let startNewButton = gameOverNode.children[0].children[1].children[1];
    expect(startNewButton.textContent).toEqual('Move to harder words?');
    TestUtils.Simulate.click(startNewButton);
    expect(onStartNewGameMockFunc).not.toBeCalled();
  });

});<|endoftext|>


<commit_before>import MagnonElement from "../../src/magnon-element.js";

import { css } from "../../src/literals.js";

import "./magnon-notification.js";

const style = css`
:host {
    display: flex;
    position: fixed;
    top: 0; right: 0; bottom: 0; left: 0;
    z-index: 1000;
    pointer-events: none;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-end;

    padding: 20px 40px;
}

::slotted(magnon-notification) {
    flex-shrink: 0;
    max-width: 30%;
}
`;

export class MagnonNotifications extends MagnonElement {
    constructor() {
        super({ content: `<slot></slot>${style}` });
    }

    static get elementName() {
        return "magnon-notifications";
    }

    static appendToBody() {
        if (window.MAGNON_COMPONENTS_DISABLE_GLOBAL) return;

        const append = () => {
            const notificationContainer = document.createElement("magnon-notifications");
            this.notificationContainer = notificationContainer;
            document.body.appendChild(notificationContainer);
        };

        if (document.readyState !== "loading") {
            append();
        } else {
            document.addEventListener("DOMContentLoaded", () => append());
        }
    }

    static send(template) {
        if (typeof template !== "string") {
            const instance = template.content.cloneNode(true);
            this.notificationContainer.appendChild(instance);
        } else {
            this.notificationContainer.innerHTML += template;
        }

        const notifications = this.notificationContainer.querySelectorAll("magnon-notification");
        return notifications[notifications.length - 1];
    }
}

MagnonNotifications.init();
MagnonNotifications.appendToBody();

window.MagnonNotifications = MagnonNotifications;
<commit_msg>Handle notification sending better<commit_after>import MagnonElement from "../../src/magnon-element.js";

import { css } from "../../src/literals.js";

import "./magnon-notification.js";

const style = css`
:host {
    display: flex;
    position: fixed;
    top: 0; right: 0; bottom: 0; left: 0;
    z-index: 1000;
    pointer-events: none;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-end;

    padding: 20px 40px;
}

::slotted(magnon-notification) {
    flex-shrink: 0;
    max-width: 30%;
}
`;

export class MagnonNotifications extends MagnonElement {
    constructor() {
        super({ content: `<slot></slot>${style}` });
    }

    static get elementName() {
        return "magnon-notifications";
    }

    static appendToBody() {
        if (window.MAGNON_COMPONENTS_DISABLE_GLOBAL) return;

        const append = () => {
            const notificationContainer = document.createElement("magnon-notifications");
            this.notificationContainer = notificationContainer;
            document.body.appendChild(notificationContainer);
        };

        if (document.readyState !== "loading") {
            append();
        } else {
            document.addEventListener("DOMContentLoaded", () => append());
        }
    }

    static send(template) {
        let notification;

        if (typeof template !== "string") {
            const instance = template.content.cloneNode(true);
            notification = instance;
        } else {
            const temp = document.createElement("div");
            temp.innerHTML = template;
            notification = temp.querySelector("magnon-notification");
        }

        this.notificationContainer.appendChild(notification);
        return notification;
    }
}

MagnonNotifications.init();
MagnonNotifications.appendToBody();

window.MagnonNotifications = MagnonNotifications;
<|endoftext|>