<commit_before>/* Dispatch area - Should be eliminate after dispatching */

var markersGroup = new L.LayerGroup();
var isDispatching = false;
var isFilterOpen = false;
var placeIcon = L.icon({
	iconUrl: '/static/icons/place_marker.png',
	iconSize: [40, 40],
	iconAnchor: [20, 40],
	popupAnchor: [0,-40]
});

var dispatchingAmbulances = {};
var numberOfDispatchingAmbulances = 0;

var currentAddress;
var currentLocation;
var currentPatients;
var newPatientIndex;

var submitDispatching = function () {

    // submit form
    $('#dispatch-form-collapse').submit();

};

var beginDispatching = function () {

    isDispatching = true;
    var filtersDiv = $('#filtersDiv');
    isFilterOpen = filtersDiv.hasClass('show');
    console.log('Begin dispatching.');

    $('#dispatchBeginButton').hide();
    $('#dispatchSubmitButton').show();
    $('#dispatchCancelButton').show();

    // open filter and available ambulances
    filtersDiv.addClass('show');
    $('#ambulance_status').addClass('show');
    $('#ambulance_AV').addClass('show');

    // Handle double click
    mymap.doubleClickZoom.disable();
    mymap.on('dblclick', function(e) {
	// update marker location
	updateCurrentMarker(e.latlng);
    });

    // Update current location
    updateCurrentLocation(mymap.getCenter());

    // Update current address
    updateCurrentAddress(currentLocation);

    // Clear current currentPatients
    currentPatients = {};
    newPatientIndex = 0;

    // Initialize patient form
    $('#patients').empty();

    // add new patient form entry
    addPatientForm(newPatientIndex);

    // resize size
    resizeMap();

    // center map
    mymap.setView(currentLocation, mymap.getZoom());

}

var endDispatching = function () {

    isDispatching = false;
    dispatchingAmbulances = {};
    console.log('End dispatching.');

    // remove marker
    markersGroup.clearLayers();

    // unselect priority
    $('input:radio[name=priority]:checked').prop('checked', false);

    // clear description
    $('#comment').val('');

    // clear ambulances buttons
    $('#ambulance-selection :button').remove();
    $('#ambulance-selection-message').show();

    // clear patients
    $('#patients').empty();

    // show buttons
    $('#dispatchBeginButton').show();
    $('#dispatchSubmitButton').hide();
    $('#dispatchCancelButton').hide();

    // close dispatch panel
    $('#dispatchDiv').removeClass('show');

    if (!isFilterOpen) {
        // close filter panel
        $('#filtersDiv').removeClass('show');
    }

    // remove dblclick handler
    mymap.off('dblclick');
    mymap.doubleClickZoom.enable();

    // invalidate map size
    mymap.invalidateSize();

}

var removeFromDispatchingList = function(ambulance) {

    // delete from dispatching list
    delete dispatchingAmbulances[ambulance.id];
    numberOfDispatchingAmbulances--;

    // show message if last button
    if (numberOfDispatchingAmbulances == 0)
        $('#ambulance-selection-message').show();

}

var addToDispatchingList = function(ambulance) {

    // quick return if null or not dispatching
    if (ambulance == null || !isDispatching)
        return;

    // add ambulance to dispatching list
    console.log('Adding ambulance ' + ambulance.identifier + ' to dispatching list');

    // already in?
    if (ambulance.id in dispatchingAmbulances) {
        console.log('Already in dispatching list, skip');
        return;
    }

    // not available?
    if (ambulance.status != STATUS_AVAILABLE) {
        console.log('Ambulance is not available');
        bsalert('Can only dispatch available ambulances!');
        return;
    }

    // hide message if first button
    if (numberOfDispatchingAmbulances == 0)
        $('#ambulance-selection-message').hide();

    // add ambulance to list of dispatching ambulances
    dispatchingAmbulances[ambulance.id] = ambulance;
    numberOfDispatchingAmbulances++;

    // add button to ambulance dispatch grid
    $('#ambulance-selection').append(
        '<button id="dispatch-button-' + ambulance.id + '"'
        + ' value="' + ambulance.id + '"'
        + ' type="button" class="btn btn-sm '+ ambulance_buttons['AV'] + '"'
        + ' style="margin: 2px 2px;"'
        + ' draggable="true">'
        + ambulance.identifier
        + '</button>'
    );
    $('#dispatch-button-' + ambulance.id)
        .on('dragstart', function (e) {
            // on start of drag, copy information and fade button
            console.log('dragstart');
            this.style.opacity = '0.4';
            e.originalEvent.dataTransfer.setData("text/plain", ambulance.id);
        })
        .on('dragend', function (e) {
            console.log('dragend');
            if (e.originalEvent.dataTransfer.dropEffect == 'none') {
                // Remove button if not dropped back
                removeFromDispatchingList(ambulance);
                // Remove button
                $(this).remove();
            } else {
                // Restore opacity if dropped back in
                this.style.opacity = '1.0';
            }
        });
}

var updateCurrentMarker = function(latlng) {

    // update current location
    updateCurrentLocation(latlng);

    // update address?
    if ($('#update-address').prop('checked'))
        updateCurrentAddress(latlng);

}

var updateCurrentLocation = function(location) {

    console.log('Setting current location to: ' + location.lat + ', ' + location.lng);

    // set currentLocation
    currentLocation = location;

    // update coordinates on form
    $('#curr-lat').html(currentLocation.lat.toFixed(6));
    $('#curr-lng').html(currentLocation.lng.toFixed(6));

    // remove existing marker
    markersGroup.clearLayers();

    // laydown marker
    var marker = L.marker(location,
        {
            icon: placeIcon,
            draggable: true
        })
        .addTo(markersGroup);
    markersGroup.addTo(mymap);

    // pan to location: kaung and mauricio though it made more sense to not pan
    // mymap.panTo(location);

    // marker can be dragged on the dispatch map
    marker.on('dragend', function(e) {

	// update current marker
	updateCurrentMarker(marker.getLatLng());

    });
}

var updateCurrentAddress = function(location) {

    var options = {
        types: 'address',
        limit: 1
    };
    geocoder.reverse(location, options,
        function (results, status) {

        if (status != "success") {
            bsalert("Could not geocode:\nError " + status + ", " + results['error']);
            return;
        }

        // quick return if found nothing
        if (results.length == 0) {
            console.log('Got nothing from geocode');
            return;
        }

        // parse features into current address
        currentAddress = geocoder.parse_feature(results[0]);

        console.log(
            'Setting currentAddress to:'
            + '\nnumber: ' + currentAddress['number']
            + '\nstreet: ' + currentAddress['street']
            + '\nunit: ' + currentAddress['unit']
            + '\nlocation: ' + currentAddress['location']['latitude']
            + ',' + currentAddress['location']['longitude']
            + '\nneighborhood: ' + currentAddress['neighborhood']
            + '\nzipcode: ' + currentAddress['zipcode']
            + '\ncity: ' + currentAddress['city']
            + '\nstate: ' + currentAddress['state']
            + '\ncountry: ' + currentAddress['country']
        );

        // set input text
        $('#street').val(currentAddress['street_address']);

    });

}

var updateCoordinates = function() {

    // when the user changes the street address
    var address =$('#street').val();

    // quick return if no address
    if (!address)
        return;

    // otherwise geocode and update
    var options = {
        types: 'address',
        limit: 1,
        autocomplete: 'true'
    };
    geocoder.geocode(address, options,
        function (results, status) {

            if (status != "success") {
                bsalert("Could not geocode:\nError " + status + ", " + results['error']);
                return;
            }

            // quick return if found nothing
            if (results.length == 0) {
                console.log('Got nothing from geocode');
                return;
            }

            // parse features into address
            var address = geocoder.parse_feature(results[0]);

            console.log(
                'Setting currentLocation to:'
                + '\nnumber: ' + address['number']
                + '\nstreet: ' + address['street']
                + '\nunit: ' + address['unit']
                + '\nlocation: ' + address['location']['latitude']
                + ',' + address['location']['longitude']
                + '\nneighborhood: ' + address['neighborhood']
                + '\nzipcode: ' + address['zipcode']
                + '\ncity: ' + address['city']
                + '\nstate: ' + address['state']
                + '\ncountry: ' + address['country']
            );

            // set current location
            updateCurrentLocation({
                lat: address['location']['latitude'],
                lng: address['location']['longitude']
            });

        });
}

function dispatchCall() {

    var form = {};

    // call information
    var street_address = $('#street').val().trim();
    form['details'] = $('#comment').val().trim();
    form['priority'] = $('input:radio[name=priority]:checked').val();

    // checks
    if (form["priority"] === undefined) {
        bsalert("Please select the priority level.");
        return;
    }
    if (numberOfDispatchingAmbulances == 0) {
        bsalert("Please dispatch at least one ambulance.");
        return;
    }

    // address information
    form['neighborhood'] = currentAddress['neighborhood'];
    form['city'] = currentAddress['city'];
    form['state'] = currentAddress['state'];
    form['country'] = currentAddress['country'];
    form['zipcode'] = currentAddress['zipcode'];
    form['location'] = currentAddress['location'];

    // Has the user modified the street address?
    if (street_address != currentAddress['street_address']) {

        // parse street address
        var address = geocoder.parse_street_address(street_address, form['country']);

        form['number'] = address['number'];
        form['street'] = address['street'];
        form['unit'] = address['unit'];

    } else {

        form['number'] = currentAddress['number'];
        form['street'] = currentAddress['street'];
        form['unit'] = currentAddress['unit'];

    }

    // Make sure blanks are undefined
    if (form['number'] == "") {
        form['number'] = undefined;
    }

    // ambulances
    var ambulances = [];
    for (var id in dispatchingAmbulances) {
        if (dispatchingAmbulances.hasOwnProperty(id))
            ambulances.push({ 'ambulance_id': id });
		}
    form['ambulancecall_set'] = ambulances;

    // patients
    var patients = [];
    for (var index in currentPatients)
        if (currentPatients.hasOwnProperty(index)) {
            var patient = currentPatients[index];
            var obj = { 'name': patient[0] };
            if (patient[1])
                // add age
                obj['age'] = parseInt(patient[1]);
            patients.push(obj);
        }
    form['patient_set'] = patients;

    // make json call
    var postJsonUrl = APIBaseUrl + 'call/';
    console.log("Form:");
    console.log(form);
    console.log("Will post:");
    console.log(JSON.stringify(form));

    var CSRFToken = Cookies.get('csrftoken');
    console.log('csrftoken = ' + CSRFToken);

    // retrieve csrf token
    $.ajaxSetup({
        beforeSend: function (xhr, settings) {
            if (!CSRFSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", CSRFToken);
            }
        }
    });

    // make ajax call
    $.ajax({
        url: postJsonUrl,
        type: 'POST',
        dataType: 'json',
        contentType: 'application/json',
        data: JSON.stringify(form),
        success: function (data) {

            // Log success
            console.log("Succesfully posted new call.");

            // End dispatching
            endDispatching();

        },
        error: function (jqXHR, textStatus, errorThrown) {

            // Log failure
            console.log("Failed to post new call.");
            console.log(jqXHR.responseText);

            // Show modal
            bsalert(jqXHR.responseText, 'alert-danger', 'Failure');

            // Do not end dispatching to give user chance to make changes
            // endDispatching();

        }
    });
}

// patient functions

var addPatient = function(index) {

    console.log('Adding patient index ' + index);

    var name = $('#patient-' + index + '-name').val().trim();
    var age = $('#patient-' + index + '-age').val().trim();

    // is name empty?
    if (!name) {
        bsalert('Empty name');
        return;
    }

    // add name
    currentPatients[index] = [name, age];
    newPatientIndex++;

    // change button symbol
    var symbol = $('#patient-' + index + '-symbol');
    symbol.removeClass('fa-plus');
    symbol.addClass('fa-minus');

    // change button action from add to remove
    $('#patients').find('#patient-' + index + '-button')
        .off()
        .on('click', function(e) { removePatient(index); });

    // add new form
    addPatientForm(newPatientIndex);

}

var removePatient = function(index) {

    console.log('Removing patient index ' + index);

    // remove from storage
    delete currentPatients[index];

    // remove from form
    $('#patients')
        .find('#patient-' + index + '-form')
        .remove();

}

var addPatientForm = function(index) {

    // add new patient form entry
    $('#patients').append(newPatientForm(index, 'fa-plus'));

    // bind addPatient to click
    $('#patients')
        .on('click',
            '#patient-' + index + '-button',
            function(e) { addPatient(index); });

}

var newPatientForm = function(index, symbol) {

    var html =
        '
' +
            '
' +
                '<input id="patient-' + index + '-name" ' +
                       'type="text" ' +
                       'class="form-control" ' +
                       'placeholder="Name">' +
            '
' +
            '
' +
                '<input id="patient-' + index + '-age" ' +
                       'type="number" min="0" ' +
                       'class="form-control" ' +
                       'placeholder="Age">' +
            '
' +
            '
' +
                '<button class="btn btn-default btn-block new-patient" ' +
                       ' type="button" ' +
                       ' id="patient-' + index + '-button">' +
                    '' +
                '</button>' +
            '
' +
        '
';

    // console.log('html = "' + html + '"');

    return html;

}

// Ready function
$(function() {

    // connect actions to inputs

    $("#street").change(function () {

        // update coordinates?
        if ($('#update-coordinates').prop('checked'))
            updateCoordinates();

    });

    $('#update-coordinates').change(function () {

        // update coordinates?
        if ($('#update-coordinates').prop('checked'))
            updateCoordinates();

    });

    $('#update-address').change(function () {

        // update address?
        if ($('#update-address').prop('checked'))
            updateCurrentAddress(currentLocation);

    });

    $('#dispatch-form-collapse').submit(function (e) {

        // prevent normal form submission
        e.preventDefault();

        // dispatch call
        dispatchCall();

    });

});

// CSRF functions
function CSRFSafeMethod(method) {
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
<commit_msg>Fixing problems with javascript dispatching form.<commit_after>/* Dispatch area - Should be eliminate after dispatching */

var markersGroup = new L.LayerGroup();
var isDispatching = false;
var isFilterOpen = false;
var placeIcon = L.icon({
	iconUrl: '/static/icons/place_marker.png',
	iconSize: [40, 40],
	iconAnchor: [20, 40],
	popupAnchor: [0,-40]
});

var dispatchingAmbulances = {};
var numberOfDispatchingAmbulances = 0;

var currentAddress;
var currentLocation;
var currentPatients;

var submitDispatching = function () {

    // submit form
    $('#dispatch-form-collapse').submit();

};

var beginDispatching = function () {

    isDispatching = true;
    var filtersDiv = $('#filtersDiv');
    isFilterOpen = filtersDiv.hasClass('show');
    console.log('Begin dispatching.');

    $('#dispatchBeginButton').hide();
    $('#dispatchSubmitButton').show();
    $('#dispatchCancelButton').show();

    // open filter and available ambulances
    filtersDiv.addClass('show');
    $('#ambulance_status').addClass('show');
    $('#ambulance_AV').addClass('show');

    // Handle double click
    mymap.doubleClickZoom.disable();
    mymap.on('dblclick', function(e) {
	// update marker location
	updateCurrentMarker(e.latlng);
    });

    // Update current location
    updateCurrentLocation(mymap.getCenter());

    // Update current address
    updateCurrentAddress(currentLocation);

    // Clear current currentPatients
    currentPatients = {};

    // Initialize patient form
    $('#patients').empty();

    // add new patient form entry
    addPatientForm(0);

    // resize size
    resizeMap();

    // center map
    mymap.setView(currentLocation, mymap.getZoom());

}

var endDispatching = function () {

    isDispatching = false;
    dispatchingAmbulances = {};
    console.log('End dispatching.');

    // remove marker
    markersGroup.clearLayers();

    // unselect priority
    $('input:radio[name=priority]:checked').prop('checked', false);

    // clear description
    $('#comment').val('');

    // clear ambulances buttons
    $('#ambulance-selection :button').remove();
    $('#ambulance-selection-message').show();

    // clear patients
    $('#patients').empty();

    // show buttons
    $('#dispatchBeginButton').show();
    $('#dispatchSubmitButton').hide();
    $('#dispatchCancelButton').hide();

    // close dispatch panel
    $('#dispatchDiv').removeClass('show');

    if (!isFilterOpen) {
        // close filter panel
        $('#filtersDiv').removeClass('show');
    }

    // remove dblclick handler
    mymap.off('dblclick');
    mymap.doubleClickZoom.enable();

    // invalidate map size
    mymap.invalidateSize();

}

var removeFromDispatchingList = function(ambulance) {

    // delete from dispatching list
    delete dispatchingAmbulances[ambulance.id];
    numberOfDispatchingAmbulances--;

    // show message if last button
    if (numberOfDispatchingAmbulances == 0)
        $('#ambulance-selection-message').show();

}

var addToDispatchingList = function(ambulance) {

    // quick return if null or not dispatching
    if (ambulance == null || !isDispatching)
        return;

    // add ambulance to dispatching list
    console.log('Adding ambulance ' + ambulance.identifier + ' to dispatching list');

    // already in?
    if (ambulance.id in dispatchingAmbulances) {
        console.log('Already in dispatching list, skip');
        return;
    }

    // not available?
    if (ambulance.status != STATUS_AVAILABLE) {
        console.log('Ambulance is not available');
        bsalert('Can only dispatch available ambulances!');
        return;
    }

    // hide message if first button
    if (numberOfDispatchingAmbulances == 0)
        $('#ambulance-selection-message').hide();

    // add ambulance to list of dispatching ambulances
    dispatchingAmbulances[ambulance.id] = ambulance;
    numberOfDispatchingAmbulances++;

    // add button to ambulance dispatch grid
    $('#ambulance-selection').append(
        '<button id="dispatch-button-' + ambulance.id + '"'
        + ' value="' + ambulance.id + '"'
        + ' type="button" class="btn btn-sm '+ ambulance_buttons['AV'] + '"'
        + ' style="margin: 2px 2px;"'
        + ' draggable="true">'
        + ambulance.identifier
        + '</button>'
    );
    $('#dispatch-button-' + ambulance.id)
        .on('dragstart', function (e) {
            // on start of drag, copy information and fade button
            console.log('dragstart');
            this.style.opacity = '0.4';
            e.originalEvent.dataTransfer.setData("text/plain", ambulance.id);
        })
        .on('dragend', function (e) {
            console.log('dragend');
            if (e.originalEvent.dataTransfer.dropEffect == 'none') {
                // Remove button if not dropped back
                removeFromDispatchingList(ambulance);
                // Remove button
                $(this).remove();
            } else {
                // Restore opacity if dropped back in
                this.style.opacity = '1.0';
            }
        });
}

var updateCurrentMarker = function(latlng) {

    // update current location
    updateCurrentLocation(latlng);

    // update address?
    if ($('#update-address').prop('checked'))
        updateCurrentAddress(latlng);

}

var updateCurrentLocation = function(location) {

    console.log('Setting current location to: ' + location.lat + ', ' + location.lng);

    // set currentLocation
    currentLocation = location;

    // update coordinates on form
    $('#curr-lat').html(currentLocation.lat.toFixed(6));
    $('#curr-lng').html(currentLocation.lng.toFixed(6));

    // remove existing marker
    markersGroup.clearLayers();

    // laydown marker
    var marker = L.marker(location,
        {
            icon: placeIcon,
            draggable: true
        })
        .addTo(markersGroup);
    markersGroup.addTo(mymap);

    // pan to location: kaung and mauricio though it made more sense to not pan
    // mymap.panTo(location);

    // marker can be dragged on the dispatch map
    marker.on('dragend', function(e) {

	// update current marker
	updateCurrentMarker(marker.getLatLng());

    });
}

var updateCurrentAddress = function(location) {

    var options = {
        types: 'address',
        limit: 1
    };
    geocoder.reverse(location, options,
        function (results, status) {

        if (status != "success") {
            bsalert("Could not geocode:\nError " + status + ", " + results['error']);
            return;
        }

        // quick return if found nothing
        if (results.length == 0) {
            console.log('Got nothing from geocode');
            return;
        }

        // parse features into current address
        currentAddress = geocoder.parse_feature(results[0]);

        console.log(
            'Setting currentAddress to:'
            + '\nnumber: ' + currentAddress['number']
            + '\nstreet: ' + currentAddress['street']
            + '\nunit: ' + currentAddress['unit']
            + '\nlocation: ' + currentAddress['location']['latitude']
            + ',' + currentAddress['location']['longitude']
            + '\nneighborhood: ' + currentAddress['neighborhood']
            + '\nzipcode: ' + currentAddress['zipcode']
            + '\ncity: ' + currentAddress['city']
            + '\nstate: ' + currentAddress['state']
            + '\ncountry: ' + currentAddress['country']
        );

        // set input text
        $('#street').val(currentAddress['street_address']);

    });

}

var updateCoordinates = function() {

    // when the user changes the street address
    var address =$('#street').val();

    // quick return if no address
    if (!address)
        return;

    // otherwise geocode and update
    var options = {
        types: 'address',
        limit: 1,
        autocomplete: 'true'
    };
    geocoder.geocode(address, options,
        function (results, status) {

            if (status != "success") {
                bsalert("Could not geocode:\nError " + status + ", " + results['error']);
                return;
            }

            // quick return if found nothing
            if (results.length == 0) {
                console.log('Got nothing from geocode');
                return;
            }

            // parse features into address
            var address = geocoder.parse_feature(results[0]);

            console.log(
                'Setting currentLocation to:'
                + '\nnumber: ' + address['number']
                + '\nstreet: ' + address['street']
                + '\nunit: ' + address['unit']
                + '\nlocation: ' + address['location']['latitude']
                + ',' + address['location']['longitude']
                + '\nneighborhood: ' + address['neighborhood']
                + '\nzipcode: ' + address['zipcode']
                + '\ncity: ' + address['city']
                + '\nstate: ' + address['state']
                + '\ncountry: ' + address['country']
            );

            // set current location
            updateCurrentLocation({
                lat: address['location']['latitude'],
                lng: address['location']['longitude']
            });

        });
}

function dispatchCall() {

    var form = {};

    // call information
    var street_address = $('#street').val().trim();
    form['details'] = $('#comment').val().trim();
    form['priority'] = $('input:radio[name=priority]:checked').val();

    // checks
    if (form["priority"] === undefined) {
        bsalert("Please select the priority level.");
        return;
    }
    if (numberOfDispatchingAmbulances == 0) {
        bsalert("Please dispatch at least one ambulance.");
        return;
    }

    // address information
    form['neighborhood'] = currentAddress['neighborhood'];
    form['city'] = currentAddress['city'];
    form['state'] = currentAddress['state'];
    form['country'] = currentAddress['country'];
    form['zipcode'] = currentAddress['zipcode'];
    form['location'] = currentAddress['location'];

    // Has the user modified the street address?
    if (street_address != currentAddress['street_address']) {

        // parse street address
        var address = geocoder.parse_street_address(street_address, form['country']);

        form['number'] = address['number'];
        form['street'] = address['street'];
        form['unit'] = address['unit'];

    } else {

        form['number'] = currentAddress['number'];
        form['street'] = currentAddress['street'];
        form['unit'] = currentAddress['unit'];

    }

    // Make sure blanks are undefined
    if (form['number'] == "") {
        form['number'] = undefined;
    }

    // ambulances
    var ambulances = [];
    for (var id in dispatchingAmbulances) {
        if (dispatchingAmbulances.hasOwnProperty(id))
            ambulances.push({ 'ambulance_id': id });
		}
    form['ambulancecall_set'] = ambulances;

    // patients
    var patients = [];
    for (var index in currentPatients)
        if (currentPatients.hasOwnProperty(index)) {
            var patient = currentPatients[index];
            var obj = { 'name': patient[0] };
            if (patient[1])
                // add age
                obj['age'] = parseInt(patient[1]);
            patients.push(obj);
        }
    form['patient_set'] = patients;

    // make json call
    var postJsonUrl = APIBaseUrl + 'call/';
    console.log("Form:");
    console.log(form);
    console.log("Will post:");
    console.log(JSON.stringify(form));

    var CSRFToken = Cookies.get('csrftoken');
    console.log('csrftoken = ' + CSRFToken);

    // retrieve csrf token
    $.ajaxSetup({
        beforeSend: function (xhr, settings) {
            if (!CSRFSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", CSRFToken);
            }
        }
    });

    // make ajax call
    $.ajax({
        url: postJsonUrl,
        type: 'POST',
        dataType: 'json',
        contentType: 'application/json',
        data: JSON.stringify(form),
        success: function (data) {

            // Log success
            console.log("Succesfully posted new call.");

            // End dispatching
            endDispatching();

        },
        error: function (jqXHR, textStatus, errorThrown) {

            // Log failure
            console.log("Failed to post new call.");
            console.log(jqXHR.responseText);

            // Show modal
            bsalert(jqXHR.responseText, 'alert-danger', 'Failure');

            // Do not end dispatching to give user chance to make changes
            // endDispatching();

        }
    });
}

// patient functions

var addPatient = function(index) {

    console.log('Adding patient index ' + index);

    var name = $('#patient-' + index + '-name').val().trim();
    var age = $('#patient-' + index + '-age').val().trim();

    // is name empty?
    if (!name) {
        bsalert('Empty name');
        return;
    }

    // add name
    currentPatients[index] = [name, age];

    // change button symbol
    var symbol = $('#patient-' + index + '-symbol');
    symbol.removeClass('fa-plus');
    symbol.addClass('fa-minus');

    // change button action from add to remove
    $('#patients').find('#patient-' + index + '-button')
        .off()
        .on('click', function(e) { removePatient(index); });

    // add new form
    addPatientForm(index + 1);

}

var removePatient = function(index) {

    console.log('Removing patient index ' + index);

    // remove from storage
    delete currentPatients[index];

    // remove from form
    $('#patients')
        .find('#patient-' + index + '-form')
        .remove();

}

var addPatientForm = function(index) {

    // add new patient form entry
    $('#patients').append(newPatientForm(index, 'fa-plus'));

    // bind addPatient to click
    $('#patients')
        .on('click',
            '#patient-' + index + '-button',
            function(e) { addPatient(index); });

}

var newPatientForm = function(index, symbol) {

    var html =
        '
' +
            '
' +
                '<input id="patient-' + index + '-name" ' +
                       'type="text" ' +
                       'class="form-control" ' +
                       'placeholder="Name">' +
            '
' +
            '
' +
                '<input id="patient-' + index + '-age" ' +
                       'type="number" min="0" ' +
                       'class="form-control" ' +
                       'placeholder="Age">' +
            '
' +
            '
' +
                '<button class="btn btn-default btn-block new-patient" ' +
                       ' type="button" ' +
                       ' id="patient-' + index + '-button">' +
                    '' +
                '</button>' +
            '
' +
        '
';

    // console.log('html = "' + html + '"');

    return html;

}

// Ready function
$(function() {

    // connect actions to inputs

    $("#street").change(function () {

        // update coordinates?
        if ($('#update-coordinates').prop('checked'))
            updateCoordinates();

    });

    $('#update-coordinates').change(function () {

        // update coordinates?
        if ($('#update-coordinates').prop('checked'))
            updateCoordinates();

    });

    $('#update-address').change(function () {

        // update address?
        if ($('#update-address').prop('checked'))
            updateCurrentAddress(currentLocation);

    });

    $('#dispatch-form-collapse').submit(function (e) {

        // prevent normal form submission
        e.preventDefault();

        // dispatch call
        dispatchCall();

    });

});

// CSRF functions
function CSRFSafeMethod(method) {
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
<|endoftext|>


<commit_before>import Users from './collection.js';
import intersection from 'lodash/intersection';
import compact from 'lodash/compact';
import map from 'lodash/map';
import difference from 'lodash/difference';
import { Utils } from 'meteor/vulcan:lib';

/**
 * @summary Users.groups object
 */
Users.groups = {};

/**
 * @summary Group class
 */
class Group {

  constructor() {
    this.actions = [];
  }

  can(actions) {
    actions = Array.isArray(actions) ? actions : [actions];
    this.actions = this.actions.concat(actions.map(a => a.toLowerCase()));
  }

  cannot(actions) {
    actions = Array.isArray(actions) ? actions : [actions];
    this.actions = _.difference(this.actions, actions.map(a => a.toLowerCase()));
  }

}

////////////////////
// Helpers        //
////////////////////

/**
 * @summary create a new group
 * @param {String} groupName
 */
Users.createGroup = groupName => {
  Users.groups[groupName] = new Group();
};

/**
 * @summary get a list of a user's groups
 * @param {Object} user
 */
Users.getGroups = (user, document) => {

  let userGroups = [];

  if (!user) { // guests user

    userGroups = ['guests'];

  } else {

    userGroups = ['members'];

    if (document && Users.owns(user, document)) {
      userGroups.push('owners');
    }

    if (user.groups) { // custom groups
      userGroups = userGroups.concat(user.groups);
    }

    if (Users.isAdmin(user)) { // admin
      userGroups.push('admins');
    }

  }

  return userGroups;

};

/**
 * @summary get a list of all the actions a user can perform
 * @param {Object} user
 */
Users.getActions = user => {
  let userGroups = Users.getGroups(user);
  if (!userGroups.includes('guests')) {
    // always give everybody permission for guests actions, too
    userGroups.push('guests');
  }
  let groupActions = userGroups.map(groupName => {
    // note: make sure groupName corresponds to an actual group
    const group = Users.groups[groupName];
    return group && group.actions;
  });
  return _.unique(_.flatten(groupActions));
};

/**
 * @summary check if a user is a member of a group
 * @param {Array} user
 * @param {String} group or array of groups
 */
Users.isMemberOf = (user, groupOrGroups, document) => {
  const groups = Array.isArray(groupOrGroups) ? groupOrGroups : [groupOrGroups];
  return intersection(Users.getGroups(user, document), groups).length > 0;
};

/**
 * @summary check if a user can perform at least one of the specified actions
 * @param {Object} user
 * @param {String/Array} action or actions
 */
Users.canDo = (user, actionOrActions) => {
  const authorizedActions = Users.getActions(user);
  const actions = Array.isArray(actionOrActions) ? actionOrActions : [actionOrActions];
  return Users.isAdmin(user) || intersection(authorizedActions, actions).length > 0;
};

// DEPRECATED
// TODO: remove this
/**
 * @summary Check if a user can edit a document
 * @param {Object} user - The user performing the action
 * @param {Object} document - The document being edited
 */
// Users.canEdit = function (user, document) {

//   user = (typeof user === 'undefined') ? Meteor.user() : user;

//   // note(apollo): use of `__typename` given by react-apollo
//   //const collectionName = document.getCollectionName();
//   const collectionName = document.__typename ? Utils.getCollectionNameFromTypename(document.__typename) : document.getCollectionName();

//   if (!user || !document) {
//     return false;
//   }

//   if (document.hasOwnProperty('isDeleted') && document.isDeleted) return false;

//   if (Users.owns(user, document)) {
//     // if this is user's document, check if user can edit own documents
//     return Users.canDo(user, `${collectionName}.edit.own`);
//   } else {
//     // if this is not user's document, check if they can edit all documents
//     return Users.canDo(user, `${collectionName}.edit.all`);
//   }

// };

/**
 * @summary Check if a user owns a document
 * @param {Object|string} userOrUserId - The user or their userId
 * @param {Object} document - The document to check (post, comment, user object, etc.)
 */
Users.owns = function (user, document) {
  try {
    if (!!document.userId) {
      // case 1: document is a post or a comment, use userId to check
      return user._id === document.userId;
    } else {
      // case 2: document is a user, use _id or slug to check
      return document.slug ? user.slug === document.slug : user._id === document._id;
    }
  } catch (e) {
    return false; // user not logged in
  }
};

/**
 * @summary Check if a user is an admin
 * @param {Object|string} userOrUserId - The user or their userId
 */
Users.isAdmin = function (userOrUserId) {
  try {
    var user = Users.getUser(userOrUserId);
    return !!user && !!user.isAdmin;
  } catch (e) {
    return false; // user not logged in
  }
};
Users.isAdminById = Users.isAdmin;

export const isAdmin = Users.isAdmin;

/**
 * @summary Check if a user can view a field
 * @param {Object} user - The user performing the action
 * @param {Object} field - The field being edited or inserted
 */
Users.canReadField = function (user, field, document) {
  const canRead = field.canRead || field.viewableBy; //OpenCRUD backwards compatibility
  if (canRead) {
    if (typeof canRead === 'function') {
      // if canRead is a function, execute it with user and document passed. it must return a boolean
      return canRead(user, document);
    } else if (typeof canRead === 'string') {
      // if canRead is just a string, we assume it's the name of a group and pass it to isMemberOf
      return canRead === 'guests' || Users.isMemberOf(user, canRead);
    } else if (Array.isArray(canRead) && canRead.length > 0) {
      // if canRead is an array, we do a recursion on every item and return true if one of the items return true
      return canRead.some(group => Users.canReadField(user, { canRead: group }, document));
    }
  }
  return false;
};

/**
 * @summary Get a list of fields viewable by a user
 * @param {Object} user - The user performing the action
 * @param {Object} collection - The collection
 * @param {Object} document - Optionally, get a list for a specific document
 */
Users.getViewableFields = function (user, collection, document) {
  return Users.getReadableProjection(user, collection, document);
};

Users.getReadableFields = function (user, collection, document) {
  return compact(map(collection.simpleSchema()._schema,
    (field, fieldName) => {
      if (fieldName.indexOf('.$') > -1) return null;
      return Users.canReadField(user, field, document) ? fieldName : null;
    }
  ));
};

Users.getReadableProjection = function (user, collection, document) {
  return Utils.arrayToFields(Users.getReadableFields(user, collection, document));
};

// collection helper
Users.helpers({
  getViewableFields(collection, document) {
    return Users.getViewableFields(this, collection, document);
  }
});

/**
 * @summary Check if a user can access a list of fields
 * @param {Object} user - The user performing the action
 * @param {Object} collection - The collection
 * @param {Object} fields - The list of fields
 */
Users.checkFields = (user, collection, fields) => {
  const viewableFields = Users.getReadableFields(user, collection);
  const diff = difference(fields, viewableFields);

  if (diff.length) {
    throw new Error(
      `You don't have permission to filter collection ${collection.options.collectionName} by the following fields: ${diff.join(
        ', '
      )}.`
    );
  }
  return true;
};

/**
 * @summary For a given document or list of documents, keep only fields viewable by current user
 * @param {Object} user - The user performing the action
 * @param {Object} collection - The collection
 * @param {Object} document - The document being returned by the resolver
 */
Users.restrictViewableFields = function (user, collection, docOrDocs) {

  if (!docOrDocs) return {};

  const restrictDoc = document => {

    // get array of all keys viewable by user
    const viewableKeys = Users.getReadableFields(user, collection, document);
    const restrictedDocument = _.clone(document);

    // loop over each property in the document and delete it if it's not viewable
    _.forEach(restrictedDocument, (value, key) => {
      if (!viewableKeys.includes(key)) {
        delete restrictedDocument[key];
      }
    });

    return restrictedDocument;

  };

  return Array.isArray(docOrDocs) ? docOrDocs.map(restrictDoc) : restrictDoc(docOrDocs);

};

/**
 * @summary Check if a user can submit a field
 * @param {Object} user - The user performing the action
 * @param {Object} field - The field being edited or inserted
 */
Users.canCreateField = function (user, field) {
  const canCreate = field.canCreate || field.insertableBy; //OpenCRUD backwards compatibility
  if (canCreate) {
    if (typeof canCreate === 'function') {
      // if canCreate is a function, execute it with user and document passed. it must return a boolean
      return canCreate(user);
    } else if (typeof canCreate === 'string') {
      // if canCreate is just a string, we assume it's the name of a group and pass it to isMemberOf
      // note: if canCreate is 'guests' then anybody can create it
      return canCreate === 'guests' || Users.isMemberOf(user, canCreate);
    } else if (Array.isArray(canCreate) && canCreate.length > 0) {
      // if canCreate is an array, we do a recursion on every item and return true if one of the items return true
      return canCreate.some(group => Users.canCreateField(user, { canCreate: group }));
    }
  }
  return false;
};

/** @function
 * Check if a user can edit a field
 * @param {Object} user - The user performing the action
 * @param {Object} field - The field being edited or inserted
 * @param {Object} document - The document being edited or inserted
 */
Users.canUpdateField = function (user, field, document) {
  const canUpdate = field.canUpdate || field.editableBy; //OpenCRUD backwards compatibility

  if (canUpdate) {
    if (typeof canUpdate === 'function') {
      // if canUpdate is a function, execute it with user and document passed. it must return a boolean
      return canUpdate(user, document);
    } else if (typeof canUpdate === 'string') {
      // if canUpdate is just a string, we assume it's the name of a group and pass it to isMemberOf
      // note: if canUpdate is 'guests' then anybody can create it
      return canUpdate === 'guests' || Users.isMemberOf(user, canUpdate);
    } else if (Array.isArray(canUpdate) && canUpdate.length > 0) {
      // if canUpdate is an array, we look at every item and return true if one of the items return true
      return canUpdate.some(group => Users.canUpdateField(user, { canUpdate: group }, document));

    }
  }
  return false;
};

////////////////////
// Initialize     //
////////////////////

/**
 * @summary initialize the 3 out-of-the-box groups
 */
Users.createGroup('guests'); // non-logged-in users
Users.createGroup('members'); // regular users

const membersActions = [
  'user.create',
  'user.update.own',
  // OpenCRUD backwards compatibility
  'users.new',
  'users.edit.own',
  'users.remove.own',
];
Users.groups.members.can(membersActions);

Users.createGroup('admins'); // admin users

const adminActions = [
  'user.create',
  'user.update.all',
  'user.delete.all',
  'setting.update',
  // OpenCRUD backwards compatibility
  'users.new',
  'users.edit.all',
  'users.remove.all',
  'settings.edit',
];
Users.groups.admins.can(adminActions);
<commit_msg>add deprecate message<commit_after>import Users from './collection.js';
import intersection from 'lodash/intersection';
import compact from 'lodash/compact';
import map from 'lodash/map';
import difference from 'lodash/difference';
import { Utils, deprecate } from 'meteor/vulcan:lib';

/**
 * @summary Users.groups object
 */
Users.groups = {};

/**
 * @summary Group class
 */
class Group {

  constructor() {
    this.actions = [];
  }

  can(actions) {
    actions = Array.isArray(actions) ? actions : [actions];
    this.actions = this.actions.concat(actions.map(a => a.toLowerCase()));
  }

  cannot(actions) {
    actions = Array.isArray(actions) ? actions : [actions];
    this.actions = _.difference(this.actions, actions.map(a => a.toLowerCase()));
  }

}

////////////////////
// Helpers        //
////////////////////

/**
 * @summary create a new group
 * @param {String} groupName
 */
Users.createGroup = groupName => {
  Users.groups[groupName] = new Group();
};

/**
 * @summary get a list of a user's groups
 * @param {Object} user
 */
Users.getGroups = (user, document) => {

  let userGroups = [];

  if (!user) { // guests user

    userGroups = ['guests'];

  } else {

    userGroups = ['members'];

    if (document && Users.owns(user, document)) {
      userGroups.push('owners');
    }

    if (user.groups) { // custom groups
      userGroups = userGroups.concat(user.groups);
    }

    if (Users.isAdmin(user)) { // admin
      userGroups.push('admins');
    }

  }

  return userGroups;

};

/**
 * @summary get a list of all the actions a user can perform
 * @param {Object} user
 */
Users.getActions = user => {
  let userGroups = Users.getGroups(user);
  if (!userGroups.includes('guests')) {
    // always give everybody permission for guests actions, too
    userGroups.push('guests');
  }
  let groupActions = userGroups.map(groupName => {
    // note: make sure groupName corresponds to an actual group
    const group = Users.groups[groupName];
    return group && group.actions;
  });
  return _.unique(_.flatten(groupActions));
};

/**
 * @summary check if a user is a member of a group
 * @param {Array} user
 * @param {String} group or array of groups
 */
Users.isMemberOf = (user, groupOrGroups, document) => {
  const groups = Array.isArray(groupOrGroups) ? groupOrGroups : [groupOrGroups];
  return intersection(Users.getGroups(user, document), groups).length > 0;
};

/**
 * @summary check if a user can perform at least one of the specified actions
 * @param {Object} user
 * @param {String/Array} action or actions
 */
Users.canDo = (user, actionOrActions) => {
  const authorizedActions = Users.getActions(user);
  const actions = Array.isArray(actionOrActions) ? actionOrActions : [actionOrActions];
  return Users.isAdmin(user) || intersection(authorizedActions, actions).length > 0;
};

// DEPRECATED
// TODO: remove this
/**
 * @summary Check if a user can edit a document
 * @param {Object} user - The user performing the action
 * @param {Object} document - The document being edited
 */
// Users.canEdit = function (user, document) {

//   user = (typeof user === 'undefined') ? Meteor.user() : user;

//   // note(apollo): use of `__typename` given by react-apollo
//   //const collectionName = document.getCollectionName();
//   const collectionName = document.__typename ? Utils.getCollectionNameFromTypename(document.__typename) : document.getCollectionName();

//   if (!user || !document) {
//     return false;
//   }

//   if (document.hasOwnProperty('isDeleted') && document.isDeleted) return false;

//   if (Users.owns(user, document)) {
//     // if this is user's document, check if user can edit own documents
//     return Users.canDo(user, `${collectionName}.edit.own`);
//   } else {
//     // if this is not user's document, check if they can edit all documents
//     return Users.canDo(user, `${collectionName}.edit.all`);
//   }

// };

/**
 * @summary Check if a user owns a document
 * @param {Object|string} userOrUserId - The user or their userId
 * @param {Object} document - The document to check (post, comment, user object, etc.)
 */
Users.owns = function (user, document) {
  try {
    if (!!document.userId) {
      // case 1: document is a post or a comment, use userId to check
      return user._id === document.userId;
    } else {
      // case 2: document is a user, use _id or slug to check
      return document.slug ? user.slug === document.slug : user._id === document._id;
    }
  } catch (e) {
    return false; // user not logged in
  }
};

/**
 * @summary Check if a user is an admin
 * @param {Object|string} userOrUserId - The user or their userId
 */
Users.isAdmin = function (userOrUserId) {
  try {
    var user = Users.getUser(userOrUserId);
    return !!user && !!user.isAdmin;
  } catch (e) {
    return false; // user not logged in
  }
};
Users.isAdminById = Users.isAdmin;

export const isAdmin = Users.isAdmin;

/**
 * @summary Check if a user can view a field
 * @param {Object} user - The user performing the action
 * @param {Object} field - The field being edited or inserted
 */
Users.canReadField = function (user, field, document) {
  const canRead = field.canRead || field.viewableBy; //OpenCRUD backwards compatibility
  if (canRead) {
    if (typeof canRead === 'function') {
      // if canRead is a function, execute it with user and document passed. it must return a boolean
      return canRead(user, document);
    } else if (typeof canRead === 'string') {
      // if canRead is just a string, we assume it's the name of a group and pass it to isMemberOf
      return canRead === 'guests' || Users.isMemberOf(user, canRead);
    } else if (Array.isArray(canRead) && canRead.length > 0) {
      // if canRead is an array, we do a recursion on every item and return true if one of the items return true
      return canRead.some(group => Users.canReadField(user, { canRead: group }, document));
    }
  }
  return false;
};

/**
 * @summary Get a list of fields viewable by a user
 * @param {Object} user - The user performing the action
 * @param {Object} collection - The collection
 * @param {Object} document - Optionally, get a list for a specific document
 */
Users.getViewableFields = function (user, collection, document) {
  deprecate('1.13.4', 'getViewableFields is deprecated. Use Users.getReadableProjection to get a Mongo projection, or Users.getReadableFields if you need an array of field.');
  return Users.getReadableProjection(user, collection, document);
};

Users.getReadableFields = function (user, collection, document) {
  return compact(map(collection.simpleSchema()._schema,
    (field, fieldName) => {
      if (fieldName.indexOf('.$') > -1) return null;
      return Users.canReadField(user, field, document) ? fieldName : null;
    }
  ));
};

Users.getReadableProjection = function (user, collection, document) {
  return Utils.arrayToFields(Users.getReadableFields(user, collection, document));
};

// collection helper
Users.helpers({
  getViewableFields(collection, document) {
    return Users.getViewableFields(this, collection, document);
  }
});

/**
 * @summary Check if a user can access a list of fields
 * @param {Object} user - The user performing the action
 * @param {Object} collection - The collection
 * @param {Object} fields - The list of fields
 */
Users.checkFields = (user, collection, fields) => {
  const viewableFields = Users.getReadableFields(user, collection);
  const diff = difference(fields, viewableFields);

  if (diff.length) {
    throw new Error(
      `You don't have permission to filter collection ${collection.options.collectionName} by the following fields: ${diff.join(
        ', '
      )}.`
    );
  }
  return true;
};

/**
 * @summary For a given document or list of documents, keep only fields viewable by current user
 * @param {Object} user - The user performing the action
 * @param {Object} collection - The collection
 * @param {Object} document - The document being returned by the resolver
 */
Users.restrictViewableFields = function (user, collection, docOrDocs) {

  if (!docOrDocs) return {};

  const restrictDoc = document => {

    // get array of all keys viewable by user
    const viewableKeys = Users.getReadableFields(user, collection, document);
    const restrictedDocument = _.clone(document);

    // loop over each property in the document and delete it if it's not viewable
    _.forEach(restrictedDocument, (value, key) => {
      if (!viewableKeys.includes(key)) {
        delete restrictedDocument[key];
      }
    });

    return restrictedDocument;

  };

  return Array.isArray(docOrDocs) ? docOrDocs.map(restrictDoc) : restrictDoc(docOrDocs);

};

/**
 * @summary Check if a user can submit a field
 * @param {Object} user - The user performing the action
 * @param {Object} field - The field being edited or inserted
 */
Users.canCreateField = function (user, field) {
  const canCreate = field.canCreate || field.insertableBy; //OpenCRUD backwards compatibility
  if (canCreate) {
    if (typeof canCreate === 'function') {
      // if canCreate is a function, execute it with user and document passed. it must return a boolean
      return canCreate(user);
    } else if (typeof canCreate === 'string') {
      // if canCreate is just a string, we assume it's the name of a group and pass it to isMemberOf
      // note: if canCreate is 'guests' then anybody can create it
      return canCreate === 'guests' || Users.isMemberOf(user, canCreate);
    } else if (Array.isArray(canCreate) && canCreate.length > 0) {
      // if canCreate is an array, we do a recursion on every item and return true if one of the items return true
      return canCreate.some(group => Users.canCreateField(user, { canCreate: group }));
    }
  }
  return false;
};

/** @function
 * Check if a user can edit a field
 * @param {Object} user - The user performing the action
 * @param {Object} field - The field being edited or inserted
 * @param {Object} document - The document being edited or inserted
 */
Users.canUpdateField = function (user, field, document) {
  const canUpdate = field.canUpdate || field.editableBy; //OpenCRUD backwards compatibility

  if (canUpdate) {
    if (typeof canUpdate === 'function') {
      // if canUpdate is a function, execute it with user and document passed. it must return a boolean
      return canUpdate(user, document);
    } else if (typeof canUpdate === 'string') {
      // if canUpdate is just a string, we assume it's the name of a group and pass it to isMemberOf
      // note: if canUpdate is 'guests' then anybody can create it
      return canUpdate === 'guests' || Users.isMemberOf(user, canUpdate);
    } else if (Array.isArray(canUpdate) && canUpdate.length > 0) {
      // if canUpdate is an array, we look at every item and return true if one of the items return true
      return canUpdate.some(group => Users.canUpdateField(user, { canUpdate: group }, document));

    }
  }
  return false;
};

////////////////////
// Initialize     //
////////////////////

/**
 * @summary initialize the 3 out-of-the-box groups
 */
Users.createGroup('guests'); // non-logged-in users
Users.createGroup('members'); // regular users

const membersActions = [
  'user.create',
  'user.update.own',
  // OpenCRUD backwards compatibility
  'users.new',
  'users.edit.own',
  'users.remove.own',
];
Users.groups.members.can(membersActions);

Users.createGroup('admins'); // admin users

const adminActions = [
  'user.create',
  'user.update.all',
  'user.delete.all',
  'setting.update',
  // OpenCRUD backwards compatibility
  'users.new',
  'users.edit.all',
  'users.remove.all',
  'settings.edit',
];
Users.groups.admins.can(adminActions);
<|endoftext|>


<commit_before>/** @jsx React.DOM */

var MyDescription = React.createClass({
  render: function() {
    var tag="Make data pop!";
    var desc = "This group focuses on human factors in data science.";
    return (

                {tag}

                {desc}

    );
  }
});

var MySchedule = React.createClass({
  render: function() {
    var schedule = [{
                  date: "2014-06-02"
                , topic:"Mixer"
                , description: ""
                , speakers: []
                }, {
                  date: "2014-06-09 19:00:00"
                , topic:"Data Viz Weight Loss"
                , description: "We'll be looking at several examples of improving presentation of data."
                , speakers: []
    }];
    return (

                 Schedule

            Every Monday evening, 7-9pm.

            Check the NYC Python Calendar for meeting locations.

                {moment(schedule[1].date).fromNow()} - "{schedule[1].topic}"

                {schedule[1].description}

    );
  }
});

React.renderComponent(
  <MyDescription />,
  document.getElementById('description')
);

React.renderComponent(
  <MySchedule />,
  document.getElementById('schedule')
);

<commit_msg>update schedule<commit_after>/** @jsx React.DOM */

var MyDescription = React.createClass({
  render: function() {
    var tag="Make data pop!";
    var desc = "This group focuses on human factors in data science.";
    return (

                {tag}

                {desc}

    );
  }
});

var MySchedule = React.createClass({
  render: function() {
    var schedule = [{
                  date: "2014-06-02"
                , topic:"Mixer"
                , description: ""
                , speakers: []
                }, {
                  date: "2014-06-09 19:00:00"
                , topic:"Data Viz Weight Loss"
                , description: "We'll be looking at several examples of improving presentation of data."
                , speakers: []
		}, {
		  date: "2014-06-16 19:00:00"
		, topic: "Study Hall"
		, description: ""
		, speakers: []
		}, {
		  date: "2014-06-22 19:00:00"
		, topic: "Analyzing rap lyrics"
		, description: "The topic for this week is about analyzing rap lyrics.. Here are some examples of work we will be looking at:
 Analyzing Rap Lyrics with Python
The Largest Vocabulary in Hip Hop"
		, speakers: []
		}, {
		  date: "2014-06-30 19:00:00"
		, topic: "Show and tell"
		, description: "Chris Jenkins, a Ph.D. Candidate in experimental psychology from the University of New Mexico will be describing how he got started with scientific programming packages in Python. He'll show us how he's been using IPython Notebook, pandas, matplotlib, and Bokeh to analyze data from his research on differences in rhythmic timing accuracy."
		, speakers: []
    }];
    var n = schedule.length;
    return (

                 Schedule

            Every Monday evening, 7-9pm.

            Check the NYC Python Calendar for meeting locations.

                {moment(schedule[n-1].date).fromNow()} - "{schedule[n-1].topic}"

                {schedule[n-1].description}

    );
  }
});

React.renderComponent(
  <MyDescription />,
  document.getElementById('description')
);

React.renderComponent(
  <MySchedule />,
  document.getElementById('schedule')
);

<|endoftext|>


<commit_before>'use strict';

describe('unit tests for module ng.django.url', function () {

    describe('test urls', function () {

        beforeEach(function () {
            // ng.django.urls - core module
            // ng.django.urls - pattern mocks for testing, from mocks/urlmocks.js
            module('ng.django.urls', 'ng.django.urls.mocks');
        });

        describe('general url reverser tests', function () {
            it('should raise an error when no matching url is found', inject(function (djangoUrl) {
                expect(function () {
                    djangoUrl.resolve('home12')
                }).toThrow();
            }));
        });

        describe('test url reverser for simple urls without parameters', function () {
            it('should ignore not-needed parameters', inject(function (djangoUrl) {
                expect(djangoUrl.resolve('home')).toBe('/');
                expect(djangoUrl.resolve('home', [2, 3])).toBe('/');
                expect(djangoUrl.resolve('home', {'some_data': 2})).toBe('/');
            }));
        });

        describe("test url reverser for patterns that require arguments", function () {

            it("should fill arguments correctly", inject(function (djangoUrl) {
                expect(djangoUrl.resolve('api:overview', [2])).toBe('/api/2/overview/');
                expect(djangoUrl.resolve('api:visitors', [3, 4])).toBe('/api/3/visitors/4/');
            }));

            it("should ignore extra arguments", inject(function (djangoUrl) {
                expect(djangoUrl.resolve('api:overview', [2, 3])).toBe('/api/2/overview/')
            }));

            it("should raise an error when there are not enough arguments", inject(function (djangoUrl) {
                expect(function () {
                    djangoUrl.resolve('api:overview', [])
                }).toThrow();
            }));

            it("should not raise an exception when not given any arguments if none are required",
                inject(function (djangoUrl) {
                    expect(function () {
                        djangoUrl.resolve('api:overview')
                    }).not.toThrow();
                }));
        });

        describe("test url reverser for resolving urls with kwargs", function () {

            it("should fill kwargs correctly", inject(function (djangoUrl) {
                expect(djangoUrl.resolve('api:overview', {'website_id': 2})).toBe('/api/2/overview/');
                expect(djangoUrl.resolve('api:visitors', {'website_id': 2, 'visitor_id': 3}))
                    .toBe('/api/2/visitors/3/');
            }));

            it("should ignore extra kwargs", inject(function (djangoUrl) {
                expect(djangoUrl.resolve('api:overview', {'website_id': 2, 'visitor_id': 3}))
                    .toBe('/api/2/overview/');
            }));

            it("should return parametrized url template for missing kwargs", inject(function (djangoUrl) {
                expect(djangoUrl.resolve('api:overview', {})).toBe('/api/:website_id/overview/');
                expect(djangoUrl.resolve('api:visitors', {'visitor_id': 3}))
                    .toBe('/api/:website_id/visitors/3/');
                expect(djangoUrl.resolve('api:visitors', {'website_id': 2}))
                    .toBe('/api/2/visitors/:visitor_id/');
            }));

            it("should default to kwargs mode when no args/kwargs are given (parametrized urls)",
                inject(function (djangoUrl) {
                        expect(djangoUrl.resolve('api:overview')).toBe('/api/:website_id/overview/');
                    }
                ));
        });
    });
});
<commit_msg>fix jasmine test, renamed .resolve() to .reverse()<commit_after>'use strict';

describe('unit tests for module ng.django.url', function () {

    describe('test urls', function () {

        beforeEach(function () {
            // ng.django.urls - core module
            // ng.django.urls - pattern mocks for testing, from mocks/urlmocks.js
            module('ng.django.urls', 'ng.django.urls.mocks');
        });

        describe('general url reverser tests', function () {
            it('should raise an error when no matching url is found', inject(function (djangoUrl) {
                expect(function () {
                    djangoUrl.reverse('home12')
                }).toThrow();
            }));
        });

        describe('test url reverser for simple urls without parameters', function () {
            it('should ignore not-needed parameters', inject(function (djangoUrl) {
                expect(djangoUrl.reverse('home')).toBe('/');
                expect(djangoUrl.reverse('home', [2, 3])).toBe('/');
                expect(djangoUrl.reverse('home', {'some_data': 2})).toBe('/');
            }));
        });

        describe("test url reverser for patterns that require arguments", function () {

            it("should fill arguments correctly", inject(function (djangoUrl) {
                expect(djangoUrl.reverse('api:overview', [2])).toBe('/api/2/overview/');
                expect(djangoUrl.reverse('api:visitors', [3, 4])).toBe('/api/3/visitors/4/');
            }));

            it("should ignore extra arguments", inject(function (djangoUrl) {
                expect(djangoUrl.reverse('api:overview', [2, 3])).toBe('/api/2/overview/')
            }));

            it("should raise an error when there are not enough arguments", inject(function (djangoUrl) {
                expect(function () {
                    djangoUrl.reverse('api:overview', [])
                }).toThrow();
            }));

            it("should not raise an exception when not given any arguments if none are required",
                inject(function (djangoUrl) {
                    expect(function () {
                        djangoUrl.reverse('api:overview')
                    }).not.toThrow();
                }));
        });

        describe("test url reverser for resolving urls with kwargs", function () {

            it("should fill kwargs correctly", inject(function (djangoUrl) {
                expect(djangoUrl.reverse('api:overview', {'website_id': 2})).toBe('/api/2/overview/');
                expect(djangoUrl.reverse('api:visitors', {'website_id': 2, 'visitor_id': 3}))
                    .toBe('/api/2/visitors/3/');
            }));

            it("should ignore extra kwargs", inject(function (djangoUrl) {
                expect(djangoUrl.reverse('api:overview', {'website_id': 2, 'visitor_id': 3}))
                    .toBe('/api/2/overview/');
            }));

            it("should return parametrized url template for missing kwargs", inject(function (djangoUrl) {
                expect(djangoUrl.reverse('api:overview', {})).toBe('/api/:website_id/overview/');
                expect(djangoUrl.reverse('api:visitors', {'visitor_id': 3}))
                    .toBe('/api/:website_id/visitors/3/');
                expect(djangoUrl.reverse('api:visitors', {'website_id': 2}))
                    .toBe('/api/2/visitors/:visitor_id/');
            }));

            it("should default to kwargs mode when no args/kwargs are given (parametrized urls)",
                inject(function (djangoUrl) {
                        expect(djangoUrl.reverse('api:overview')).toBe('/api/:website_id/overview/');
                    }
                ));
        });
    });
});
<|endoftext|>


<commit_before>'use strict';

var themeName = 'Theme Name',
    themeUri = 'http://themeuri.com',
    author = 'Author Name',
    authorUri = 'http://yourdomain.com',
    themeSlug = 'theme_slug',
    themeColor = '000000',
    sanitizedSlug = themeSlug.replace(/[^a-z0-9_]+/ig,'_'),
    imageName = themeName.replace(/\s/gi,'+');

module.exports = function(grunt) {
  grunt.initConfig({
    themeName: themeName,
    themeUri: themeUri,
    author: author,
    authorUri: authorUri,
    sanitizedSlug: sanitizedSlug,
    imageName: imageName,
    themeColor: themeColor,
    themeDir: (__dirname.split('/').pop()),
    pkg: grunt.file.readJSON('package.json'),

    shell: {
      target: {
        command: [
          'git clone https://github.com/Automattic/_s.git',
          'git clone git@hawk.tip.com:wordpress/boilerplate-sass.git'
        ]
      }
    },

    curl: {
      screenshot: {
        src: 'http://placehold.it/1200x900/<%= themeColor %>/fff/&text=<%=  imageName %>',
        dest: 'screenshot.png'
      }
    },

    replace: {
      slug: {
        options: {
          usePrefix: false,
          patterns: [
            {
              match: '\'_s\'',
              replacement: '\'<%= sanitizedSlug %>\''
            },
            {
              match: '_s_',
              replacement: '<%= sanitizedSlug %>_'
            },
            {
              match: ' _s',
              replacement: ' <%= sanitizedSlug %>'
            },
            {
              match: '_s-',
              replacement: '<%= sanitizedSlug %>-'
            },
            {
              match: 'initial-scale=1">',
              replacement: 'initial-scale=1">\n<meta name="theme-color" value="#<%= themeColor %>">'
            }
          ]
        },
        files: [
          {
            expand: true,
            cwd: '.',
            src: [
              '*.{php,md,css}',
              '{inc,js,languages,layouts}/**/*.*'
            ],
            dest: '.'
          }
        ]
      },
      scss: {
        options: {
          usePrefix: false,
          patterns: [
            {
              match: 'Theme Name: _s',
              replacement: 'Theme Name: <%= themeName %>'
            },
            {
              match: 'Theme URI: http://underscores.me/',
              replacement: 'Theme URI: <%= themeUri %>'
            },
            {
              match: 'Author: Automattic',
              replacement: 'Author: <%= author %>'
            },
            {
              match: 'Author URI: http://automattic.com/',
              replacement: 'Author URI: <%= authorUri %>'
            },
            {
              match: 'Text Domain: _s',
              replacement: 'Text Domain: <%= sanitizedSlug %>'
            },
            {
              match: '/*\nTheme Name',
              replacement: '/*!\nTheme Name'
            },
            {
              match: '@import "normalize";',
              replacement: '@import "normalize";\n@import "boilerplate/boilerplate";\n@import "../bower_components/susy/sass/susy";\n@import "../bower_components/breakpoint-sass/stylesheets/breakpoint";\n'
            }
          ]
        },
        files: [
          {
            expand: true,
            cwd: '.',
            src: ['style.*', 'sass/style.*']
          }
        ]
      },
      footer: {
        options: {
          usePrefix: false,
          patterns: [
            {
              match: 'Automattic',
              replacement: '<%= author %>'
            },
            {
              match: 'http://automattic.com/',
              replacement: '<%= authorUri %>'
            }
          ]
        },
        files: [
          {
            expand: true,
            cwd: '.',
            src: 'footer.php'
          }
        ]
      },
      cssMap: {
        options: {
          usePrefix: false,
          patterns: [
            {
              match: /\/\*\#\ssourceMappingURL.+\*\//gi,
              replacement: ''
            }
          ]
        },
        files: [
          {
            expand: true,
            cwd: 'deploy/<%= themeDir %>',
            src: 'style.css',
            dest: 'deploy/<%= themeDir %>'
          }
        ]
      }
    },

    sass: {
      dev: {
        options: {
          sourceMap: true,
          outputStyle: 'expanded',
          require: 'susy'
        },
        files: [
          {
            expand: true,
            cwd: 'sass',
            src: ['**/style.scss'],
            dest: '.',
            ext: '.css'
          }
        ]
      },
      dist: {
        options: {
          sourceMap: false,
          outputStyle: 'compressed',
          require: 'susy'
        },
        files: [
          {
            expand: true,
            cwd: 'sass',
            src: ['**/style.scss'],
            dest: '.',
            ext: '.css'
          }
        ]
      }
    },

    autoprefixer: {
      options: {
        browsers: [
          'last 3 versions'
        ],
        map: true
      },
      dist: {
        files: [
          {
            expand: true,
            cwd: '.',
            src: ['*.css'],
            dest: '.',
            ext: '.css'
          }
        ]
      }
    },

    csscomb: {
      options: {
        config: 'csscomb.json'
      },
      server: {
        files: {
          'deploy/style.css': ['deploy/style.css'],
        }
      },
    },

    cssmin: {
      server: {
        files: [{
          expand: true,
          cwd: 'deploy',
          src: 'style.css',
          dest: 'deploy',
          ext: '.css'
        }]
      }
    },

    jshint: {
      options: {
        node: true,
        reporter: require('jshint-stylish')
      },
      all: ['js/**/*.js'],
    },

    watch: {
      options: {
        livereload: true
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      scss: {
        files: '**/*.scss',
        tasks: ['sass:dev', 'autoprefixer']
      },
      jshint: {
        files: 'js/**/*.js',
        tasks: ['jshint']
      },
      livereload: {
        files: [
          '{,*/}*.html',
          '{,*/}*.php',
          'js/{,*/}*.js',
          'css/{,*/}*.css',
          'images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      },
    },

    clean: {
      pre: ['_s','boilerplate-sass','.git','.gitignore'],
      post: ['deploy']
    },

    copy: {
      pre: {
        files: [
          {
            expand: true,
            cwd: '_s',
            src: [
              '**/*.*',
              '!.{*}',
              '!*.md',
              '!codesniffer.*',
              '!wpcom.*'
            ],
            dest: '.'
          },
          {
            expand: true,
            cwd: 'boilerplate-sass',
            src: '**/*.*',
            dest: 'sass'
          }
        ]
      },
      post: {
        files: [
          {
            expand: true,
            src: [
              '**/*.*',
              '!**/*.map',
              '!sass/**/*.*',
              '!node_modules/**/*.*',
              '!bower_components/**/*.*',
              '!{package,bower,csscomb}.json',
              '!Gruntfile.js'
            ],
            dest: 'deploy/<%= themeDir %>'
          }
        ]
      }
    },

    imageoptim: {
      options: {
        imageAlpha: true
      },
      src: ['deploy/**/*.{jpg,jpeg,png,gif}']
    },

    svgmin: {
      dist: {
        files: [
          {
            expand: true,
            cwd: 'deploy',
            src: '**/*.svg'
          }
        ]
      }
    },

    uglify: {
      dist: {
        files: [
          {
            expand: true,
            cwd: 'deploy/js',
            dest: 'deploy/js',
            src: '*.js'
          }
        ]
      }
    },

    compress: {
      main: {
        options: {
          archive: 'deploy/<%= themeDir %>.zip'
        },
        files: [
          {
            expand: true,
            cwd: 'deploy/<%= themeDir %>',
            src: '**/*.*'
          }
        ]
      }
    }
  });

  // Load these required NPM tasks:
  grunt.loadNpmTasks('grunt-autoprefixer');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-compress');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-csscomb');
  grunt.loadNpmTasks('grunt-curl');
  grunt.loadNpmTasks('grunt-imageoptim');
  grunt.loadNpmTasks('grunt-svgmin');
  grunt.loadNpmTasks('grunt-replace');
  grunt.loadNpmTasks('grunt-sass');
  grunt.loadNpmTasks('grunt-shell');
  grunt.loadNpmTasks('grunt-svgmin');

  grunt.registerTask('serve', function (target) {
    grunt.task.run(['watch']);
  });

  grunt.registerTask('init', [
    'shell',
    'copy:pre',
    'clean:pre',
    'replace:slug',
    'replace:scss',
    'curl:screenshot'
  ]);
  grunt.registerTask('default', [
    'clean:post',
    'serve'
  ]);
  grunt.registerTask('build', [
    'sass:dist',
    'autoprefixer',
    'clean:post',
    'copy:post',
    'uglify',
    'csscomb',
    'cssmin',
    'imageoptim',
    'svgmin',
    'replace:cssMap',
    'compress'
  ]);
};
<commit_msg>fix grunt-shell typo<commit_after>'use strict';

var themeName = 'Theme Name',
    themeUri = 'http://themeuri.com',
    author = 'Author Name',
    authorUri = 'http://yourdomain.com',
    themeSlug = 'theme_slug',
    themeColor = '000000',
    sanitizedSlug = themeSlug.replace(/[^a-z0-9_]+/ig,'_'),
    imageName = themeName.replace(/\s/gi,'+');

module.exports = function(grunt) {
  grunt.initConfig({
    themeName: themeName,
    themeUri: themeUri,
    author: author,
    authorUri: authorUri,
    sanitizedSlug: sanitizedSlug,
    imageName: imageName,
    themeColor: themeColor,
    themeDir: (__dirname.split('/').pop()),
    pkg: grunt.file.readJSON('package.json'),

    shell: {
      target: {
        command: [
          'git clone https://github.com/Automattic/_s.git',
          'git clone git@hawk.tip.com:wordpress/boilerplate-sass.git'
        ].join('&&')
      }
    },

    curl: {
      screenshot: {
        src: 'http://placehold.it/1200x900/<%= themeColor %>/fff/&text=<%=  imageName %>',
        dest: 'screenshot.png'
      }
    },

    replace: {
      slug: {
        options: {
          usePrefix: false,
          patterns: [
            {
              match: '\'_s\'',
              replacement: '\'<%= sanitizedSlug %>\''
            },
            {
              match: '_s_',
              replacement: '<%= sanitizedSlug %>_'
            },
            {
              match: ' _s',
              replacement: ' <%= sanitizedSlug %>'
            },
            {
              match: '_s-',
              replacement: '<%= sanitizedSlug %>-'
            },
            {
              match: 'initial-scale=1">',
              replacement: 'initial-scale=1">\n<meta name="theme-color" value="#<%= themeColor %>">'
            }
          ]
        },
        files: [
          {
            expand: true,
            cwd: '.',
            src: [
              '*.{php,md,css}',
              '{inc,js,languages,layouts}/**/*.*'
            ],
            dest: '.'
          }
        ]
      },
      scss: {
        options: {
          usePrefix: false,
          patterns: [
            {
              match: 'Theme Name: _s',
              replacement: 'Theme Name: <%= themeName %>'
            },
            {
              match: 'Theme URI: http://underscores.me/',
              replacement: 'Theme URI: <%= themeUri %>'
            },
            {
              match: 'Author: Automattic',
              replacement: 'Author: <%= author %>'
            },
            {
              match: 'Author URI: http://automattic.com/',
              replacement: 'Author URI: <%= authorUri %>'
            },
            {
              match: 'Text Domain: _s',
              replacement: 'Text Domain: <%= sanitizedSlug %>'
            },
            {
              match: '/*\nTheme Name',
              replacement: '/*!\nTheme Name'
            },
            {
              match: '@import "normalize";',
              replacement: '@import "normalize";\n@import "boilerplate/boilerplate";\n@import "../bower_components/susy/sass/susy";\n@import "../bower_components/breakpoint-sass/stylesheets/breakpoint";\n'
            }
          ]
        },
        files: [
          {
            expand: true,
            cwd: '.',
            src: ['style.*', 'sass/style.*']
          }
        ]
      },
      footer: {
        options: {
          usePrefix: false,
          patterns: [
            {
              match: 'Automattic',
              replacement: '<%= author %>'
            },
            {
              match: 'http://automattic.com/',
              replacement: '<%= authorUri %>'
            }
          ]
        },
        files: [
          {
            expand: true,
            cwd: '.',
            src: 'footer.php'
          }
        ]
      },
      cssMap: {
        options: {
          usePrefix: false,
          patterns: [
            {
              match: /\/\*\#\ssourceMappingURL.+\*\//gi,
              replacement: ''
            }
          ]
        },
        files: [
          {
            expand: true,
            cwd: 'deploy/<%= themeDir %>',
            src: 'style.css',
            dest: 'deploy/<%= themeDir %>'
          }
        ]
      }
    },

    sass: {
      dev: {
        options: {
          sourceMap: true,
          outputStyle: 'expanded',
          require: 'susy'
        },
        files: [
          {
            expand: true,
            cwd: 'sass',
            src: ['**/style.scss'],
            dest: '.',
            ext: '.css'
          }
        ]
      },
      dist: {
        options: {
          sourceMap: false,
          outputStyle: 'compressed',
          require: 'susy'
        },
        files: [
          {
            expand: true,
            cwd: 'sass',
            src: ['**/style.scss'],
            dest: '.',
            ext: '.css'
          }
        ]
      }
    },

    autoprefixer: {
      options: {
        browsers: [
          'last 3 versions'
        ],
        map: true
      },
      dist: {
        files: [
          {
            expand: true,
            cwd: '.',
            src: ['*.css'],
            dest: '.',
            ext: '.css'
          }
        ]
      }
    },

    csscomb: {
      options: {
        config: 'csscomb.json'
      },
      server: {
        files: {
          'deploy/style.css': ['deploy/style.css'],
        }
      },
    },

    cssmin: {
      server: {
        files: [{
          expand: true,
          cwd: 'deploy',
          src: 'style.css',
          dest: 'deploy',
          ext: '.css'
        }]
      }
    },

    jshint: {
      options: {
        node: true,
        reporter: require('jshint-stylish')
      },
      all: ['js/**/*.js'],
    },

    watch: {
      options: {
        livereload: true
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      scss: {
        files: '**/*.scss',
        tasks: ['sass:dev', 'autoprefixer']
      },
      jshint: {
        files: 'js/**/*.js',
        tasks: ['jshint']
      },
      livereload: {
        files: [
          '{,*/}*.html',
          '{,*/}*.php',
          'js/{,*/}*.js',
          'css/{,*/}*.css',
          'images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      },
    },

    clean: {
      pre: ['_s','boilerplate-sass','.git','.gitignore'],
      post: ['deploy']
    },

    copy: {
      pre: {
        files: [
          {
            expand: true,
            cwd: '_s',
            src: [
              '**/*.*',
              '!.{*}',
              '!*.md',
              '!codesniffer.*',
              '!wpcom.*'
            ],
            dest: '.'
          },
          {
            expand: true,
            cwd: 'boilerplate-sass',
            src: '**/*.*',
            dest: 'sass'
          }
        ]
      },
      post: {
        files: [
          {
            expand: true,
            src: [
              '**/*.*',
              '!**/*.map',
              '!sass/**/*.*',
              '!node_modules/**/*.*',
              '!bower_components/**/*.*',
              '!{package,bower,csscomb}.json',
              '!Gruntfile.js'
            ],
            dest: 'deploy/<%= themeDir %>'
          }
        ]
      }
    },

    imageoptim: {
      options: {
        imageAlpha: true
      },
      src: ['deploy/**/*.{jpg,jpeg,png,gif}']
    },

    svgmin: {
      dist: {
        files: [
          {
            expand: true,
            cwd: 'deploy',
            src: '**/*.svg'
          }
        ]
      }
    },

    uglify: {
      dist: {
        files: [
          {
            expand: true,
            cwd: 'deploy/js',
            dest: 'deploy/js',
            src: '*.js'
          }
        ]
      }
    },

    compress: {
      main: {
        options: {
          archive: 'deploy/<%= themeDir %>.zip'
        },
        files: [
          {
            expand: true,
            cwd: 'deploy/<%= themeDir %>',
            src: '**/*.*'
          }
        ]
      }
    }
  });

  // Load these required NPM tasks:
  grunt.loadNpmTasks('grunt-autoprefixer');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-compress');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-csscomb');
  grunt.loadNpmTasks('grunt-curl');
  grunt.loadNpmTasks('grunt-imageoptim');
  grunt.loadNpmTasks('grunt-svgmin');
  grunt.loadNpmTasks('grunt-replace');
  grunt.loadNpmTasks('grunt-sass');
  grunt.loadNpmTasks('grunt-shell');
  grunt.loadNpmTasks('grunt-svgmin');

  grunt.registerTask('serve', function (target) {
    grunt.task.run(['watch']);
  });

  grunt.registerTask('init', [
    'shell',
    'copy:pre',
    'clean:pre',
    'replace:slug',
    'replace:scss',
    'curl:screenshot'
  ]);
  grunt.registerTask('default', [
    'clean:post',
    'serve'
  ]);
  grunt.registerTask('build', [
    'sass:dist',
    'autoprefixer',
    'clean:post',
    'copy:post',
    'uglify',
    'csscomb',
    'cssmin',
    'imageoptim',
    'svgmin',
    'replace:cssMap',
    'compress'
  ]);
};
<|endoftext|>


<commit_before>module.exports = function(grunt) {
  var exec = require('child_process').exec;

  // Alias tasks for the most common sets of tasks.
  // Most of the time, you will use these.

  // By default, (i.e., if you invoke `grunt` without arguments), do
  // a new build.
  this.registerTask('default', ['build']);

  // Build a new version of the library
  this.registerTask('build', "Builds a distributable version of Conductor.js", ['clean', 'jshint', 'concat:conductor', 'transpile', 'concat:dist']);

  // Build a dev version of the library
  this.registerTask('build-dev', "Builds a development version of Conductor.js", ['clean', 'concat:conductor', 'transpile', 'concat:dist']);

  // Run a server. This is ideal for running the QUnit tests in the browser.
  this.registerTask('server', ['concat:tests', 'build-dev', 'connect', 'watch']);

  this.registerTask('tutorial:retag', function () {
    var done = this.async();

    exec('git log -n 1 --format=%s | grep -q -p ^Tutorial', function (sin, sout, serr) {
      if (sin instanceof Error) {
        grunt.fail.fatal("The commit message of HEAD does not begin with 'Tutorial:'.  You must run 'tutorial:retag' from the last tutorial commit.");
      }
    });

    exec('git rev-list --reverse tutorial-setup..', function (sin, sout, serr) {
      var lastSha, cmd;
      sout.replace(/\n$/, '').split("\n").forEach(function (sha, i) {
        var cmd = "git tag -f tutorial-" + (i+1) + " " + sha;
        exec(cmd);
        grunt.log.write(cmd + "\n");
        lastSha = sha;
      });

      cmd = "git tag -f tutorial " + lastSha;
      exec(cmd);
      grunt.log.write(cmd + "\n");

      done();
    });
  });

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    connect: {
      server: {},

      options: {
        port: 8000,
        base: '.'
      }
    },

    watch: {
      files: ['lib/**', 'vendor/*', 'test/tests/*'],
      tasks: ['build-dev', 'concat:tests']
    },

    transpile: {
      amd: {
        options: {
          format: 'amd',
          name: 'conductor'
        },

        src: "tmp/conductor.js",
        dest: "tmp/conductor.amd.js"
      },

      globals: {
        options: {
          name: 'conductor',
          format: 'globals'
        },

        src: "tmp/conductor.js",
        dest: "tmp/conductor.browser.js"
      }
    },

    clean: ["dist"],

    concat: {
      conductor: {
        src: ['lib/conductor.js', 'lib/utils/*.js', 'lib/conductor/*.js', 'lib/consumers/*.js', 'lib/services/*.js', 'lib/services.js'],
        dest: 'tmp/conductor.js'
      },

      tests: {
        src: ['test/test_helpers.js', 'test/tests/**/*_test.js'],
        dest: 'tmp/conductor_tests.js'
      },

      dist: {
        src: ['lib/loader.js', 'vendor/uuid.core.js', 'vendor/kamino.js', 'vendor/message_channel.js', 'vendor/rsvp.amd.js', 'vendor/oasis.amd.js', 'tmp/conductor.browser.js'],
        dest: 'dist/<%= pkg.name %>-<%= pkg.version %>.js'
      }
    },

    jshint: {
      options: {
        jshintrc: './.jshintrc'
      },
      all: ['Gruntfile.js', 'lib/**/*.js', 'test/tests/**/*.js']
    },

    'saucelabs-qunit': {
      all: {
        options: {
          urls: [
            'http://localhost:8000/test/index.html'
          ],
          tunnelTimeout: 5,
          /*global process */
          build: process.env.TRAVIS_JOB_ID,
          concurrency: 3,
          browsers: [{
            browserName: 'internet explorer',
            version: '8',
            platform: 'Windows XP'
          },{
            browserName: 'internet explorer',
            version: '9',
            platform: 'Windows 7'
          },{
            browserName: 'internet explorer',
            version: '10',
            platform: 'Windows 8'
          },{
            browserName: 'chrome',
            version: '27',
            platform: 'Windows 8'
          },{
            browserName: 'firefox',
            version: '21',
            platform: 'Windows 8'
          },{
            browserName: 'safari',
            version: '6',
            platform: 'OS X 10.8'
          }],
          testname: "Conductor.js qunit tests",
          testTimeout: 15000,
          testInterval: 5000
        }
      }
    }

  });

  // Load tasks from npm
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-saucelabs');

  // Multi-task for es6-module-transpiler
  this.registerMultiTask('transpile', "Transpile ES6 modules into AMD, CJS or globals", function() {
    var Compiler = require("es6-module-transpiler/lib/compiler");

    var options = this.options({
      format: 'amd'
    });

    this.files.forEach(function(f) {
      var contents = f.src.map(function(path) {
        var compiler = new Compiler(grunt.file.read(path), options.name, options);
        var format;

        switch (options.format) {
          case 'amd':
            console.log("Compiling " + path + " to AMD");
            format = compiler.toAMD;
            break;
          case 'globals':
            format = compiler.toGlobals;
            break;
          case 'commonjs':
            format = compiler.toCJS;
            break;
        }
        return format.call(compiler);
      });

      grunt.file.write(f.dest, contents);
    });
  });

  grunt.registerTask('test', ['concat:tests', 'connect', 'saucelabs-qunit']);
};
<commit_msg>Remove Internet Explorer from Saucelabs until there is an official support<commit_after>module.exports = function(grunt) {
  var exec = require('child_process').exec;

  // Alias tasks for the most common sets of tasks.
  // Most of the time, you will use these.

  // By default, (i.e., if you invoke `grunt` without arguments), do
  // a new build.
  this.registerTask('default', ['build']);

  // Build a new version of the library
  this.registerTask('build', "Builds a distributable version of Conductor.js", ['clean', 'jshint', 'concat:conductor', 'transpile', 'concat:dist']);

  // Build a dev version of the library
  this.registerTask('build-dev', "Builds a development version of Conductor.js", ['clean', 'concat:conductor', 'transpile', 'concat:dist']);

  // Run a server. This is ideal for running the QUnit tests in the browser.
  this.registerTask('server', ['concat:tests', 'build-dev', 'connect', 'watch']);

  this.registerTask('tutorial:retag', function () {
    var done = this.async();

    exec('git log -n 1 --format=%s | grep -q -p ^Tutorial', function (sin, sout, serr) {
      if (sin instanceof Error) {
        grunt.fail.fatal("The commit message of HEAD does not begin with 'Tutorial:'.  You must run 'tutorial:retag' from the last tutorial commit.");
      }
    });

    exec('git rev-list --reverse tutorial-setup..', function (sin, sout, serr) {
      var lastSha, cmd;
      sout.replace(/\n$/, '').split("\n").forEach(function (sha, i) {
        var cmd = "git tag -f tutorial-" + (i+1) + " " + sha;
        exec(cmd);
        grunt.log.write(cmd + "\n");
        lastSha = sha;
      });

      cmd = "git tag -f tutorial " + lastSha;
      exec(cmd);
      grunt.log.write(cmd + "\n");

      done();
    });
  });

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    connect: {
      server: {},

      options: {
        port: 8000,
        base: '.'
      }
    },

    watch: {
      files: ['lib/**', 'vendor/*', 'test/tests/*'],
      tasks: ['build-dev', 'concat:tests']
    },

    transpile: {
      amd: {
        options: {
          format: 'amd',
          name: 'conductor'
        },

        src: "tmp/conductor.js",
        dest: "tmp/conductor.amd.js"
      },

      globals: {
        options: {
          name: 'conductor',
          format: 'globals'
        },

        src: "tmp/conductor.js",
        dest: "tmp/conductor.browser.js"
      }
    },

    clean: ["dist"],

    concat: {
      conductor: {
        src: ['lib/conductor.js', 'lib/utils/*.js', 'lib/conductor/*.js', 'lib/consumers/*.js', 'lib/services/*.js', 'lib/services.js'],
        dest: 'tmp/conductor.js'
      },

      tests: {
        src: ['test/test_helpers.js', 'test/tests/**/*_test.js'],
        dest: 'tmp/conductor_tests.js'
      },

      dist: {
        src: ['lib/loader.js', 'vendor/uuid.core.js', 'vendor/kamino.js', 'vendor/message_channel.js', 'vendor/rsvp.amd.js', 'vendor/oasis.amd.js', 'tmp/conductor.browser.js'],
        dest: 'dist/<%= pkg.name %>-<%= pkg.version %>.js'
      }
    },

    jshint: {
      options: {
        jshintrc: './.jshintrc'
      },
      all: ['Gruntfile.js', 'lib/**/*.js', 'test/tests/**/*.js']
    },

    'saucelabs-qunit': {
      all: {
        options: {
          urls: [
            'http://localhost:8000/test/index.html'
          ],
          tunnelTimeout: 5,
          /*global process */
          build: process.env.TRAVIS_JOB_ID,
          concurrency: 3,
          browsers: [{
            browserName: 'chrome',
            version: '27',
            platform: 'Windows 8'
          },{
            browserName: 'firefox',
            version: '21',
            platform: 'Windows 8'
          },{
            browserName: 'safari',
            version: '6',
            platform: 'OS X 10.8'
          }],
          testname: "Conductor.js qunit tests",
          testTimeout: 15000,
          testInterval: 5000
        }
      }
    }

  });

  // Load tasks from npm
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-saucelabs');

  // Multi-task for es6-module-transpiler
  this.registerMultiTask('transpile', "Transpile ES6 modules into AMD, CJS or globals", function() {
    var Compiler = require("es6-module-transpiler/lib/compiler");

    var options = this.options({
      format: 'amd'
    });

    this.files.forEach(function(f) {
      var contents = f.src.map(function(path) {
        var compiler = new Compiler(grunt.file.read(path), options.name, options);
        var format;

        switch (options.format) {
          case 'amd':
            console.log("Compiling " + path + " to AMD");
            format = compiler.toAMD;
            break;
          case 'globals':
            format = compiler.toGlobals;
            break;
          case 'commonjs':
            format = compiler.toCJS;
            break;
        }
        return format.call(compiler);
      });

      grunt.file.write(f.dest, contents);
    });
  });

  grunt.registerTask('test', ['concat:tests', 'connect', 'saucelabs-qunit']);
};
<|endoftext|>