<commit_before>var net = require('net');
var fs = require('fs');
var NeplWholeMeta = require('./NeplWholeMeta');
var NeplTXEntry = require('./NeplTXEntry.js');

function NeplReader(options){
    var self = this;

    this.volume  = options.volume     ? options.volume     : __dirname + 'txvol';
    this.meta    = options.meta       ? options.meta       : __dirname + 'meta';
    this.name    = options.name       ? options.name       : 'nepl_reader';

    if( !options.consumer || typeof options.consumer !== 'function'){
        throw new Error('invalid consumer');
    }
    else{
        this.consumer = options.consumer;
    }

   self.initLogFiles(self, this.volume, this.meta, this.name);
}

NeplReader.prototype.doConsumer = function(curr,prev){
    var self = this;
    var volumeName = self.wholeMeta.volumeName;
    var currentByte = curr.size;
    var previousByte = prev.size;
    var writtenByte = currentByte - previousByte - 1;
    var tx = new Buffer(writtenByte);
    fs.open(self.volume, 'r', function(err, fd){
        var len = fs.readSync(fd, tx, 0, writtenByte, previousByte);
        var txEntry = new NeplTXEntry();
        txEntry.parse(tx.toString());
        self.consumer(txEntry);
        self.wholeMeta.ownMetas[self.name]['lastVolTxnCnt']++;
        var byteSum = parseInt(self.wholeMeta.ownMetas[self.name]['lastVolByteCnt']) + len;
        self.wholeMeta.ownMetas[self.name]['lastVolByteCnt'] = byteSum + '';
        self.wholeMeta.updateMeta(self.meta);
    });
}

/* Initialization of volume file
 *  In this function, only task is make volume file, if there is no ones.
 *  The format of volume file obeyed to NeplTX Class.
 */

NeplReader.initVolume = function(self, volumeFd){
    var initVolume = new Buffer('init volume\n');
    fs.write(volumeFd, initVolume, 0, initVolume.length, 0, function(err, written, buffer){
        if(err) throw new Error('NeplReader: Cannot initialize volume file');
    });
}

/* Initialization of meta file
 *  The format of meta file is below.
 *
 *  VolumeName=volume_name
 *  LastTimeStamp=(new Date().getTime())
 *  ByteOffset=(The offser of last read record)
 *  RecordNum=(The total record count)
 *
 */

NeplReader.initMeta = function(self, metaFd, name){
    var wholeMt = self.wholeMeta;
    var metaStr = new Buffer(wholeMt.stringBuffer() + wholeMt.ownMetaStringBuffer());
    var metaStrLen = metaStr.length;
    fs.write(metaFd, metaStr, 0, metaStrLen, 0, function(err, written, buffer){
        if(err) throw new Error('NeplReader: Cannot initialize meta file');
        self.wholeMeta.parse(self.meta);
    });
}

NeplReader.prototype.initLogFiles = function(self, volume, meta, name){
    fs.exists(volume, function(exists){
        fs.open(volume, 'w+', function(err, fd){
            if(err) throw new Error('NeplReader: Cannot find available volume file');
        });
    });

    fs.exists(meta, function(exists){
        var options = {};
        options.volumeName = name;
        options.metaFile = __dirname + '/meta';
        var wholeMt = new NeplWholeMeta(options);
        self.wholeMeta = wholeMt;
        self.txEntry = new NeplTXEntry(options);

        if(!exists){
            // If there are no meta file, add whole meta data and own meta data.
            fs.open(meta, 'wx', function(err, fd){
                if(err) throw new Error('NeplReader: Cannot find available meta file');
                // Init meta
                NeplReader.initMeta(self, fd, name);
                // Parse meta
                self.wholeMeta.parse(self.meta);
                // In this timing, wholemeta is completed
                fs.watchFile(self.volume, function(curr, prev){
                    self.doConsumer(curr, prev);
                    self.wholeMeta.updateMeta(self.meta);
                });
            });
        }
        else{
            // Only add own meta data
            self.wholeMeta.parse(self.meta);
            if( self.wholeMeta.ownMetas[name] ){
                fs.watchFile(self.volume, function(curr, prev){
                    self.doConsumer(curr, prev);
                    self.wholeMeta.updateMeta(self.meta);
                });
            }
            else{
                fs.appendFile(meta, self.wholeMeta.ownMetaStringBuffer(), function(err){
                    if(err) throw new Error('NeplReader: Cannot write own meta data');
                    self.wholeMeta.parse(self.meta);
                    // In this timing, wholemeta is completed

                    fs.watchFile(self.volume, function(curr, prev){
                        self.doConsumer(curr, prev);
                        self.wholeMeta.updateMeta(self.meta);
                    });
                });
            }
        }

    });
}

NeplReader.prototype.run = function(){

}

module.exports = NeplReader;

<commit_msg>[Bug fix] Don\'t recreate txvol file<commit_after>var net = require('net');
var fs = require('fs');
var NeplWholeMeta = require('./NeplWholeMeta');
var NeplTXEntry = require('./NeplTXEntry.js');

function NeplReader(options){
    var self = this;

    this.volume  = options.volume     ? options.volume     : __dirname + 'txvol';
    this.meta    = options.meta       ? options.meta       : __dirname + 'meta';
    this.name    = options.name       ? options.name       : 'nepl_reader';

    if( !options.consumer || typeof options.consumer !== 'function'){
        throw new Error('invalid consumer');
    }
    else{
        this.consumer = options.consumer;
    }

   self.initLogFiles(self, this.volume, this.meta, this.name);
}

NeplReader.prototype.doConsumer = function(curr,prev){
    var self = this;
    var volumeName = self.wholeMeta.volumeName;
    var currentByte = curr.size;
    var previousByte = prev.size;
    var writtenByte = currentByte - previousByte - 1;
    var tx = new Buffer(writtenByte);
    fs.open(self.volume, 'r', function(err, fd){
        var len = fs.readSync(fd, tx, 0, writtenByte, previousByte);
        var txEntry = new NeplTXEntry();
        txEntry.parse(tx.toString());
        self.consumer(txEntry);
        self.wholeMeta.ownMetas[self.name]['lastVolTxnCnt']++;
        var byteSum = parseInt(self.wholeMeta.ownMetas[self.name]['lastVolByteCnt']) + len;
        self.wholeMeta.ownMetas[self.name]['lastVolByteCnt'] = byteSum + '';
        self.wholeMeta.updateMeta(self.meta);
    });
}

/* Initialization of volume file
 *  In this function, only task is make volume file, if there is no ones.
 *  The format of volume file obeyed to NeplTX Class.
 */

NeplReader.initVolume = function(self, volumeFd){
    var initVolume = new Buffer('init volume\n');
    fs.write(volumeFd, initVolume, 0, initVolume.length, 0, function(err, written, buffer){
        if(err) throw new Error('NeplReader: Cannot initialize volume file');
    });
}

/* Initialization of meta file
 *  The format of meta file is below.
 *
 *  VolumeName=volume_name
 *  LastTimeStamp=(new Date().getTime())
 *  ByteOffset=(The offser of last read record)
 *  RecordNum=(The total record count)
 *
 */

NeplReader.initMeta = function(self, metaFd, name){
    var wholeMt = self.wholeMeta;
    var metaStr = new Buffer(wholeMt.stringBuffer() + wholeMt.ownMetaStringBuffer());
    var metaStrLen = metaStr.length;
    fs.write(metaFd, metaStr, 0, metaStrLen, 0, function(err, written, buffer){
        if(err) throw new Error('NeplReader: Cannot initialize meta file');
        self.wholeMeta.parse(self.meta);
    });
}

NeplReader.prototype.initLogFiles = function(self, volume, meta, name){
    fs.exists(volume, function(exists){
        if(!exists){
            fs.open(volume, 'w+', function(err, fd){
                if(err) throw new Error('NeplReader: Cannot find available volume file');
            });
        }
    });

    fs.exists(meta, function(exists){
        var options = {};
        options.volumeName = name;
        options.metaFile = __dirname + '/meta';
        var wholeMt = new NeplWholeMeta(options);
        self.wholeMeta = wholeMt;
        self.txEntry = new NeplTXEntry(options);

        if(!exists){
            // If there are no meta file, add whole meta data and own meta data.
            fs.open(meta, 'wx', function(err, fd){
                if(err) throw new Error('NeplReader: Cannot find available meta file');
                // Init meta
                NeplReader.initMeta(self, fd, name);
                // Parse meta
                self.wholeMeta.parse(self.meta);
                // In this timing, wholemeta is completed
                fs.watchFile(self.volume, function(curr, prev){
                    self.doConsumer(curr, prev);
                    self.wholeMeta.updateMeta(self.meta);
                });
            });
        }
        else{
            // Only add own meta data
            self.wholeMeta.parse(self.meta);
            if( self.wholeMeta.ownMetas[name] ){
                fs.watchFile(self.volume, function(curr, prev){
                    self.doConsumer(curr, prev);
                    self.wholeMeta.updateMeta(self.meta);
                });
            }
            else{
                fs.appendFile(meta, self.wholeMeta.ownMetaStringBuffer(), function(err){
                    if(err) throw new Error('NeplReader: Cannot write own meta data');
                    self.wholeMeta.parse(self.meta);
                    // In this timing, wholemeta is completed

                    fs.watchFile(self.volume, function(curr, prev){
                        self.doConsumer(curr, prev);
                        self.wholeMeta.updateMeta(self.meta);
                    });
                });
            }
        }

    });
}

NeplReader.prototype.run = function(){

}

module.exports = NeplReader;

<|endoftext|>


<commit_before>/**
 *
 * Requires the following icnlude in the django template:
 *
 *     {% include "extjshelpers/AssignmentGroupOverviewExtjsClasses.django.html" %}
 */
Ext.define('devilry.extjshelpers.assignmentgroup.AssignmentGroupOverview', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.assignmentgroupoverview',
    cls: 'widget-assignmentgroupoverview',
    requires: [
        'devilry.extjshelpers.assignmentgroup.DeliveryInfo',
        'devilry.extjshelpers.assignmentgroup.AssignmentGroupDetailsPanel',
        'devilry.extjshelpers.assignmentgroup.DeliveriesOnSingleGroupListing',
        'devilry.extjshelpers.assignmentgroup.DeadlinesOnSingleGroupListing',
        'devilry.extjshelpers.assignmentgroup.StaticFeedbackInfo',
        'devilry.extjshelpers.assignmentgroup.StaticFeedbackEditor',
        'devilry.extjshelpers.assignmentgroup.AssignmentGroupTitle',
        'devilry.extjshelpers.assignmentgroup.AssignmentGroupTodoList',
        'devilry.extjshelpers.SingleRecordContainer'
    ],

    config: {
        /**
         * @cfg
        * ID of the div to render title to. Defaults to 'content-heading'.
        */
        renderTitleTo: 'content-heading',

        /**
         * @cfg
        * ID of the div to render the body to. Defaults to 'content-main'.
        */
        renderTo: 'content-main',

        /**
         * @cfg
         * Enable creation of new feedbacks? Defaults to ``false``.
         * See: {@link devilry.extjshelpers.assignmentgroup.DeliveryInfo#canExamine}.
         *
         * When this is ``true``, the authenticated user still needs to have
         * permission to POST new feedbacks for the view to work.
         */
        canExamine: false,

        /**
         * @cfg
         * AssignmentGroup ID.
         */
        assignmentgroupid: undefined,

        /**
         * @cfg
         * Use the administrator RESTful interface to store drafts? If this is
         * ``false``, we use the examiner RESTful interface.
         */
        isAdministrator: false
    },

    constructor: function(config) {
        this.initConfig(config);
        this.callParent([config]);
    },

    initComponent: function() {
        this.createAttributes();
        this.createLayout();
        this.callParent(arguments);
        this.loadAssignmentgroupRecord();
        this.selectDeliveryIfInQueryString();
    },

    /**
     * @private
     */
    createAttributes: function() {
        this.assignmentgroup_recordcontainer = Ext.create('devilry.extjshelpers.SingleRecordContainer');
        this.delivery_recordcontainer = Ext.create('devilry.extjshelpers.SingleRecordContainer');
        this.gradeeditor_config_recordcontainer = Ext.create('devilry.extjshelpers.SingleRecordContainer');

        Ext.create('devilry.extjshelpers.assignmentgroup.AssignmentGroupTitle', {
            renderTo: this.renderTitleTo,
            singlerecordontainer: this.assignmentgroup_recordcontainer
        });

        this.role = !this.canExamine? 'student': this.isAdministrator? 'administrator': 'examiner';
        this.assignmentgroupmodel = Ext.ModelManager.getModel(this.getSimplifiedClassName('SimplifiedAssignmentGroup'));
        this.deliverymodel = Ext.ModelManager.getModel(this.getSimplifiedClassName('SimplifiedDelivery'));
        this.filemetastore = Ext.data.StoreManager.lookup(this.getSimplifiedClassName('SimplifiedFileMetaStore'));
        this.staticfeedbackstore = Ext.data.StoreManager.lookup(this.getSimplifiedClassName('SimplifiedStaticFeedbackStore'));

        if(this.canExamine) {
            this.gradeeditor_config_model = Ext.ModelManager.getModel(Ext.String.format(
                'devilry.apps.gradeeditors.simplified.{0}.SimplifiedConfig',
                this.role
            ));

            this.assignmentgroupstore = Ext.data.StoreManager.lookup(this.getSimplifiedClassName('SimplifiedAssignmentGroupStore'));
            this.deadlinemodel = Ext.ModelManager.getModel(this.getSimplifiedClassName('SimplifiedDeadline'));

            this.assignmentgroup_recordcontainer.addListener('setRecord', this.onSetAssignmentGroup, this);
        }

    },

    /**
     * @private
     */
    onSetAssignmentGroup: function() {
        this.closeopenbtn.setText(this.assignmentgroup_recordcontainer.record.data.is_open? 'Close group': 'Open group');
    },

    /**
     * @private
     */
    loadAssignmentgroupRecord: function() {
        this.assignmentgroupmodel.load(this.assignmentgroupid, {
            scope: this,
            success: function(record) {
                this.assignmentgroup_recordcontainer.setRecord(record);
                this.loadGradeEditorConfigModel();
            },
            failure: function() {
                // TODO: Handle errors
            }
        });
    },

    /**
     * @private
     */
    loadGradeEditorConfigModel: function() {
        if(this.canExamine) {
            this.gradeeditor_config_model.load(this.assignmentgroup_recordcontainer.record.data.parentnode, {
                scope: this,
                success: function(record) {
                    this.gradeeditor_config_recordcontainer.setRecord(record);
                },
                failure: function() {
                    // TODO: Handle errors
                }
            });
        }
    },

    /**
     * @private
     */
    getSimplifiedClassName: function(name) {
        var classname = Ext.String.format(
            'devilry.apps.{0}.simplified.{1}',
            this.role, name
        );
        return classname;

    },

    /**
     * @private
     */
    createLayout: function() {
        this.onOtherDeliveriesBtn = Ext.ComponentManager.create({
            xtype: 'button',
            menu: [], // To get an arrow
            id: 'tooltip-other-deliveries',
            text: 'Deliveries',
            scale: 'large',
            enableToggle: true,
            listeners: {
                scope: this,
                click: this.onOtherDeliveries
            }
        });

        var tbarItems = [{
            xtype: 'button',
            menu: [], // To get an arrow
            id: 'tooltip-deliveries',
            text: 'Deadlines',
            scale: 'large',
            enableToggle: true,
            listeners: {
                scope: this,
                click: this.onDeadlines
            }
        }, this.onOtherDeliveriesBtn, '->', {
            xtype: 'deliveryinfo',
            delivery_recordcontainer: this.delivery_recordcontainer,
            filemetastore: this.filemetastore
        }];

        if(this.canExamine) {
            var onUncorrectedGroupsBtn = Ext.ComponentManager.create({
                xtype: 'button',
                menu: [], // To get an arrow
                id: 'tooltip-uncorrected-groups',
                text: 'To-do',
                scale: 'large',
                enableToggle: true,
                listeners: {
                    scope: this,
                    click: this.onUncorrectedGroups
                }
            });
            Ext.Array.insert(tbarItems, 0, [onUncorrectedGroupsBtn]);

            this.closeopenbtn = Ext.ComponentManager.create({
                xtype: 'button',
                menu: [], // To get an arrow
                text: '',
                scale: 'large',
                enableToggle: true,
                listeners: {
                    scope: this,
                    click: this.onCloseOrOpenGroup
                }
            });
            Ext.Array.insert(tbarItems, 3, [this.closeopenbtn]);
        }

        Ext.apply(this, {
            xtype: 'panel',
            frame: false,
            layout: 'fit',
            tbar: tbarItems,
            items: [{
                xtype: this.canExamine? 'staticfeedbackeditor': 'staticfeedbackinfo',
                staticfeedbackstore: this.staticfeedbackstore,
                delivery_recordcontainer: this.delivery_recordcontainer,
                isAdministrator: this.isAdministrator, // Only required by staticfeedbackeditor
                gradeeditor_config_recordcontainer: this.gradeeditor_config_recordcontainer // Only required by staticfeedbackeditor
            }]
        });
    },

    /**
     * @private
     */
    onUncorrectedGroups: function(button) {
        var groupsWindow = Ext.create('Ext.window.Window', {
            title: 'To-to list (Groups that have not been closed)',
            height: 500,
            width: 400,
            modal: true,
            layout: 'fit',
            items: {
                xtype: 'assignmentgrouptodolist',
                assignmentgroup_recordcontainer: this.assignmentgroup_recordcontainer,
                store: this.assignmentgroupstore
            },
            listeners: {
                scope: this,
                close: function() {
                    button.toggle(false);
                }
            }
        });
        groupsWindow.show();
        groupsWindow.alignTo(button, 'bl', [0, 0]);
    },

    /**
     * @private
     */
    onOtherDeliveries: function(button) {
        if(!this.deliveriesWindow) {
            this.deliveriesWindow = Ext.create('Ext.window.Window', {
                title: 'Deliveries',
                height: 500,
                width: 400,
                modal: true,
                layout: 'fit',
                closeAction: 'hide',
                items: {
                    xtype: 'deliveriesonsinglegrouplisting',
                    assignmentgroup_recordcontainer: this.assignmentgroup_recordcontainer,
                    delivery_recordcontainer: this.delivery_recordcontainer,
                    deliverymodel: this.deliverymodel,
                    deadlinemodel: this.deadlinemodel,
                    enableDeadlineCreation: this.canExamine
                },
                listeners: {
                    scope: this,
                    close: function() {
                        this.onOtherDeliveriesBtn.toggle(false);
                    }
                }
            });
        }
        this.deliveriesWindow.show();
        if(button) {
            this.deliveriesWindow.alignTo(button, 'bl', [0, 0]);
        }
    },

    /**
     * @private
     */
    onCloseOrOpenGroup: function(button) {
        if(this.assignmentgroup_recordcontainer.record.data.is_open) {
            this.onCloseGroup(button);
        } else {
            this.onOpenGroup(button);
        }
    },

    /**
     * @private
     */
    onOpenGroup: function(button) {
        var win = Ext.MessageBox.show({
            title: 'Are you sure you want to open this group?',
            msg: '
This will allow students to add more deliveries. ' +
                'Normally Devilry will close groups automatically when:
'+
                '
' +
                '
you have given a passing grade.
' +
                '
students have failed to get a passing grade more than the configured maximum number of times.
' +
                '
' +
                '
And you normally do not open it again unless you want students to add a new delivery.
',
            buttons: Ext.Msg.YESNO,
            scope: this,
            closable: false,
            fn: function(buttonId) {
                if(buttonId == 'yes') {
                    this.assignmentgroup_recordcontainer.record.data.is_open = true;
                    this.assignmentgroup_recordcontainer.record.save({
                        scope: this,
                        success: function(record) {
                            this.assignmentgroup_recordcontainer.fireSetRecordEvent();
                        },
                        failure: function() {
                            throw "Failed to open group."
                        }
                    });
                }
                button.toggle(false);
            }
        });
        win.alignTo(button, 'bl', [0, 0]);
    },

    /**
     * @private
     */
    onCloseGroup: function(button) {
        var win = Ext.MessageBox.show({
            title: 'Are you sure you want to close this group?',
            msg: '
This will prevent students from adding more deliveries.' +
                'Normally Devilry will close groups automatically when:
'+
                '
' +
                '
you have given a passing grade.
' +
                '
students have failed to get a passing grade more than the configured maximum number of times.
' +
                '
' +
                '
However you may have to close a group manually if no maximum number of tries have been configured, or if you want the current feedback to be stored as the final feedback for this group.
',
            buttons: Ext.Msg.YESNO,
            scope: this,
            closable: false,
            fn: function(buttonId) {
                if(buttonId == 'yes') {
                    this.assignmentgroup_recordcontainer.record.data.is_open = false;
                    this.assignmentgroup_recordcontainer.record.save({
                        scope: this,
                        success: function(record) {
                            this.assignmentgroup_recordcontainer.fireSetRecordEvent();
                        },
                        failure: function() {
                            throw "Failed to close group."
                        }
                    });
                }
                button.toggle(false);
            }
        });
        win.alignTo(button, 'bl', [0, 0]);
    },

    /**
     * @private
     */
    onDeadlines: function(button) {
        var deadlinesWindow = Ext.create('Ext.window.Window', {
            title: 'Deadlines',
            width: 600,
            height: 400,
            modal: true,
            layout: 'fit',
            closeAction: 'hide',
            items: {
                xtype: 'deadlinesonsinglegrouplisting',
                assignmentgroup_recordcontainer: this.assignmentgroup_recordcontainer,
                delivery_recordcontainer: this.delivery_recordcontainer,
                deliverymodel: this.deliverymodel,
                deadlinemodel: this.deadlinemodel,
                enableDeadlineCreation: this.canExamine
            },
            listeners: {
                scope: this,
                close: function() {
                    button.toggle(false);
                }
            }
        });
        deadlinesWindow.show();
        deadlinesWindow.alignTo(button, 'bl', [0, 0]);
    },

    /**
     * @private
     */
    selectDeliveryIfInQueryString: function() {
        var query = Ext.Object.fromQueryString(window.location.search);
        if(query.deliveryid) {
            var deliveryid = parseInt(query.deliveryid);
            this.deliverymodel.load(deliveryid, {
                scope: this,
                success: function(record) {
                    if(this.assignmentgroupid == record.data.deadline__assignment_group) {
                        this.delivery_recordcontainer.setRecord(record);
                    } else {
                        throw Ext.String.format(
                            'Delivery {0} is not in AssignmentGroup {1}',
                            deliveryid,
                            this.assignmentgroupid
                        );
                    }
                },
                failure: function() {
                    // TODO: Handle errors
                }
            });
        } else {
            this.onOtherDeliveries();
        }
    }
});
<commit_msg>apps.extjshelpers: AssignmentGroupOverview docs.<commit_after>/**
 *
 * Requires the following icnlude in the django template:
 *
 *     {% include "extjshelpers/AssignmentGroupOverviewExtjsClasses.django.html" %}
 */
Ext.define('devilry.extjshelpers.assignmentgroup.AssignmentGroupOverview', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.assignmentgroupoverview',
    cls: 'widget-assignmentgroupoverview',
    requires: [
        'devilry.extjshelpers.assignmentgroup.DeliveryInfo',
        'devilry.extjshelpers.assignmentgroup.AssignmentGroupDetailsPanel',
        'devilry.extjshelpers.assignmentgroup.DeliveriesOnSingleGroupListing',
        'devilry.extjshelpers.assignmentgroup.DeadlinesOnSingleGroupListing',
        'devilry.extjshelpers.assignmentgroup.StaticFeedbackInfo',
        'devilry.extjshelpers.assignmentgroup.StaticFeedbackEditor',
        'devilry.extjshelpers.assignmentgroup.AssignmentGroupTitle',
        'devilry.extjshelpers.assignmentgroup.AssignmentGroupTodoList',
        'devilry.extjshelpers.SingleRecordContainer'
    ],

    config: {
        /**
         * @cfg
        * ID of the div to render title to. Defaults to 'content-heading'.
        */
        renderTitleTo: 'content-heading',

        /**
         * @cfg
        * ID of the div to render the body to. Defaults to 'content-main'.
        */
        renderTo: 'content-main',

        /**
         * @cfg
         * Enable creation of new feedbacks? Defaults to ``false``.
         * See: {@link devilry.extjshelpers.assignmentgroup.DeliveryInfo#canExamine}.
         *
         * When this is ``true``, the authenticated user still needs to have
         * permission to POST new feedbacks for the view to work.
         */
        canExamine: false,

        /**
         * @cfg
         * AssignmentGroup ID.
         */
        assignmentgroupid: undefined,

        /**
         * @cfg
         * Use the administrator RESTful interface to store drafts? If this is
         * ``false``, we use the examiner RESTful interface.
         */
        isAdministrator: false
    },

    constructor: function(config) {
        this.initConfig(config);
        this.callParent([config]);
    },

    initComponent: function() {
        this.createAttributes();
        this.createLayout();
        this.callParent(arguments);
        this.loadAssignmentgroupRecord();
        this.selectDeliveryIfInQueryString();
    },

    /**
     * @private
     */
    createAttributes: function() {
        this.assignmentgroup_recordcontainer = Ext.create('devilry.extjshelpers.SingleRecordContainer');
        this.delivery_recordcontainer = Ext.create('devilry.extjshelpers.SingleRecordContainer');
        this.gradeeditor_config_recordcontainer = Ext.create('devilry.extjshelpers.SingleRecordContainer');

        Ext.create('devilry.extjshelpers.assignmentgroup.AssignmentGroupTitle', {
            renderTo: this.renderTitleTo,
            singlerecordontainer: this.assignmentgroup_recordcontainer
        });

        this.role = !this.canExamine? 'student': this.isAdministrator? 'administrator': 'examiner';
        this.assignmentgroupmodel = Ext.ModelManager.getModel(this.getSimplifiedClassName('SimplifiedAssignmentGroup'));
        this.deliverymodel = Ext.ModelManager.getModel(this.getSimplifiedClassName('SimplifiedDelivery'));
        this.filemetastore = Ext.data.StoreManager.lookup(this.getSimplifiedClassName('SimplifiedFileMetaStore'));
        this.staticfeedbackstore = Ext.data.StoreManager.lookup(this.getSimplifiedClassName('SimplifiedStaticFeedbackStore'));

        if(this.canExamine) {
            this.gradeeditor_config_model = Ext.ModelManager.getModel(Ext.String.format(
                'devilry.apps.gradeeditors.simplified.{0}.SimplifiedConfig',
                this.role
            ));

            this.assignmentgroupstore = Ext.data.StoreManager.lookup(this.getSimplifiedClassName('SimplifiedAssignmentGroupStore'));
            this.deadlinemodel = Ext.ModelManager.getModel(this.getSimplifiedClassName('SimplifiedDeadline'));

            this.assignmentgroup_recordcontainer.addListener('setRecord', this.onSetAssignmentGroup, this);
        }

    },

    /**
     * @private
     */
    onSetAssignmentGroup: function() {
        this.closeopenbtn.setText(this.assignmentgroup_recordcontainer.record.data.is_open? 'Close group': 'Open group');
    },

    /**
     * @private
     */
    loadAssignmentgroupRecord: function() {
        this.assignmentgroupmodel.load(this.assignmentgroupid, {
            scope: this,
            success: function(record) {
                this.assignmentgroup_recordcontainer.setRecord(record);
                this.loadGradeEditorConfigModel();
            },
            failure: function() {
                // TODO: Handle errors
            }
        });
    },

    /**
     * @private
     */
    loadGradeEditorConfigModel: function() {
        if(this.canExamine) {
            this.gradeeditor_config_model.load(this.assignmentgroup_recordcontainer.record.data.parentnode, {
                scope: this,
                success: function(record) {
                    this.gradeeditor_config_recordcontainer.setRecord(record);
                },
                failure: function() {
                    // TODO: Handle errors
                }
            });
        }
    },

    /**
     * @private
     */
    getSimplifiedClassName: function(name) {
        var classname = Ext.String.format(
            'devilry.apps.{0}.simplified.{1}',
            this.role, name
        );
        return classname;

    },

    /**
     * @private
     */
    createLayout: function() {
        this.onOtherDeliveriesBtn = Ext.ComponentManager.create({
            xtype: 'button',
            menu: [], // To get an arrow
            id: 'tooltip-other-deliveries',
            text: 'Deliveries',
            scale: 'large',
            enableToggle: true,
            listeners: {
                scope: this,
                click: this.onOtherDeliveries
            }
        });

        var tbarItems = [{
            xtype: 'button',
            menu: [], // To get an arrow
            id: 'tooltip-deliveries',
            text: 'Deadlines',
            scale: 'large',
            enableToggle: true,
            listeners: {
                scope: this,
                click: this.onDeadlines
            }
        }, this.onOtherDeliveriesBtn, '->', {
            xtype: 'deliveryinfo',
            delivery_recordcontainer: this.delivery_recordcontainer,
            filemetastore: this.filemetastore
        }];

        if(this.canExamine) {
            var onUncorrectedGroupsBtn = Ext.ComponentManager.create({
                xtype: 'button',
                menu: [], // To get an arrow
                id: 'tooltip-uncorrected-groups',
                text: 'To-do',
                scale: 'large',
                enableToggle: true,
                listeners: {
                    scope: this,
                    click: this.onUncorrectedGroups
                }
            });
            Ext.Array.insert(tbarItems, 0, [onUncorrectedGroupsBtn]);

            this.closeopenbtn = Ext.ComponentManager.create({
                xtype: 'button',
                menu: [], // To get an arrow
                text: '',
                scale: 'large',
                enableToggle: true,
                listeners: {
                    scope: this,
                    click: this.onCloseOrOpenGroup
                }
            });
            Ext.Array.insert(tbarItems, 3, [this.closeopenbtn]);
        }

        Ext.apply(this, {
            xtype: 'panel',
            frame: false,
            layout: 'fit',
            tbar: tbarItems,
            items: [{
                xtype: this.canExamine? 'staticfeedbackeditor': 'staticfeedbackinfo',
                staticfeedbackstore: this.staticfeedbackstore,
                delivery_recordcontainer: this.delivery_recordcontainer,
                isAdministrator: this.isAdministrator, // Only required by staticfeedbackeditor
                gradeeditor_config_recordcontainer: this.gradeeditor_config_recordcontainer // Only required by staticfeedbackeditor
            }]
        });
    },

    /**
     * @private
     */
    onUncorrectedGroups: function(button) {
        var groupsWindow = Ext.create('Ext.window.Window', {
            title: 'To-do list (Open groups on this assignment)',
            height: 500,
            width: 400,
            modal: true,
            layout: 'fit',
            items: {
                xtype: 'assignmentgrouptodolist',
                assignmentgroup_recordcontainer: this.assignmentgroup_recordcontainer,
                store: this.assignmentgroupstore
            },
            listeners: {
                scope: this,
                close: function() {
                    button.toggle(false);
                }
            }
        });
        groupsWindow.show();
        groupsWindow.alignTo(button, 'bl', [0, 0]);
    },

    /**
     * @private
     */
    onOtherDeliveries: function(button) {
        if(!this.deliveriesWindow) {
            this.deliveriesWindow = Ext.create('Ext.window.Window', {
                title: 'Deliveries by this group',
                height: 500,
                width: 400,
                modal: true,
                layout: 'fit',
                closeAction: 'hide',
                items: {
                    xtype: 'deliveriesonsinglegrouplisting',
                    assignmentgroup_recordcontainer: this.assignmentgroup_recordcontainer,
                    delivery_recordcontainer: this.delivery_recordcontainer,
                    deliverymodel: this.deliverymodel,
                    deadlinemodel: this.deadlinemodel,
                    enableDeadlineCreation: this.canExamine
                },
                listeners: {
                    scope: this,
                    close: function() {
                        this.onOtherDeliveriesBtn.toggle(false);
                    }
                }
            });
        }
        this.deliveriesWindow.show();
        if(button) {
            this.deliveriesWindow.alignTo(button, 'bl', [0, 0]);
        }
    },

    /**
     * @private
     */
    onCloseOrOpenGroup: function(button) {
        if(this.assignmentgroup_recordcontainer.record.data.is_open) {
            this.onCloseGroup(button);
        } else {
            this.onOpenGroup(button);
        }
    },

    /**
     * @private
     */
    onOpenGroup: function(button) {
        var win = Ext.MessageBox.show({
            title: 'Are you sure you want to open this group?',
            msg: '
This will allow students to add more deliveries. ' +
                'Normally Devilry will close groups automatically when:
'+
                '
' +
                '
you have given a passing grade.
' +
                '
students have failed to get a passing grade more than the configured maximum number of times.
' +
                '
' +
                '
And you normally do not open it again unless you want students to add a new delivery.
',
            buttons: Ext.Msg.YESNO,
            scope: this,
            closable: false,
            fn: function(buttonId) {
                if(buttonId == 'yes') {
                    this.assignmentgroup_recordcontainer.record.data.is_open = true;
                    this.assignmentgroup_recordcontainer.record.save({
                        scope: this,
                        success: function(record) {
                            this.assignmentgroup_recordcontainer.fireSetRecordEvent();
                        },
                        failure: function() {
                            throw "Failed to open group."
                        }
                    });
                }
                button.toggle(false);
            }
        });
        win.alignTo(button, 'bl', [0, 0]);
    },

    /**
     * @private
     */
    onCloseGroup: function(button) {
        var win = Ext.MessageBox.show({
            title: 'Are you sure you want to close this group?',
            msg: '
This will prevent students from adding more deliveries.' +
                'Normally Devilry will close groups automatically when:
'+
                '
' +
                '
you have given a passing grade.
' +
                '
students have failed to get a passing grade more than the configured maximum number of times.
' +
                '
' +
                '
However you may have to close a group manually if no maximum number of tries have been configured, or if you want the current feedback to be stored as the final feedback for this group.
',
            buttons: Ext.Msg.YESNO,
            scope: this,
            closable: false,
            fn: function(buttonId) {
                if(buttonId == 'yes') {
                    this.assignmentgroup_recordcontainer.record.data.is_open = false;
                    this.assignmentgroup_recordcontainer.record.save({
                        scope: this,
                        success: function(record) {
                            this.assignmentgroup_recordcontainer.fireSetRecordEvent();
                        },
                        failure: function() {
                            throw "Failed to close group."
                        }
                    });
                }
                button.toggle(false);
            }
        });
        win.alignTo(button, 'bl', [0, 0]);
    },

    /**
     * @private
     */
    onDeadlines: function(button) {
        var deadlinesWindow = Ext.create('Ext.window.Window', {
            title: 'Deadlines for this group',
            width: 600,
            height: 400,
            modal: true,
            layout: 'fit',
            closeAction: 'hide',
            items: {
                xtype: 'deadlinesonsinglegrouplisting',
                assignmentgroup_recordcontainer: this.assignmentgroup_recordcontainer,
                delivery_recordcontainer: this.delivery_recordcontainer,
                deliverymodel: this.deliverymodel,
                deadlinemodel: this.deadlinemodel,
                enableDeadlineCreation: this.canExamine
            },
            listeners: {
                scope: this,
                close: function() {
                    button.toggle(false);
                }
            }
        });
        deadlinesWindow.show();
        deadlinesWindow.alignTo(button, 'bl', [0, 0]);
    },

    /**
     * @private
     */
    selectDeliveryIfInQueryString: function() {
        var query = Ext.Object.fromQueryString(window.location.search);
        if(query.deliveryid) {
            var deliveryid = parseInt(query.deliveryid);
            this.deliverymodel.load(deliveryid, {
                scope: this,
                success: function(record) {
                    if(this.assignmentgroupid == record.data.deadline__assignment_group) {
                        this.delivery_recordcontainer.setRecord(record);
                    } else {
                        throw Ext.String.format(
                            'Delivery {0} is not in AssignmentGroup {1}',
                            deliveryid,
                            this.assignmentgroupid
                        );
                    }
                },
                failure: function() {
                    // TODO: Handle errors
                }
            });
        } else {
            this.onOtherDeliveries();
        }
    }
});
<|endoftext|>


<commit_before>define(['jquery', 'kbwidget', 'bootstrap'], function($) {
    $.KBWidget({
        name: 'kbaseNarrativeCellMenu',
        parent: 'kbaseWidget',
        options: {cell: null, kbWidget:null, kbWidgetType:null},

        init: function(options) {
            this._super(options);

            // console.log(['cell menu', this.options.cell]);

            var $deleteBtn = $('<button type="button" class="btn btn-default btn-xs" data-toggle="tooltip" data-placement="left" Title="Delete Cell">')
                             .css({"background-color": "transparent"})
                             .append($(''))
                             .click($.proxy(function() {
                                 this.trigger('deleteCell.Narrative', IPython.notebook.get_selected_index());
                             }, this));

            var $btn = $('<button type="button" data-toggle="dropdown" aria-haspopup="true" class="btn btn-default btn-xs">')
                       .css({"background-color": "transparent"})
                       .append($(''));

            this.$menu = $('
')
                         .addClass('dropdown-menu')
                         .css({
                                'right' : '0',
                                'left' : 'auto',
                                'margin' : '0'
                         });

            if (window.kbconfig && window.kbconfig.mode === "debug") {
                this.addMenuItem({
                    icon: 'fa fa-code',
                    text: 'View Job Submission',
                    action: function() {
                        var metadata = IPython.notebook.get_selected_cell().metadata;
                        var stackTrace = [];
                        if (metadata['kb-cell'] && metadata['kb-cell']['stackTrace'])
                            stackTrace = metadata['kb-cell']['stackTrace'];
                        console.log(stackTrace);
                        var cell = IPython.notebook.insert_cell_below('code');
                        if (stackTrace instanceof Array) {
                            cell.set_text('job_info=' + stackTrace[stackTrace.length - 1] + '\njob_info');
                            IPython.notebook.get_selected_cell().execute();
                        }
                        else {
                            cell.set_text('job_info=' + stackTrace);
                        }
                    }
                });
            }

            this.addMenuItem({
                icon: 'fa fa-arrow-up',
                text: 'Move Cell Up',
                action: function() {
                    IPython.notebook.move_cell_up();
                }
            });

            this.addMenuItem({
                icon: 'fa fa-arrow-down',
                text: 'Move Cell Down',
                action: function() {
                    IPython.notebook.move_cell_down();
                }
            });

            this.addMenuItem({
                icon: 'fa fa-caret-square-o-up',
                text: 'Insert Cell Above',
                action: function() {
                    var cell = IPython.notebook.insert_cell_above('markdown');
                }
            });

            this.addMenuItem({
                icon: 'fa fa-caret-square-o-down',
                text: 'Insert Cell Below',
                action: function() {
                    var cell = IPython.notebook.insert_cell_below('markdown');
                }
            });

            // only add this if it was controlled by a KBase Widget
            if(this.options.kbWidget && this.options.kbWidgetType) {
                this.addMenuItem({
                    icon: 'fa fa-copy',
                    text: 'Duplicate Cell',
                    action: $.proxy(function() {
                        // get the current state, and clear it of its running state
                        var kbWidget = options.kbWidget
                        var currentState = kbWidget.getState();
                        if(this.options.kbWidgetType === 'method') {
                            // put the method in the narrative
                            this.trigger('methodClicked.Narrative', kbWidget.method);

                            // the method initializes an internal method input widget, but in an async way
                            // so we have to wait and check when that is done.  When it is, we can update state
                            var newCell = IPython.notebook.get_selected_cell();
                            var newWidget = $('#'+$(newCell.get_text())[0].id).kbaseNarrativeMethodCell();
                            var updateState = function(state) {
                                if(newWidget.$inputWidget) {
                                    // if the $inputWidget is not null, we are good to go, so set the state
                                    newWidget.loadState(currentState.params);
                                } else {
                                    // not ready yet, keep waiting
                                    window.setTimeout(updateState,500);
                                }
                            };
                            window.setTimeout(updateState,50);

                        }
                    }, this)
                });
            }

            // if (this.options.cell && this.options.cell.metadata['kb-cell'] === undefined) {
            //     this.addMenuItem({
            //         icon: 'fa fa-terminal',
            //         text: 'Toggle Cell Type',
            //         action: function() {
            //             if (this.options.cell.cell_type === "markdown") {
            //                 IPython.notebook.to_code();
            //             }
            //             else {

            //             }
            //         },
            //         disable: true
            //     });
            // }

            this.addMenuItem({
                icon: 'fa fa-trash-o',
                text: 'Delete Cell',
                action: $.proxy(function() {
                                    this.trigger('deleteCell.Narrative', IPython.notebook.get_selected_index());
                                }, this)
            });

            // this shows whether the app is running
            this.$runningIcon = $("").addClass("fa fa-circle-o-notch fa-spin")
                                .css({"color": "rgb(42,121,191)"})
                                .hide();
            // this shows on error
            this.$errorIcon =  $("").addClass("fa fa-exclamation-triangle")
                              .css({"color": "red"})
                              .hide();

            this.$elem.append(
                $('')
                    .append(this.$runningIcon)
                    .append(this.$errorIcon)
                    .append($deleteBtn)
                    .append($('')
                              .append($btn)
                              .append(this.$menu)));
            $deleteBtn.tooltip();

            return this;
        },

        addMenuItem: function(item) {
            var label = '';
            if (item.icon)
                label += ' ';
            if (item.text)
                label += ' ' + item.text;
            var $item = $('')
                        .append(label)
                        .click($.proxy(function(event) {
                            event.preventDefault();
                            event.stopPropagation();
                            if (!item.disable) {
                                if (item.action)
                                    item.action();
                                this.$menu.dropdown('toggle');
                            }
                        }, this));
            var $itemElem = $('
').append($item);
            if (item.disable)
                $itemElem.addClass('disabled');
            this.$menu.append($itemElem);
        },
    });
});
<commit_msg>add support for showing title, icon (not in toolbar, but the hook is here for now), run and error icons, panel collapse control (chevron)<commit_after>/*global define,IPython*/
/*jslint white:true,browser:true*/
define(['jquery', 'kbwidget', 'bootstrap'], function ($) {
    'use strict';
    $.KBWidget({
        name: 'kbaseNarrativeCellMenu',
        parent: 'kbaseWidget',
        options: {cell: null, kbWidget: null, kbWidgetType: null},
        genId: function () {
            if (!this.lastId) {
                this.lastId = 1;
            } else {
                this.lastId += 1;
            }
            return 'kbaseNarrativeCellMenu_' + this.lastId;
        },
        init: function (options) {
            var self = this;
            this._super(options);

            // console.log(['cell menu', this.options.cell]);
//            var outputPane = this.$elem.closest('.cell').find('.inner_cell > div:nth-child(2)').get(0);
//            if (!outputPane.id) {
//                outputPane.id = this.genId();
//            }
//            this.$elem.data('ouputPaneId', outputPane.id);

            var $deleteBtn = $('<button type="button" class="btn btn-default btn-xs" data-toggle="tooltip" data-placement="left" Title="Delete Cell">')
                .append($(''))
                .click($.proxy(function () {
                    this.trigger('deleteCell.Narrative', IPython.notebook.get_selected_index());
                }, this)),
                $menuBtn = $('<button type="button" data-toggle="dropdown" aria-haspopup="true" class="btn btn-default btn-xs">')
                .append($('')),
                // $collapseBtn = $('<button class="btn btn-default" role="button" data-toggle="collapse" href="#' + outputPane.id + '" aria-controls="' + $outputPane.id + '">Open</button>'),
                $collapseBtn = $('<button type="button" class="btn btn-default btn-xs" role="button"></button>')
                .on('click', function () {
                    self.$elem.trigger('toggle-output');
                    var icon = $(this).find('span.fa');
                    if (icon.hasClass('fa-chevron-right')) {
                        icon.removeClass('fa-chevron-right');
                        icon.addClass('fa-chevron-down');
                    } else {
                        icon.removeClass('fa-chevron-down');
                        icon.addClass('fa-chevron-right');
                    }
                })

            this.$elem.on('toggle-output', function () {
                var pane = self.$elem.closest('.cell').find('.inner_cell > div:nth-child(3)');
                if (pane) {
                    pane.toggle();
                }
            });

            this.$menu = $('
')
                .addClass('dropdown-menu dropdown-menu-right');

            if (window.kbconfig && window.kbconfig.mode === "debug") {
                this.addMenuItem({
                    icon: 'fa fa-code',
                    text: 'View Job Submission',
                    action: function () {
                        var metadata = IPython.notebook.get_selected_cell().metadata,
                            stackTrace = [],
                            cell = IPython.notebook.insert_cell_below('code');
                        if (metadata['kb-cell'] && metadata['kb-cell'].stackTrace) {
                            stackTrace = metadata['kb-cell'].stackTrace;
                        }
                        console.log(stackTrace);
                        if (stackTrace instanceof Array) {
                            cell.set_text('job_info=' + stackTrace[stackTrace.length - 1] + '\njob_info');
                            IPython.notebook.get_selected_cell().execute();
                        } else {
                            cell.set_text('job_info=' + stackTrace);
                        }
                    }
                });
            }

            this.addMenuItem({
                icon: 'fa fa-arrow-up',
                text: 'Move Cell Up',
                action: function () {
                    IPython.notebook.move_cell_up();
                }
            });

            this.addMenuItem({
                icon: 'fa fa-arrow-down',
                text: 'Move Cell Down',
                action: function () {
                    IPython.notebook.move_cell_down();
                }
            });

            this.addMenuItem({
                icon: 'fa fa-caret-square-o-up',
                text: 'Insert Cell Above',
                action: function () {
                    IPython.notebook.insert_cell_above('markdown');
                }
            });

            this.addMenuItem({
                icon: 'fa fa-caret-square-o-down',
                text: 'Insert Cell Below',
                action: function () {
                    IPython.notebook.insert_cell_below('markdown');
                }
            });

            // only add this if it was controlled by a KBase Widget
            if (this.options.kbWidget && this.options.kbWidgetType) {
                this.addMenuItem({
                    icon: 'fa fa-copy',
                    text: 'Duplicate Cell',
                    action: $.proxy(function () {
                        // get the current state, and clear it of its running state
                        var kbWidget = options.kbWidget,
                            currentState = kbWidget.getState();
                        if (this.options.kbWidgetType === 'method') {
                            // put the method in the narrative
                            this.trigger('methodClicked.Narrative', kbWidget.method);

                            // the method initializes an internal method input widget, but in an async way
                            // so we have to wait and check when that is done.  When it is, we can update state
                            var newCell = IPython.notebook.get_selected_cell();
                            var newWidget = $('#' + $(newCell.get_text())[0].id).kbaseNarrativeMethodCell();
                            var updateState = function (state) {
                                if (newWidget.$inputWidget) {
                                    // if the $inputWidget is not null, we are good to go, so set the state
                                    newWidget.loadState(currentState.params);
                                } else {
                                    // not ready yet, keep waiting
                                    window.setTimeout(updateState, 500);
                                }
                            };
                            window.setTimeout(updateState, 50);
                        }
                    }, this)
                });
            }

            // if (this.options.cell && this.options.cell.metadata['kb-cell'] === undefined) {
            //     this.addMenuItem({
            //         icon: 'fa fa-terminal',
            //         text: 'Toggle Cell Type',
            //         action: function() {
            //             if (this.options.cell.cell_type === "markdown") {
            //                 IPython.notebook.to_code();
            //             }
            //             else {

            //             }
            //         },
            //         disable: true
            //     });
            // }
//
//            this.addMenuItem({
//                icon: 'fa fa-trash-o',
//                text: 'Delete Cell',
//                action: $.proxy(function () {
//                    this.trigger('deleteCell.Narrative', IPython.notebook.get_selected_index());
//                }, this)
//            });

            // this shows whether the app is running
            var self = this;
            this.$runningIcon = $("")
                .addClass("fa fa-circle-o-notch fa-spin")
                .css({color: "rgb(42,121,191)"})
                .hide();
            this.$elem.data('runningIcon', this.$runningIcon);
            this.$elem.on('start-running', function () {
                self.$runningIcon.show();
            });
            this.$elem.on('stop-running', function () {
                self.$runningIcon.hide();
            });

            // this shows on error
            this.$errorIcon = $("")
                .addClass("fa fa-exclamation-triangle")
                .css({color: "red"})
                .hide();
            this.$elem.data('errorIcon', this.$errorIcon);
            this.$elem.on('show-error', function () {
                self.$errorIcon.hide();
            });
            this.$elem.on('hide-error', function () {
                self.$errorIcon.hide();
            });

            var $dropdownMenu = $('')
                .append($menuBtn)
                .append(this.$menu);

            this.$elem.on('show-buttons', function () {
                $deleteBtn.removeClass('disabled');
                $dropdownMenu.find('.btn').removeClass('disabled');
            });
            this.$elem.on('hide-buttons', function () {
                $deleteBtn.addClass('disabled');
                $dropdownMenu.find('.btn').addClass('disabled');
            });

            this.$elem.append(
                $('
')
                .append($('
')
                    .append($('
')
                        .append($('
')
                            .append($collapseBtn)
                        )
                    )
                    .append($('
')
                        .append($('
')
                            .append('') // title here
                        )
                    )
                    .append($('
')
                        .append($('
')
                            .append(this.$runningIcon)
                            .append(this.$errorIcon)
                            .append($deleteBtn)
                            .append($dropdownMenu)
                        )
                    )
                )
            );
            $deleteBtn.tooltip();
            // Set up title.
            var $titleNode = this.$elem.find('[data-element="title"]');
            this.$elem.on('set-title', function (e, title) {
                e.stopPropagation();
                $titleNode.html(title);
            });

            // And an icon -- hack to go into the input prompt for now...
            //$cell =  = $(options.cell.element);

            this.$elem.on('set-icon', function (e, icon) {
                var $cell = self.$elem.closest('.cell'),
                    $iconNode = $cell.find('.prompt');
                console.log('ICON');
                console.log($cell);
                console.log($iconNode);
                e.stopPropagation();
                var wrapped = '
' + icon + '
';
                $iconNode.html(wrapped)

                console.log(wrapped);
                console.log($iconNode);
            });
            var $cell = (options && options.cell && $(options.cell.element)) || self.$elem.closest('.cell');
            console.log('CELL');
            console.log($cell);
            var icon = $cell.data('icon');
            if (icon) {
                this.$elem.trigger('set-icon', [icon]);
            }

            // but maybe have the title already.
            var title = $cell.data('title');
            if (title) {
                this.$elem.trigger('set-title', [title]);
            }

            return this;
        },
        addMenuItem: function (item) {
            var label = '';
            if (item.icon) {
                label += ' ';
            }
            if (item.text) {
                label += ' ' + item.text;
            }
            var $item = $('')
                .append(label)
                .click($.proxy(function (event) {
                    event.preventDefault();
                    event.stopPropagation();
                    if (!item.disable) {
                        if (item.action)
                            item.action();
                        this.$menu.dropdown('toggle');
                    }
                }, this));
            var $itemElem = $('
').append($item);
            if (item.disable) {
                $itemElem.addClass('disabled');
            }
            this.$menu.append($itemElem);
        }
    });
});<|endoftext|>


<commit_before>const dal = require('../dal');
const status = require('http-status-codes');
const Ajv = require('ajv');
const loggers = require('../../../services/logs/loggers');

// Schema validator
const ajv = new Ajv({
  coerceTypes: true,
  useDefaults: true,
});

// Schema for getFiltered query
const querySchema = {
  type: 'object',
  properties: {
    creator: {
      type: 'string',
      minLength: 1,
    },
    deleted: {
      type: 'boolean',
      default: false,
    },
    limit: {
      type: 'integer',
      default: 100,
    },
    order: {
      type: 'string',
      enum: ['rel', 'az', 'za', 'newest', 'oldest'],
      default: 'rel',
    },
    skip: {
      type: 'integer',
      default: 0,
    },
    text: {
      type: 'string',
      minLength: 1,
    },
  },
  additionalProperties: false,
};
const validateQuery = ajv.compile(querySchema);

module.exports = function (req, res, next) {
  // Parameters:
  //   req.query
  //     see dal.getFiltered for params
  //
  // Response on success:
  //   JSON array of markers

  // Data types are coerced, thus req.query is modified.
  // validateQuery returns bool to inform if validation was successful
  if (!validateQuery(req.query)) {
    // To output validation erros.
    // console.log('ERROR', validateQuery.errors);
    return res.sendStatus(status.BAD_REQUEST);
  }

  dal.getFiltered(req.query, (err, markers) => {
    if (err) {
      return next(err);
    }

    // Search successful
    if (req.query.text) {
      loggers.log(req.user.name + ' searched for "' + req.query.text + '".');
    } else {
      loggers.log(req.user.name + ' searched without keywords.');
    }

    return res.json(markers);
  });
};
<commit_msg>Allow anonymous getFiltered search calls<commit_after>const dal = require('../dal');
const status = require('http-status-codes');
const Ajv = require('ajv');
const loggers = require('../../../services/logs/loggers');

// Schema validator
const ajv = new Ajv({
  coerceTypes: true,
  useDefaults: true,
});

// Schema for getFiltered query
const querySchema = {
  type: 'object',
  properties: {
    creator: {
      type: 'string',
      minLength: 1,
    },
    deleted: {
      type: 'boolean',
      default: false,
    },
    limit: {
      type: 'integer',
      default: 100,
    },
    order: {
      type: 'string',
      enum: ['rel', 'az', 'za', 'newest', 'oldest'],
      default: 'rel',
    },
    skip: {
      type: 'integer',
      default: 0,
    },
    text: {
      type: 'string',
      minLength: 1,
    },
  },
  additionalProperties: false,
};
const validateQuery = ajv.compile(querySchema);

module.exports = function (req, res, next) {
  // Parameters:
  //   req.query
  //     see dal.getFiltered for params
  //
  // Response on success:
  //   JSON array of markers

  // Data types are coerced, thus req.query is modified.
  // validateQuery returns bool to inform if validation was successful
  if (!validateQuery(req.query)) {
    // To output validation erros.
    // console.log('ERROR', validateQuery.errors);
    return res.sendStatus(status.BAD_REQUEST);
  }

  dal.getFiltered(req.query, (err, markers) => {
    if (err) {
      return next(err);
    }

    // Search successful. Log searches with keywords
    if (req.query.text) {
      const name = req.user ? req.user.name : 'Anonymous';
      loggers.log(name + ' searched for "' + req.query.text + '".');
    }

    return res.json(markers);
  });
};
<|endoftext|>


<commit_before>/**
 *  Copyright (c) 2014, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

import "Map"
import "Vector"
import "is"
/* global Map, Vector, is */
/* exported OrderedMap */

class OrderedMap extends Map {

  // @pragma Construction

  constructor(sequence) {
    if (sequence && sequence.constructor === OrderedMap) {
      return sequence;
    }
    if (!sequence || sequence.length === 0) {
      return OrderedMap.empty();
    }
    return OrderedMap.empty().merge(sequence);
  }

  static empty() {
    return EMPTY_ORDERED_MAP || (EMPTY_ORDERED_MAP = makeOrderedMap());
  }

  toString() {
    return this.__toString('OrderedMap {', '}');
  }

  // @pragma Access

  get(k, notSetValue) {
    if (k != null && this._map) {
      var index = this._map.get(k);
      if (index != null) {
        return this._vector.get(index)[1];
      }
    }
    return notSetValue;
  }

  // @pragma Modification

  clear() {
    if (this.__ownerID) {
      this.length = 0;
      this._map = this._vector = null;
      return this;
    }
    return OrderedMap.empty();
  }

  set(k, v) {
    if (k == null) {
      return this;
    }
    var newMap = this._map;
    var newVector = this._vector;
    if (newMap) {
      var index = newMap.get(k);
      if (index == null) {
        newMap = newMap.set(k, newVector.length);
        newVector = newVector.push([k, v]);
      } else if (newVector.get(index)[1] !== v) {
        newVector = newVector.set(index, [k, v]);
      }
    } else {
      newVector = Vector.empty().__ensureOwner(this.__ownerID).set(0, [k, v]);
      newMap = Map.empty().__ensureOwner(this.__ownerID).set(k, 0);
    }
    if (this.__ownerID) {
      this.length = newMap.length;
      this._map = newMap;
      this._vector = newVector;
      return this;
    }
    return newVector === this._vector ? this : makeOrderedMap(newMap, newVector);
  }

  delete(k) {
    if (k == null || this._map == null) {
      return this;
    }
    var index = this._map.get(k);
    if (index == null) {
      return this;
    }
    var newMap = this._map.delete(k);
    var newVector = this._vector.delete(index);

    if (newMap.length === 0) {
      return this.clear();
    }
    if (this.__ownerID) {
      this.length = newMap.length;
      this._map = newMap;
      this._vector = newVector;
      return this;
    }
    return newMap === this._map ? this : makeOrderedMap(newMap, newVector);
  }

  __iterate(fn, reverse) {
    return this._vector ? this._vector.fromEntries().__iterate(fn, reverse) : 0;
  }

  __deepEqual(other) {
    var iterator = this._vector.__iterator__();
    return other.every((v, k) => {
      var entry = iterator.next();
      entry && (entry = entry[1]);
      return entry && is(k, entry[0]) && is(v, entry[1]);
    });
  }

  __ensureOwner(ownerID) {
    if (ownerID === this.__ownerID) {
      return this;
    }
    var newMap = this._map && this._map.__ensureOwner(ownerID);
    var newVector = this._vector && this._vector.__ensureOwner(ownerID);
    if (!ownerID) {
      this.__ownerID = ownerID;
      this._map = newMap;
      this._vector = newVector;
      return this;
    }
    return makeOrderedMap(newMap, newVector, ownerID);
  }
}

OrderedMap.from = OrderedMap;

function makeOrderedMap(map, vector, ownerID) {
  var omap = Object.create(OrderedMap.prototype);
  omap.length = map ? map.length : 0;
  omap._map = map;
  omap._vector = vector;
  omap.__ownerID = ownerID;
  return omap;
}

var EMPTY_ORDERED_MAP;
<commit_msg>fix ordered map deep equal<commit_after>/**
 *  Copyright (c) 2014, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

import "Map"
import "Vector"
import "is"
/* global Map, Vector, is */
/* exported OrderedMap */

class OrderedMap extends Map {

  // @pragma Construction

  constructor(sequence) {
    if (sequence && sequence.constructor === OrderedMap) {
      return sequence;
    }
    if (!sequence || sequence.length === 0) {
      return OrderedMap.empty();
    }
    return OrderedMap.empty().merge(sequence);
  }

  static empty() {
    return EMPTY_ORDERED_MAP || (EMPTY_ORDERED_MAP = makeOrderedMap());
  }

  toString() {
    return this.__toString('OrderedMap {', '}');
  }

  // @pragma Access

  get(k, notSetValue) {
    if (k != null && this._map) {
      var index = this._map.get(k);
      if (index != null) {
        return this._vector.get(index)[1];
      }
    }
    return notSetValue;
  }

  // @pragma Modification

  clear() {
    if (this.__ownerID) {
      this.length = 0;
      this._map = this._vector = null;
      return this;
    }
    return OrderedMap.empty();
  }

  set(k, v) {
    if (k == null) {
      return this;
    }
    var newMap = this._map;
    var newVector = this._vector;
    if (newMap) {
      var index = newMap.get(k);
      if (index == null) {
        newMap = newMap.set(k, newVector.length);
        newVector = newVector.push([k, v]);
      } else if (newVector.get(index)[1] !== v) {
        newVector = newVector.set(index, [k, v]);
      }
    } else {
      newVector = Vector.empty().__ensureOwner(this.__ownerID).set(0, [k, v]);
      newMap = Map.empty().__ensureOwner(this.__ownerID).set(k, 0);
    }
    if (this.__ownerID) {
      this.length = newMap.length;
      this._map = newMap;
      this._vector = newVector;
      return this;
    }
    return newVector === this._vector ? this : makeOrderedMap(newMap, newVector);
  }

  delete(k) {
    if (k == null || this._map == null) {
      return this;
    }
    var index = this._map.get(k);
    if (index == null) {
      return this;
    }
    var newMap = this._map.delete(k);
    var newVector = this._vector.delete(index);

    if (newMap.length === 0) {
      return this.clear();
    }
    if (this.__ownerID) {
      this.length = newMap.length;
      this._map = newMap;
      this._vector = newVector;
      return this;
    }
    return newMap === this._map ? this : makeOrderedMap(newMap, newVector);
  }

  __iterate(fn, reverse) {
    return this._vector ? this._vector.fromEntries().__iterate(fn, reverse) : 0;
  }

  __deepEqual(other) {
    var iterator = this._vector.__iterator__();
    return other.every((v, k) => {
      var entry = iterator.next().value;
      entry && (entry = entry[1]);
      return entry && is(k, entry[0]) && is(v, entry[1]);
    });
  }

  __ensureOwner(ownerID) {
    if (ownerID === this.__ownerID) {
      return this;
    }
    var newMap = this._map && this._map.__ensureOwner(ownerID);
    var newVector = this._vector && this._vector.__ensureOwner(ownerID);
    if (!ownerID) {
      this.__ownerID = ownerID;
      this._map = newMap;
      this._vector = newVector;
      return this;
    }
    return makeOrderedMap(newMap, newVector, ownerID);
  }
}

OrderedMap.from = OrderedMap;

function makeOrderedMap(map, vector, ownerID) {
  var omap = Object.create(OrderedMap.prototype);
  omap.length = map ? map.length : 0;
  omap._map = map;
  omap._vector = vector;
  omap.__ownerID = ownerID;
  return omap;
}

var EMPTY_ORDERED_MAP;
<|endoftext|>


<commit_before>var NotificationsDropdown = require('../../../../../../javascripts/cartodb/common/views/dashboard_header/notifications/dropdown_view');
var UserNotifications = require('../../../../../../javascripts/cartodb/common/views/dashboard_header/notifications/view');
var cdbAdmin = require('cdb.admin');
var LocalStorage = require('../../../../../../javascripts/cartodb/common/local_storage');
var NotificationsModel = require('../../../../../../javascripts/cartodb/common/views/dashboard_header/notifications/model');
var OrganizationNotificationsModel = require('../../../../../../javascripts/cartodb/common/views/dashboard_header/notifications/organization-model');

describe("common/views/dashboard_header/notifications/view", function() {
  var view, user;

  beforeEach(function() {
    jasmine.Ajax.install();

    jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*/notifications')).andReturn({
      status: 200
    });

    window.organization_notifications = [
      {
        id: 'n-1',
        icon: 'alert',
        html_body: '
html_body
\n',
        received_at: '2017-03-24T16:58:28+00:00',
        read_at: null
      }
    ];

    this.localStorage = new LocalStorage('test');
    this.localStorage.remove('notification.close_limits');

    user = new cdbAdmin.User({
      base_url: 'http://coronelli.carto.com/u/test-user',
      account_type: 'CORONELLI',
      remaining_byte_quota: 900,
      quota_in_bytes: 1000,
      trial_ends_at: null,
      username: 'test-user',
      table_count: 1,
      id: 123,
      api_key: 'xyz123'
    });

    spyOn(NotificationsDropdown.prototype, 'initialize').and.callThrough();
    spyOn(OrganizationNotificationsModel.prototype, 'initialize').and.callThrough();
    spyOn(OrganizationNotificationsModel.prototype, 'markAsRead').and.callThrough();

    view = new UserNotifications({
      user: user,
      localStorage: this.localStorage
    });
  });

  describe('render', function() {
    it('should render properly', function() {
      view.render();
      expect(view.$el.hasClass('UserNotifications')).toBeTruthy();
      expect(view.$('.UserNotifications-Icon').length).toBe(1);
      expect(view.$('.UserNotifications-badge').length).toBe(1);
      expect(view.$('.UserNotifications-badge').text()).toBe('1');
      expect(view.$el.hasClass('has--alerts')).toBeTruthy();

      user.set('remaining_byte_quota', 10);
      expect(view.$('.UserNotifications-badge').length).toBe(1);
      expect(view.$('.UserNotifications-badge').text()).toBe('2');
      expect(view.$el.hasClass('has--alerts')).toBeTruthy();

      user.set('remaining_byte_quota', 1000);
      expect(view.$('.UserNotifications-badge').length).toBe(1);
      expect(view.$('.UserNotifications-badge').text()).toBe('1');
      expect(view.$el.hasClass('has--alerts')).toBeTruthy();
    });

    it('should have no leaks', function() {
      view.render();
      expect(view).toHaveNoLeaks();
    });

  });

  describe('click', function() {
    beforeEach(function() {
      view.render();
      spyOn(NotificationsDropdown.prototype, 'render').and.callThrough();
      spyOn(NotificationsDropdown.prototype, 'open').and.callThrough();
      spyOn(NotificationsDropdown.prototype, 'on').and.callThrough();
      spyOn(NotificationsDropdown.prototype, 'clean').and.callThrough();

      this.clickSettings = function() {
        view.$el.click();
      };
    });

    it('should create dropdown view with expected params', function() {
      this.clickSettings();
      expect(NotificationsDropdown.prototype.initialize).toHaveBeenCalled();
      expect(OrganizationNotificationsModel.prototype.initialize).toHaveBeenCalledTimes(1);

      var viewToBeInstantiatedWith = NotificationsDropdown.prototype.initialize.calls.argsFor(0)[0];
      expect(viewToBeInstantiatedWith.target[0]).toEqual(view.$el[0]);
      expect(viewToBeInstantiatedWith.template_base).toEqual('common/views/dashboard_header/notifications/templates/dropdown');
    });

    it('should have rendered and opened the dropdown view', function() {
      this.clickSettings();
      expect(NotificationsDropdown.prototype.render).toHaveBeenCalled();
      expect(NotificationsDropdown.prototype.open).toHaveBeenCalled();
    });

    it('should add the dropdown view to the child views', function() {
      var spy = spyOn(view, 'addView');
      this.clickSettings();
      expect(spy).toHaveBeenCalled();
    });

    it('should mark organization notification as read after the dropdown is hidden', function(done) {
      expect(view.collection.length).toBe(1);

      view._onDropdownHidden(view.notification);

      setTimeout(function() {
        expect(OrganizationNotificationsModel.prototype.markAsRead).toHaveBeenCalledTimes(1);
        expect(view.collection.length).toBe(0);
        done();
      }, 400);
    });

    it('should clean up dropdown view after the dropdown is hidden', function(done) {
      this.clickSettings();

      expect(NotificationsDropdown.prototype.on).toHaveBeenCalledWith('onDropdownHidden', jasmine.any(Function), view);

      // trigger the listener callback manually to verify assertions below
      NotificationsDropdown.prototype.on.calls.argsFor(0)[1].call(view);

      setTimeout(function() {
        expect(NotificationsDropdown.prototype.clean).toHaveBeenCalled();
        expect(view.$el.hasClass('has--alerts')).toBeFalsy();
        done();
      }, 400);
    });

    it('should close any other open dialogs', function() {
      spyOn(cdb.god, 'trigger');
      this.clickSettings();
      expect(cdb.god.trigger).toHaveBeenCalledWith('closeDialogs');
    });
  });

  describe('collection', function() {
    it('should generate collection data', function() {
      expect(view.collection.size()).toBe(1);
    });

    it('should reset collection data when user data is changed', function() {
      var spy = spyOn(view.collection, 'reset');
      user.set('remaining_byte_quota', 10);
      expect(spy).toHaveBeenCalled();
    });
  });

  afterEach(function() {
    jasmine.Ajax.uninstall();

    view.clean();
  });

});
<commit_msg>make tests pass<commit_after>var NotificationsDropdown = require('../../../../../../javascripts/cartodb/common/views/dashboard_header/notifications/dropdown_view');
var UserNotifications = require('../../../../../../javascripts/cartodb/common/views/dashboard_header/notifications/view');
var cdbAdmin = require('cdb.admin');
var LocalStorage = require('../../../../../../javascripts/cartodb/common/local_storage');
var NotificationsModel = require('../../../../../../javascripts/cartodb/common/views/dashboard_header/notifications/model');
var OrganizationNotificationsModel = require('../../../../../../javascripts/cartodb/common/views/dashboard_header/notifications/organization-model');

describe("common/views/dashboard_header/notifications/view", function() {
  var view, user;

  beforeEach(function() {
    jasmine.Ajax.install();

    jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*/notifications')).andReturn({
      status: 200
    });

    window.organization_notifications = [
      {
        id: 'n-1',
        icon: 'alert',
        html_body: '
html_body
\n',
        received_at: '2017-03-24T16:58:28+00:00',
        read_at: null
      }
    ];

    this.localStorage = new LocalStorage('test');
    this.localStorage.remove('notification.close_limits');

    user = new cdbAdmin.User({
      base_url: 'http://coronelli.carto.com/u/test-user',
      account_type: 'CORONELLI',
      remaining_byte_quota: 900,
      quota_in_bytes: 1000,
      trial_ends_at: null,
      username: 'test-user',
      table_count: 1,
      id: 123,
      api_key: 'xyz123'
    });

    spyOn(NotificationsDropdown.prototype, 'initialize').and.callThrough();
    spyOn(OrganizationNotificationsModel.prototype, 'initialize').and.callThrough();
    spyOn(OrganizationNotificationsModel.prototype, 'markAsRead').and.callThrough();

    view = new UserNotifications({
      user: user,
      localStorage: this.localStorage
    });
  });

  describe('render', function() {
    it('should render properly', function() {
      view.render();
      expect(view.$el.hasClass('UserNotifications')).toBeTruthy();
      expect(view.$('.UserNotifications-Icon').length).toBe(1);
      expect(view.$('.UserNotifications-badge').length).toBe(1);
      expect(view.$('.UserNotifications-badge').text()).toBe('1');
      expect(view.$el.hasClass('has--alerts')).toBeTruthy();

      user.set('remaining_byte_quota', 10);
      expect(view.$('.UserNotifications-badge').length).toBe(1);
      expect(view.$('.UserNotifications-badge').text()).toBe('2');
      expect(view.$el.hasClass('has--alerts')).toBeTruthy();

      user.set('remaining_byte_quota', 1000);
      expect(view.$('.UserNotifications-badge').length).toBe(1);
      expect(view.$('.UserNotifications-badge').text()).toBe('1');
      expect(view.$el.hasClass('has--alerts')).toBeTruthy();
    });

    it('should have no leaks', function() {
      view.render();
      expect(view).toHaveNoLeaks();
    });

  });

  describe('click', function() {
    beforeEach(function() {
      view.render();
      spyOn(NotificationsDropdown.prototype, 'render').and.callThrough();
      spyOn(NotificationsDropdown.prototype, 'open').and.callThrough();
      spyOn(NotificationsDropdown.prototype, 'on');
      spyOn(NotificationsDropdown.prototype, 'clean').and.callThrough();

      this.clickSettings = function() {
        view.$el.click();
      };
    });

    it('should create dropdown view with expected params', function() {
      this.clickSettings();
      expect(NotificationsDropdown.prototype.initialize).toHaveBeenCalled();
      expect(OrganizationNotificationsModel.prototype.initialize).toHaveBeenCalledTimes(1);

      var viewToBeInstantiatedWith = NotificationsDropdown.prototype.initialize.calls.argsFor(0)[0];
      expect(viewToBeInstantiatedWith.target[0]).toEqual(view.$el[0]);
      expect(viewToBeInstantiatedWith.template_base).toEqual('common/views/dashboard_header/notifications/templates/dropdown');
    });

    it('should have rendered and opened the dropdown view', function() {
      this.clickSettings();
      expect(NotificationsDropdown.prototype.render).toHaveBeenCalled();
      expect(NotificationsDropdown.prototype.open).toHaveBeenCalled();
    });

    it('should add the dropdown view to the child views', function() {
      var spy = spyOn(view, 'addView');
      this.clickSettings();
      expect(spy).toHaveBeenCalled();
    });

    it('should mark organization notification as read after the dropdown is hidden', function(done) {
      expect(view.collection.length).toBe(1);

      this.clickSettings();

      // trigger the listener callback manually to verify assertions below
      view._onDropdownHidden(view.notification);

      setTimeout(function() {
        expect(OrganizationNotificationsModel.prototype.markAsRead).toHaveBeenCalledTimes(1);
        expect(view.collection.length).toBe(0);
        done();
      }, 400);
    });

    it('should clean up dropdown view after the dropdown is hidden', function(done) {
      this.clickSettings();

      expect(NotificationsDropdown.prototype.on).toHaveBeenCalledWith('onDropdownHidden', jasmine.any(Function), view);

      // trigger the listener callback manually to verify assertions below
      view._onDropdownHidden(view.notification);

      setTimeout(function() {
        expect(NotificationsDropdown.prototype.clean).toHaveBeenCalled();
        expect(view.$el.hasClass('has--alerts')).toBeFalsy();
        done();
      }, 400);
    });

    it('should close any other open dialogs', function() {
      spyOn(cdb.god, 'trigger');
      this.clickSettings();
      expect(cdb.god.trigger).toHaveBeenCalledWith('closeDialogs');
    });
  });

  describe('collection', function() {
    it('should generate collection data', function() {
      expect(view.collection.size()).toBe(1);
    });

    it('should reset collection data when user data is changed', function() {
      var spy = spyOn(view.collection, 'reset');
      user.set('remaining_byte_quota', 10);
      expect(spy).toHaveBeenCalled();
    });
  });

  afterEach(function() {
    jasmine.Ajax.uninstall();

    view.clean();
  });

});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { Animated, Dimensions, Image, StyleSheet, View, Text, TouchableHighlight } from 'react-native';
import { Constants } from 'expo';
import RootNavigation from '../navigation/RootNavigation';

const PAGE_WIDTH = Dimensions.get('window').width;
const PAGES = [
	{
		message: "The best solution to finding the proper rest you need.",
		moreMessage: "Swipe to get started",
		backgroundColor: "#233243"
	},
	{
		message: "Listen in a quiet place",
		moreMessage: "Important!",
		backgroundColor: "#7E8578"
	},
	{
		message: "Get super comfey",
		moreMessage: "Like, reaallyyy comfey.",
		backgroundColor: "#C17918"
	},
	{
		message: "Turn phone off",
		moreMessage: "More best practices in the 'app guide'",
		backgroundColor: "#0B5F6A"
	},
	{
		message: "Choose meditation",
		moreMessage: "Pick the meditation that works with your schedule.",
		backgroundColor: "#695065"
	}
]

class TourScreen extends Component {

	state = {
    scroll: new Animated.Value(0),
  };

  render() {

    const position = Animated.divide(this.state.scroll, PAGE_WIDTH);
    const backgroundColor = position.interpolate({
      inputRange: PAGES.map((_, i) => i),
      outputRange: PAGES.map(p => p.backgroundColor),
    });

		function renderIf(condition, content) {
	    if (condition) {
	        return content;
	    } else {
	        return null;
	    }
		}

    return (
			<View style={styles.container}>
					<Animated.View style={[ StyleSheet.absoluteFill, { backgroundColor } ]} />
					 <View
		          style={{
		            position: 'absolute',
		            top: 0,
		            left: 0,
		            width: '100%',
		            height: '100%',
		          }}
		        >
						<Image
							style={styles.image}
							source={ require('../assets/images/clouds/clouds-tour-5.png') } />
					</View>
					<Animated.ScrollView
						horizontal
						pagingEnabled
						showsHorizontalScrollIndicator={false}
						scrollEventThrottle={16}
						onScroll={Animated.event(
							[ { nativeEvent: { contentOffset: { x: this.state.scroll } } } ],
						)}>

						{PAGES.map((page, i) => (
							<View key={i} style={styles.page}>
								<View style={[ styles.card ]}>
									<Text style={styles.message}>{page.message}</Text>
									<Text style={styles.moreMessage}>{page.moreMessage}</Text>
									{renderIf(i===4,
										<View style={styles.button}>
											<TouchableHighlight onPress={() => {this.props.handler()}}>
												<Text style={styles.buttonText}>
													Get Started
												</Text>
											</TouchableHighlight>
										</View>
	                )}
								</View>
							</View>
						))}

					</Animated.ScrollView>
			</View>
		);
	}
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  image: {
  	flex: 1,
  	resizeMode: 'contain',
  	top: 100
  },
  message: {
    fontSize: PAGE_WIDTH/12,
    fontWeight: 'bold',
    color: '#fff',
    backgroundColor: 'transparent',
    textAlign: 'center'
  },
  moreMessage: {
    fontSize: PAGE_WIDTH/18,
    color: '#fff',
    backgroundColor: 'transparent',
    marginTop: 20,
    lineHeight: 25,
    textAlign: 'center'
  },
  page: {
    width: PAGE_WIDTH,
    paddingTop: Constants.statusBarHeight + 48,
  },
  card: {
    position: 'absolute',
    margin: 12,
    marginTop: PAGE_WIDTH / 3,
    left: 12,
    top: 0,
    right: 0,
    borderRadius: 8,
    paddingHorizontal: 24,
    paddingTop: 16,
    paddingBottom: 300,
  },
  button: {
    flex: 1,
    backgroundColor: 'rgba(0,0,0, 0.3)',
    position: 'absolute',
    margin: 12,
    marginTop: 300,
    alignSelf: 'center',
    borderRadius: 50,
    // paddingLeft: 10,
    // paddingRight: 10
  },
  buttonText: {
    margin: 15,
    marginLeft: 50,
    marginRight: 40,
    color: '#fff',
    fontSize: PAGE_WIDTH/16,
    // fontVariant: ['small-caps'],
  }
});

export default TourScreen;
<commit_msg>Setup switch statement for card images in Tour<commit_after>import React, { Component } from 'react';
import { Animated, Dimensions, Image, StyleSheet, View, Text, TouchableHighlight } from 'react-native';
import { Constants } from 'expo';
import RootNavigation from '../navigation/RootNavigation';

const PAGE_WIDTH = Dimensions.get('window').width;
const PAGES = [
	{
		id: 1,
		message: "The best solution to finding the proper rest you need.",
		moreMessage: "Swipe to get started",
		backgroundColor: "#233243"
	},
	{
		id: 2,
		message: "Listen in a quiet place",
		moreMessage: "Important!",
		backgroundColor: "#7E8578"
	},
	{
		id: 3,
		message: "Get super comfey",
		moreMessage: "Like, reaallyyy comfey.",
		backgroundColor: "#C17918"
	},
	{
		id: 4,
		message: "Turn phone off",
		moreMessage: "More best practices in the 'app guide'",
		backgroundColor: "#0B5F6A"
	},
	{
		id: 5,
		message: "Choose meditation",
		moreMessage: "Pick the meditation that works with your schedule.",
		backgroundColor: "#695065"
	}
]

class TourScreen extends Component {

  constructor(props) {
    super(props);
    this.state = {
    	scroll: new Animated.Value(0),
    	currentCard: 1
    }
   }

  componentDidMount() {
  	this.state.currentCard = 0
  }

_getImage() {
	switch (this.state.currentCard) {
		case 1:
			return require('../assets/images/clouds/clouds-tour-5.png')
			break;
		case 2:
			return require('../assets/images/clouds/clouds-tour-2.png')
			break;
		case 3:
			return require('../assets/images/clouds/clouds-tour-3.png')
			break;
		case 4:
			return require('../assets/images/clouds/clouds-tour-4.png')
			break;
		case 5:
			return require('../assets/images/clouds/clouds-tour-5.png')
			break;
	}
}

  render() {

    const position = Animated.divide(this.state.scroll, PAGE_WIDTH);
    const backgroundColor = position.interpolate({
      inputRange: PAGES.map((_, i) => i),
      outputRange: PAGES.map(p => p.backgroundColor),
    });

		function renderIf(condition, content) {
	    if (condition) {
	        return content;
	    } else {
	        return null;
	    }
		}

    return (
			<View style={styles.container}>
					<Animated.View style={[ StyleSheet.absoluteFill, { backgroundColor } ]} />
					 <View
		          style={{
		            position: 'absolute',
		            top: 0,
		            left: 0,
		            width: '100%',
		            height: '100%',
		          }}
		        >
						<Image
							style={styles.image}
							source={ this._getImage() } />
					</View>
					<Animated.ScrollView
						horizontal
						pagingEnabled
						showsHorizontalScrollIndicator={false}
						scrollEventThrottle={16}
						onScroll={Animated.event(
							[ { nativeEvent: { contentOffset: { x: this.state.scroll } } } ],
						)}>

						{PAGES.map((page, i) => (
							<View key={i} style={styles.page}>
								<View style={[ styles.card ]}>
									<Text style={styles.message}>{page.message}</Text>
									<Text style={styles.moreMessage}>{page.moreMessage}</Text>
									{renderIf(i===4,
										<View style={styles.button}>
											<TouchableHighlight onPress={() => {this.props.handler()}}>
												<Text style={styles.buttonText}>
													Get Started
												</Text>
											</TouchableHighlight>
										</View>
	                )}
								</View>
							</View>
						))}

					</Animated.ScrollView>
			</View>
		);
	}
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  image: {
  	flex: 1,
  	resizeMode: 'contain',
  	top: 100
  },
  message: {
    fontSize: PAGE_WIDTH/12,
    fontWeight: 'bold',
    color: '#fff',
    backgroundColor: 'transparent',
    textAlign: 'center'
  },
  moreMessage: {
    fontSize: PAGE_WIDTH/18,
    color: '#fff',
    backgroundColor: 'transparent',
    marginTop: 20,
    lineHeight: 25,
    textAlign: 'center'
  },
  page: {
    width: PAGE_WIDTH,
    paddingTop: Constants.statusBarHeight + 48,
  },
  card: {
    position: 'absolute',
    margin: 12,
    marginTop: PAGE_WIDTH / 3,
    left: 12,
    top: 0,
    right: 0,
    borderRadius: 8,
    paddingHorizontal: 24,
    paddingTop: 16,
    paddingBottom: 300,
  },
  button: {
    flex: 1,
    backgroundColor: 'rgba(0,0,0, 0.3)',
    position: 'absolute',
    margin: 12,
    marginTop: 300,
    alignSelf: 'center',
    borderRadius: 50,
    // paddingLeft: 10,
    // paddingRight: 10
  },
  buttonText: {
    margin: 15,
    marginLeft: 50,
    marginRight: 40,
    color: '#fff',
    fontSize: PAGE_WIDTH/16,
    // fontVariant: ['small-caps'],
  }
});

export default TourScreen;
<|endoftext|>