<commit_before>Ext.define('devilry.statistics.Loader', {
    extend: 'Ext.util.Observable',
    requires: [
        'devilry.extjshelpers.AsyncActionPool',
        'devilry.statistics.AggregatedPeriodDataForStudentBase',
        'devilry.statistics.LabelManager'
    ],

    constructor: function(periodid, config) {
        this._students_by_releatedid = {};
        this.periodid = periodid;
        this.labelManager = Ext.create('devilry.statistics.LabelManager', {
            loader: this
            //listeners: {
                //scope: this,
                //changedMany: this._onDataChanged
            //}
        });

        this.assignment_store = Ext.create('Ext.data.Store', {
            model: 'devilry.apps.administrator.simplified.SimplifiedAssignment',
            remoteFilter: true,
            remoteSort: true
        });
        this.assignment_ids = [];

        this.addEvents('loaded', 'datachange', 'filterApplied', 'filterCleared');
        // Copy configured listeners into *this* object so that the base class's
        // constructor will add them.
        this.listeners = config.listeners;

        Ext.getBody().mask('Loading all data about all students on the period', 'page-load-mask');
        this.callParent(arguments);

        // NOTE: Commented out this code, and the code in _checkLoadComplete
        // because it causes issues on windows (or perhaps all remote clients).
        // It works locally though. It is supposed to enable loading of all
        // elements in parallel.
        //this._loadedCount = 0;
        //this._loadAssignments();
        //this._loadAllGroupsInPeriod();
        //this._loadPeriod();
        //this._loadAllStudentLabels();
        //this._loadAllCandidatesInPeriod();
        //this._loadAllRelatedStudents();

        this._loaders = [
            this._loadAssignments,
            this._loadAllGroupsInPeriod,
            this._loadPeriod,
            this._loadAllStudentLabels,
            this._loadAllCandidatesInPeriod,
            this._loadAllRelatedStudents
        ]
        this._checkLoadComplete();
    },

    _checkLoadComplete: function() {
        //this._loadedCount ++;
        //if(this._loadedCount >= 6) {
            //this._onLoaded();
        //};
        if(this._loaders.length > 0) {
            var loader = this._loaders.pop();
            Ext.bind(loader, this)();
        } else {
            this._onLoaded();
        }
    },

    /**
     * @private
     */
    _loadAssignments: function() {
        this.assignment_store.pageSize = 100000; // TODO: avoid UGLY hack
        this.assignment_store.proxy.setDevilryFilters([{
            field: 'parentnode',
            comp: 'exact',
            value: this.periodid
        }]);
        this.assignment_store.proxy.setDevilryOrderby(['publishing_time']);
        this.assignment_store.load({
            scope: this,
            callback: this._onAssignmentsLoaded
        });
    },

    /**
     * @private
     */
    _onAssignmentsLoaded: function(assignmentrecords, op) {
        if(!op.success) {
            this._handleLoadError('Failed to load assignments', op);
            return;
        }
        this._tmpAssignmentsWithAllGroupsLoaded = 0;
        Ext.each(assignmentrecords, function(assignmentrecord, index) {
            this.assignment_ids.push(assignmentrecord.get('id'));
        }, this);
        this._checkLoadComplete();
    },

    /**
     * @private
     */
    _loadAllRelatedStudents: function() {
        this.relatedstudent_store = Ext.create('Ext.data.Store', {
            model: 'devilry.apps.administrator.simplified.SimplifiedRelatedStudent',
            remoteFilter: true,
            remoteSort: true
        });
        this.relatedstudent_store.pageSize = 100000; // TODO: avoid UGLY hack
        this.relatedstudent_store.proxy.setDevilryFilters([{
            field: 'period',
            comp: 'exact',
            value: this.periodid
        }]);
        this.relatedstudent_store.load({
            scope: this,
            callback: this._onLoadAllRelatedStudents
        });
    },

    /**
     * @private
     */
    _onLoadAllRelatedStudents: function(records, op) {
        if(!op.success) {
            this._handleLoadError('Failed to load related students', op);
            return;
        }
        this._checkLoadComplete();
    },

    /**
     * @private
     */
    _loadAllCandidatesInPeriod: function() {
        this.candidate_store = Ext.create('Ext.data.Store', {
            model: 'devilry.apps.administrator.simplified.SimplifiedCandidate',
            remoteFilter: true,
            remoteSort: true
        });
        this.candidate_store.pageSize = 100000; // TODO: avoid UGLY hack
        this.candidate_store.proxy.setDevilryFilters([{
            field: 'assignment_group__parentnode__parentnode',
            comp: 'exact',
            value: this.periodid
        }]);
        this.candidate_store.load({
            scope: this,
            callback: this._onLoadAllCandidatesInPeriod
        });
    },

    /**
     * @private
     */
    _onLoadAllCandidatesInPeriod: function(records, op) {
        if(!op.success) {
            this._handleLoadError('Failed to load candidates', op);
            return;
        }
        this._checkLoadComplete();
    },

    /**
     * @private
     */
    _loadAllStudentLabels: function() {
        this.relatedstudentkeyvalue_store = Ext.create('Ext.data.Store', {
            model: 'devilry.apps.administrator.simplified.SimplifiedRelatedStudentKeyValue',
            remoteFilter: true,
            remoteSort: true
        });
        this.relatedstudentkeyvalue_store.pageSize = 100000; // TODO: avoid UGLY hack
        this.relatedstudentkeyvalue_store.proxy.setDevilryFilters([{
            field: 'relatedstudent__period',
            comp: 'exact',
            value: this.periodid
        }, {
            field: 'application',
            comp: 'exact',
            value: this.labelManager.application_id
        }]);
        this.relatedstudentkeyvalue_store.load({
            scope: this,
            callback: this._onLoadAllStudentLabels
        });
    },

    /**
     * @private
     */
    _onLoadAllStudentLabels: function(records, op) {
        if(!op.success) {
            this._handleLoadError('Failed to load labels', op);
            return;
        }
        this._checkLoadComplete();
    },

    /**
     * @private
     */
    _loadPeriod: function() {
        Ext.ModelManager.getModel('devilry.apps.administrator.simplified.SimplifiedPeriod').load(this.periodid, {
            scope: this,
            callback: function(record, op) {
                if(!op.success) {
                    this._handleLoadError('Failed to load period', op);
                    return;
                }
                this.periodRecord = record;
                this._checkLoadComplete();
            }
        });
    },

    /**
     * @private
     */
    _loadAllGroupsInPeriod: function() {
        this.assignmentgroup_store = Ext.create('Ext.data.Store', {
            model: 'devilry.apps.administrator.simplified.SimplifiedAssignmentGroup',
            remoteFilter: true,
            remoteSort: true
        });
        this.assignmentgroup_store.pageSize = 100000; // TODO: avoid UGLY hack
        this.assignmentgroup_store.proxy.setDevilryFilters([{
            field: 'parentnode__parentnode',
            comp: 'exact',
            value: this.periodid
        }]);
        this.assignmentgroup_store.load({
            scope: this,
            callback: this._onLoadAllGroupsInPeriod
        });
    },

    /**
     * @private
     */
    _onLoadAllGroupsInPeriod: function(grouprecords, op) {
        if(!op.success) {
            this._handleLoadError('Failed to load assignment groups', op);
            return;
        }
        this._checkLoadComplete();
    },

    _addAllRelatedStudentsToStore: function() {
        Ext.each(this.relatedstudent_store.data.items, function(relatedStudentRecord, index) {
            var userid = relatedStudentRecord.get('user')
            var username = relatedStudentRecord.get('user__username')
            var record = Ext.create('devilry.statistics.AggregatedPeriodDataForStudentGenerated', {
                userid: userid,
                username: username,
                full_name: relatedStudentRecord.get('user__devilryuserprofile__full_name'),
                labelKeys: []
            });
            this.store.add(record);
            record.labels = [];
            record.assignment_store = this.assignment_store;
            record.relatedStudentRecord = relatedStudentRecord;
            record.groupsByAssignmentId = {};
            this._students_by_releatedid[relatedStudentRecord.get('id')] = record;
        }, this);
    },

    _addLabelsToStore: function() {
        Ext.each(this.relatedstudentkeyvalue_store.data.items, function(appKeyValueRecord, index) {
            var relatedstudent_id = appKeyValueRecord.get('relatedstudent');
            var label = appKeyValueRecord.get('key');
            var labelDescription = appKeyValueRecord.get('value');
            var studentRecord = this._students_by_releatedid[relatedstudent_id];
            studentRecord.labels[label] = appKeyValueRecord;
            studentRecord.setLabelKeysFromLabels();
        }, this);
    },

    _addAssignmentsToStore: function() {
        var assignment_ids = [];
        Ext.each(this.assignment_store.data.items, function(assignmentRecord, index) {
            assignment_ids.push(assignmentRecord.get('id'));
        }, this);
        Ext.each(this.store.data.items, function(studentRecord, index) {
            var groupsByAssignmentId = studentRecord.groupsByAssignmentId;
            Ext.each(this.assignment_store.data.items, function(assignmentRecord, index) {
                groupsByAssignmentId[assignmentRecord.get('id')] = {
                    candidates: [],
                    assignmentGroupRecord: null,
                    scaled_points: null
                };
            }, this);
            studentRecord.assignment_ids = assignment_ids;
        }, this);
    },

    _addGroupsToStore: function() {
        Ext.each(this.candidate_store.data.items, function(candidateRecord, index) {
            var student_id = candidateRecord.get('student');
            var studentRecord = this.store.getById(student_id);
            if(studentRecord) {
                var assignmentgroup_id = candidateRecord.get('assignment_group');
                var assignmentGroupRecord = this.assignmentgroup_store.getById(assignmentgroup_id);

                var groupsByAssignmentId = studentRecord.groupsByAssignmentId;
                var assignment_id = assignmentGroupRecord.get('parentnode');
                var group = groupsByAssignmentId[assignment_id];
                group.candidates.push(candidateRecord); // This will add only unique candidate records, since we only fetch distinct candidates
                group.assignmentGroupRecord = assignmentGroupRecord; // This will be overwritten for each candidate, but that does not matter, since they overwrite with the same record
            }
        }, this);
    },

    _mergeDataIntoStore: function() {
        this._addAllRelatedStudentsToStore();
        this._addLabelsToStore();
        this._addAssignmentsToStore();
        this._addGroupsToStore();
    },

    _onLoaded: function() {
        this._createStore();
        this.store.suspendEvents();
        this._mergeDataIntoStore();
        this.updateScaledPoints();
        this.store.resumeEvents();
        this.store.fireEvent('datachanged');
        this.fireEvent('loaded', this);
    },

    /**
     * @private
     */
    //_addGroupToStudent: function(username, grouprecord) {
        //if(!this._students[username]) {
            ////console.error(Ext.String.format('Skipped {0} because the user is not a related student.', username));
            //return;
        //}
        //var student = this._students[username];
        //var assignmentRecord = this.assignment_store.getById(grouprecord.data.parentnode);
        //student.groupsByAssignmentId[grouprecord.data.parentnode] = {
            //parentnode: grouprecord.get('parentnode'),
            //points: grouprecord.data.feedback__points,
            //is_passing_grade: grouprecord.data.feedback__is_passing_grade
        //};
    //},

    //_onDataChanged: function() {
    //},

    _createModel: function() {
        var fields = ['userid', 'username', 'full_name', 'labelKeys', 'student', 'totalScaledPoints'];
        Ext.each(this.assignment_store.data.items, function(assignmentRecord, index) {
            fields.push(assignmentRecord.get('short_name'));
            var scaledPointdataIndex = assignmentRecord.get('id') + '::scaledPoints';
            fields.push(scaledPointdataIndex);
        }, this);
        var model = Ext.define('devilry.statistics.AggregatedPeriodDataForStudentGenerated', {
            extend: 'devilry.statistics.AggregatedPeriodDataForStudentBase',
            idProperty: 'userid',
            fields: fields
        });
    },

    _createStore: function() {
        this._createModel();
        this.store = Ext.create('Ext.data.Store', {
            model: 'devilry.statistics.AggregatedPeriodDataForStudentGenerated',
            autoSync: false,
            proxy: 'memory'
        });

        //Ext.Object.each(this._students, function(username, student, index) {
            //var record = Ext.create('devilry.statistics.AggregatedPeriodDataForStudentBase', this.assignment_store, {
                //username: username,
                //full_name: student.relatedstudent.get('user__devilryuserprofile__full_name'),
                //relatedstudent: student.relatedstudent,
                //labelKeys: Ext.Object.getKeys(student.labels),
                //groupsByAssignmentId: student.groupsByAssignmentId,
                //labels: student.labels
            //});
            //store.add(record);
        //}, this);
        //return store;
    },

    updateScaledPoints: function() {
        this.store.suspendEvents(); // without suspendEvents/resumeEvents, each record change fires an update event, which makes updateScaledPoints take forever for huge datasets.
        Ext.each(this.store.data.items, function(studentRecord, index) {
            studentRecord.updateScaledPoints();
        }, this);
        this.store.resumeEvents();
        this.store.fireEvent('datachanged');
    },

    filterBy: function(description, fn, scope) {
        this.store.filterBy(fn, scope);
        this.fireEvent('filterApplied', this, description);
    },

    clearFilter: function() {
        this.store.clearFilter();
        this.fireEvent('filterCleared', this);
    },

    _handleLoadError: function(details, op) {
        Ext.getBody().unmask();
        var httperror = 'Lost connection with server';
        if(op.error.status !== 0) {
            httperror = Ext.String.format('{0} {1}', op.error.status, op.error.statusText);
        }
        Ext.MessageBox.show({
            title: 'Failed to load the period overview',
            msg: '
This is usually caused by an unstable server connection. Try reloading the page.
' +
                Ext.String.format('
Error details: {0}: {1}
', httperror, details),
            buttons: Ext.Msg.OK,
            icon: Ext.Msg.ERROR,
            closable: false
        });
    }
});
<commit_msg>apps.statistics...Loader: Minor change.<commit_after>Ext.define('devilry.statistics.Loader', {
    extend: 'Ext.util.Observable',
    requires: [
        'devilry.extjshelpers.AsyncActionPool',
        'devilry.statistics.AggregatedPeriodDataForStudentBase',
        'devilry.statistics.LabelManager'
    ],

    constructor: function(periodid, config) {
        this._students_by_releatedid = {};
        this.periodid = periodid;
        this.labelManager = Ext.create('devilry.statistics.LabelManager', {
            loader: this
            //listeners: {
                //scope: this,
                //changedMany: this._onDataChanged
            //}
        });

        this.assignment_store = Ext.create('Ext.data.Store', {
            model: 'devilry.apps.administrator.simplified.SimplifiedAssignment',
            remoteFilter: true,
            remoteSort: true
        });
        this.assignment_ids = [];

        this.addEvents('loaded', 'datachange', 'filterApplied', 'filterCleared');
        // Copy configured listeners into *this* object so that the base class's
        // constructor will add them.
        this.listeners = config.listeners;

        Ext.getBody().mask('Loading all data about all students on the period', 'page-load-mask');
        this.callParent(arguments);

        // NOTE: Commented out this code, and the code in _checkLoadComplete
        // because it causes issues on windows (or perhaps all remote clients).
        // It works locally though. It is supposed to enable loading of all
        // elements in parallel.
        //this._loadedCount = 0;
        //this._loadAssignments();
        //this._loadAllGroupsInPeriod();
        //this._loadPeriod();
        //this._loadAllStudentLabels();
        //this._loadAllCandidatesInPeriod();
        //this._loadAllRelatedStudents();

        this._loaders = [
            this._loadAssignments,
            this._loadAllGroupsInPeriod,
            this._loadPeriod,
            this._loadAllStudentLabels,
            this._loadAllCandidatesInPeriod,
            this._loadAllRelatedStudents
        ]
        this._checkLoadComplete();
    },

    _checkLoadComplete: function() {
        //this._loadedCount ++;
        //if(this._loadedCount >= 6) {
            //this._onLoaded();
        //};
        if(this._loaders.length > 0) {
            var loader = this._loaders.pop();
            Ext.bind(loader, this)();
        } else {
            this._onLoaded();
        }
    },

    /**
     * @private
     */
    _loadAssignments: function() {
        this.assignment_store.pageSize = 100000; // TODO: avoid UGLY hack
        this.assignment_store.proxy.setDevilryFilters([{
            field: 'parentnode',
            comp: 'exact',
            value: this.periodid
        }]);
        this.assignment_store.proxy.setDevilryOrderby(['publishing_time']);
        this.assignment_store.load({
            scope: this,
            callback: this._onAssignmentsLoaded
        });
    },

    /**
     * @private
     */
    _onAssignmentsLoaded: function(assignmentrecords, op) {
        if(!op.success) {
            this._handleLoadError('Failed to load assignments', op);
            return;
        }
        this._tmpAssignmentsWithAllGroupsLoaded = 0;
        Ext.each(assignmentrecords, function(assignmentrecord, index) {
            this.assignment_ids.push(assignmentrecord.get('id'));
        }, this);
        this._checkLoadComplete();
    },

    /**
     * @private
     */
    _loadAllRelatedStudents: function() {
        this.relatedstudent_store = Ext.create('Ext.data.Store', {
            model: 'devilry.apps.administrator.simplified.SimplifiedRelatedStudent',
            remoteFilter: true,
            remoteSort: true
        });
        this.relatedstudent_store.pageSize = 100000; // TODO: avoid UGLY hack
        this.relatedstudent_store.proxy.setDevilryFilters([{
            field: 'period',
            comp: 'exact',
            value: this.periodid
        }]);
        this.relatedstudent_store.load({
            scope: this,
            callback: this._onLoadAllRelatedStudents
        });
    },

    /**
     * @private
     */
    _onLoadAllRelatedStudents: function(records, op) {
        if(!op.success) {
            this._handleLoadError('Failed to load related students', op);
            return;
        }
        this._checkLoadComplete();
    },

    /**
     * @private
     */
    _loadAllCandidatesInPeriod: function() {
        this.candidate_store = Ext.create('Ext.data.Store', {
            model: 'devilry.apps.administrator.simplified.SimplifiedCandidate',
            remoteFilter: true,
            remoteSort: true
        });
        this.candidate_store.pageSize = 100000; // TODO: avoid UGLY hack
        this.candidate_store.proxy.setDevilryFilters([{
            field: 'assignment_group__parentnode__parentnode',
            comp: 'exact',
            value: this.periodid
        }]);
        this.candidate_store.load({
            scope: this,
            callback: this._onLoadAllCandidatesInPeriod
        });
    },

    /**
     * @private
     */
    _onLoadAllCandidatesInPeriod: function(records, op) {
        if(!op.success) {
            this._handleLoadError('Failed to load candidates', op);
            return;
        }
        this._checkLoadComplete();
    },

    /**
     * @private
     */
    _loadAllStudentLabels: function() {
        this.relatedstudentkeyvalue_store = Ext.create('Ext.data.Store', {
            model: 'devilry.apps.administrator.simplified.SimplifiedRelatedStudentKeyValue',
            remoteFilter: true,
            remoteSort: true
        });
        this.relatedstudentkeyvalue_store.pageSize = 100000; // TODO: avoid UGLY hack
        this.relatedstudentkeyvalue_store.proxy.setDevilryFilters([{
            field: 'relatedstudent__period',
            comp: 'exact',
            value: this.periodid
        }, {
            field: 'application',
            comp: 'exact',
            value: this.labelManager.application_id
        }]);
        this.relatedstudentkeyvalue_store.load({
            scope: this,
            callback: this._onLoadAllStudentLabels
        });
    },

    /**
     * @private
     */
    _onLoadAllStudentLabels: function(records, op) {
        if(!op.success) {
            this._handleLoadError('Failed to load labels', op);
            return;
        }
        this._checkLoadComplete();
    },

    /**
     * @private
     */
    _loadPeriod: function() {
        Ext.ModelManager.getModel('devilry.apps.administrator.simplified.SimplifiedPeriod').load(this.periodid, {
            scope: this,
            callback: function(record, op) {
                if(!op.success) {
                    this._handleLoadError('Failed to load period', op);
                    return;
                }
                this.periodRecord = record;
                this._checkLoadComplete();
            }
        });
    },

    /**
     * @private
     */
    _loadAllGroupsInPeriod: function() {
        this.assignmentgroup_store = Ext.create('Ext.data.Store', {
            model: 'devilry.apps.administrator.simplified.SimplifiedAssignmentGroup',
            remoteFilter: true,
            remoteSort: true
        });
        this.assignmentgroup_store.pageSize = 100000; // TODO: avoid UGLY hack
        this.assignmentgroup_store.proxy.setDevilryFilters([{
            field: 'parentnode__parentnode',
            comp: 'exact',
            value: this.periodid
        }]);
        this.assignmentgroup_store.load({
            scope: this,
            callback: this._onLoadAllGroupsInPeriod
        });
    },

    /**
     * @private
     */
    _onLoadAllGroupsInPeriod: function(grouprecords, op) {
        if(!op.success) {
            this._handleLoadError('Failed to load assignment groups', op);
            return;
        }
        this._checkLoadComplete();
    },

    _addAllRelatedStudentsToStore: function() {
        Ext.each(this.relatedstudent_store.data.items, function(relatedStudentRecord, index) {
            var userid = relatedStudentRecord.get('user')
            var username = relatedStudentRecord.get('user__username')
            var record = Ext.create('devilry.statistics.AggregatedPeriodDataForStudentGenerated', {
                userid: userid,
                username: username,
                full_name: relatedStudentRecord.get('user__devilryuserprofile__full_name'),
                labelKeys: []
            });
            this.store.add(record);
            record.labels = [];
            record.assignment_store = this.assignment_store;
            record.relatedStudentRecord = relatedStudentRecord;
            record.groupsByAssignmentId = {};
            this._students_by_releatedid[relatedStudentRecord.get('id')] = record;
        }, this);
    },

    _addLabelsToStore: function() {
        Ext.each(this.relatedstudentkeyvalue_store.data.items, function(appKeyValueRecord, index) {
            var relatedstudent_id = appKeyValueRecord.get('relatedstudent');
            var label = appKeyValueRecord.get('key');
            var labelDescription = appKeyValueRecord.get('value');
            var studentRecord = this._students_by_releatedid[relatedstudent_id];
            studentRecord.labels[label] = appKeyValueRecord;
            studentRecord.setLabelKeysFromLabels();
        }, this);
    },

    _addAssignmentsToStore: function() {
        var assignment_ids = [];
        Ext.each(this.assignment_store.data.items, function(assignmentRecord, index) {
            assignment_ids.push(assignmentRecord.get('id'));
        }, this);
        Ext.each(this.store.data.items, function(studentRecord, index) {
            var groupsByAssignmentId = studentRecord.groupsByAssignmentId;
            Ext.each(this.assignment_store.data.items, function(assignmentRecord, index) {
                groupsByAssignmentId[assignmentRecord.get('id')] = {
                    candidates: [],
                    assignmentGroupRecord: null,
                    scaled_points: null
                };
            }, this);
            studentRecord.assignment_ids = assignment_ids;
        }, this);
    },

    _addGroupsToStore: function() {
        Ext.each(this.candidate_store.data.items, function(candidateRecord, index) {
            var student_id = candidateRecord.get('student');
            var studentRecord = this.store.getById(student_id);
            if(studentRecord) {
                var assignmentgroup_id = candidateRecord.get('assignment_group');
                var assignmentGroupRecord = this.assignmentgroup_store.getById(assignmentgroup_id);

                var groupsByAssignmentId = studentRecord.groupsByAssignmentId;
                var assignment_id = assignmentGroupRecord.get('parentnode');
                var group = groupsByAssignmentId[assignment_id];
                group.candidates.push(candidateRecord); // This will add only unique candidate records, since we only fetch distinct candidates
                group.assignmentGroupRecord = assignmentGroupRecord; // This will be overwritten for each candidate, but that does not matter, since they overwrite with the same record
            }
        }, this);
    },

    _mergeDataIntoStore: function() {
        this._addAllRelatedStudentsToStore();
        this._addLabelsToStore();
        this._addAssignmentsToStore();
        this._addGroupsToStore();
    },

    _onLoaded: function() {
        this._createStore();
        this.store.suspendEvents();
        this._mergeDataIntoStore();
        this.updateScaledPoints();
        this.store.resumeEvents();
        this.store.fireEvent('datachanged');
        this.fireEvent('loaded', this);
    },

    /**
     * @private
     */
    //_addGroupToStudent: function(username, grouprecord) {
        //if(!this._students[username]) {
            ////console.error(Ext.String.format('Skipped {0} because the user is not a related student.', username));
            //return;
        //}
        //var student = this._students[username];
        //var assignmentRecord = this.assignment_store.getById(grouprecord.data.parentnode);
        //student.groupsByAssignmentId[grouprecord.data.parentnode] = {
            //parentnode: grouprecord.get('parentnode'),
            //points: grouprecord.data.feedback__points,
            //is_passing_grade: grouprecord.data.feedback__is_passing_grade
        //};
    //},

    //_onDataChanged: function() {
    //},

    _createModel: function() {
        var fields = ['userid', 'username', 'full_name', 'labelKeys', 'totalScaledPoints'];
        Ext.each(this.assignment_store.data.items, function(assignmentRecord, index) {
            fields.push(assignmentRecord.get('short_name'));
            var scaledPointdataIndex = assignmentRecord.get('id') + '::scaledPoints';
            fields.push(scaledPointdataIndex);
        }, this);
        var model = Ext.define('devilry.statistics.AggregatedPeriodDataForStudentGenerated', {
            extend: 'devilry.statistics.AggregatedPeriodDataForStudentBase',
            idProperty: 'userid',
            fields: fields
        });
    },

    _createStore: function() {
        this._createModel();
        this.store = Ext.create('Ext.data.Store', {
            model: 'devilry.statistics.AggregatedPeriodDataForStudentGenerated',
            autoSync: false,
            proxy: 'memory'
        });

        //Ext.Object.each(this._students, function(username, student, index) {
            //var record = Ext.create('devilry.statistics.AggregatedPeriodDataForStudentBase', this.assignment_store, {
                //username: username,
                //full_name: student.relatedstudent.get('user__devilryuserprofile__full_name'),
                //relatedstudent: student.relatedstudent,
                //labelKeys: Ext.Object.getKeys(student.labels),
                //groupsByAssignmentId: student.groupsByAssignmentId,
                //labels: student.labels
            //});
            //store.add(record);
        //}, this);
        //return store;
    },

    updateScaledPoints: function() {
        this.store.suspendEvents(); // without suspendEvents/resumeEvents, each record change fires an update event, which makes updateScaledPoints take forever for huge datasets.
        Ext.each(this.store.data.items, function(studentRecord, index) {
            studentRecord.updateScaledPoints();
        }, this);
        this.store.resumeEvents();
        this.store.fireEvent('datachanged');
    },

    filterBy: function(description, fn, scope) {
        this.store.filterBy(fn, scope);
        this.fireEvent('filterApplied', this, description);
    },

    clearFilter: function() {
        this.store.clearFilter();
        this.fireEvent('filterCleared', this);
    },

    _handleLoadError: function(details, op) {
        Ext.getBody().unmask();
        var httperror = 'Lost connection with server';
        if(op.error.status !== 0) {
            httperror = Ext.String.format('{0} {1}', op.error.status, op.error.statusText);
        }
        Ext.MessageBox.show({
            title: 'Failed to load the period overview',
            msg: '
This is usually caused by an unstable server connection. Try reloading the page.
' +
                Ext.String.format('
Error details: {0}: {1}
', httperror, details),
            buttons: Ext.Msg.OK,
            icon: Ext.Msg.ERROR,
            closable: false
        });
    }
});
<|endoftext|>


<commit_before>/* global describe, it */

var $require = require('proxyquire');
var expect = require('chai').expect;
var sinon = require('sinon');
var factory = require('../../../../com/authorize/http/response/code');

describe('http/authorize/response/code', function() {

  it('should be annotated', function() {
    expect(factory['@implements']).to.equal('http://i.authnomicon.org/oauth2/authorization/http/ResponseType');
    expect(factory['@type']).to.equal('code');
  });

  it('should create response type', function(done) {
    var container = new Object();
    container.components = sinon.stub();
    container.components.withArgs('http://i.authnomicon.org/oauth2/authorization/http/ResponseMode').returns([]);

    var acs = new Object();
    acs.issue = sinon.stub().yieldsAsync(null, 'SplxlOBeZQQYbYS6WxSbIA');

    var codeSpy = sinon.stub();
    var factory = $require('../../../../com/authorize/http/response/code', {
      'oauth2orize': {
        grant: { code: codeSpy }
      }
    });

    factory(acs, null, container)
      .then(function(type) {

        expect(codeSpy.callCount).to.equal(1);
        expect(codeSpy.args[0][0]).to.deep.equal({ modes: {} });
        expect(codeSpy.args[0][1]).to.be.a('function');

        done();
      })
      .catch(function(error) {
        console.log(error);
      });

  })

  describe('creating grant', function() {
    var container = new Object();
    container.components = sinon.stub()
    container.components.withArgs('http://i.authnomicon.org/oauth2/authorization/http/ResponseMode').returns([]);
    var acs = new Object();
    acs.issue = sinon.stub().yieldsAsync(null, 'SplxlOBeZQQYbYS6WxSbIA');

    var codeSpy = sinon.stub();

    var factory = $require('../../../../com/authorize/http/response/code',
      { 'oauth2orize': { grant: { code: codeSpy } } });

    var grant;
    before(function(done) {
      var promise = factory(acs, null, container);
      promise.then(function(g) {
        grant = g;
        done();
      });
    });

    it('should create grant', function() {
      expect(codeSpy.callCount).to.equal(1);
      expect(codeSpy.args[0][0]).to.deep.equal({ modes: {} });
      expect(codeSpy.args[0][1]).to.be.a('function');
    });

    describe('issue', function() {
      var code;

      before(function(done) {
        var client = {
          id: 's6BhdRkqt3',
          name: 'Example Client',
          redirectURIs: [ 'https://client.example.com/cb' ]
        };
        var user = {
          id: '248289761001',
          displayName: 'Jane Doe'
        };
        var ares = {
          allow: true,
          scope: [ 'profile', 'email' ]
        }
        var areq = {
          type: 'code',
          clientID: 's6BhdRkqt3',
          redirectURI: 'https://client.example.com/cb',
          state: 'xyz'
        }

        var issue = codeSpy.args[0][1];
        issue(client, 'https://client.example.com/cb', user, ares, areq, {}, function(err, c) {
          if (err) { return done(err); }
          code = c;
          done();
        });
      });

      it('should issue authorization code', function() {
        expect(acs.issue.callCount).to.equal(1);
        expect(acs.issue.args[0][0]).to.deep.equal({
          client: {
            id: 's6BhdRkqt3',
            name: 'Example Client',
            redirectURIs: [ 'https://client.example.com/cb' ]
          },
          redirectURI: 'https://client.example.com/cb',
          user: {
            id: '248289761001',
            displayName: 'Jane Doe'
          },
          grant: {
            allow: true,
            scope: [ 'profile', 'email' ]
          }
        });
      });

      it('should yield authorization code', function() {
        expect(code).to.equal('SplxlOBeZQQYbYS6WxSbIA');
      });
    }); // issue

  }); // creating grant

  // TODO: createing grant with response modes

});
<commit_msg>Clean up tests.<commit_after>/* global describe, it */

var $require = require('proxyquire');
var expect = require('chai').expect;
var sinon = require('sinon');
var factory = require('../../../../com/authorize/http/response/code');

describe('http/authorize/response/code', function() {

  it('should be annotated', function() {
    expect(factory['@implements']).to.equal('http://i.authnomicon.org/oauth2/authorization/http/ResponseType');
    expect(factory['@type']).to.equal('code');
  });

  it('should create response type without response modes', function(done) {
    var container = new Object();
    container.components = sinon.stub();
    container.components.withArgs('http://i.authnomicon.org/oauth2/authorization/http/ResponseMode').returns([]);
    var acs = new Object();
    acs.issue = sinon.stub().yieldsAsync(null, 'SplxlOBeZQQYbYS6WxSbIA');

    var codeSpy = sinon.stub();
    var factory = $require('../../../../com/authorize/http/response/code', {
      'oauth2orize': {
        grant: { code: codeSpy }
      }
    });

    factory(acs, undefined, container)
      .then(function(type) {
        expect(codeSpy).to.be.calledOnce;
        expect(codeSpy).to.be.calledWith({ modes: {} });
        done();
      })
      .catch(function(error) {
        done(error);
      });
  }); // should create response type without response modes

  describe('creating grant', function() {
    var container = new Object();
    container.components = sinon.stub()
    container.components.withArgs('http://i.authnomicon.org/oauth2/authorization/http/ResponseMode').returns([]);
    var acs = new Object();
    acs.issue = sinon.stub().yieldsAsync(null, 'SplxlOBeZQQYbYS6WxSbIA');

    var codeSpy = sinon.stub();

    var factory = $require('../../../../com/authorize/http/response/code',
      { 'oauth2orize': { grant: { code: codeSpy } } });

    var grant;
    before(function(done) {
      var promise = factory(acs, null, container);
      promise.then(function(g) {
        grant = g;
        done();
      });
    });

    it('should create grant', function() {
      expect(codeSpy.callCount).to.equal(1);
      expect(codeSpy.args[0][0]).to.deep.equal({ modes: {} });
      expect(codeSpy.args[0][1]).to.be.a('function');
    });

    describe('issue', function() {
      var code;

      before(function(done) {
        var client = {
          id: 's6BhdRkqt3',
          name: 'Example Client',
          redirectURIs: [ 'https://client.example.com/cb' ]
        };
        var user = {
          id: '248289761001',
          displayName: 'Jane Doe'
        };
        var ares = {
          allow: true,
          scope: [ 'profile', 'email' ]
        }
        var areq = {
          type: 'code',
          clientID: 's6BhdRkqt3',
          redirectURI: 'https://client.example.com/cb',
          state: 'xyz'
        }

        var issue = codeSpy.args[0][1];
        issue(client, 'https://client.example.com/cb', user, ares, areq, {}, function(err, c) {
          if (err) { return done(err); }
          code = c;
          done();
        });
      });

      it('should issue authorization code', function() {
        expect(acs.issue.callCount).to.equal(1);
        expect(acs.issue.args[0][0]).to.deep.equal({
          client: {
            id: 's6BhdRkqt3',
            name: 'Example Client',
            redirectURIs: [ 'https://client.example.com/cb' ]
          },
          redirectURI: 'https://client.example.com/cb',
          user: {
            id: '248289761001',
            displayName: 'Jane Doe'
          },
          grant: {
            allow: true,
            scope: [ 'profile', 'email' ]
          }
        });
      });

      it('should yield authorization code', function() {
        expect(code).to.equal('SplxlOBeZQQYbYS6WxSbIA');
      });
    }); // issue

  }); // creating grant

  // TODO: createing grant with response modes

});
<|endoftext|>


<commit_before>// ==UserScript==
// @name        Group Discussion Links
// @namespace   https://github.com/jamesjonesmath/canvancement
// @description Create links from a group discussion that will return you back to the main course page or the discussion page
// @include     https://*.instructure.com/groups/*/discussion_topics/*
// @version     1
// @grant       none
// ==/UserScript==
(function() {
  try {
    // There are some configuration settings that you can customize
    // show_names: true will look up the names of the course and discussion for
    // the links. Setting it false will use a generic "Course > Description"
    // switch_to_discussion: true will switch to the corresponding discussion
    // when switching to a new group, false will mimic the default Canvas
    // behavior of going to the main group page

    var config = {
      'show_names' : true,
      'switch_to_discussion' : true
    };

    var pathRegex = new RegExp('^/groups/[0-9]+/discussion_topics/[0-9]+$');
    if (!pathRegex.test(document.location.pathname)) {
      return;
    }
    if (typeof ENV.DISCUSSION === 'undefined' || typeof ENV.DISCUSSION.SEQUENCE === 'undefined') {
      return;
    }
    var courseId = ENV.DISCUSSION.SEQUENCE.COURSE_ID;
    var discussionId = ENV.DISCUSSION.SEQUENCE.ASSET_ID;
    var infoElement = document.querySelector('div.discussion-subtitle + span.discussion-subtitle');
    if (!infoElement) {
      return;
    }
    var courseLink = infoElement.querySelector('a');
    courseLink.textContent = 'Course';
    infoElement.appendChild(document.createTextNode(' > '));
    var discussionLink = document.createElement('a');
    discussionLink.href = '/courses/' + courseId + '/discussion_topics/' + discussionId;
    discussionLink.textContent = 'Discussion';
    infoElement.appendChild(discussionLink);
    if (config.show_names) {
      $.ajax({
        'url' : '/api/v1/courses/' + courseId,
        'dataType' : 'json',
        'timeout' : 2000
      }).done(function(data) {
        var name = typeof data.course_code !== 'undefined' ? data.course_code : data.name;
        courseLink.textContent = name;
      });
      $.ajax({
        'url' : '/api/v1/courses/' + courseId + '/discussion_topics/' + discussionId,
        'dataType' : 'json',
        'timeout' : 2000
      }).done(function(data) {
        discussionLink.textContent = data.title;
      });
    }
    if (config.switch_to_discussion) {
      var groupSelector = 'div#left-side > ul > li a';
      var groupLinks = document.querySelectorAll(groupSelector);
      if (groupLinks.length) {
        for (var i = 0; i < groupLinks.length; i++) {
          groupLinks[i].href = groupLinks[i].href + '/discussion_topics?root_discussion_topic_id=' + discussionId;
        }
      }
    }
  } catch (e) {
    console.log(e);
  }
})();
<commit_msg>not using groupSelector anywhere else<commit_after>// ==UserScript==
// @name        Group Discussion Links
// @namespace   https://github.com/jamesjonesmath/canvancement
// @description Create links from a group discussion that will return you back to the main course page or the discussion page
// @include     https://*.instructure.com/groups/*/discussion_topics/*
// @version     1
// @grant       none
// ==/UserScript==
(function() {
  try {
    // There are some configuration settings that you can customize
    // show_names: true will look up the names of the course and discussion for
    // the links. Setting it false will use a generic "Course > Description"
    // switch_to_discussion: true will switch to the corresponding discussion
    // when switching to a new group, false will mimic the default Canvas
    // behavior of going to the main group page

    var config = {
      'show_names' : true,
      'switch_to_discussion' : true
    };

    var pathRegex = new RegExp('^/groups/[0-9]+/discussion_topics/[0-9]+$');
    if (!pathRegex.test(document.location.pathname)) {
      return;
    }
    if (typeof ENV.DISCUSSION === 'undefined' || typeof ENV.DISCUSSION.SEQUENCE === 'undefined') {
      return;
    }
    var courseId = ENV.DISCUSSION.SEQUENCE.COURSE_ID;
    var discussionId = ENV.DISCUSSION.SEQUENCE.ASSET_ID;
    var infoElement = document.querySelector('div.discussion-subtitle + span.discussion-subtitle');
    if (!infoElement) {
      return;
    }
    var courseLink = infoElement.querySelector('a');
    courseLink.textContent = 'Course';
    infoElement.appendChild(document.createTextNode(' > '));
    var discussionLink = document.createElement('a');
    discussionLink.href = '/courses/' + courseId + '/discussion_topics/' + discussionId;
    discussionLink.textContent = 'Discussion';
    infoElement.appendChild(discussionLink);
    if (config.show_names) {
      $.ajax({
        'url' : '/api/v1/courses/' + courseId,
        'dataType' : 'json',
        'timeout' : 2000
      }).done(function(data) {
        var name = typeof data.course_code !== 'undefined' ? data.course_code : data.name;
        courseLink.textContent = name;
      });
      $.ajax({
        'url' : '/api/v1/courses/' + courseId + '/discussion_topics/' + discussionId,
        'dataType' : 'json',
        'timeout' : 2000
      }).done(function(data) {
        discussionLink.textContent = data.title;
      });
    }
    if (config.switch_to_discussion) {
      var groupLinks = document.querySelectorAll('div#left-side > ul > li a');
      if (groupLinks) {
        for (var i = 0; i < groupLinks.length; i++) {
          groupLinks[i].href = groupLinks[i].href + '/discussion_topics?root_discussion_topic_id=' + discussionId;
        }
      }
    }
  } catch (e) {
    console.log(e);
  }
})();
<|endoftext|>


<commit_before>import { expect } from 'chai';

import {
  beforeEach,
  describe,
  it,
} from '@bigtest/mocha';

import FeefinesInteractor from '../interactors/feefines';
import setupApplication from '../helpers/setup-application';

describe('Manual charges', () => {
  setupApplication({ scenarios: ['templates'] });

  beforeEach(async function () {
    this.visit('/settings/users/feefinestable');
    await FeefinesInteractor.whenLoaded();
  });

  it('renders proper amount of rows', () => {
    expect(FeefinesInteractor.list.rowCount).to.equal(1);
  });

  it('renders proper amount of columns', () => {
    expect(FeefinesInteractor.list.rows(0).cellCount).to.equal(5);
  });

  it('renders proper values for the first row', () => {
    const firstRow = FeefinesInteractor.list.rows(0);

    expect(firstRow.cells(0).text).to.equal('Feefine 1');
    expect(firstRow.cells(1).text).to.equal('200.00');
    expect(firstRow.cells(2).text).to.equal('-');
    expect(firstRow.cells(3).text).to.equal('-');
  });

  describe('delete manual charge', () => {
    it('confirmation modal is not visible until delete button is clicked', () => {
      expect(FeefinesInteractor.confirmationModal.isPresent).to.be.false;
    });

    describe('is visible', () => {
      beforeEach(async () => {
        await FeefinesInteractor.list.rows(0).deleteButton.click();
      });

      it('when delete button is clicked', () => {
        expect(FeefinesInteractor.confirmationModal.isPresent).to.be.true;
      });
    });

    describe('confirmation modal disappears', () => {
      beforeEach(async () => {
        await FeefinesInteractor.list.rows(0).deleteButton.click();
        await FeefinesInteractor.confirmationModal.cancelButton.click();
      });

      it('when cancel button is clicked', () => {
        expect(FeefinesInteractor.confirmationModal.isPresent).to.be.false;
      });
    });

    describe('upon click on confirm button initiates the deletion process and in case of success', () => {
      beforeEach(async () => {
        await FeefinesInteractor.list.rows(0).deleteButton.click();
        await FeefinesInteractor.confirmationModal.confirmButton.click();
      });

      it('confirmation modal disappears', () => {
        expect(FeefinesInteractor.confirmationModal.isPresent).to.be.false;
      });

      it('the successful toast appears', () => {
        expect(FeefinesInteractor.callout.successCalloutIsPresent).to.be.true;
      });

      it('renders empty message', () => {
        expect(FeefinesInteractor.list.isPresent).to.be.false;
      });
    });
  });

  describe('cancel edit manual feefine', () => {
    beforeEach(async () => {
      await FeefinesInteractor.list.rows(0).editButton.click();
      await FeefinesInteractor.list.rows(0).feeTypeField.fillAndBlur('Changed value');
      await FeefinesInteractor.list.rows(0).defaultAmountField.fillAndBlur('300');
      await FeefinesInteractor.list.rows(0).actionNoticeField.selectAndBlur('Template 1');
      await FeefinesInteractor.list.rows(0).cancelButton.click();
    });

    it('renders proper values after cancel', () => {
      const firstRow = FeefinesInteractor.list.rows(0);

      expect(firstRow.cells(0).text).to.equal('Feefine 1');
      expect(firstRow.cells(1).text).to.equal('200.00');
      expect(firstRow.cells(3).text).to.equal('-');
    });
  });

  describe('edit manual charge', () => {
    beforeEach(async () => {
      await FeefinesInteractor.list.rows(0).editButton.click();
      await FeefinesInteractor.list.rows(0).feeTypeField.fillAndBlur('Changed value');
      await FeefinesInteractor.list.rows(0).defaultAmountField.fillAndBlur('300');
      await FeefinesInteractor.list.rows(0).actionNoticeField.selectAndBlur('Template 1');
      await FeefinesInteractor.list.rows(0).saveButton.click();
    });

    it('renders proper values after update', () => {
      const firstRow = FeefinesInteractor.list.rows(0);

      expect(firstRow.cells(0).text).to.equal('Changed value');
      expect(firstRow.cells(1).text).to.equal('300.00');
      expect(firstRow.cells(3).text).to.equal('Template 1');
    });
  });

  describe('create new manual feature', () => {
    beforeEach(async () => {
      await FeefinesInteractor.newManualFeeButton.click();
      await FeefinesInteractor.list.rows(0).feeTypeField.fillAndBlur('New feefine');
      await FeefinesInteractor.list.rows(0).defaultAmountField.fillAndBlur('100');
      await FeefinesInteractor.list.rows(0).actionNoticeField.selectAndBlur('Template 2');
      await FeefinesInteractor.list.rows(0).saveButton.click();
    });

    it('renders proper values after update', () => {
      const lastRow = FeefinesInteractor.list.rows(1);

      expect(lastRow.cells(0).text).to.equal('New feefine');
      expect(lastRow.cells(1).text).to.equal('100.00');
      expect(lastRow.cells(3).text).to.equal('Template 2');
    });

    it('renders proper amount of rows', () => {
      expect(FeefinesInteractor.list.rowCount).to.equal(2);
    });
  });
});
<commit_msg>Update data<commit_after>import { expect } from 'chai';

import {
  beforeEach,
  describe,
  it,
} from '@bigtest/mocha';

import FeefinesInteractor from '../interactors/feefines';
import setupApplication from '../helpers/setup-application';

describe('Manual charges', () => {
  setupApplication({ scenarios: ['templates'] });

  beforeEach(async function () {
    this.visit('/settings/users/feefinestable');
    await FeefinesInteractor.whenLoaded();
  });

  it('renders proper amount of rows', () => {
    expect(FeefinesInteractor.list.rowCount).to.equal(1);
  });

  it('renders proper amount of columns', () => {
    expect(FeefinesInteractor.list.rows(0).cellCount).to.equal(5);
  });

  it('renders proper values for the first row', () => {
    const firstRow = FeefinesInteractor.list.rows(0);

    expect(firstRow.cells(0).text).to.equal('Feefine 1');
    expect(firstRow.cells(1).text).to.equal('1000.00');
    expect(firstRow.cells(2).text).to.equal('-');
    expect(firstRow.cells(3).text).to.equal('-');
  });

  describe('delete manual charge', () => {
    it('confirmation modal is not visible until delete button is clicked', () => {
      expect(FeefinesInteractor.confirmationModal.isPresent).to.be.false;
    });

    describe('is visible', () => {
      beforeEach(async () => {
        await FeefinesInteractor.list.rows(0).deleteButton.click();
      });

      it('when delete button is clicked', () => {
        expect(FeefinesInteractor.confirmationModal.isPresent).to.be.true;
      });
    });

    describe('confirmation modal disappears', () => {
      beforeEach(async () => {
        await FeefinesInteractor.list.rows(0).deleteButton.click();
        await FeefinesInteractor.confirmationModal.cancelButton.click();
      });

      it('when cancel button is clicked', () => {
        expect(FeefinesInteractor.confirmationModal.isPresent).to.be.false;
      });
    });

    describe('upon click on confirm button initiates the deletion process and in case of success', () => {
      beforeEach(async () => {
        await FeefinesInteractor.list.rows(0).deleteButton.click();
        await FeefinesInteractor.confirmationModal.confirmButton.click();
      });

      it('confirmation modal disappears', () => {
        expect(FeefinesInteractor.confirmationModal.isPresent).to.be.false;
      });

      it('the successful toast appears', () => {
        expect(FeefinesInteractor.callout.successCalloutIsPresent).to.be.true;
      });

      it('renders empty message', () => {
        expect(FeefinesInteractor.list.isPresent).to.be.false;
      });
    });
  });

  describe('cancel edit manual feefine', () => {
    beforeEach(async () => {
      await FeefinesInteractor.list.rows(0).editButton.click();
      await FeefinesInteractor.list.rows(0).feeTypeField.fillAndBlur('Changed value');
      await FeefinesInteractor.list.rows(0).defaultAmountField.fillAndBlur('300');
      await FeefinesInteractor.list.rows(0).actionNoticeField.selectAndBlur('Template 1');
      await FeefinesInteractor.list.rows(0).cancelButton.click();
    });

    it('renders proper values after cancel', () => {
      const firstRow = FeefinesInteractor.list.rows(0);

      expect(firstRow.cells(0).text).to.equal('Feefine 1');
      expect(firstRow.cells(1).text).to.equal('1000.00');
      expect(firstRow.cells(3).text).to.equal('-');
    });
  });

  describe('edit manual charge', () => {
    beforeEach(async () => {
      await FeefinesInteractor.list.rows(0).editButton.click();
      await FeefinesInteractor.list.rows(0).feeTypeField.fillAndBlur('Changed value');
      await FeefinesInteractor.list.rows(0).defaultAmountField.fillAndBlur('300');
      await FeefinesInteractor.list.rows(0).actionNoticeField.selectAndBlur('Template 1');
      await FeefinesInteractor.list.rows(0).saveButton.click();
    });

    it('renders proper values after update', () => {
      const firstRow = FeefinesInteractor.list.rows(0);

      expect(firstRow.cells(0).text).to.equal('Changed value');
      expect(firstRow.cells(1).text).to.equal('300.00');
      expect(firstRow.cells(3).text).to.equal('Template 1');
    });
  });

  describe('create new manual feature', () => {
    beforeEach(async () => {
      await FeefinesInteractor.newManualFeeButton.click();
      await FeefinesInteractor.list.rows(0).feeTypeField.fillAndBlur('New feefine');
      await FeefinesInteractor.list.rows(0).defaultAmountField.fillAndBlur('100');
      await FeefinesInteractor.list.rows(0).actionNoticeField.selectAndBlur('Template 2');
      await FeefinesInteractor.list.rows(0).saveButton.click();
    });

    it('renders proper values after update', () => {
      const lastRow = FeefinesInteractor.list.rows(1);

      expect(lastRow.cells(0).text).to.equal('New feefine');
      expect(lastRow.cells(1).text).to.equal('100.00');
      expect(lastRow.cells(3).text).to.equal('Template 2');
    });

    it('renders proper amount of rows', () => {
      expect(FeefinesInteractor.list.rowCount).to.equal(2);
    });
  });
});
<|endoftext|>


<commit_before>angular.module('travel.landing', [])

.controller('LandingController', function ($scope, $window, $state, $rootScope, CurrentInfo, City, Groups, Util) {
  var data;
  $scope.data = { };

  $scope.sendData = function() {
    $window.sessionStorage.setItem('knowhere', Util.transToPermalink($scope.data.destination));
    $rootScope.currentUser = $rootScope.currentUser || "anonymous";
    $scope.data.group = $scope.data.group || "anonymous";

    Groups.createGroup({
      groupName: $scope.data.group,
      userInfo: $rootScope.currentUser,
      destination: $window.sessionStorage.getItem('knowhere')
    })
    .then(function (res) {
      if (!res) return;

      $scope.currentGroup = res.data;
    });

    $state.go('results');
  };
});
<commit_msg>(enhance) Refactor and enhance LandingController<commit_after>angular.module('travel.landing', [])

.controller('LandingController',
            function ($scope, $window, $state, $rootScope, Groups, Util) {
  $scope.data = { };

  $scope.sendData = function() {
    $window.sessionStorage.setItem('knowhere',
                                Util.transToPermalink($scope.data.destination));
    $rootScope.currentUser = $rootScope.currentUser || "anonymous";
    $scope.data.group = $scope.data.group || "anonymous";

    Groups.createGroup({
      groupName: $scope.data.group,
      userInfo: $rootScope.currentUser,
      destination: $window.sessionStorage.getItem('knowhere')
    })
    .then(function successCb (res) {
      $scope.currentGroup = res.data;
    }, function errCb (res) {
      return console.error(res);
    });

    $state.go('results');
  };
});
<|endoftext|>


<commit_before>angular.module('travel.results', ['ui.bootstrap', 'ngAnimate'])

.controller('ResultsController', function ($scope, $window, $rootScope, $state, $uibModal, CurrentInfo, Venues, Groups, Util) {
  $scope.venues = [];
  $scope.filteredVenues = [];
  $scope.city = $rootScope.destination;
  $scope.heading = null;
  $scope.groups = [];
  // Sets image carousel interval
  $scope.myInterval = 5000;
  $scope.noWrapSlides = false;
  $scope.detailedInfo = $rootScope.detailedInfo;

  ////////////////// GET ALL THE GROUPS OF A USER //////////////////////

  $scope.getUserGroups = function() {
    Groups.getUserGroups($scope);
  };

  ////////////////// SELECTING A GROUP WILL REROUTE TO RESULTS PAGE //////////////////////

  $scope.selectGroup = Groups.selectGroup(function () {
    $state.go('results');
  });

  ////////////////// FILTER FOR RESTAURANTS/ATTRACTIONS/HOTELS //////////////////////

  $scope.filterVenues = function (venueTypeId) {
    // set heading to appropriate value
    Util.setHeading($scope, venueTypeId);

    // populate venues with appropriate results
<<<<<<< HEAD
    $scope.filteredVenues = Util.filterVenues($scope.venues, venueTypeId);
=======
    $scope.venues.forEach( function(venue) {
      if (venue.venue_type_id === filterType) {
        venues.push(venue);
      }
    });
    $scope.filteredVenues = venues;
>>>>>>> (feat) basic groups template
  };

  ////////////////// GET ALL VENUES BASED ON A DESTINATION CITY //////////////////////

  $scope.getVenuesOfDestination = function (destinationId) {
    if (!destinationId) return;

    var query = {
      destinationId: destinationId
    };
    Venues.getVenues(query)
      .then(function(venuesInfo) {
        if (!Array.isArray(venuesInfo)) return;

        CurrentInfo.destination.venues = $scope.venues = venuesInfo;
        $scope.filterVenues(1);
      })
      .catch(function(error){
        console.error(error);
      });
  };

  ////////////////// GET DETAILED INFO OF A VENUE //////////////////////

  $scope.getDetailedVenueInfo = function(venueId) {
    var query = {
      venueId : venueId
    };
    Venues.getDetailedVenueInfo(query)
      .then(function(venueInfo) {
        $rootScope.detailedInfo = $scope.detailedInfo = venueInfo;
        $scope.openModal();
      })
      .catch(function(error){
        console.error(error);
      });
  };
  $scope.exit = function(){
    $uibModalInstance.close();
  };
  $scope.openModal = function() {
    var modalInstance = $uibModal.open({
      animation: $scope.animationsEnabled,
      templateUrl: 'app/results/venueInfo.html',
      controller: 'ResultsController',
    });
  };

  ////////////////// ADD TO RESULT LIST //////////////////////

  $scope.addToRatings = function(venueData) {
    var data = {
      venue: venueData,
      userId : $rootScope.currentUser._id,
      groupId : $rootScope.currentGroup._id,
      rating: 5
    };
    console.log(data);
    Venues.addRating(data);
  };

  $scope.addToRatingsMain = function(venueId) {
    var query = {
      venueId : venueId
    };
    Venues.getDetailedVenueInfo(query)
      .then(function(venueInfo) {
        var data = {
          venue: venueInfo,
          userId : $rootScope.currentUser._id,
          groupId : $rootScope.currentGroup._id,
          rating: 5
        };
        Venues.addRating(data);
      })
      .catch(function(error){
        console.error(error);
      });
  };

  ////////////////// INIT STATE //////////////////////

  $scope.getUserGroups();
  $scope.getVenuesOfDestination($rootScope.destination.id);
});
<commit_msg>(fix) merge conflicts<commit_after>angular.module('travel.results', ['ui.bootstrap', 'ngAnimate'])

.controller('ResultsController', function ($scope, $window, $rootScope, $state, $uibModal, CurrentInfo, Venues, Groups, Util) {
  $scope.venues = [];
  $scope.filteredVenues = [];
  $scope.city = $rootScope.destination;
  $scope.heading = null;
  $scope.groups = [];
  // Sets image carousel interval
  $scope.myInterval = 5000;
  $scope.noWrapSlides = false;
  $scope.detailedInfo = $rootScope.detailedInfo;

  ////////////////// GET ALL THE GROUPS OF A USER //////////////////////

  $scope.getUserGroups = function() {
    Groups.getUserGroups($scope);
  };

  ////////////////// SELECTING A GROUP WILL REROUTE TO RESULTS PAGE //////////////////////

  $scope.selectGroup = Groups.selectGroup(function () {
    $state.go('results');
  });

  ////////////////// FILTER FOR RESTAURANTS/ATTRACTIONS/HOTELS //////////////////////

  $scope.filterVenues = function (venueTypeId) {
    // set heading to appropriate value
    Util.setHeading($scope, venueTypeId);

    // populate venues with appropriate results
    $scope.filteredVenues = Util.filterVenues($scope.venues, venueTypeId);
  };

  ////////////////// GET ALL VENUES BASED ON A DESTINATION CITY //////////////////////

  $scope.getVenuesOfDestination = function (destinationId) {
    if (!destinationId) return;

    var query = {
      destinationId: destinationId
    };
    Venues.getVenues(query)
      .then(function(venuesInfo) {
        if (!Array.isArray(venuesInfo)) return;

        CurrentInfo.destination.venues = $scope.venues = venuesInfo;
        $scope.filterVenues(1);
      })
      .catch(function(error){
        console.error(error);
      });
  };

  ////////////////// GET DETAILED INFO OF A VENUE //////////////////////

  $scope.getDetailedVenueInfo = function(venueId) {
    var query = {
      venueId : venueId
    };
    Venues.getDetailedVenueInfo(query)
      .then(function(venueInfo) {
        $rootScope.detailedInfo = $scope.detailedInfo = venueInfo;
        $scope.openModal();
      })
      .catch(function(error){
        console.error(error);
      });
  };
  $scope.exit = function(){
    $uibModalInstance.close();
  };
  $scope.openModal = function() {
    var modalInstance = $uibModal.open({
      animation: $scope.animationsEnabled,
      templateUrl: 'app/results/venueInfo.html',
      controller: 'ResultsController',
    });
  };

  ////////////////// ADD TO RESULT LIST //////////////////////

  $scope.addToRatings = function(venueData) {
    var data = {
      venue: venueData,
      userId : $rootScope.currentUser._id,
      groupId : $rootScope.currentGroup._id,
      rating: 5
    };
    console.log(data);
    Venues.addRating(data);
  };

  $scope.addToRatingsMain = function(venueId) {
    var query = {
      venueId : venueId
    };
    Venues.getDetailedVenueInfo(query)
      .then(function(venueInfo) {
        var data = {
          venue: venueInfo,
          userId : $rootScope.currentUser._id,
          groupId : $rootScope.currentGroup._id,
          rating: 5
        };
        Venues.addRating(data);
      })
      .catch(function(error){
        console.error(error);
      });
  };

  ////////////////// INIT STATE //////////////////////

  $scope.getUserGroups();
  $scope.getVenuesOfDestination($rootScope.destination.id);
});
<|endoftext|>


<commit_before>//make a function that checks if user
//then if that user belongs to this trip or vice versa
//if both are true then render edit/view button, and tool bar, otherwise don't
//set permissions to anyone can see but only owners can edit

import React, { Component } from 'react'
import Canvas from './Canvas'
import { Provider } from 'react-redux'
import { database, auth } from 'APP/db/firebase'
import store from 'APP/app/store'
import ToolBox from './ToolBox'
import { Grid, Col, Button } from 'react-bootstrap'

export default class CanvasContainer extends Component {
  constructor (props) {
    super(props)

    this.state = {
      selected: null,
      store: null,
      editable: false,
      userId: null,
      canEdit: false,
    }

    this.selectElement = this.selectElement.bind(this)
    this.toggleMode = this.toggleMode.bind(this)
    this.renderView = this.renderView.bind(this)
    this.renderEditButton = this.renderEditButton.bind(this)
    this.clearSelectedIfDeleted = this.clearSelectedIfDeleted.bind(this)
  }

  toggleMode() {
    this.setState({
      editable: !this.state.editable,
    })
  }

  //this function is called from inside page when we move an element
  selectElement (type, id) {
    this.setState({
      selected: {id: id, type: type}
    })
  }

  //this function gets passed down to Page so that selected is cleared before delete
  //otherwise there is a bug when you delete the currently selected element
  clearSelectedIfDeleted (type, id) {
    const selected = this.state.selected
    console.log('CLEARING SELECTED IF DELETED')
    if (type === selected.type && id === selected.id) {
      console.log('CLEARING SELECTED')
      this.setState({ selected: null })
    }
  }

  //when this component mounts, figure out the firebase path from params
  componentWillMount () {
    const tripId = this.props.params.tripId
    const tripActionsRef = database.ref(`tripActions/${tripId}`)
    const tripUsersRef = database.ref(`tripUsers/${tripId}`)

    //replays the actions from the Firebase db to get to 'current state'
    this.setState({
      store: store(tripActionsRef),
      tripInfoRef: database.ref(`tripInfo/${tripId}`)
    }, () => {
      this.state.tripInfoRef.on('value', (snap) => this.setState({
          tripInfo: snap.val()
        }))
    })

    //sets a current user listener from Firebase auth
    this.unsubscribe = auth.onAuthStateChanged((user) => {
      if (user) {
        //set user id on state for rendring the edit/view button
        this.setState({
          userId: user.uid,
        })
        //get the object of users that belong to this trip from Firebase db
        tripUsersRef.on('value', (snap) => {
          let tripUsers = snap.val()
          //get an array of values from the tripUsers object
          let isCollaborator = Object.values(tripUsers).includes(user.uid)
          //if current user is in the array, then they can edit, else they can't
          this.setState({
            editable: isCollaborator,
            canEdit: isCollaborator,
          })
        })
      } else { //if there is no user or then they can't edit
        this.setState({
          userId: null,
          editable: false,
          canEdit: false,
        })
      }
    })
  }

  componentWillUnmount () {
    //add cleanup from auth.userChange listener
    this.unsubscribe()
  }

  renderView() {
    return this.state.editable ?
    //render this if editable is true
        <Col lg={2}>
          <ToolBox
            tripInfo={this.state.tripInfo}
            tripInfoRef={this.state.tripInfoRef}
            selected={this.state.selected}
            tripId={this.props.params.tripId}
          />
        </Col>

    //render this if editable is false
    : null
  }

  renderEditButton() {
    return this.state.canEdit ?
                      (<Button onClick={this.toggleMode}>
                        {this.state.editable ? "View" : "Edit" }
                      </Button>)
                     : null
  }

  render () {
    if (!this.state) return null
    let tripInfo = this.state.tripInfo || null
    return (

        {this.renderEditButton()}
        {
          tripInfo ?

{`${tripInfo.name}, ${tripInfo.description}, ${tripInfo.startDate}`}

          : null
        }
        <Provider store={this.state.store}>
          <Grid id="canvas-wrapper">
            {this.renderView()}
            <Col lg={10}>
              <Canvas editable={this.state.editable}
                      selectElement={this.selectElement}
                      clearSelectedIfDeleted={this.clearSelectedIfDeleted} />
            </Col>
          </Grid>
        </Provider>

    )
  }
}
<commit_msg>Update CanvasContainer.js<commit_after>//make a function that checks if user
//then if that user belongs to this trip or vice versa
//if both are true then render edit/view button, and tool bar, otherwise don't
//set permissions to anyone can see but only owners can edit

import React, { Component } from 'react'
import Canvas from './Canvas'
import { Provider } from 'react-redux'
import { database, auth } from 'APP/db/firebase'
import store from 'APP/app/store'
import ToolBox from './ToolBox'
import { Grid, Col, Button } from 'react-bootstrap'

export default class CanvasContainer extends Component {
  constructor (props) {
    super(props)

    this.state = {
      selected: null,
      store: null,
      editable: false,
      userId: null,
      canEdit: false,
    }

    this.selectElement = this.selectElement.bind(this)
    this.toggleMode = this.toggleMode.bind(this)
    this.renderView = this.renderView.bind(this)
    this.renderEditButton = this.renderEditButton.bind(this)
    this.clearSelectedIfDeleted = this.clearSelectedIfDeleted.bind(this)
  }

  toggleMode() {
    this.setState({
      editable: !this.state.editable,
    })
  }

  //this function is called from inside page when we move an element
  selectElement (type, id) {
    this.setState({
      selected: {id: id, type: type}
    })
  }

  //this function gets passed down to Page so that selected is cleared before delete
  //otherwise there is a bug when you delete the currently selected element
  clearSelectedIfDeleted (type, id) {
    const selected = this.state.selected
    if (type === selected.type && id === selected.id) {
      this.setState({ selected: null })
    }
  }

  //when this component mounts, figure out the firebase path from params
  componentWillMount () {
    const tripId = this.props.params.tripId
    const tripActionsRef = database.ref(`tripActions/${tripId}`)
    const tripUsersRef = database.ref(`tripUsers/${tripId}`)

    //replays the actions from the Firebase db to get to 'current state'
    this.setState({
      store: store(tripActionsRef),
      tripInfoRef: database.ref(`tripInfo/${tripId}`)
    }, () => {
      this.state.tripInfoRef.on('value', (snap) => this.setState({
          tripInfo: snap.val()
        }))
    })

    //sets a current user listener from Firebase auth
    this.unsubscribe = auth.onAuthStateChanged((user) => {
      if (user) {
        //set user id on state for rendring the edit/view button
        this.setState({
          userId: user.uid,
        })
        //get the object of users that belong to this trip from Firebase db
        tripUsersRef.on('value', (snap) => {
          let tripUsers = snap.val()
          //get an array of values from the tripUsers object
          let isCollaborator = Object.values(tripUsers).includes(user.uid)
          //if current user is in the array, then they can edit, else they can't
          this.setState({
            editable: isCollaborator,
            canEdit: isCollaborator,
          })
        })
      } else { //if there is no user or then they can't edit
        this.setState({
          userId: null,
          editable: false,
          canEdit: false,
        })
      }
    })
  }

  componentWillUnmount () {
    //add cleanup from auth.userChange listener
    this.unsubscribe()
  }

  renderView() {
    return this.state.editable ?
    //render this if editable is true
        <Col lg={2}>
          <ToolBox
            tripInfo={this.state.tripInfo}
            tripInfoRef={this.state.tripInfoRef}
            selected={this.state.selected}
            tripId={this.props.params.tripId}
          />
        </Col>

    //render this if editable is false
    : null
  }

  renderEditButton() {
    return this.state.canEdit ?
                      (<Button onClick={this.toggleMode}>
                        {this.state.editable ? "View" : "Edit" }
                      </Button>)
                     : null
  }

  render () {
    if (!this.state) return null
    let tripInfo = this.state.tripInfo || null
    return (

        {this.renderEditButton()}
        {
          tripInfo ?

{`${tripInfo.name}, ${tripInfo.description}, ${tripInfo.startDate}`}

          : null
        }
        <Provider store={this.state.store}>
          <Grid id="canvas-wrapper">
            {this.renderView()}
            <Col lg={10}>
              <Canvas editable={this.state.editable}
                      selectElement={this.selectElement}
                      clearSelectedIfDeleted={this.clearSelectedIfDeleted} />
            </Col>
          </Grid>
        </Provider>

    )
  }
}
<|endoftext|>


<commit_before>import React, { useState, useCallback } from 'react';
import { Header, Image, Modal } from 'semantic-ui-react';
import { useTranslation } from 'react-i18next';

import HelpButton from '../HelpButton';
import { agplDisclaimer } from './const';

import logoImg from '../../../resources/media/logo_full_light.png';
import styles from './styles.scss';

const HelpModal = () => {
  const [isOpen, setIsOpen] = useState(false);
  const handleOpen = useCallback(() => setIsOpen(true), []);
  const handleClose = useCallback(() => setIsOpen(false), []);
  const { t } = useTranslation('help');

  return (
    <Modal
      open={isOpen}
      onClose={handleClose}
      basic
      centered
      dimmer='blurring'
      trigger={<HelpButton onClick={handleOpen} />}
      className={styles.help_modal}
    >
      <Modal.Header>{t('about')}</Modal.Header>
      <Modal.Content image>
        <Image wrapped size='medium' src={logoImg} />
        <Modal.Description>

            {t('header')}

            Copyright © nukeop 2019,
            {t('released')}

            {t('thanks')}

        </Modal.Description>
      </Modal.Content>
      <Modal.Content>
        <Modal.Description>{agplDisclaimer}</Modal.Description>
      </Modal.Content>
    </Modal>
  );
};

export default HelpModal;
<commit_msg>Open author link in external browser, fixes #406<commit_after>import React, { useState, useCallback } from 'react';
import electron from 'electron';
import { Header, Image, Modal } from 'semantic-ui-react';
import { useTranslation } from 'react-i18next';

import HelpButton from '../HelpButton';
import { agplDisclaimer } from './const';

import logoImg from '../../../resources/media/logo_full_light.png';
import styles from './styles.scss';

const handleAuthorClick = () => {
  electron.shell.openExternal('https://github.com/nukeop');
};

const HelpModal = () => {
  const [isOpen, setIsOpen] = useState(false);
  const handleOpen = useCallback(() => setIsOpen(true), []);
  const handleClose = useCallback(() => setIsOpen(false), []);
  const { t } = useTranslation('help');

  return (
    <Modal
      open={isOpen}
      onClose={handleClose}
      basic
      centered
      dimmer='blurring'
      trigger={<HelpButton onClick={handleOpen} />}
      className={styles.help_modal}
    >
      <Modal.Header>{t('about')}</Modal.Header>
      <Modal.Content image>
        <Image wrapped size='medium' src={logoImg} />
        <Modal.Description>

            {t('header')}

            Copyright © nukeop 2019,
            {t('released')}

            {t('thanks')}

        </Modal.Description>
      </Modal.Content>
      <Modal.Content>
        <Modal.Description>{agplDisclaimer}</Modal.Description>
      </Modal.Content>
    </Modal>
  );
};

export default HelpModal;
<|endoftext|>


<commit_before>// @flow
import React, {Component} from 'react'
import {findDOMNode} from 'react-dom'
import styles from './Infinite.css'

type Props = {
	rootMargin?: string,
	onIntersect: () => void,
	children?: any,
	style?: Object
};

export default class Infinite extends Component {
	props: Props;
	sentinel: Component<*, *, *>;
	root: Component<*, *, *>;
	io: Object;

	componentDidMount() {
		const sentinel = this.sentinel
		this.io = new IntersectionObserver(entries => { // eslint-disable-line no-undef
			if (entries[0].intersectionRatio <= 0) {
				return
			}
			requestAnimationFrame(() => {
				this.handleOnIntersect()
			})
		}, {
			root: this.root,
			rootMargin: this.props.rootMargin || '1000px',
		})
		this.io.observe(findDOMNode(sentinel))
	}

	handleOnIntersect() {
		this.props.onIntersect()
	}

	handleRefs = (c: Component<*, *, *>) => {
		this.sentinel = c
	}

	handleRootRefs = (c: Component<*, *, *>) => {
		this.root = c
		console.log(this.root)
	}

	render() {
		return (
			<div
				ref={this.handleRootRefs}
				style={this.props.style}
				className={styles.base}
				>
				{this.props.children}
				<div ref={this.handleRefs} style={{height: 100}}/>
			</div>
		)
	}
}
<commit_msg>Refactor Infinite<commit_after>// @flow
import React, {Component} from 'react'
import {findDOMNode} from 'react-dom'
import styles from './Infinite.css'

type Props = {
	rootMargin?: string,
	onIntersect: () => void,
	children?: any,
	style?: Object
};

export default class Infinite extends Component {
	props: Props;
	sentinel: Component<*, *, *>;
	root: Component<*, *, *>;
	io: Object;

	componentDidMount() {
		const sentinel = this.sentinel
		this.io = new IntersectionObserver(entries => { // eslint-disable-line no-undef
			if (entries[0].intersectionRatio <= 0) {
				return
			}
			requestAnimationFrame(() => {
				this.handleOnIntersect()
			})
		}, {
			root: this.root,
			rootMargin: this.props.rootMargin || '1000px',
		})
		this.io.observe(findDOMNode(sentinel))
	}

	handleOnIntersect() {
		this.props.onIntersect()
	}

	render() {
		const {style, children} = this.props
		return (
			<div
				ref={c => { // eslint-disable-line react/jsx-no-bind
					this.root = c
				}}
				style={style}
				className={styles.base}
				>
				{children}
				<div
					ref={c => { // eslint-disable-line react/jsx-no-bind
						this.sentinel = c
					}}
					style={{height: 100}}
					/>
			</div>
		)
	}
}
<|endoftext|>


<commit_before>import Ember from "ember";

let scrollTop = 0;
let handler = function(e) {
  window.scrollTo(0, scrollTop);
  window.removeEventListener('scroll', handler);
};

let FocusingOutlet = Ember.Component.extend({
  _routing: Ember.inject.service('-routing'),
  positionalParams: ['inputOutletName'], // needed for Ember 1.13.[0-5] and 2.0.0-beta.[1-3] support
  tagName: 'div',
  classNames: ['focusing-outlet'],

  shouldFocus: false,

  didReceiveAttrs() {
    this._super(...arguments);
    this.set('outletName', this.attrs.inputOutletName || 'main');
  },

  didInsertElement() {
    this._super(...arguments);
    this.setFocus();
  },

  setFocus() {
    if (!this.element) { return; }

    let shouldFocus = this.get('shouldFocus');

    if (shouldFocus) {
      // One shouldn't set an attribute when they mean to set a property.
      // Except when a property is only settable if the attribute is present.
      // We have to make the element interactive prior to focusing it.
      this.element.setAttribute('tabindex', '-1');

      // If we don't do this, the scroll triggered by the focus will be unfortunate.
      // This effectively swallows one scroll event.
      scrollTop = document.body.scrollTop;
      window.addEventListener('scroll', handler);

      // Set the focus to the target outlet wrapper.
      this.element.focus();
    } else {
      this.element.removeAttribute('tabindex');
    }
  },

  actions: {
    checkFocus(outletState) {
      let outletName = this.get('outletName');

      let pivotHandler = Ember.getOwner(this)._stashedHandlerInfos.pivotHandler.handler.routeName;
      let currentRoute = outletState[outletName].render.name;

      let shouldFocus = (pivotHandler === currentRoute);
      this.set('shouldFocus', shouldFocus);

      this.setFocus();
    }
  }
});

FocusingOutlet.reopenClass({
  positionalParams: ['inputOutletName']
});

export default FocusingOutlet;
<commit_msg>NVDA was snagging a reference to the previous element contents. Delay setting focus.<commit_after>import Ember from "ember";

let scrollTop = 0;
let handler = function(e) {
  window.scrollTo(0, scrollTop);
  window.removeEventListener('scroll', handler);
};

let FocusingOutlet = Ember.Component.extend({
  _routing: Ember.inject.service('-routing'),
  positionalParams: ['inputOutletName'], // needed for Ember 1.13.[0-5] and 2.0.0-beta.[1-3] support
  tagName: 'div',
  classNames: ['focusing-outlet'],

  shouldFocus: false,

  didReceiveAttrs() {
    this._super(...arguments);
    this.set('outletName', this.attrs.inputOutletName || 'main');
  },

  didInsertElement() {
    this._super(...arguments);
    this.setFocus();
  },

  setFocus() {
    if (!this.element) { return; }

    let shouldFocus = this.get('shouldFocus');

    if (shouldFocus) {
      // One shouldn't set an attribute when they mean to set a property.
      // Except when a property is only settable if the attribute is present.
      // We have to make the element interactive prior to focusing it.
      this.element.setAttribute('tabindex', '-1');

      // If we don't do this, the scroll triggered by the focus will be unfortunate.
      // This effectively swallows one scroll event.
      scrollTop = document.body.scrollTop;
      window.addEventListener('scroll', handler);

      // Set the focus to the target outlet wrapper.
      Ember.run.schedule('afterRender', this, function() { this.element.focus(); });
    } else {
      this.element.removeAttribute('tabindex');
    }
  },

  actions: {
    checkFocus(outletState) {
      let outletName = this.get('outletName');

      let pivotHandler = Ember.getOwner(this)._stashedHandlerInfos.pivotHandler.handler.routeName;
      let currentRoute = outletState[outletName].render.name;

      let shouldFocus = (pivotHandler === currentRoute);
      this.set('shouldFocus', shouldFocus);

      this.setFocus();
    }
  }
});

FocusingOutlet.reopenClass({
  positionalParams: ['inputOutletName']
});

export default FocusingOutlet;
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Component.extend({
  actions: {
    toggleHelp: function () {
      this.toggleProperty('showHelp');
    },
    acknowledgeNoDataModal: function () {
      this.model.set('statement.noData', false);
      this.set('showNotReadyMessage', false);
    },

    nextStep: function () {
      if (this.isReady()) {
        this.sendAction();
      }
      else {
        this.set('showNotReadyMessage', true);
      }
    }
  },
  isReady: function () {

    //if any of this array is true, then we're ready to move forward
    var options = ['identityData', 'locationData', 'userBehaviourData', 'computerNetworkData', 'financialData', 'otherData'];
    for (var i = options.length - 1; i >= 0; i--) {
      if (this.get('model.statement.' + options[i])) {
        return true;
      }
    }
    return false;
  }

});
<commit_msg>more comments<commit_after>import Ember from 'ember';

export default Ember.Component.extend({
  actions: {
    toggleHelp: function () {
      this.toggleProperty('showHelp');
    },
    acknowledgeNoDataModal: function () {
      this.model.set('statement.noData', false);
      this.set('showNotReadyMessage', false);
    },

    nextStep: function () {
      //check is we are ready to move forward
      if (this.isReady()) {
        this.sendAction();
      }
      else {
        //we're not ready, so show the model message
        this.set('showNotReadyMessage', true);
      }
    }
  },
  isReady: function () {
    /**
    Check if our form is filled out
    */

    //if any of this array is true, then we're ready to move forward
    var options = ['identityData', 'locationData', 'userBehaviourData', 'computerNetworkData', 'financialData', 'otherData'];
    for (var i = options.length - 1; i >= 0; i--) {
      if (this.get('model.statement.' + options[i])) {
        return true;
      }
    }
    return false;
  }

});
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Component.extend({
  actions: {
    nextStep: function () {
      //check is we are ready to move forward
      if (this.isReady()) {
        this.sendAction();
      }
      else {
        //we're not ready, so show the model message
        this.set('showNotReadyMessage', true);
      }
    }
  },
  isReady: function () {
    /**
    Check if our form is filled out
    */

    //if any of this array is true, then we're ready to move forward
    var options = ['identityData', 'locationData', 'userBehaviourData', 'computerNetworkData', 'financialData', 'otherData'];
    for (var i = options.length - 1; i >= 0; i--) {
      if (this.get('model.statement.' + options[i])) {
        return true;
      }
    }
    return false;
  }

});
<commit_msg>use array.any, instead of a for loop.<commit_after>import Ember from 'ember';

export default Ember.Component.extend({
  actions: {
    nextStep: function () {
      //check is we are ready to move forward
      if (this.isReady()) {
        this.sendAction();
      }
      else {
        //we're not ready, so show the model message
        this.set('showNotReadyMessage', true);
      }
    }
  },
  isReady: function () {
    /**
    Check if our form is filled out
    */
    //if any of this array is true, then we're ready to move forward
    var options = ['identityData', 'locationData', 'userBehaviourData', 'computerNetworkData', 'financialData', 'otherData'];
    var self = this;

    return options.any(function(option) {
      if (self.get('model.statement.' + option)) {
        return true;
      }
    });
  }

});

<|endoftext|>


<commit_before>export default {
    name: 'demoJavascript',
    data() {
        return {
            msg: 'demoJavascript : javascript component' + abp.services.app.demo1Service()
        }
    }
}<commit_msg>minor fix<commit_after>export default {
    name: 'demoJavascript',
    data() {
        return {
            msg: 'demoJavascript : javascript component: ' + abp.services.app.demo1Service.getMyGreeting()
        }
    }
}<|endoftext|>


<commit_before>/**
* NOTE: GATracker This is a workaround due to react-ga depending on onUpdate in redux-router before React v16
* Should possibly be replaced when react-ga has updated their package.
* Initializes Google analytics and sends a tracker on page view changes.
*/
import React from 'react';
import PropTypes from 'prop-types';
import GoogleAnalytics from 'react-ga';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { makeSelectPathname } from 'containers/App/selectors';
import TRACKING_ID from './trackingId';

export class GATracker extends React.Component {
  componentDidMount() {
    GoogleAnalytics.initialize(TRACKING_ID, { debug: false });
    this.track(this.props.pathname);
  }

  componentWillReceiveProps(nextProps) {
    const currentPath = this.props.pathname;
    const nextPath = nextProps.pathname;

    if (currentPath !== nextPath) {
      this.track(nextPath);
    }
  }

  track(pathname) {
    GoogleAnalytics.set({ pathname });
    GoogleAnalytics.pageview(pathname);
  }

  render() {
    return null;
  }
}

GATracker.propTypes = {
  pathname: PropTypes.string.isRequired,
};

const mapStateToProps = createStructuredSelector({
  pathname: makeSelectPathname(),
});

export default connect(mapStateToProps, null)(GATracker);
<commit_msg>activating debug mode<commit_after>/**
* NOTE: GATracker This is a workaround due to react-ga depending on onUpdate in redux-router before React v16
* Should possibly be replaced when react-ga has updated their package.
* Initializes Google analytics and sends a tracker on page view changes.
*/
import React from 'react';
import PropTypes from 'prop-types';
import GoogleAnalytics from 'react-ga';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { makeSelectPathname } from 'containers/App/selectors';
import TRACKING_ID from './trackingId';

export class GATracker extends React.Component {
  componentDidMount() {
    GoogleAnalytics.initialize(TRACKING_ID, { debug: true });
    this.track(this.props.pathname);
  }

  componentWillReceiveProps(nextProps) {
    const currentPath = this.props.pathname;
    const nextPath = nextProps.pathname;

    if (currentPath !== nextPath) {
      this.track(nextPath);
    }
  }

  track(pathname) {
    GoogleAnalytics.set({ pathname });
    GoogleAnalytics.pageview(pathname);
  }

  render() {
    return null;
  }
}

GATracker.propTypes = {
  pathname: PropTypes.string.isRequired,
};

const mapStateToProps = createStructuredSelector({
  pathname: makeSelectPathname(),
});

export default connect(mapStateToProps, null)(GATracker);
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict'

const join = require('path').join
const minimist = require('minimist')
const GitAddAndCommit = require(join(__dirname, '../'))

//
// Set things up for minimist
//
let booleans = {
	c: 'caseSensitive',
	h: 'help',
	i: 'interactive'
}
let needArgs = {
	e: 'except',
	o: 'only'
}
const CLI_OPTIONS = Object.assign({}, booleans, needArgs)

let argv = minimist(process.argv.slice(2), {
	alias: CLI_OPTIONS,
	boolean: Object.keys(booleans).map(key => booleans[key])
})

//
// Setup options object
//
let options = {}
setupOptions(options)
parseFileTypesToFind(options)
// options.args = argv._.slice()
new GitAddAndCommit(argv._.slice(), options)

//
// Helpers
//
function setupOptions(options) {
	for (let option of Object.keys(booleans).map(key => booleans[key])) {
		options[option] = argv[option]
	}
}

function parseFileTypesToFind(options) {
	let _letterToFileMap = {
		d: 'deleted',
		m: 'modified',
		s: 'staged',
		u: 'untracked'
	}

	// defaults
	let find = {
		deleted: true,
		modified: true,
		staged: true,
		untracked: true
	}

	for (let file of argv.except && argv.except.split(',') || []) { // that's a cool line
		delete find[_letterToFileMap[file[0] && file[0].toLowerCase()]] // another cool line
	}

	// need to delete all find files if `argv.only` is present
	find = argv.only ? {} : find

	for (let file of argv.only && argv.only.split(',') || []) {
		find[_letterToFileMap[file[0].toLowerCase()]] = true
	}

	options.find = Object.keys(find)
}
<commit_msg>Unused line<commit_after>#!/usr/bin/env node
'use strict'

const join = require('path').join
const minimist = require('minimist')
const GitAddAndCommit = require(join(__dirname, '../'))

//
// Set things up for minimist
//
let booleans = {
	c: 'caseSensitive',
	h: 'help',
	i: 'interactive'
}
let needArgs = {
	e: 'except',
	o: 'only'
}
const CLI_OPTIONS = Object.assign({}, booleans, needArgs)

let argv = minimist(process.argv.slice(2), {
	alias: CLI_OPTIONS,
	boolean: Object.keys(booleans).map(key => booleans[key])
})

//
// Setup options object
//
let options = {}
setupOptions(options)
parseFileTypesToFind(options)
new GitAddAndCommit(argv._.slice(), options)

//
// Helpers
//
function setupOptions(options) {
	for (let option of Object.keys(booleans).map(key => booleans[key])) {
		options[option] = argv[option]
	}
}

function parseFileTypesToFind(options) {
	let _letterToFileMap = {
		d: 'deleted',
		m: 'modified',
		s: 'staged',
		u: 'untracked'
	}

	// defaults
	let find = {
		deleted: true,
		modified: true,
		staged: true,
		untracked: true
	}

	for (let file of argv.except && argv.except.split(',') || []) { // that's a cool line
		delete find[_letterToFileMap[file[0] && file[0].toLowerCase()]] // another cool line
	}

	// need to delete all find files if `argv.only` is present
	find = argv.only ? {} : find

	for (let file of argv.only && argv.only.split(',') || []) {
		find[_letterToFileMap[file[0].toLowerCase()]] = true
	}

	options.find = Object.keys(find)
}
<|endoftext|>


<commit_before>export const styles = () => ({
  bold: {
    fontWeight: 'bold',
  },
  welcomeHeader: {
    marginTop: 0,
    marginBottom: '5%',
  },
  centeredSection: {
    marginLeft: '10%',
    marginRight: '10%',
  },
  truncated: {
    marginRight: '10%',
    textAlign: 'justify',
    textOverflow: 'ellipsis',
    overflow: 'hidden',
    maxHeight: '120px',
  },
  expanded: {
    marginRight: '10%',
    textAlign: 'justify',
  },
  textLink: {
    textDecoration: 'none',
    textColor: 'black',
    color: 'black',
  },
  banner: {
    marginTop: 50,
    width: '100%',
  },
  welcome: {
    marginRight: '10%',
    textAlign: 'left',
  },
  homePaper: {
    backgroundColor: '#eeeeee',
    cornerRadius: 40,
    padding: 10,
    paddingTop: 5,
    minWidth: 280,
    maxWidth: 300,
    textAlign: 'left',
    align: 'left',
  },
  paperInfo: {
    minHeight: 30,
    marginBottom: 10,
    color: 'black',
    textColor: 'black',
  },

});
<commit_msg>Fixed button styling<commit_after>export const styles = () => ({
  bold: {
    fontWeight: 'bold',
  },
  buttonLink: {
    textTransformation: 'none',
    textDecoration: 'none',
    fontColor: 'white',
    color: 'white',
    fontWeight: 'bold',

  },
  welcomeHeader: {
    marginTop: 0,
    marginBottom: '5%',
  },
  centeredSection: {
    marginLeft: '10%',
    marginRight: '10%',
  },
  truncated: {
    marginRight: '10%',
    textAlign: 'justify',
    textOverflow: 'ellipsis',
    overflow: 'hidden',
    maxHeight: '120px',
  },
  expanded: {
    marginRight: '10%',
    textAlign: 'justify',
  },
  textLink: {
    textDecoration: 'none',
    textColor: 'black',
    color: 'black',
  },
  banner: {
    marginTop: 50,
    width: '100%',
  },
  welcome: {
    marginRight: '10%',
    textAlign: 'left',
  },
  homePaper: {
    backgroundColor: '#eeeeee',
    cornerRadius: 40,
    padding: 10,
    paddingTop: 5,
    minWidth: 280,
    maxWidth: 300,
    textAlign: 'left',
    align: 'left',
  },
  paperInfo: {
    minHeight: 30,
    marginBottom: 10,
    color: 'black',
    textColor: 'black',
  },

});
<|endoftext|>