<commit_before>var AD = require('AppDev');
var $ = require('jquery');

module.exports = $.Window('AppDev.UI.AddContactWindow', {
    setup: function() {
        // When this class is created, initialize the static fields object
        this.fields.forEach(function(field) {
            // The database field name defaults to contact_ prepended to the field's name
            field.field = field.field || 'contact_'+field.name;

            field.callback = field.callback || 'change'+$.capitalize(field.name);
            field.labelId = field.label || field.name+'Label';
        });
    },
    dependencies: ['ChooseOptionWindow'],

    // Return an array of all the values in a multivalue dictionary
    flattenMultivalue: function(multivalue) {
        var values = [];
        $.each(multivalue, function(key, value) {
            // "value" is an array of values
            values = values.concat(value);
        });
        return values;
    },

    // Return a new contact model instance
    createContact: function(attrs) {
        var localContact = attrs.contact_recordId === null ? null : Ti.Contacts.getPersonByID(attrs.contact_recordId);
        var fullName = '', firstName = '', lastName = '', nickname = '', defaultPhone = null, defaultEmail = null, note = '';
        if (localContact) {
            fullName = localContact.fullName || '';
            firstName = localContact.firstName || '';
            lastName = localContact.lastName || '';
            nickname = localContact.nickname || '';
            if (AD.Platform.isAndroid) {
                // Android does not allow access to the firstName, lastName, or nickname properties, so attempt to guess them
                var nameParts = fullName.split(' ');
                firstName = firstName || nameParts[0];
                lastName = lastName || (nameParts.length === 1 ? '' : nameParts[nameParts.length - 1]);
                nickname = nickname || firstName;
            }

            // Use the first phone number and email address as the default
            defaultPhone = this.flattenMultivalue(localContact.phone)[0];
            defaultEmail = this.flattenMultivalue(localContact.email)[0];

            note = localContact.note || '';
        }
        var defaultYear = 1;

        // Populate the contact model fields with the new contact's information
        var baseAttrs = {
            contact_firstName: firstName,
            contact_lastName: lastName,
            contact_nickname: nickname,
            campus_uuid: null,
            year_id: defaultYear,
            contact_phone: defaultPhone,
            contact_email: defaultEmail,
            contact_notes: note
        };
        var mergedAttrs = $.extend(baseAttrs, attrs);
        mergedAttrs.campus_label = mergedAttrs.campus_uuid ? AD.Models.Campus.cache.getById(mergedAttrs.campus_uuid).getLabel() : '';
        mergedAttrs.year_label = AD.Models.Year.cache.getById(mergedAttrs.year_id).getLabel();
        return new AD.Models.Contact(mergedAttrs);
    },

    fields: [
        {name: 'firstName', type: 'text'},
        {name: 'lastName', type: 'text'},
        {name: 'campus', type: 'choice', field: 'campus_label'},
        {name: 'year', type: 'choice', field: 'year_label'},
        {name: 'phone', type: 'choice/text', keyboardType: Ti.UI.KEYBOARD_PHONE_PAD, autocapitalization: Ti.UI.TEXT_AUTOCAPITALIZATION_NONE},
        {name: 'email', type: 'choice/text', keyboardType: Ti.UI.KEYBOARD_EMAIL, autocapitalization: Ti.UI.TEXT_AUTOCAPITALIZATION_NONE},
        {name: 'notes', type: 'text', multiline: true}
    ],

    actions: [{
        callback: 'save',
        menuItem: false,
        onClose: true
    }, {
        title: 'cancel',
        callback: 'cancel',
        rightNavButton: true
    }]
}, {
    init: function(options) {
        var _this = this;

        this.operation = options.operation;
        var getContactDfd = $.Deferred();

        // This handler must be attached before the handler that calls this.initialize in $.Window
        getContactDfd.done(this.proxy(function(contactData) {
            this.inAddressBook = contactData.localContact ? true : false;

            // Build the fields array which is the same as the static fields array, with types expanded
            this.fields = this.constructor.fields.map(function(field) {
                // Expand the type property
                var types = field.type.split('/');
                var type = types[(this.inAddressBook || types.length === 1) ? 0 : 1];

                // Proxy the choice callback
                var callback = null;
                if (type === 'choice') {
                    callback = this.proxy(field.callback);
                }

                // Clone the field to prevent aliasing
                return $.extend({}, field, {type: type, callback: callback});
            }, this);

            this.contact = contactData.contact;
            this.attrs = this.contact.attrs(); // the changed contact attrs
            this.localContact = contactData.localContact;
            this.window.title = AD.Localize(this.operation+'Contact');
            this.open();
        }));

        // Initialize the base $.Window object
        // Pass in deferreds to delay the execution of this.create and this.initialize until a contact is chosen
        this._super({
            createDfd: getContactDfd.promise(),
            initializeDfd: getContactDfd.promise(),
            createParams: {
                layout: 'vertical'
            }
        });

        if (this.operation === 'import') {
            // Load an existing contact from the user's address book
            var chooseContactDfd = $.Deferred();
            Titanium.Contacts.showContacts({
                canceled: chooseContactDfd.reject,
                selectedPerson: function(event) {
                    chooseContactDfd.resolve(event.person);
                }
            });
            chooseContactDfd.done(this.proxy(function(selectedContact) {
                var contactRecordId = selectedContact.recordId || selectedContact.id; // recordId on iOS and id on Android
                var existingContacts = AD.Models.Contact.cache.query({contact_recordId: contactRecordId});
                var contact = null;
                if (existingContacts.length > 0) {
                    if (existingContacts.length > 1) {
                        console.warn('Found multiple contacts with the same recordId!');
                    }
                    // A contact was chosen that already exists, so edit the contact
                    contact = existingContacts[0];
                    this.operation = 'edit';
                }
                else {
                    contact = this.constructor.createContact({contact_recordId: contactRecordId});
                }
                getContactDfd.resolve({
                    contact: contact,
                    localContact: selectedContact
                });
            })).fail(this.dfd.reject); // Cancel the add contact operation
        }
        else if (this.operation === 'edit') {
            // Load the existing contact from the address book
            var recordId = options.existingContact.contact_recordId;
            var localContact = recordId === null ? null : Ti.Contacts.getPersonByID(recordId);
            getContactDfd.resolve({
                contact: options.existingContact,
                localContact: localContact
            });
        }
        else if (this.operation === 'create') {
            // Create a contact model not tied to an address book entry
            var contact = this.constructor.createContact({contact_recordId: null});
            getContactDfd.resolve({
                contact: contact,
                localContact: null
            });
        }
    },

    // Create each of the form fields
    create: function() {
        var labelWidth = 80;
        var rowHeight = 40;

        var focusedTextField = null;
        var hideKeyboard = function() {
            if (focusedTextField) {
                // Unfocus the previously selected text field to hide the keyboard
                focusedTextField.blur();
                focusedTextField = null;
            }
        };

        // Scrollable container that will hold the field rows on non-iPhone platforms
        var table = Ti.UI.createScrollView({
            left: 0,
            top: 0,
            width: AD.UI.screenWidth,
            height: Ti.UI.FILL,
            layout: 'vertical',
            scrollType: 'vertical',
            contentHeight: 'auto',
            showVerticalScrollIndicator: true
        });

        // Create the form fields
        // On iPhone, attempt to mimic the built-in Contacts app

        // Create each of the field views
        var rows = [];
        this.fields.forEach(function(field, index) {
            // Create the field row container, a table view row on iPhone and a generic view on other platforms
            var fieldRow = AD.Platform.isiPhone ? Ti.UI.createTableViewRow({}) : Ti.UI.createView({
                left: AD.UI.padding,
                right: 0,
                top: 0
            });
            fieldRow.height = rowHeight;
            fieldRow.index = index;

            // Create the field name label
            var label = Ti.UI.createLabel({
                left: 0,
                width: labelWidth,
                height: Ti.UI.SIZE,
                text: AD.Localize(field.name)
            });
            fieldRow.add(label);
            if (AD.Platform.isiPhone) {
                label.applyProperties({
                    text: label.text.toLowerCase(),
                    textAlign: 'right',
                    color: AD.UI.systemBlueColor,
                    font: {fontSize: 15, fontWeight: 'bold'} // medium-small bold
                });
            }

            var fieldValue = this.attrs[field.field];
            var fieldView = null;
            if (field.type === 'choice') {
                // Create the value label
                if (AD.Platform.isiPhone) {
                    fieldView = Ti.UI.createLabel({
                        left: labelWidth + AD.UI.padding,
                        width: Ti.UI.FILL,
                        height: Ti.UI.FILL,
                        text: fieldValue
                    });
                }
                else {
                    fieldView = Ti.UI.createButton({
                        left: labelWidth + AD.UI.padding,
                        right: AD.UI.padding,
                        center: { y: rowHeight / 2 },
                        height: AD.UI.buttonHeight,
                        title: fieldValue || AD.Localize('unspecified')
                    });
                    // When a choice row is clicked, call the callback that will presumably allow the user to choose a value
                    fieldView.addEventListener('click', field.callback);
                }
            }
            else if (field.type === 'text') {
                if (field.multiline === true) {
                    fieldView = Ti.UI.createTextArea({
                        left: labelWidth + AD.UI.padding,
                        right: AD.UI.padding,
                        height: Ti.UI.FILL,
                        font: AD.UI.Fonts.small,
                        suppressReturn: false
                    });
                    // Make the row taller to accommodate the text area
                    fieldRow.height *= 3;
                }
                else {
                    fieldView = Ti.UI.createTextField({
                        left: labelWidth + AD.UI.padding,
                        right: AD.UI.padding,
                        center: { y: rowHeight / 2 },
                        height: AD.UI.textFieldHeight
                    });
                }

                fieldView.value = fieldValue;

                if (field.keyboardType) {
                    fieldView.keyboardType = field.keyboardType;
                }
                if (field.autocapitalization) {
                    fieldView.autocapitalization = field.autocapitalization;
                }
            }

            fieldView.addEventListener('focus', function() {
                // Keep track of which text field (or text area) is currently selected
                focusedTextField = fieldView;
            });

            // Add the field to the row
            fieldRow.add(this.record(field.labelId, fieldView));
            rows.push(fieldRow);

            if (!AD.Platform.isiPhone) {
                table.add(fieldRow);
            }
        }, this);

        // On iPhone, hideKeyboard does not work when called from the window click
        // handler, so workaround by calling hideKeybaord from the table click handler
        if (AD.Platform.isiPhone) {
            // Create the fields table that holds the year, phone number, and email address fields
            var iPhoneTable = this.add(Ti.UI.createTableView({
                data: rows,
                style: Ti.UI.iPhone.TableViewStyle.GROUPED
            }));
            iPhoneTable.addEventListener('click', this.proxy(function(event) {
                hideKeyboard();

                var field = this.fields[event.row.index];
                if (field.type === 'choice') {
                    // When a choice row is clicked, call the callback that will presumably allow the user to choose a value
                    field.callback();
                }
            }));
        }
        else {
            this.add(table);

            // Click anywhere on the window to hide the keyboard
            this.window.addEventListener('click', function(event) {
                hideKeyboard();
            });
        }
    },

    // Set the initial contents of the form fields
    initialize: function() {
        var localContact = this.localContact;
        this.phoneNumbers = localContact && this.constructor.flattenMultivalue(localContact.phone);
        this.emailAddresses = localContact && this.constructor.flattenMultivalue(localContact.email);
    },

    // Handlers for allowing the user to change the contact's campus, year, phone number, and e-mail address
    changeCampus: function() {
        // Allow the user to set the contact's campus
        this.createWindow('ChooseOptionWindow', {
            groupName: 'campus',
            Model: 'Campus',
            initial: this.attrs.campus_uuid,
            editable: true
        }).getDeferred().done(this.proxy(function(campus) {
            // A campus was chosen
            var label = campus ? campus.getLabel() : null;
            this.attrs.campus_uuid = campus ? campus.getId() : null;
            this.attrs.campus_label = label;
            var campusLabel = this.getChild('campusLabel');
            campusLabel.text = campusLabel.title = label;
        }));
    },
    changeYear: function() {
        // Allow the user to choose the year of this contact
        this.createWindow('ChooseOptionWindow', {
            groupName: 'year',
            Model: 'Year',
            initial: this.attrs.year_id
        }).getDeferred().done(this.proxy(function(year) {
            // A year was chosen
            var label = year.getLabel();
            this.attrs.year_id = year.getId();
            this.attrs.year_label = label;
            var yearLabel = this.getChild('yearLabel');
            yearLabel.text = yearLabel.title = label;
        }));
    },
    changePhone: function() {
        // Allow the user to choose the phone number to associate with this contact
        this.createWindow('ChooseOptionWindow', {
            groupName: 'phone',
            initial: this.attrs.contact_phone,
            options: this.phoneNumbers
        }).getDeferred().done(this.proxy(function(phoneNumber) {
            // A phone number was chosen
            this.attrs.contact_phone = phoneNumber.value;
            var phoneLabel = this.getChild('phoneLabel');
            phoneLabel.text = phoneLabel.title = phoneNumber.value;
        }));
    },
    changeEmail: function() {
        // Allow the user to choose the email address to associate with this contact
        this.createWindow('ChooseOptionWindow', {
            groupName: 'email',
            initial: this.attrs.contact_email,
            options: this.emailAddresses
        }).getDeferred().done(this.proxy(function(emailAddress) {
            // An email address was chosen
            this.attrs.contact_email = emailAddress.value;
            var emailLabel = this.getChild('emailLabel');
            emailLabel.text = emailLabel.title = emailAddress.value;
        }));
    },

    // Update the contact model and close the window
    save: function() {
        // Read the values of the text fields
        this.fields.forEach(function(field) {
            if (field.type === 'text') {
                this.attrs[field.field] = this.children[field.labelId].value;
            }
        }, this);

        // regular expression testing on email
        // it passes all validate emails and rejects some common mistakes
        if(this.attrs.contact_email !== null && (!/[^@]+@[^@]+\.[^@]+/.test(this.attrs.contact_email))) {
            alert("Email is not valid: " + this.attrs.contact_email);
        }

        // Apply the changes to the contact
        var oldCampus_uuid = this.contact.attr('campus_uuid');
        this.contact.attrs(this.attrs);

        if (this.options.autoSave !== false) {
            // Create/update the contact's record in the database unless
            // explicitly prevented by the autoSave option being set to false
            this.contact.save();

            var campus_uuid = this.contact.attr('campus_uuid');
            if (campus_uuid && campus_uuid !== oldCampus_uuid) {
                // The contact's campus changed, so create the missing ContactStep entries
                var contact_uuid = this.contact.getId();
                var existingContactSteps = this.contact.getSteps();
                var indexedExistingContactSteps = $.indexArray(existingContactSteps, 'step_uuid');
                var steps = AD.Models.Step.cache.query({
                    campus_uuid: campus_uuid
                });
                steps.forEach(function(step) {
                    var step_uuid = step.getId();
                    if (!indexedExistingContactSteps[step_uuid]) {
                        // The ContactStep for this contact-step pair does not yet exist, so create it now
                        var contactStep = new AD.Models.ContactStep({
                            contact_uuid: contact_uuid,
                            step_uuid: step_uuid,
                            step_label: step.getLabel(),
                            step_date: null
                        });
                        contactStep.save();
                    }
                });
            }
        }

        this.dfd.resolve(this.contact);
    }
});
<commit_msg>Do not attempt to validate empty email addresses<commit_after>var AD = require('AppDev');
var $ = require('jquery');

module.exports = $.Window('AppDev.UI.AddContactWindow', {
    setup: function() {
        // When this class is created, initialize the static fields object
        this.fields.forEach(function(field) {
            // The database field name defaults to contact_ prepended to the field's name
            field.field = field.field || 'contact_'+field.name;

            field.callback = field.callback || 'change'+$.capitalize(field.name);
            field.labelId = field.label || field.name+'Label';
        });
    },
    dependencies: ['ChooseOptionWindow'],

    // Return an array of all the values in a multivalue dictionary
    flattenMultivalue: function(multivalue) {
        var values = [];
        $.each(multivalue, function(key, value) {
            // "value" is an array of values
            values = values.concat(value);
        });
        return values;
    },

    // Return a new contact model instance
    createContact: function(attrs) {
        var localContact = attrs.contact_recordId === null ? null : Ti.Contacts.getPersonByID(attrs.contact_recordId);
        var fullName = '', firstName = '', lastName = '', nickname = '', defaultPhone = null, defaultEmail = null, note = '';
        if (localContact) {
            fullName = localContact.fullName || '';
            firstName = localContact.firstName || '';
            lastName = localContact.lastName || '';
            nickname = localContact.nickname || '';
            if (AD.Platform.isAndroid) {
                // Android does not allow access to the firstName, lastName, or nickname properties, so attempt to guess them
                var nameParts = fullName.split(' ');
                firstName = firstName || nameParts[0];
                lastName = lastName || (nameParts.length === 1 ? '' : nameParts[nameParts.length - 1]);
                nickname = nickname || firstName;
            }

            // Use the first phone number and email address as the default
            defaultPhone = this.flattenMultivalue(localContact.phone)[0];
            defaultEmail = this.flattenMultivalue(localContact.email)[0];

            note = localContact.note || '';
        }
        var defaultYear = 1;

        // Populate the contact model fields with the new contact's information
        var baseAttrs = {
            contact_firstName: firstName,
            contact_lastName: lastName,
            contact_nickname: nickname,
            campus_uuid: null,
            year_id: defaultYear,
            contact_phone: defaultPhone,
            contact_email: defaultEmail,
            contact_notes: note
        };
        var mergedAttrs = $.extend(baseAttrs, attrs);
        mergedAttrs.campus_label = mergedAttrs.campus_uuid ? AD.Models.Campus.cache.getById(mergedAttrs.campus_uuid).getLabel() : '';
        mergedAttrs.year_label = AD.Models.Year.cache.getById(mergedAttrs.year_id).getLabel();
        return new AD.Models.Contact(mergedAttrs);
    },

    fields: [
        {name: 'firstName', type: 'text'},
        {name: 'lastName', type: 'text'},
        {name: 'campus', type: 'choice', field: 'campus_label'},
        {name: 'year', type: 'choice', field: 'year_label'},
        {name: 'phone', type: 'choice/text', keyboardType: Ti.UI.KEYBOARD_PHONE_PAD, autocapitalization: Ti.UI.TEXT_AUTOCAPITALIZATION_NONE},
        {name: 'email', type: 'choice/text', keyboardType: Ti.UI.KEYBOARD_EMAIL, autocapitalization: Ti.UI.TEXT_AUTOCAPITALIZATION_NONE},
        {name: 'notes', type: 'text', multiline: true}
    ],

    actions: [{
        callback: 'save',
        menuItem: false,
        onClose: true
    }, {
        title: 'cancel',
        callback: 'cancel',
        rightNavButton: true
    }]
}, {
    init: function(options) {
        var _this = this;

        this.operation = options.operation;
        var getContactDfd = $.Deferred();

        // This handler must be attached before the handler that calls this.initialize in $.Window
        getContactDfd.done(this.proxy(function(contactData) {
            this.inAddressBook = contactData.localContact ? true : false;

            // Build the fields array which is the same as the static fields array, with types expanded
            this.fields = this.constructor.fields.map(function(field) {
                // Expand the type property
                var types = field.type.split('/');
                var type = types[(this.inAddressBook || types.length === 1) ? 0 : 1];

                // Proxy the choice callback
                var callback = null;
                if (type === 'choice') {
                    callback = this.proxy(field.callback);
                }

                // Clone the field to prevent aliasing
                return $.extend({}, field, {type: type, callback: callback});
            }, this);

            this.contact = contactData.contact;
            this.attrs = this.contact.attrs(); // the changed contact attrs
            this.localContact = contactData.localContact;
            this.window.title = AD.Localize(this.operation+'Contact');
            this.open();
        }));

        // Initialize the base $.Window object
        // Pass in deferreds to delay the execution of this.create and this.initialize until a contact is chosen
        this._super({
            createDfd: getContactDfd.promise(),
            initializeDfd: getContactDfd.promise(),
            createParams: {
                layout: 'vertical'
            }
        });

        if (this.operation === 'import') {
            // Load an existing contact from the user's address book
            var chooseContactDfd = $.Deferred();
            Titanium.Contacts.showContacts({
                canceled: chooseContactDfd.reject,
                selectedPerson: function(event) {
                    chooseContactDfd.resolve(event.person);
                }
            });
            chooseContactDfd.done(this.proxy(function(selectedContact) {
                var contactRecordId = selectedContact.recordId || selectedContact.id; // recordId on iOS and id on Android
                var existingContacts = AD.Models.Contact.cache.query({contact_recordId: contactRecordId});
                var contact = null;
                if (existingContacts.length > 0) {
                    if (existingContacts.length > 1) {
                        console.warn('Found multiple contacts with the same recordId!');
                    }
                    // A contact was chosen that already exists, so edit the contact
                    contact = existingContacts[0];
                    this.operation = 'edit';
                }
                else {
                    contact = this.constructor.createContact({contact_recordId: contactRecordId});
                }
                getContactDfd.resolve({
                    contact: contact,
                    localContact: selectedContact
                });
            })).fail(this.dfd.reject); // Cancel the add contact operation
        }
        else if (this.operation === 'edit') {
            // Load the existing contact from the address book
            var recordId = options.existingContact.contact_recordId;
            var localContact = recordId === null ? null : Ti.Contacts.getPersonByID(recordId);
            getContactDfd.resolve({
                contact: options.existingContact,
                localContact: localContact
            });
        }
        else if (this.operation === 'create') {
            // Create a contact model not tied to an address book entry
            var contact = this.constructor.createContact({contact_recordId: null});
            getContactDfd.resolve({
                contact: contact,
                localContact: null
            });
        }
    },

    // Create each of the form fields
    create: function() {
        var labelWidth = 80;
        var rowHeight = 40;

        var focusedTextField = null;
        var hideKeyboard = function() {
            if (focusedTextField) {
                // Unfocus the previously selected text field to hide the keyboard
                focusedTextField.blur();
                focusedTextField = null;
            }
        };

        // Scrollable container that will hold the field rows on non-iPhone platforms
        var table = Ti.UI.createScrollView({
            left: 0,
            top: 0,
            width: AD.UI.screenWidth,
            height: Ti.UI.FILL,
            layout: 'vertical',
            scrollType: 'vertical',
            contentHeight: 'auto',
            showVerticalScrollIndicator: true
        });

        // Create the form fields
        // On iPhone, attempt to mimic the built-in Contacts app

        // Create each of the field views
        var rows = [];
        this.fields.forEach(function(field, index) {
            // Create the field row container, a table view row on iPhone and a generic view on other platforms
            var fieldRow = AD.Platform.isiPhone ? Ti.UI.createTableViewRow({}) : Ti.UI.createView({
                left: AD.UI.padding,
                right: 0,
                top: 0
            });
            fieldRow.height = rowHeight;
            fieldRow.index = index;

            // Create the field name label
            var label = Ti.UI.createLabel({
                left: 0,
                width: labelWidth,
                height: Ti.UI.SIZE,
                text: AD.Localize(field.name)
            });
            fieldRow.add(label);
            if (AD.Platform.isiPhone) {
                label.applyProperties({
                    text: label.text.toLowerCase(),
                    textAlign: 'right',
                    color: AD.UI.systemBlueColor,
                    font: {fontSize: 15, fontWeight: 'bold'} // medium-small bold
                });
            }

            var fieldValue = this.attrs[field.field];
            var fieldView = null;
            if (field.type === 'choice') {
                // Create the value label
                if (AD.Platform.isiPhone) {
                    fieldView = Ti.UI.createLabel({
                        left: labelWidth + AD.UI.padding,
                        width: Ti.UI.FILL,
                        height: Ti.UI.FILL,
                        text: fieldValue
                    });
                }
                else {
                    fieldView = Ti.UI.createButton({
                        left: labelWidth + AD.UI.padding,
                        right: AD.UI.padding,
                        center: { y: rowHeight / 2 },
                        height: AD.UI.buttonHeight,
                        title: fieldValue || AD.Localize('unspecified')
                    });
                    // When a choice row is clicked, call the callback that will presumably allow the user to choose a value
                    fieldView.addEventListener('click', field.callback);
                }
            }
            else if (field.type === 'text') {
                if (field.multiline === true) {
                    fieldView = Ti.UI.createTextArea({
                        left: labelWidth + AD.UI.padding,
                        right: AD.UI.padding,
                        height: Ti.UI.FILL,
                        font: AD.UI.Fonts.small,
                        suppressReturn: false
                    });
                    // Make the row taller to accommodate the text area
                    fieldRow.height *= 3;
                }
                else {
                    fieldView = Ti.UI.createTextField({
                        left: labelWidth + AD.UI.padding,
                        right: AD.UI.padding,
                        center: { y: rowHeight / 2 },
                        height: AD.UI.textFieldHeight
                    });
                }

                fieldView.value = fieldValue;

                if (field.keyboardType) {
                    fieldView.keyboardType = field.keyboardType;
                }
                if (field.autocapitalization) {
                    fieldView.autocapitalization = field.autocapitalization;
                }
            }

            fieldView.addEventListener('focus', function() {
                // Keep track of which text field (or text area) is currently selected
                focusedTextField = fieldView;
            });

            // Add the field to the row
            fieldRow.add(this.record(field.labelId, fieldView));
            rows.push(fieldRow);

            if (!AD.Platform.isiPhone) {
                table.add(fieldRow);
            }
        }, this);

        // On iPhone, hideKeyboard does not work when called from the window click
        // handler, so workaround by calling hideKeybaord from the table click handler
        if (AD.Platform.isiPhone) {
            // Create the fields table that holds the year, phone number, and email address fields
            var iPhoneTable = this.add(Ti.UI.createTableView({
                data: rows,
                style: Ti.UI.iPhone.TableViewStyle.GROUPED
            }));
            iPhoneTable.addEventListener('click', this.proxy(function(event) {
                hideKeyboard();

                var field = this.fields[event.row.index];
                if (field.type === 'choice') {
                    // When a choice row is clicked, call the callback that will presumably allow the user to choose a value
                    field.callback();
                }
            }));
        }
        else {
            this.add(table);

            // Click anywhere on the window to hide the keyboard
            this.window.addEventListener('click', function(event) {
                hideKeyboard();
            });
        }
    },

    // Set the initial contents of the form fields
    initialize: function() {
        var localContact = this.localContact;
        this.phoneNumbers = localContact && this.constructor.flattenMultivalue(localContact.phone);
        this.emailAddresses = localContact && this.constructor.flattenMultivalue(localContact.email);
    },

    // Handlers for allowing the user to change the contact's campus, year, phone number, and e-mail address
    changeCampus: function() {
        // Allow the user to set the contact's campus
        this.createWindow('ChooseOptionWindow', {
            groupName: 'campus',
            Model: 'Campus',
            initial: this.attrs.campus_uuid,
            editable: true
        }).getDeferred().done(this.proxy(function(campus) {
            // A campus was chosen
            var label = campus ? campus.getLabel() : null;
            this.attrs.campus_uuid = campus ? campus.getId() : null;
            this.attrs.campus_label = label;
            var campusLabel = this.getChild('campusLabel');
            campusLabel.text = campusLabel.title = label;
        }));
    },
    changeYear: function() {
        // Allow the user to choose the year of this contact
        this.createWindow('ChooseOptionWindow', {
            groupName: 'year',
            Model: 'Year',
            initial: this.attrs.year_id
        }).getDeferred().done(this.proxy(function(year) {
            // A year was chosen
            var label = year.getLabel();
            this.attrs.year_id = year.getId();
            this.attrs.year_label = label;
            var yearLabel = this.getChild('yearLabel');
            yearLabel.text = yearLabel.title = label;
        }));
    },
    changePhone: function() {
        // Allow the user to choose the phone number to associate with this contact
        this.createWindow('ChooseOptionWindow', {
            groupName: 'phone',
            initial: this.attrs.contact_phone,
            options: this.phoneNumbers
        }).getDeferred().done(this.proxy(function(phoneNumber) {
            // A phone number was chosen
            this.attrs.contact_phone = phoneNumber.value;
            var phoneLabel = this.getChild('phoneLabel');
            phoneLabel.text = phoneLabel.title = phoneNumber.value;
        }));
    },
    changeEmail: function() {
        // Allow the user to choose the email address to associate with this contact
        this.createWindow('ChooseOptionWindow', {
            groupName: 'email',
            initial: this.attrs.contact_email,
            options: this.emailAddresses
        }).getDeferred().done(this.proxy(function(emailAddress) {
            // An email address was chosen
            this.attrs.contact_email = emailAddress.value;
            var emailLabel = this.getChild('emailLabel');
            emailLabel.text = emailLabel.title = emailAddress.value;
        }));
    },

    // Update the contact model and close the window
    save: function() {
        // Read the values of the text fields
        this.fields.forEach(function(field) {
            if (field.type === 'text') {
                this.attrs[field.field] = this.children[field.labelId].value;
            }
        }, this);

        // Regular expression testing on email
        // It passes all valid emails and rejects some common mistakes
        var contact_email = this.attrs.contact_email;
        if (contact_email && (!/[^@]+@[^@]+\.[^@]+/.test(contact_email))) {
            alert('Email is not valid: ' + contact_email);
        }

        // Apply the changes to the contact
        var oldCampus_uuid = this.contact.attr('campus_uuid');
        this.contact.attrs(this.attrs);

        if (this.options.autoSave !== false) {
            // Create/update the contact's record in the database unless
            // explicitly prevented by the autoSave option being set to false
            this.contact.save();

            var campus_uuid = this.contact.attr('campus_uuid');
            if (campus_uuid && campus_uuid !== oldCampus_uuid) {
                // The contact's campus changed, so create the missing ContactStep entries
                var contact_uuid = this.contact.getId();
                var existingContactSteps = this.contact.getSteps();
                var indexedExistingContactSteps = $.indexArray(existingContactSteps, 'step_uuid');
                var steps = AD.Models.Step.cache.query({
                    campus_uuid: campus_uuid
                });
                steps.forEach(function(step) {
                    var step_uuid = step.getId();
                    if (!indexedExistingContactSteps[step_uuid]) {
                        // The ContactStep for this contact-step pair does not yet exist, so create it now
                        var contactStep = new AD.Models.ContactStep({
                            contact_uuid: contact_uuid,
                            step_uuid: step_uuid,
                            step_label: step.getLabel(),
                            step_date: null
                        });
                        contactStep.save();
                    }
                });
            }
        }

        this.dfd.resolve(this.contact);
    }
});
<|endoftext|>


<commit_before>//FirstView Component Constructor
function FirstView() {
	//create object instance, a parasitic subclass of Observable
	var self = Ti.UI.createView();

	Titanium.API.info("drupanium_authentication app started... Running FirstView.js...");

	/**
	 * Drupanium
	 * Use drupanium_application.js component to create an object for your drupanium implementation
	 */
	var DrupaniumApp = require('/drupanium_framework/drupanium_application');

	//set the url paths for the rest api on your drupal site
	//change the URLS for your own drupal site
	DrupaniumApp.setDrupaniumURLS('rest_path', 'http://api.drupanium.org/api/rest/');
	DrupaniumApp.setDrupaniumURLS('site_path', 'http://api.drupanium.org/');

	/**
	 * Drupanium
	 * Use drupanium_auth.js component to create an object for authentication and session handling
	 */
	var DrupaniumAuthentication = require('/drupanium_framework/drupanium_auth');

	/*
	if (Titanium.App.Properties.getInt("userUid")) {

		Titanium.API.info("The Titanium.App.Properties.getInt('userUid') seems to bet set. Maybe there is a session already authenticated. If you can't do any operations try to log out first");
		Titanium.API.info("Current session: \nDrupal uid: " + Titanium.App.Properties.getInt("userUid") + "\nDrupal sessid: " + Titanium.App.Properties.getString("userSessionId") + "\nDrupal session_name: " + Titanium.App.Properties.getString("userSessionName"));

		alert("Welcome back uid: " + Titanium.App.Properties.getInt("userUid"));
	}
	*/

	//create a label for network conectivity status
	var labelConnectionStatus = Ti.UI.createLabel({
		color:'#000000',
		text: 'Network status: ',
		height:'auto',
		width:'auto',
		bottom: 10
	});

	self.add(labelConnectionStatus);

	//test network connectiviy using drupaniumCheckConnectivity and update the status label
	if(DrupaniumApp.drupaniumCheckConnectivity()) {
		labelConnectionStatus.text = labelConnectionStatus.text + ' Online';
	} else {
		labelConnectionStatus.text = labelConnectionStatus.text + ' Offline';
	}

	// Create the username textfield
	var usernameTextfield = Titanium.UI.createTextField({
		hintText:'username',
		height:35,
		top:10,
		left:10,
		paddingLeft:5,
		width:300,
		font:{fontSize:16},
		borderWidth:2,
		borderColor:'#bbb',
		borderRadius:5,
		// This is very important. Don't auto capitalize the first letter of the password.
		autocapitalization:Titanium.UI.TEXT_AUTOCAPITALIZATION_NONE,
	});

	self.add(usernameTextfield);

	// Create the password textfield
	var passwordTextfield = Titanium.UI.createTextField({
		hintText:'password',
		height:35,
		top:50,
		left:10,
		paddingLeft:5,
		width:300,
		font:{fontSize:16},
		borderWidth:2,
		borderColor:'#bbb',
		borderRadius:5,
		// This is very important. Don't auto capitalize the first letter of the password.
		autocapitalization:Titanium.UI.TEXT_AUTOCAPITALIZATION_NONE,
		// Mask the password so nobody sees it
		passwordMask:true,
	});

	self.add(passwordTextfield);

	// Create the login button
	var loginButton = Titanium.UI.createButton({
		title:'Login',
		height:40,
		width:200,
		top:100
	});

	self.add(loginButton);

	// Add the click event listener for the login button.
	// this click event will call a method from the DrupaniumAuthentication object
	// and try to authenticate the user
	loginButton.addEventListener('click', function(e) {

		// Check if the fields are empty
		// you should add extra validation to increase secutiry and meet your drupal user settings usernames and passwords
		if (usernameTextfield.value.length==0 || passwordTextfield.value.length==0) {
			alert("login and password fields musn't be empty");
			//this return will stop the function
			return;
		}

		// Call the drupaniumAuthenticate method to authenticate the user.
		// see the documentation on drupanium_auth.js for params and more information on this method
		DrupaniumAuthentication.drupaniumAuthenticate(usernameTextfield.value, passwordTextfield.value, DrupaniumApp.getDrupaniumURLS('rest_path'),5000 , function(data, httpCodeStatus){

			//if the callback function returns the array object (data)
			if (data) {
				// Use the helper methods to set the properties coming
				// from the data object returned in the callback function
				DrupaniumAuthentication.setDrupaniumSessionInformation('userUid', data.user.uid);
				DrupaniumAuthentication.setDrupaniumSessionInformation('userSessionId', data.sessid);
				DrupaniumAuthentication.setDrupaniumSessionInformation('userSessionName', data.session_name);

				// Set the session properties for the application.
				// you do this so you can have the session variables available during the lifecyle of your application
				// this also allows you to check if the user is already logged in or not when the application is launched
				Titanium.App.Properties.setInt("userUid", data.user.uid);
	 			Titanium.App.Properties.setString("userSessionId", data.sessid);
	 			Titanium.App.Properties.setString("userSessionName", data.session_name);

	 			Titanium.API.info("Setting data.user.ui, data.sessid, data.session_name into Titanium.App.Properties...");

				// Hide the keyboard if the authentication succeeds.
				hideLoginKeyboard();

				//clear textfields
				usernameTextfield.value='';
				passwordTextfield.value='';

				//welcome the user
				alert("Welcome user of uid: " + DrupaniumAuthentication.getDrupaniumSessionInformation("userUid"))

				// Log to the console to see if we were able to set the info to the session properties
				// this will help you with debugging
				Titanium.API.info("Authenticated with success.");
				Titanium.API.info("Drupal uid: " + DrupaniumAuthentication.getDrupaniumSessionInformation("userUid"));
				Titanium.API.info("Drupal sessid: " + DrupaniumAuthentication.getDrupaniumSessionInformation("userSessionId"));
				Titanium.API.info("Drupal session_name: " + DrupaniumAuthentication.getDrupaniumSessionInformation("userSessionName"));
			}
			else {
				Titanium.API.info("Authentication Failed!");
				alert("Authentication Failed!");
			}
		});
	});

	// Add the logout button
	var logoutButton = Titanium.UI.createButton({
		title:'Logout',
		height:40,
		width:200,
		top:150
	});

	self.add(logoutButton);

	 // Add the click event listener for the logout button.
	 // this click event will call a method from the DrupaniumAuthentication object
	 // and try to destroy the current session (logout)
	logoutButton.addEventListener('click', function(e) {

		// Call the drupaniumAuthenticate method to authenticate the user
		// see the documentation on drupanium_auth.js for params and more information on this method
		DrupaniumAuthentication.drupaniumTerminateSession(DrupaniumApp.getDrupaniumURLS('rest_path'),5000 , function(status){
			if (status) {
				// We remove all the properties since the user is requesting to logout
				Titanium.App.Properties.removeProperty("userUid");
				Titanium.App.Properties.removeProperty("userSessionId");
				Titanium.App.Properties.removeProperty("userSessionName");

				Titanium.API.info("Removing data.user.ui, data.sessid, data.session_name into Titanium.App.Properties...");

				alert("Logged out!");
			}
			else {
				Titanium.API.info("Log out failed!");
				alert("Log out failed! Please try again.");
			}
		});

		// Hide the keyboard if the authentication succeeds.
		hideLoginKeyboard();
	});

	// Call this function whenever you want to hide the login keyboard that pops out from the textfields
	// this is the equivalent to the resignFirstResponder
	// if you have coded before in Objective-C iOS SDK
	function hideLoginKeyboard() {
		usernameTextfield.blur();
		passwordTextfield.blur();
	}

	/**
	 * Drupanium
	 * Use drupanium_user.js component to create an object for your user related actions
	 */
	var DrupaniumUser = require('/drupanium_framework/drupanium_user');

	var showUserProfile = Titanium.UI.createButton({
		title:'Show User Info',
		height:40,
		width:200,
		bottom:35,
	});

	self.add(showUserProfile);

	showUserProfile.addEventListener('click', function(e) {

		// Call the drupaniumUser method to get the User Info
		// see the documentation on drupanium_auth.js for params and more information on this method
		DrupaniumUser.drupaniumGetUser(DrupaniumAuthentication.getDrupaniumSessionInformation("userUid"), DrupaniumApp.getDrupaniumURLS('rest_path') ,DrupaniumApp.getDrupaniumURLS('site_path') ,5000 , function(data){
			if (data) {

				// Use these data fields if you're using custom fields for profile. These are the fields which are defined in the api.drupanium.org
				//alert(data.field_fullname.und[0].value);
				//alert(data.field_country.und[0].value);
				//alert(data.field_aboutme.und[0].value);

				DrupaniumUser.setDrupaniumUSER('userName', data.name);
				DrupaniumUser.setDrupaniumUSER('userProfilePicture', data.picture.filename);

				Titanium.API.info("Drupal username: " + DrupaniumUser.getDrupaniumUSER('userName'));
				Titanium.API.info("Drupal profile picture filename: " + DrupaniumUser.getDrupaniumUSER('userProfilePicture'));

	// We create an ImageView to hold the image from the user Profile
	var userPicture = Ti.UI.createImageView({
		// We use the property image and the SITE_PATH instead of the REST_PATH
		// also notice how data.picture.filename is build, since data.picture is also
		// an object inside data
		image : DrupaniumApp.getDrupaniumURLS('site_path') + 'sites/default/files/pictures/' + DrupaniumUser.getDrupaniumUSER('userProfilePicture'),
		bottom : 100,
		width : 100,
		height : 100,
	});

	self.add(userPicture);

	// We create a label to hold the username from the user Object.
	var userNameLabel = Ti.UI.createLabel({
		color:'#000000',
		text: 'userName: ' + DrupaniumUser.getDrupaniumUSER('userName'),
		height:'auto',
		width:'auto',
		bottom: 210
	});

	self.add(userNameLabel);

			}
			else {
				//Titanium.API.log("failure");
			}
		});
	});

	return self;
}

module.exports = FirstView;
<commit_msg>slightly code organization to FirstView.js<commit_after>//FirstView Component Constructor
function FirstView() {
	//create object instance, a parasitic subclass of Observable
	var self = Ti.UI.createView();

	Titanium.API.info("drupanium_authentication app started... Running FirstView.js...");

	/**
	 * Drupanium
	 * Use drupanium_application.js component to create an object for your drupanium implementation
	 */
	var DrupaniumApp = require('/drupanium_framework/drupanium_application');

	//set the url paths for the rest api on your drupal site
	//change the URLS for your own drupal site
	DrupaniumApp.setDrupaniumURLS('rest_path', 'http://api.drupanium.org/api/rest/');
	DrupaniumApp.setDrupaniumURLS('site_path', 'http://api.drupanium.org/');

	/**
	 * Drupanium
	 * Use drupanium_auth.js component to create an object for authentication and session handling
	 */
	var DrupaniumAuthentication = require('/drupanium_framework/drupanium_auth');

	/*
	if (Titanium.App.Properties.getInt("userUid")) {

		Titanium.API.info("The Titanium.App.Properties.getInt('userUid') seems to bet set. Maybe there is a session already authenticated. If you can't do any operations try to log out first");
		Titanium.API.info("Current session: \nDrupal uid: " + Titanium.App.Properties.getInt("userUid") + "\nDrupal sessid: " + Titanium.App.Properties.getString("userSessionId") + "\nDrupal session_name: " + Titanium.App.Properties.getString("userSessionName"));

		alert("Welcome back uid: " + Titanium.App.Properties.getInt("userUid"));
	}
	*/

	//create a label for network conectivity status
	var labelConnectionStatus = Ti.UI.createLabel({
		color:'#000000',
		text: 'Network status: ',
		height:'auto',
		width:'auto',
		bottom: 10
	});

	self.add(labelConnectionStatus);

	//test network connectiviy using drupaniumCheckConnectivity and update the status label
	if(DrupaniumApp.drupaniumCheckConnectivity()) {
		labelConnectionStatus.text = labelConnectionStatus.text + ' Online';
	} else {
		labelConnectionStatus.text = labelConnectionStatus.text + ' Offline';
	}

	// Create the username textfield
	var usernameTextfield = Titanium.UI.createTextField({
		hintText:'username',
		height:35,
		top:10,
		left:10,
		paddingLeft:5,
		width:300,
		font:{fontSize:16},
		borderWidth:2,
		borderColor:'#bbb',
		borderRadius:5,
		// This is very important. Don't auto capitalize the first letter of the password.
		autocapitalization:Titanium.UI.TEXT_AUTOCAPITALIZATION_NONE,
	});

	self.add(usernameTextfield);

	// Create the password textfield
	var passwordTextfield = Titanium.UI.createTextField({
		hintText:'password',
		height:35,
		top:50,
		left:10,
		paddingLeft:5,
		width:300,
		font:{fontSize:16},
		borderWidth:2,
		borderColor:'#bbb',
		borderRadius:5,
		// This is very important. Don't auto capitalize the first letter of the password.
		autocapitalization:Titanium.UI.TEXT_AUTOCAPITALIZATION_NONE,
		// Mask the password so nobody sees it
		passwordMask:true,
	});

	self.add(passwordTextfield);

	// Create the login button
	var loginButton = Titanium.UI.createButton({
		title:'Login',
		height:40,
		width:200,
		top:100
	});

	self.add(loginButton);

	// Add the click event listener for the login button.
	// this click event will call a method from the DrupaniumAuthentication object
	// and try to authenticate the user
	loginButton.addEventListener('click', function(e) {

		// Check if the fields are empty
		// you should add extra validation to increase secutiry and meet your drupal user settings usernames and passwords
		if (usernameTextfield.value.length==0 || passwordTextfield.value.length==0) {
			alert("login and password fields musn't be empty");
			//this return will stop the function
			return;
		}

		// Call the drupaniumAuthenticate method to authenticate the user.
		// see the documentation on drupanium_auth.js for params and more information on this method
		DrupaniumAuthentication.drupaniumAuthenticate(usernameTextfield.value, passwordTextfield.value, DrupaniumApp.getDrupaniumURLS('rest_path'),5000 , function(data, httpCodeStatus){

			//if the callback function returns the array object (data)
			if (data) {
				// Use the helper methods to set the properties coming
				// from the data object returned in the callback function
				DrupaniumAuthentication.setDrupaniumSessionInformation('userUid', data.user.uid);
				DrupaniumAuthentication.setDrupaniumSessionInformation('userSessionId', data.sessid);
				DrupaniumAuthentication.setDrupaniumSessionInformation('userSessionName', data.session_name);

				// Set the session properties for the application.
				// you do this so you can have the session variables available during the lifecyle of your application
				// this also allows you to check if the user is already logged in or not when the application is launched
				Titanium.App.Properties.setInt("userUid", data.user.uid);
	 			Titanium.App.Properties.setString("userSessionId", data.sessid);
	 			Titanium.App.Properties.setString("userSessionName", data.session_name);

	 			Titanium.API.info("Setting data.user.ui, data.sessid, data.session_name into Titanium.App.Properties...");

				// Hide the keyboard if the authentication succeeds.
				hideLoginKeyboard();

				//clear textfields
				usernameTextfield.value='';
				passwordTextfield.value='';

				//welcome the user
				alert("Welcome user of uid: " + DrupaniumAuthentication.getDrupaniumSessionInformation("userUid"))

				// Log to the console to see if we were able to set the info to the session properties
				// this will help you with debugging
				Titanium.API.info("Authenticated with success.");
				Titanium.API.info("Drupal uid: " + DrupaniumAuthentication.getDrupaniumSessionInformation("userUid"));
				Titanium.API.info("Drupal sessid: " + DrupaniumAuthentication.getDrupaniumSessionInformation("userSessionId"));
				Titanium.API.info("Drupal session_name: " + DrupaniumAuthentication.getDrupaniumSessionInformation("userSessionName"));
			}
			else {
				Titanium.API.info("Authentication Failed!");
				alert("Authentication Failed!");
			}
		});
	});

	// Add the logout button
	var logoutButton = Titanium.UI.createButton({
		title:'Logout',
		height:40,
		width:200,
		top:150
	});

	self.add(logoutButton);

	 // Add the click event listener for the logout button.
	 // this click event will call a method from the DrupaniumAuthentication object
	 // and try to destroy the current session (logout)
	logoutButton.addEventListener('click', function(e) {

		// Call the drupaniumAuthenticate method to authenticate the user
		// see the documentation on drupanium_auth.js for params and more information on this method
		DrupaniumAuthentication.drupaniumTerminateSession(DrupaniumApp.getDrupaniumURLS('rest_path'),5000 , function(status){
			if (status) {
				// We remove all the properties since the user is requesting to logout
				Titanium.App.Properties.removeProperty("userUid");
				Titanium.App.Properties.removeProperty("userSessionId");
				Titanium.App.Properties.removeProperty("userSessionName");

				Titanium.API.info("Removing data.user.ui, data.sessid, data.session_name into Titanium.App.Properties...");

				alert("Logged out!");
			}
			else {
				Titanium.API.info("Log out failed!");
				alert("Log out failed! Please try again.");
			}
		});

		// Hide the keyboard if the authentication succeeds.
		hideLoginKeyboard();
	});

	// Call this function whenever you want to hide the login keyboard that pops out from the textfields
	// this is the equivalent to the resignFirstResponder
	// if you have coded before in Objective-C iOS SDK
	function hideLoginKeyboard() {
		usernameTextfield.blur();
		passwordTextfield.blur();
	}

	/**
	 * Drupanium
	 * Use drupanium_user.js component to create an object for your user related actions
	 */
	var DrupaniumUser = require('/drupanium_framework/drupanium_user');

	var showUserProfile = Titanium.UI.createButton({
		title:'Show User Info',
		height:40,
		width:200,
		bottom:35,
	});

	self.add(showUserProfile);

	showUserProfile.addEventListener('click', function(e) {

		// Call the drupaniumUser method to get the User Info
		// see the documentation on drupanium_auth.js for params and more information on this method
		DrupaniumUser.drupaniumGetUser(DrupaniumAuthentication.getDrupaniumSessionInformation("userUid"), DrupaniumApp.getDrupaniumURLS('rest_path') ,DrupaniumApp.getDrupaniumURLS('site_path') ,5000 , function(data){
			if (data) {

				// Use these data fields if you're using custom fields for profile. These are the fields which are defined in the api.drupanium.org
				//alert(data.field_fullname.und[0].value);
				//alert(data.field_country.und[0].value);
				//alert(data.field_aboutme.und[0].value);

				DrupaniumUser.setDrupaniumUSER('userName', data.name);
				DrupaniumUser.setDrupaniumUSER('userProfilePicture', data.picture.filename);

				Titanium.API.info("Drupal username: " + DrupaniumUser.getDrupaniumUSER('userName'));
				Titanium.API.info("Drupal profile picture filename: " + DrupaniumUser.getDrupaniumUSER('userProfilePicture'));

	// We create an ImageView to hold the image from the user Profile
	var userPicture = Ti.UI.createImageView({
		// We use the property image and the SITE_PATH instead of the REST_PATH
		// also notice how data.picture.filename is build, since data.picture is also
		// an object inside data
		image : DrupaniumApp.getDrupaniumURLS('site_path') + 'sites/default/files/pictures/' + DrupaniumUser.getDrupaniumUSER('userProfilePicture'),
		bottom : 100,
		width : 100,
		height : 100,
	});

	self.add(userPicture);

	// We create a label to hold the username from the user Object.
	var userNameLabel = Ti.UI.createLabel({
		color:'#000000',
		text: 'userName: ' + DrupaniumUser.getDrupaniumUSER('userName'),
		height:'auto',
		width:'auto',
		bottom: 210
	});

	self.add(userNameLabel);

			}
			else {
				//Titanium.API.log("failure");
			}
		});
	});

	return self;
}

module.exports = FirstView;
<|endoftext|>


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

'use strict';

const User = require('../models/user');

exports.auth = function *auth(next) {
    const cookie = this.cookies.get('auth') || '';
    const ts = new Date();

    this.mas = this.mas || {};
    this.mas.user = null;

    const [ userId, secret ] = cookie.split('-');

    if (userId && secret) {
        const user = yield User.fetch(parseInt(userId));

        if (user && user.get('secretExpires') > ts && user.get('secret') === secret) {
            this.mas.user = user;
        }
    }

    yield next;
};
<commit_msg>Parse user id correctly<commit_after>//
//   Copyright 2014 Ilkka Oksanen <iao@iki.fi>
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing,
//   software distributed under the License is distributed on an "AS
//   IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//   express or implied.  See the License for the specific language
//   governing permissions and limitations under the License.
//

'use strict';

const User = require('../models/user');

exports.auth = function *auth(next) {
    const cookie = this.cookies.get('auth') || '';
    const ts = new Date();

    this.mas = this.mas || {};
    this.mas.user = null;

    const [ userId, secret ] = cookie.split('-');

    if (userId && secret) {
        const user = yield User.fetch(parseInt(userId.substring(1)));

        if (user && user.get('secretExpires') > ts && user.get('secret') === secret) {
            this.mas.user = user;
        }
    }

    yield next;
};
<|endoftext|>


<commit_before>function getCookie(e){var t=null;if(document.cookie&&document.cookie!=""){var n=document.cookie.split(";");for(var r=0;r<n.length;r++){var i=jQuery.trim(n[r]);if(i.substring(0,e.length+1)==e+"="){t=decodeURIComponent(i.substring(e.length+1));break}}}return t}function ajaxPost(e,t,n,r){r=$.extend({},r,{url:e,method:"post",data:t,onSuccess:function(e){$.isFunction(n)&&n(e.content)}});ajax(r)}function ajaxGet(e,t,n){n=$.extend({},n,{url:e,method:"get",onSuccess:function(e){$.isFunction(t)&&t(e.content)}});ajax(n)}var csrftoken=getCookie("csrftoken");$.ajaxSetup({crossDomain:false});var ajax=function(e,t){function u(e){return/^(GET|HEAD|OPTIONS|TRACE)$/.test(e)}function a(e){var t=document.location.host;var n=document.location.protocol;var r="//"+t;var i=n+r;return e==i||e.slice(0,i.length+1)==i+"/"||e==r||e.slice(0,r.length+1)==r+"/"||!/^(\/\/|http:|https:).*/.test(e)}function f(e){if(e.fragments){for(var t in e.fragments){$(t).replaceWith(e.fragments[t])}}if(e["inner-fragments"]){for(var n in e["inner-fragments"]){$(n).html(e["inner-fragments"][n])}}if(e["append-fragments"]){for(var r in e["append-fragments"]){$(r).append(e["append-fragments"][r])}}if(e["prepend-fragments"]){for(var i in e["prepend-fragments"]){$(i).prepend(e["prepend-fragments"][i])}}}if(typeof e==="object"){t=e;e=undefined}if(!$.isPlainObject(t))t={};t=$.extend({},ajax.DEFAULTS,t);var n=$.isFunction(t.onSuccess)?t.onSuccess:null,r=$.isFunction(t.onRedirect)?t.onRedirect:null,i=$.isFunction(t.onError)?t.onError:null,s=$.isFunction(t.onComplete)?t.onComplete:null,o=$.isFunction(t.onBeforeSend)?t.onBeforeSend:null;$.ajax({url:e,type:t.method||"get",data:t.data,beforeSend:function(e,t){if(!u(t.type)&&a(t.url)){e.setRequestHeader("X-CSRFToken",csrftoken)}o&&o(e,t)},success:function(s){switch(s.status){case 200:t["process-fragments"]&&f(s.content);n&&n(s);break;case 301:case 302:if(r)r(s.content);else window.location.href=s.content;break;default:if(i)i(s);else alert(t.method.toUpperCase()+" "+e+"   "+s.status+" "+s.statusText+"\n"+s.content);break}},error:function(n){if(i)i(n);else alert(t.method.toUpperCase()+" "+e+"   "+n.status+" "+n.statusText+"\n"+n.content)},complete:function(e){s&&s(e)}})};ajax.DEFAULTS={"process-fragments":true,onSuccess:null,onError:null,onBeforeSend:null,onComplete:null,onRedirect:null}<commit_msg>Compressed jquery.ajax.js<commit_after>function getCookie(e){var t=null;if(document.cookie&&document.cookie!=""){var n=document.cookie.split(";");for(var r=0;r<n.length;r++){var i=jQuery.trim(n[r]);if(i.substring(0,e.length+1)==e+"="){t=decodeURIComponent(i.substring(e.length+1));break}}}return t}function ajaxPost(e,t,n,r){r=$.extend({},r,{url:e,method:"post",data:t,onSuccess:function(e){$.isFunction(n)&&n(e.content)}});ajax(r)}function ajaxGet(e,t,n){n=$.extend({},n,{url:e,method:"get",onSuccess:function(e){$.isFunction(t)&&t(e.content)}});ajax(n)}var csrftoken=getCookie("csrftoken");$.ajaxSetup({crossDomain:false});var ajax=function(e,t){function u(e){return/^(GET|HEAD|OPTIONS|TRACE)$/.test(e)}function a(e){var t=document.location.host;var n=document.location.protocol;var r="//"+t;var i=n+r;return e==i||e.slice(0,i.length+1)==i+"/"||e==r||e.slice(0,r.length+1)==r+"/"||!/^(\/\/|http:|https:).*/.test(e)}function f(e){if(e.fragments){for(var t in e.fragments){$(t).replaceWith(e.fragments[t])}}if(e["inner-fragments"]){for(var n in e["inner-fragments"]){$(n).html(e["inner-fragments"][n])}}if(e["append-fragments"]){for(var r in e["append-fragments"]){$(r).append(e["append-fragments"][r])}}if(e["prepend-fragments"]){for(var i in e["prepend-fragments"]){$(i).prepend(e["prepend-fragments"][i])}}}if(typeof e==="object"){t=e;e=t.url}if(!$.isPlainObject(t))t={};t=$.extend({},ajax.DEFAULTS,t);var n=$.isFunction(t.onSuccess)?t.onSuccess:null,r=$.isFunction(t.onRedirect)?t.onRedirect:null,i=$.isFunction(t.onError)?t.onError:null,s=$.isFunction(t.onComplete)?t.onComplete:null,o=$.isFunction(t.onBeforeSend)?t.onBeforeSend:null;$.ajax({url:e,type:t.method||"get",data:t.data,beforeSend:function(e,t){if(!u(t.type)&&a(t.url)){e.setRequestHeader("X-CSRFToken",csrftoken)}o&&o(e,t)},success:function(s){switch(s.status){case 200:t["process-fragments"]&&f(s.content);n&&n(s);break;case 301:case 302:if(r)r(s.content);else window.location.href=s.content;break;default:if(i)i(s);else alert(t.method.toUpperCase()+" "+e+"   "+s.status+" "+s.statusText+"\n"+s.content);break}},error:function(n){if(i)i(n);else alert(t.method.toUpperCase()+" "+e+"   "+n.status+" "+n.statusText+"\n"+n.content)},complete:function(e){s&&s(e)}})};ajax.DEFAULTS={"process-fragments":true,onSuccess:null,onError:null,onBeforeSend:null,onComplete:null,onRedirect:null}<|endoftext|>


<commit_before>/**
 * Utils for Node library
 */
var Node = {};

/**
 * Generator version of the module 'fs'
 */
var fs = require('fs');
Node.fs = {
    /**
     * Check if a path exists
     */
    exists: function(filePath)
    {
        return function(done)
        {
            fs.stat(filePath, function(error, result)
            {
                done(null, !error);
            });
        };
    }
};

module.exports = Node;
<commit_msg>Add the generator version of fs.stat<commit_after>/**
 * Utils for Node library
 */
var Node = {};

/**
 * Generator version of the module 'fs'
 */
var fs = require('fs');
Node.fs = {};
/**
 * Check if a path exists
 */
Node.fs.exists = function(filePath)
{
    return function(done)
    {
        fs.stat(filePath, function(error, result)
        {
            done(null, !error);
        });
    };
};

/**
 * Get the stats of a file
 */
Node.fs.stat = function(filePath)
{
    return function(done)
    {
        fs.stat(filePath, done);
    };
};

module.exports = Node;
<|endoftext|>


<commit_before>'use strict';

class Util {
    static getRandomWeightedBySize(random, size, power) {
        // Choose a random value with a larger chance of having a lower index.
        var uniform = random();
        var beta = Math.pow(Math.sin(uniform * Math.PI / 2), power);
        var beta_left = (beta < 0.5) ? 2 * beta : 2 * (1 - beta);
        return Math.floor(beta_left * size);
    }

    static getRandomElementWeightedBySize(random, elements, power) {
        return elements[Util.getRandomWeightedBySize(random, elements.length, power)];
    }

    static stopToNamedId(stop) {
        return "stop_" + stop.x + "_" + stop.y;
    }

    static createChoicePool(distribution_map, factor) {
        if (!factor) factor = 100;
        var pool = [];
        Object.keys(distribution_map).forEach(key => {
            var chance = distribution_map[key];
            var amount = Math.ceil(chance * factor);
            for (var i = 0; i < amount; i++) {
                pool.push(key);
            }
        });
        return pool;
    }
}

Util.DEFAULT_DELAY_REASONS = {
    'http://purl.org/td/transportdisruption#DamagedVehicle': 0.4,
    'http://purl.org/td/transportdisruption#Strike': 0.2,
    'http://purl.org/td/transportdisruption#Accident': 0.2,
    'http://purl.org/td/transportdisruption#BadWeather': 0.1,
    'http://purl.org/td/transportdisruption#Obstruction': 0.1,
};

module.exports = Util;<commit_msg>Update default delay reasons<commit_after>'use strict';

class Util {
    static getRandomWeightedBySize(random, size, power) {
        // Choose a random value with a larger chance of having a lower index.
        var uniform = random();
        var beta = Math.pow(Math.sin(uniform * Math.PI / 2), power);
        var beta_left = (beta < 0.5) ? 2 * beta : 2 * (1 - beta);
        return Math.floor(beta_left * size);
    }

    static getRandomElementWeightedBySize(random, elements, power) {
        return elements[Util.getRandomWeightedBySize(random, elements.length, power)];
    }

    static stopToNamedId(stop) {
        return "stop_" + stop.x + "_" + stop.y;
    }

    static createChoicePool(distribution_map, factor) {
        if (!factor) factor = 100;
        var pool = [];
        Object.keys(distribution_map).forEach(key => {
            var chance = distribution_map[key];
            var amount = Math.ceil(chance * factor);
            for (var i = 0; i < amount; i++) {
                pool.push(key);
            }
        });
        return pool;
    }
}

Util.DEFAULT_DELAY_REASONS = {
    'http://purl.org/td/transportdisruption#QueuingTraffic': 0.07936507,
    'http://purl.org/td/transportdisruption#SlowTraffic': 0.01587301,

    'http://purl.org/td/transportdisruption#Derailment': 0.00158730,

    'http://purl.org/td/transportdisruption#CollisionWithAnimal': 0.01587301,
    'http://purl.org/td/transportdisruption#CollisionWithPerson': 0.03174602,
    'http://purl.org/td/transportdisruption#HeadOnCollision': 0.00158730,

    'http://purl.org/td/transportdisruption#CivilEmergency': 0.07936507,
    'http://purl.org/td/transportdisruption#PoliceInvestigation': 0.01587301,

    'http://purl.org/td/transportdisruption#IllVehicleOccupants': 0.04761904,
    'http://purl.org/td/transportdisruption#BombAlert': 0.0079365,
    'http://purl.org/td/transportdisruption#Demonstration': 0.0047619,
    'http://purl.org/td/transportdisruption#AltercationOfVehicleOccupants': 0.01587301,
    'http://purl.org/td/transportdisruption#Strike': 0.0047619,

    'http://purl.org/td/transportdisruption#StrongWinds': 0.02857142,
    'http://purl.org/td/transportdisruption#ExtremeCold': 0.01587301,
    'http://purl.org/td/transportdisruption#BadWeather': 0.03174602,
    'http://purl.org/td/transportdisruption#ExtremeHeat': 0.0079365,
    'http://purl.org/td/transportdisruption#HeavySnowfall': 0.0079365,

    'http://purl.org/td/transportdisruption#FallenTrees': 0.03174602,
    'http://purl.org/td/transportdisruption#Flooding': 0.0047619,
    'http://purl.org/td/transportdisruption#StormDamage': 0.03174602,

    'http://purl.org/td/transportdisruption#FallenPowerCables': 0.03174602,
    'http://purl.org/td/transportdisruption#DamagedTunnel': 0.00158730,

    'http://purl.org/td/transportdisruption#AbnormalLoad': 0.1111111,
    'http://purl.org/td/transportdisruption#BrokenDownTrain': 0.0079365,
    'http://purl.org/td/transportdisruption#DamagedVehicle': 0.04761904,

    'http://purl.org/td/transportdisruption#ConstructionWork': 0.04761904,

    'http://purl.org/td/transportdisruption#MaintenanceWork': 0.04761904,
    'http://purl.org/td/transportdisruption#RepairWork': 0.04761903,
    'http://purl.org/td/transportdisruption#TreeAndVegetationCuttingWork': 0.01587301,

    'http://purl.org/td/transportdisruption#TrafficSignalsFailure': 0.15873015,
};

module.exports = Util;
<|endoftext|>


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

var validator = require('validator');
var reqParams = require('./req_params');
var util = require('util');

var emptyBody = {
    code: 400,
    message: 'Request body cannot be empty!'
};

var invalidMongoParam = '%s is required and should have valid format';

//Project Rest Api validators
exports.validateSaveProject = function(req, res, next) {
    var project = req.body;
    if(project) {
        req.checkBody('name', 'Project name is required').notEmpty();
        if(!project._id) {
            req.checkBody('companyId', util.format(invalidMongoParam, 'Company id'))
                .notEmpty().isMongoId();
        }

        returnErrors(req, res, next);

    } else {
        res.status(emptyBody.code).json({msg: emptyBody.message});
    }
};

exports.validateGetProjectById = function(req, res, next) {
    req.checkParams(reqParams.projectIdParam, util.format(invalidMongoParam, 'Project id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

exports.validateGetProjectByCompanyId = function(req, res, next) {
    req.check(reqParams.companyIdParam, util.format(invalidMongoParam, 'Company id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

//User Rest API validators
exports.validateGetUserByProjectId = function(req, res, next) {
    req.check(reqParams.projectIdParam, util.format(invalidMongoParam, 'Project id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

exports.validateGetUserByCompanyId = function(req, res, next) {
    req.check(reqParams.companyIdParam, util.format(invalidMongoParam, 'Company id param'))
            .notEmpty().isMongoId();
    returnErrors(req, res, next);
};

exports.validateReplaceAssignment = function(req, res, next) {
    req.checkParams(reqParams.projectIdParam, util.format(invalidMongoParam, 'Project id param'))
            .notEmpty().isMongoId();

    var user = req.body;
    if(!user) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }

    req.checkBody('_id', util.format(invalidMongoParam, 'User id')).notEmpty().isMongoId();
    req.checkBody('assignments', 'Incorrect Assignments (Check: userId, projectId, projectName)')
        .optional().isAssignments(req.params[reqParams.projectIdParam], req.body._id);

    returnErrors(req, res, next);
};

//Company Rest API validation
exports.validateUpdateCompany = function(req, res, next) {
    var company = req.body;
    if(!company) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }
    next();
};

exports.validateCreateCompany = function(req, res, next) {
    var company = req.body;
    if(!company) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }

    req.checkBody('emails', "Property 'emails' is not an array!" ).optional().isArray();
    req.checkBody('emails', 'At least one email has incorrect format').optional().isEmails();

    returnErrors(req, res, next);
};

exports.validateGetCompanyById = function(req, res, next) {
    req.checkParams(reqParams.companyIdParam, util.format(invalidMongoParam, 'Company id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

//Timelog Rest API validation
exports.validateGetTimelogByDates = function(req, res, next) {
    req.checkParams(reqParams.userIdParam, util.format(invalidMongoParam, 'User id param'))
            .notEmpty().isMongoId();
    req.checkQuery(reqParams.projectIdParam, util.format(invalidMongoParam, 'Project id param'))
            .notEmpty().isMongoId();
    req.checkQuery(reqParams.startDateParam, util.format(invalidMongoParam, 'Start date param'))
            .notEmpty().isDate();
    req.checkQuery(reqParams.endDateParam, util.format(invalidMongoParam, 'End date param'))
            .notEmpty().isDate();

    returnErrors(req, res, next);
};

exports.validateDeleteTimelog = function(req, res, next) {
    req.checkParams(reqParams.timelogIdParam, util.format(invalidMongoParam, 'Timelog id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

exports.validateSaveTimelog = function(req, res, next) {
    var timelogs = req.body;
    if(!timelogs) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }
    req.checkBody('timelog', 'Incorrect timelog (Check: date, userId, projectId, projectName)')
        .isTimelog();

    returnErrors(req, res, next);
};

exports.validateDeactivateProject = function(req, res, next) {
    req.check(reqParams.projectIdParam, util.format(invalidMongoParam, 'Project id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

//Custom validators for express-validator
exports.isArray = function(value) {
    return Array.isArray(value);
};

exports.timelogs = function(values) {
    if(Array.isArray(values)) {
        return values.every(function(val){
            var isValid = true;
            if(val._id){
                isValid = validator.isMongoId(val._id);
            }
            isValid = isValid
                && validator.isMongoId(val.userId) //required && format
                && validator.isMongoId(val.projectId) //required && format
                && validator.isLength(val.projectName, 1) //required
                && validator.isDate(val.date); //required && format
            return isValid;
        });
    }
    return false;
};

exports.assignments = function(values, projectId, userId) {
    if(!values.length) { // if array is empty
        return true;
    }

    if(Array.isArray(values)) {
        return values.every(function(val){
            return validator.isMongoId(val.projectId)
                && validator.isMongoId(projectId)
                && (val.projectId.equals(projectId))
                && validator.isLength(val.projectName, 1)
                && validator.isMongoId(val.userId)
                && validator.isMongoId(userId)
                && (val.userId.equals(userId));
        });
    }
    return false;
};

exports.isEmails = function(values) {
    if(Array.isArray(values)) {
        return values.every(function(val) {
            return validator.isEmail(val);
        });
    }

    return false;
};

//Private part
function returnErrors(req, res, next) {
    var errors = req.validationErrors(true);
    if(errors) {
        res.status(400).json(errors);
        return;
    }

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

var validator = require('validator');
var reqParams = require('./req_params');
var util = require('util');

var emptyBody = {
    code: 400,
    message: 'Request body cannot be empty!'
};

var invalidFormatMessageTemplate = '%s is required and should have a valid format';

//Project Rest Api validators
exports.validateSaveProject = function(req, res, next) {
    var project = req.body;
    if(project) {
        req.checkBody('name', 'Project name is required').notEmpty();
        if(!project._id) {
            req.checkBody('companyId', util.format(invalidFormatMessageTemplate, 'companyId'))
                .notEmpty().isMongoId();
        }

        returnErrors(req, res, next);

    } else {
        res.status(emptyBody.code).json({msg: emptyBody.message});
    }
};

exports.validateGetProjectById = function(req, res, next) {
    req.checkParams(reqParams.projectIdParam,
        util.format(invalidFormatMessageTemplate, reqParams.projectIdParam))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

exports.validateGetProjectByCompanyId = function(req, res, next) {
    req.check(reqParams.companyIdParam,
        util.format(invalidFormatMessageTemplate, reqParams.companyIdParam))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

//User Rest API validators
exports.validateGetUserByProjectId = function(req, res, next) {
    req.check(reqParams.projectIdParam,
        util.format(invalidFormatMessageTemplate, reqParams.projectIdParam))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

exports.validateGetUserByCompanyId = function(req, res, next) {
    req.check(reqParams.companyIdParam,
        util.format(invalidFormatMessageTemplate, reqParams.companyIdParam))
            .notEmpty().isMongoId();
    returnErrors(req, res, next);
};

exports.validateReplaceAssignment = function(req, res, next) {
    req.checkParams(reqParams.projectIdParam,
        util.format(invalidFormatMessageTemplate, reqParams.projectIdParam))
            .notEmpty().isMongoId();

    var user = req.body;
    if(!user) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }

    req.checkBody('_id', util.format(invalidFormatMessageTemplate, 'User id')).notEmpty().isMongoId();
    req.checkBody('assignments', 'Incorrect Assignments (Check: userId, projectId, projectName)')
        .optional().isAssignments(req.params[reqParams.projectIdParam], req.body._id);

    returnErrors(req, res, next);
};

//Company Rest API validation
exports.validateUpdateCompany = function(req, res, next) {
    var company = req.body;
    if(!company) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }
    next();
};

exports.validateCreateCompany = function(req, res, next) {
    var company = req.body;
    if(!company) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }

    req.checkBody('emails', "Property 'emails' is not an array!" ).optional().isArray();
    req.checkBody('emails', 'At least one email has incorrect format').optional().isEmails();

    returnErrors(req, res, next);
};

exports.validateGetCompanyById = function(req, res, next) {
    req.checkParams(reqParams.companyIdParam,
        util.format(invalidFormatMessageTemplate, reqParams.companyIdParam))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

//Timelog Rest API validation
exports.validateGetTimelogByDates = function(req, res, next) {
    req.checkParams(reqParams.userIdParam,
        util.format(invalidFormatMessageTemplate, reqParams.userIdParam))
            .notEmpty().isMongoId();

    req.checkQuery(reqParams.projectIdParam,
        util.format(invalidFormatMessageTemplate, reqParams.projectIdParam))
            .notEmpty().isMongoId();

    req.checkQuery(reqParams.startDateParam,
        util.format(invalidFormatMessageTemplate, reqParams.startDateParam))
            .notEmpty().isDate();

    req.checkQuery(reqParams.endDateParam,
        util.format(invalidFormatMessageTemplate, reqParams.endDateParam))
            .notEmpty().isDate();

    returnErrors(req, res, next);
};

exports.validateDeleteTimelog = function(req, res, next) {
    req.checkParams(reqParams.timelogIdParam, util.format(invalidFormatMessageTemplate, reqParams.timelogIdParam))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

exports.validateSaveTimelog = function(req, res, next) {
    var timelogs = req.body;
    if(!timelogs) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }
    req.checkBody('timelog', 'Incorrect timelog (Check: date, userId, projectId, projectName)')
        .isTimelog();

    returnErrors(req, res, next);
};

exports.validateDeactivateProject = function(req, res, next) {
    req.check(reqParams.projectIdParam,
        util.format(invalidFormatMessageTemplate, reqParams.projectIdParam))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

//Custom validators for express-validator
exports.isArray = function(value) {
    return Array.isArray(value);
};

exports.timelogs = function(values) {
    if(Array.isArray(values)) {
        return values.every(function(val){
            var isValid = true;
            if(val._id){
                isValid = validator.isMongoId(val._id);
            }
            isValid = isValid
                && validator.isMongoId(val.userId) //required && format
                && validator.isMongoId(val.projectId) //required && format
                && validator.isLength(val.projectName, 1) //required
                && validator.isDate(val.date); //required && format
            return isValid;
        });
    }
    return false;
};

exports.assignments = function(values, projectId, userId) {
    if(!values.length) { // if array is empty
        return true;
    }

    if(Array.isArray(values)) {
        return values.every(function(val){
            return validator.isMongoId(val.projectId)
                && validator.isMongoId(projectId)
                && (val.projectId.equals(projectId))
                && validator.isLength(val.projectName, 1)
                && validator.isMongoId(val.userId)
                && validator.isMongoId(userId)
                && (val.userId.equals(userId));
        });
    }
    return false;
};

exports.isEmails = function(values) {
    if(Array.isArray(values)) {
        return values.every(function(val) {
            return validator.isEmail(val);
        });
    }

    return false;
};

//Private part
function returnErrors(req, res, next) {
    var errors = req.validationErrors(true);
    if(errors) {
        res.status(400).json(errors);
        return;
    }

    next();
}<|endoftext|>


<commit_before>/* Router: maps hash navigation */

Atmo.Router = Backbone.Router.extend({
    routes: {
        "": "instances",
        "instances": "instances",
        "instances/:instance_id": "select_instance",
        "volumes": "volumes",
        "volumes/:volume_id": "select_volume",
        "new_instance": "new_instance",
        "new_instance/:image_query": "select_image",
        "settings": "settings"
    },
    initialize: function() {
      Atmo.profile = new Atmo.Models.Profile();
      Atmo.profile.fetch({
        async: false,
        success: function(profile, foo, bar) {
          $("#username").html(profile.get('id'));
        },
        error: function() {
          window.location.replace(site_root+"/CASlogin/"+site_root+"/login/");
        }
      });
      Atmo.Utils.update_weather();
      Atmo.providers = new Atmo.Collections.Providers();
      Atmo.providers.fetch({
        async: false,
        success: function(providers, foo, bar) {
        }
      });
	  Atmo.identities = new Atmo.Collections.Identities();
	  Atmo.identities.fetch({
		  async: false,
	  });

      var identity = Atmo.profile.get('selected_identity');
      var identity_provider_id = identity.get("provider_id");

      Atmo.maintenances = new Atmo.Collections.Maintenances();
      Atmo.maintenances.fetch({
        async: false,
      });
      if (!Atmo.maintenances.in_maintenance(identity_provider_id)) {
        Atmo.instances = new Atmo.Collections.Instances();
        Atmo.volumes = new Atmo.Collections.Volumes();
        Atmo.images = new Atmo.Collections.Machines();
        Atmo.instance_types = new Atmo.Collections.Sizes();
        Atmo.instance_types.fetch({async: false});
        new Atmo.Views.Sidebar({el: $('#menu_wrapper')}).render();
        Atmo.instances.fetch({
          async: false
        });
        Atmo.volumes.fetch();
        Atmo.images.fetch();
      }

      Atmo.notifications = new Atmo.Collections.Notifications();

      this.main = new Atmo.Views.Main({el: $('#main')[0]}).render();

      setInterval(function() {
        $('#refresh_instances_button').click();
      }, 5*60*1000);

      new Atmo.Views.FeedbackLink({el: $('#feedback_link')[0]});
      Atmo.request_resources_modal = new Atmo.Views.RequestResourcesModal();
      $('body').append(Atmo.request_resources_modal.render().el);

      Atmo.alert_modal = new Atmo.Views.AlertModal();
      $('body').append(Atmo.alert_modal.render().el);

      // Populate the top menu with a provider switcher
      for (var i = 0; i < Atmo.identities.length; i++) {
	var identity = Atmo.identities.models[i];
        var identity_provider_id = identity.get('provider_id');
        var in_maintenance = Atmo.maintenances.in_maintenance(identity_provider_id);
        var result = '';
	var name = Atmo.identities.models[i].get('provider').get('location');
	$('#providers_menu').append($('
', {
	  html: function() {
	    if (identity.get('selected')){
	      result = ' ' + name;
            } else {
              result = '' + name;
            }
            if (in_maintenance) {
              result += ' ';
            }
            result += '';
            return result;
	  },
	  click: function(e) {
	    e.preventDefault();
	    var identity = $(this).data();
            if (in_maintenance) {
              Atmo.Utils.notify('Error', identity.get('provider').get('type') + ' is currently in maintenance.');
	      return false;
            }
	    if (identity.get('selected')) {
	      Atmo.Utils.notify('Error', 'You are already using ' + identity.get('provider').get('type') + ' as your provider.');
	      return false;
	    }
	    else {
	      Atmo.profile.save({'selected_identity': identity.get('id')},
				{'async': false,
				 'patch' : true,
				 success: location.reload()});
	    }
          }
        }).data(identity));
      }

      $('#contact_support').click(function(e) {
        e.preventDefault();
        $('#feedback_link').trigger('click');
      });

      // Populate version number
      $.ajax({
	type: 'GET',
	url: site_root + '/api/v1/version/',
	statusCode: {
	  200:  function(response) {
	    $('#version').html('(v. ' + response['normal'] + ')');
	  }
	},
      });

      $('#total_cpu_time a').click(function() {
	$('#cpu_modal').modal('show');
      });
      new Atmo.Views.NotificationHolder({el: $('#alert_holder')[0]});
    },
  instances: function() {
    var identity = Atmo.profile.get('selected_identity');
    var identity_provider_id = identity.get("provider_id");
    if(!Atmo.maintenances.in_maintenance(identity_provider_id)){
      this.main.show_instance_screen();
      if (Atmo.instances.models.length > 0 && !Atmo.instances.selected_instance) {
          Atmo.instances.select_instance(Atmo.instances.models[0]);
        }
	else {
	  Backbone.history.navigate('instances');
	}
        // Hide all help tips so none remain after navigating away from it
        Atmo.Utils.hide_all_help();
      }
    },
    volumes: function() {
      var identity = Atmo.profile.get('selected_identity');
      var identity_provider_id = identity.get("provider_id");
      if(!Atmo.maintenances.in_maintenance(identity_provider_id)){
        this.main.show_volume_screen();
        // Hide all help tips so none remain after navigating away from it
        Atmo.Utils.hide_all_help();
      }
    },
    new_instance: function(options) {
        this.main.show_new_instance_screen();
        // Hide all help tips so none remain after navigating away from it
        Atmo.Utils.hide_all_help();
    },
    settings: function() {
        this.main.show_settings_screen();
    },
    select_instance: function(instance_id) {
        this.instances();
        var instance;
        if (instance = Atmo.instances.get(instance_id))
            Atmo.instances.select_instance(instance);
    },
    select_volume: function(volume_id) {
        var volume = Atmo.volumes.get(volume_id);
        if (volume)
            Atmo.volumes.select_volume(volume);
        //console.log(volume_id);
    },
    select_image: function(image_query) {
        if (this.new_instance_screen)
            this.new_instance_screen.set_query(image_query);
        else
            this.new_instance({'query': image_query});
    }
});

$(document).ready(function() {
  window.app = new Atmo.Router();
  Backbone.history.start();
});
<commit_msg>Update router for new collection (Only if used by staff user)<commit_after>/* Router: maps hash navigation */

Atmo.Router = Backbone.Router.extend({
    routes: {
        "": "instances",
        "instances": "instances",
        "instances/:instance_id": "select_instance",
        "volumes": "volumes",
        "volumes/:volume_id": "select_volume",
        "new_instance": "new_instance",
        "new_instance/:image_query": "select_image",
        "settings": "settings"
    },
    initialize: function() {
      Atmo.profile = new Atmo.Models.Profile();
      Atmo.profile.fetch({
        async: false,
        success: function(profile, foo, bar) {
          $("#username").html(profile.get('id'));
        },
        error: function() {
          window.location.replace(site_root+"/CASlogin/"+site_root+"/login/");
        }
      });
      Atmo.Utils.update_weather();
      Atmo.providers = new Atmo.Collections.Providers();
      Atmo.providers.fetch({
        async: false,
        success: function(providers, foo, bar) {
        }
      });
	  Atmo.identities = new Atmo.Collections.Identities();
	  Atmo.identities.fetch({
		  async: false,
	  });

      var identity = Atmo.profile.get('selected_identity');
      var identity_provider_id = identity.get("provider_id");

      Atmo.maintenances = new Atmo.Collections.Maintenances();
      Atmo.maintenances.fetch({
        async: false,
      });
      if (!Atmo.maintenances.in_maintenance(identity_provider_id)) {
        Atmo.instances = new Atmo.Collections.Instances();
        Atmo.volumes = new Atmo.Collections.Volumes();
        Atmo.images = new Atmo.Collections.Machines();
        if (Atmo.profile.get('is_staff') === true) {
            Atmo.instance_hypervisors = new Atmo.Collections.Hypervisors();
            Atmo.instance_hypervisors.fetch({async: false});
        }
        Atmo.instance_types = new Atmo.Collections.Sizes();
        Atmo.instance_types.fetch({async: false});
        new Atmo.Views.Sidebar({el: $('#menu_wrapper')}).render();
        Atmo.instances.fetch({
          async: false
        });
        Atmo.volumes.fetch();
        Atmo.images.fetch();
      }

      Atmo.notifications = new Atmo.Collections.Notifications();

      this.main = new Atmo.Views.Main({el: $('#main')[0]}).render();

      setInterval(function() {
        $('#refresh_instances_button').click();
      }, 5*60*1000);

      new Atmo.Views.FeedbackLink({el: $('#feedback_link')[0]});
      Atmo.request_resources_modal = new Atmo.Views.RequestResourcesModal();
      $('body').append(Atmo.request_resources_modal.render().el);

      Atmo.alert_modal = new Atmo.Views.AlertModal();
      $('body').append(Atmo.alert_modal.render().el);

      // Populate the top menu with a provider switcher
      for (var i = 0; i < Atmo.identities.length; i++) {
	var identity = Atmo.identities.models[i];
        var identity_provider_id = identity.get('provider_id');
        var in_maintenance = Atmo.maintenances.in_maintenance(identity_provider_id);
        var result = '';
	var name = Atmo.identities.models[i].get('provider').get('location');
	$('#providers_menu').append($('
', {
	  html: function() {
	    if (identity.get('selected')){
	      result = ' ' + name;
            } else {
              result = '' + name;
            }
            if (in_maintenance) {
              result += ' ';
            }
            result += '';
            return result;
	  },
	  click: function(e) {
	    e.preventDefault();
	    var identity = $(this).data();
            if (in_maintenance) {
              Atmo.Utils.notify('Error', identity.get('provider').get('type') + ' is currently in maintenance.');
	      return false;
            }
	    if (identity.get('selected')) {
	      Atmo.Utils.notify('Error', 'You are already using ' + identity.get('provider').get('type') + ' as your provider.');
	      return false;
	    }
	    else {
	      Atmo.profile.save({'selected_identity': identity.get('id')},
				{'async': false,
				 'patch' : true,
				 success: location.reload()});
	    }
          }
        }).data(identity));
      }

      $('#contact_support').click(function(e) {
        e.preventDefault();
        $('#feedback_link').trigger('click');
      });

      // Populate version number
      $.ajax({
	type: 'GET',
	url: site_root + '/api/v1/version/',
	statusCode: {
	  200:  function(response) {
	    $('#version').html('(v. ' + response['normal'] + ')');
	  }
	},
      });

      $('#total_cpu_time a').click(function() {
	$('#cpu_modal').modal('show');
      });
      new Atmo.Views.NotificationHolder({el: $('#alert_holder')[0]});
    },
  instances: function() {
    var identity = Atmo.profile.get('selected_identity');
    var identity_provider_id = identity.get("provider_id");
    if(!Atmo.maintenances.in_maintenance(identity_provider_id)){
      this.main.show_instance_screen();
      if (Atmo.instances.models.length > 0 && !Atmo.instances.selected_instance) {
          Atmo.instances.select_instance(Atmo.instances.models[0]);
        }
	else {
	  Backbone.history.navigate('instances');
	}
        // Hide all help tips so none remain after navigating away from it
        Atmo.Utils.hide_all_help();
      }
    },
    volumes: function() {
      var identity = Atmo.profile.get('selected_identity');
      var identity_provider_id = identity.get("provider_id");
      if(!Atmo.maintenances.in_maintenance(identity_provider_id)){
        this.main.show_volume_screen();
        // Hide all help tips so none remain after navigating away from it
        Atmo.Utils.hide_all_help();
      }
    },
    new_instance: function(options) {
        this.main.show_new_instance_screen();
        // Hide all help tips so none remain after navigating away from it
        Atmo.Utils.hide_all_help();
    },
    settings: function() {
        this.main.show_settings_screen();
    },
    select_instance: function(instance_id) {
        this.instances();
        var instance;
        if (instance = Atmo.instances.get(instance_id))
            Atmo.instances.select_instance(instance);
    },
    select_volume: function(volume_id) {
        var volume = Atmo.volumes.get(volume_id);
        if (volume)
            Atmo.volumes.select_volume(volume);
        //console.log(volume_id);
    },
    select_image: function(image_query) {
        if (this.new_instance_screen)
            this.new_instance_screen.set_query(image_query);
        else
            this.new_instance({'query': image_query});
    }
});

$(document).ready(function() {
  window.app = new Atmo.Router();
  Backbone.history.start();
});
<|endoftext|>