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

var markersGroup = new L.LayerGroup();
console.log("\n\n\n\n");
console.log("printining markersGroup");
console.log(markersGroup);
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');

		/*
		// defined by kaung -- modeled after : https://www.mapbox.com/mapbox-gl-js/example/drag-a-point/
			the following listener on mymap listens to double click
			and moves the marker to the double clicked place.
		*/
		mymap.on('dblclick', function(e) {
			try {
				var debug = false;
				if(debug) { console.log("right click at:", e.latlng.lat, e.latlng.lng); console.log(e);}

				updateCurrentLocation(e.latlng);

				console.log("printing before prevent")
				if ($('#update-address').prop('checked')) {
            updateCurrentAddress(e.latlng);
				}
				e.preventDefault();

			}
			catch(err) {
				console.log(err);
			}
		});

    // 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.');

    markersGroup.clearLayers();

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

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

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

    // 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 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
    mymap.panTo(location);

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

        updateCurrentLocation(marker.getLatLng());

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

    });
}

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');

            // End dispatching
            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>markers can now be moved on the dispatch ambulance -- ready for pull request<commit_after>/* Dispatch area - Should be eliminate after dispatching */

var markersGroup = new L.LayerGroup();
console.log("\n\n\n\n");
console.log("printining markersGroup");
console.log(markersGroup);
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');

		/*
		// defined by kaung -- modeled after : https://www.mapbox.com/mapbox-gl-js/example/drag-a-point/
			the following listener on mymap listens to double click
			and moves the marker to the double clicked place.
		*/
		mymap.on('dblclick', function(e) {
			try {
				var debug = false;
				if(debug) { console.log("right click at:", e.latlng.lat, e.latlng.lng); console.log(e);}

				updateCurrentLocation(e.latlng);

				console.log("printing before prevent")
				if ($('#update-address').prop('checked')) {
            updateCurrentAddress(e.latlng);
				}
			}
			catch(err) {
				e.preventDefault();
				console.log(err);
			}
		});

    // 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.');

    markersGroup.clearLayers();

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

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

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

    // 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 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
    mymap.panTo(location);

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

        updateCurrentLocation(marker.getLatLng());

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

    });
}

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');

            // End dispatching
            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));
}
<|endoftext|>


<commit_before>/*************************************************************************
 * Copyright 2009-2012 Eucalyptus Systems, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/.
 *
 * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
 * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
 * additional information or have any questions.
 ************************************************************************/

(function($, eucalyptus) {
  $.widget('eucalyptus.eucadata', $.eucalyptus.eucawidget, {
    options : {
      refresh_interval_sec : REFRESH_INTERVAL_SEC * 20,
      endpoints: [{name:'instance', url: '/ec2?Action=DescribeInstances'},
                  {name:'image', url: '/ec2?Action=DescribeImages'},
                  {name:'volume', url: '/ec2?Action=DescribeVolumes'},
                  {name:'snapshot', url: '/ec2?Action=DescribeSnapshots'},
                  {name:'eip', url: '/ec2?Action=DescribeAddresses'},
                  {name:'keypair', url: '/ec2?Action=DescribeKeyPairs'},
                  {name:'sgroup', url: '/ec2?Action=DescribeSecurityGroups'},
                  {name:'zone', url: '/ec2?Action=DescribeAvailabilityZones'}
      ],
    },
    _data : {instance:null, image:null, volume:null, snapshot:null, eip:null, keypair: null, sgroup: null, zone: null},
    _callbacks : {},
    _listeners : {},
    _init : function(){ },
    _create : function(){
      var thisObj = this;
      $.each(thisObj.options.endpoints, function(idx, ep){
        var name = ep.name;
        var url = ep.url;
        thisObj._callbacks[name] = {callback: function(){
         $.ajax({
           type:"GET",
           url: url,
           data:"_xsrf="+$.cookie('_xsrf'),
           dataType:"json",
           async:"false",
           success: function(data, textStatus, jqXHR){
             if (data.results) {
               //delete thisObj._data[name];
               thisObj._data[name] = {
                 lastupdated: new Date(),
                 results: data.results
               }
               if(thisObj._listeners[name] && thisObj._listeners[name].length >0) {
                 $.each(thisObj._listeners[name], function (idx, callback){
                   callback['callback'].apply(thisObj);
                 });
               }
             }else {
               ;//TODO: how to notify errors?
             }
           },
           error: function(jqXHR, textStatus, errorThrown){ //TODO: need to call notification subsystem
             if(thisObj._data[name]){
               var last = thisObj._data[name]['lastupdated'];
               var now = new Date();
               var elapsedSec = Math.round((now-last)/1000);
               if(elapsedSec > thisObj.options.refresh_interval_sec*3){
                 delete thisObj._data[name];
                 thisObj._data[name] = null;
               }
               if(thisObj.getStatus() !== 'online'){
                 logout();
               }
            }
          }});
        }, repeat: null};
      });
      this._describe();
    },
    _destroy : function(){
    },
    _describe : function(){
      var thisObj = this;
      $.each(thisObj.options.endpoints, function(idx, ep){
        var name = ep.name;
        var interval = getRandomInt((thisObj.options.refresh_interval_sec*1000)/2,(thisObj.options.refresh_interval_sec*1000)*2);
        thisObj._callbacks[name].repeat = runRepeat(thisObj._callbacks[name].callback, interval, true); // random ms is added to distribute sync interval
      });
    },

/***** Public Methods *****/
    // e.g., get(instance, id);
    get : function(resource){
      var thisObj = this;
      if(thisObj._data[resource])
        return thisObj._data[resource].results;
      else
        return null;
    },

    addCallback : function(resource, source, callback){
      var thisObj = this;
      if (typeof(callback) === "function"){
        if(!thisObj._listeners[resource])
          thisObj._listeners[resource] = [];
        var duplicate = false;
        for (i=0; i<thisObj._listeners[resource].length; i++){
          var cb = thisObj._listeners[resource][i];
          if(cb['src'] === source){
            duplicate = true;
            break;
          }
        }
        if(!duplicate)
          thisObj._listeners[resource].push({src:source, callback: callback});
     //   alert(resource+' num cb: '+thisObj._listeners[resource].length);
      }
    },

    removeCallback : function(resource, source){
      var thisObj = this;
      if (! thisObj._listeners[resource] || thisObj._listeners[resource].length <=0)
        return;
      var toDelete = -1;
      for (i=0; i< thisObj._listeners[resource].length; i++){
        var cb = thisObj._listeners[resource][i];
        if (cb['src'] === source) {
          toDelete = i;
          break;
        }
      }
      if(toDelete>=0)
        thisObj._listeners[resource].splice(toDelete, 1);
    },

    refresh : function(resource){
      var thisObj = this;
      if(thisObj._callbacks[resource])
        thisObj._callbacks[resource].callback.apply(this);
    },

    // status: online, offline, error
    // TODO: refine implementation
    getStatus : function(){
      var thisObj = this;
      var status = 'online';
      var numOff = 0;
      $.each(thisObj._data, function(resource, data){
        if(!data || !data.results)
          numOff++;
      });
      if(numOff >= 2)
        status='offline';

      return status;
    },
/**************************/
  });
})
(jQuery, window.eucalyptus ? window.eucalyptus : window.eucalyptus = {});
<commit_msg>set back refresh rate<commit_after>/*************************************************************************
 * Copyright 2009-2012 Eucalyptus Systems, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/.
 *
 * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
 * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
 * additional information or have any questions.
 ************************************************************************/

(function($, eucalyptus) {
  $.widget('eucalyptus.eucadata', $.eucalyptus.eucawidget, {
    options : {
      refresh_interval_sec : REFRESH_INTERVAL_SEC,
      endpoints: [{name:'instance', url: '/ec2?Action=DescribeInstances'},
                  {name:'image', url: '/ec2?Action=DescribeImages'},
                  {name:'volume', url: '/ec2?Action=DescribeVolumes'},
                  {name:'snapshot', url: '/ec2?Action=DescribeSnapshots'},
                  {name:'eip', url: '/ec2?Action=DescribeAddresses'},
                  {name:'keypair', url: '/ec2?Action=DescribeKeyPairs'},
                  {name:'sgroup', url: '/ec2?Action=DescribeSecurityGroups'},
                  {name:'zone', url: '/ec2?Action=DescribeAvailabilityZones'}
      ],
    },
    _data : {instance:null, image:null, volume:null, snapshot:null, eip:null, keypair: null, sgroup: null, zone: null},
    _callbacks : {},
    _listeners : {},
    _init : function(){ },
    _create : function(){
      var thisObj = this;
      $.each(thisObj.options.endpoints, function(idx, ep){
        var name = ep.name;
        var url = ep.url;
        thisObj._callbacks[name] = {callback: function(){
         $.ajax({
           type:"GET",
           url: url,
           data:"_xsrf="+$.cookie('_xsrf'),
           dataType:"json",
           async:"false",
           success: function(data, textStatus, jqXHR){
             if (data.results) {
               //delete thisObj._data[name];
               thisObj._data[name] = {
                 lastupdated: new Date(),
                 results: data.results
               }
               if(thisObj._listeners[name] && thisObj._listeners[name].length >0) {
                 $.each(thisObj._listeners[name], function (idx, callback){
                   callback['callback'].apply(thisObj);
                 });
               }
             }else {
               ;//TODO: how to notify errors?
             }
           },
           error: function(jqXHR, textStatus, errorThrown){ //TODO: need to call notification subsystem
             if(thisObj._data[name]){
               var last = thisObj._data[name]['lastupdated'];
               var now = new Date();
               var elapsedSec = Math.round((now-last)/1000);
               if(elapsedSec > thisObj.options.refresh_interval_sec*3){
                 delete thisObj._data[name];
                 thisObj._data[name] = null;
               }
               if(thisObj.getStatus() !== 'online'){
                 logout();
               }
            }
          }});
        }, repeat: null};
      });
      this._describe();
    },
    _destroy : function(){
    },
    _describe : function(){
      var thisObj = this;
      $.each(thisObj.options.endpoints, function(idx, ep){
        var name = ep.name;
        var interval = getRandomInt((thisObj.options.refresh_interval_sec*1000)/2,(thisObj.options.refresh_interval_sec*1000)*2);
        thisObj._callbacks[name].repeat = runRepeat(thisObj._callbacks[name].callback, interval, true); // random ms is added to distribute sync interval
      });
    },

/***** Public Methods *****/
    // e.g., get(instance, id);
    get : function(resource){
      var thisObj = this;
      if(thisObj._data[resource])
        return thisObj._data[resource].results;
      else
        return null;
    },

    addCallback : function(resource, source, callback){
      var thisObj = this;
      if (typeof(callback) === "function"){
        if(!thisObj._listeners[resource])
          thisObj._listeners[resource] = [];
        var duplicate = false;
        for (i=0; i<thisObj._listeners[resource].length; i++){
          var cb = thisObj._listeners[resource][i];
          if(cb['src'] === source){
            duplicate = true;
            break;
          }
        }
        if(!duplicate)
          thisObj._listeners[resource].push({src:source, callback: callback});
     //   alert(resource+' num cb: '+thisObj._listeners[resource].length);
      }
    },

    removeCallback : function(resource, source){
      var thisObj = this;
      if (! thisObj._listeners[resource] || thisObj._listeners[resource].length <=0)
        return;
      var toDelete = -1;
      for (i=0; i< thisObj._listeners[resource].length; i++){
        var cb = thisObj._listeners[resource][i];
        if (cb['src'] === source) {
          toDelete = i;
          break;
        }
      }
      if(toDelete>=0)
        thisObj._listeners[resource].splice(toDelete, 1);
    },

    refresh : function(resource){
      var thisObj = this;
      if(thisObj._callbacks[resource])
        thisObj._callbacks[resource].callback.apply(this);
    },

    // status: online, offline, error
    // TODO: refine implementation
    getStatus : function(){
      var thisObj = this;
      var status = 'online';
      var numOff = 0;
      $.each(thisObj._data, function(resource, data){
        if(!data || !data.results)
          numOff++;
      });
      if(numOff >= 2)
        status='offline';

      return status;
    },
/**************************/
  });
})
(jQuery, window.eucalyptus ? window.eucalyptus : window.eucalyptus = {});
<|endoftext|>


<commit_before>
$(document).ready(function() {

    var template = _.template($('#banner-template').html());

    var startDate = new Date('2013-10-1 EDT');

    var SECONDS = 1000,
        MINUTES = SECONDS * 60,
        HOURS = MINUTES * 60,
        DAYS = HOURS * 24;

    var _s = _.string;

    var addCommas = function(str) {
        var tmp = _s.reverse(str);
        tmp = tmp.replace(/([0-9]{3})/g, '$1,');
        var val = _s.reverse(tmp);
        if (val.substr(0, 1) === ',') {
            val = val.substring(1, val.length);
        }
        return val;
    };

    function numberFormat(n) {
        var str = Math.round(n) + '';
        return '$' + addCommas(str);
    }

    function updateValues() {
        var currentDate = new Date(),
            timeDiff = currentDate - startDate;
        var days = Math.floor(timeDiff / DAYS),
            hours = Math.floor((timeDiff % DAYS) / HOURS),
            minutes = Math.floor((timeDiff % HOURS) / MINUTES),
            seconds = Math.floor((timeDiff % MINUTES) / SECONDS);
        var cost = timeDiff / HOURS * 12500000;
        var vals = {days: days,
                    hours: hours,
                    minutes: minutes,
                    seconds: seconds,
                    cost: numberFormat(cost),

                    // Pluralizations
                    days_pluralize: days === 1 ? '' : 's',
                    hours_pluralize: hours === 1 ? '' : 's',
                    minutes_pluralize: minutes === 1 ? '' : 's',
                    seconds_pluralize: seconds === 1 ? '' : 's'
                   };
        $('#template-wrapper').html(template(vals));
    }

    window.setInterval(updateValues, 1000)

});
<commit_msg>Fixes date issue on safari'<commit_after>
$(document).ready(function() {

    var template = _.template($('#banner-template').html());

    var startDate = 1380600000000; //Math.abs(new Date('2013-10-1 EDT'));

    var SECONDS = 1000,
        MINUTES = SECONDS * 60,
        HOURS = MINUTES * 60,
        DAYS = HOURS * 24;

    var _s = _.string;

    var addCommas = function(str) {
        var tmp = _s.reverse(str);
        tmp = tmp.replace(/([0-9]{3})/g, '$1,');
        var val = _s.reverse(tmp);
        if (val.substr(0, 1) === ',') {
            val = val.substring(1, val.length);
        }
        return val;
    };

    function numberFormat(n) {
        var str = Math.round(n) + '';
        return '$' + addCommas(str);
    }

    function updateValues() {
        var currentDate = Math.abs(new Date()),
            timeDiff = currentDate - startDate;
        //console.log('=======>', currentDate, startDate, timeDiff);
        var days = Math.floor(timeDiff / DAYS),
            hours = Math.floor((timeDiff % DAYS) / HOURS),
            minutes = Math.floor((timeDiff % HOURS) / MINUTES),
            seconds = Math.floor((timeDiff % MINUTES) / SECONDS);
        var cost = timeDiff / HOURS * 12500000;
        var vals = {days: days,
                    hours: hours,
                    minutes: minutes,
                    seconds: seconds,
                    cost: numberFormat(cost),

                    // Pluralizations
                    days_pluralize: days === 1 ? '' : 's',
                    hours_pluralize: hours === 1 ? '' : 's',
                    minutes_pluralize: minutes === 1 ? '' : 's',
                    seconds_pluralize: seconds === 1 ? '' : 's'
                   };
        $('#template-wrapper').html(template(vals));
    }

    window.setInterval(updateValues, 1000)

});
<|endoftext|>


<commit_before>var app = angular.module('identityApp', []);

// disable security checks on html
//app.config(function($sceProvider) {
//	$sceProvider.enabled(false);
//    });

// add xsrf protection as needed
app.config(['$httpProvider', function ($httpProvider) {
	    $httpProvider.defaults.xsrfCookieName = 'csrftoken';
	    $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
	}]);

app.factory('TitleSvc', function(){
  return {
      title: function() {
          if ($('h1') && $('h1')[0]){
              if($('h1')[0].innerText){ return $('h1')[0].innerText;}
              if($('h1')[0].textContent){ return $('h1')[0].textContent;}
          }
          return 'Identity.UW';
      }
  };
});

app.factory('TimeoutSvc', function(){
    var _this = this;
    _this.reloadFunc = function() {window.location.reload();};

    return {
        setReloadFunc: function(func) { _this.reloadFunc = func; },
        reload: function() {_this.reloadFunc();},
        showTimeout: function() {
            $('#timeoutModal')
                .modal('show')
                .on('shown.bs.modal', function() { $('#timeoutModal').find('button.btn-primary').focus();
            });
        }
    };
});

app.controller('TitleCtrl', ['TitleSvc', function(TitleSvc){
    var _this = this;
    this.Page = TitleSvc;
}]);

app.controller('TimeoutCtrl', ['TimeoutSvc', function(TimeoutSvc){
    this.reload = function() {
        TimeoutSvc.reload();
    };
}]);

// Our service to handle general errors (401 - invalid session, 500 - server error)
app.factory('ErrorSvc', ['$log', 'TimeoutSvc', function($log, TimeoutSvc){
    var _this = this;
    this.state = {isErrorSet: false};
    this.handleError = function(data, status) {
        if (status == 401) {
            $log.info('expired session, ', data);
            TimeoutSvc.showTimeout();
        } else if (status == 500) {
            $log.info('500 error from server,', data);
            _this.state.isErrorSet = true;
        }
    };

    return {
        handleError: _this.handleError,
        state: _this.state
    };
}]);

app.controller('ErrorCtrl', ['ErrorSvc', function(ErrorSvc){
    this.errorState = ErrorSvc.state;
}]);

// This only works well when there's only one set-focus directive in the DOM (which we do through ng-if)
// This could probably be made better or supposedly get declared differently with HTML5.
app.directive('setFocus', function($timeout) {
  return function(scope, element, attrs) {
    scope.$watch(attrs.setFocus,
      function (conditional) {
        $timeout(function() {
            conditional && element[0].focus();
        });
      },true);
  };
});

app.factory('LocationTabSvc', ['$location', '$window', function($location, $window){
    var _this = this;
    this.page = {name: '', history: [''], stepCounter: 1};
    this.reloadPages = [];
    return {
        // Call this on successful location change. We inspect the url to see if
        // it's one of the pages in our history, and set the view accordingly.
        locationChange: function(newUrl){
            // this swallows up everything up to the last slash
            newPageName = newUrl.replace(/^.*\//, '');

            // this picks up events not handled by setPage (back/forward button or
            // visiting the link directly.
            if(newPageName != _this.page.name){
                // if the new page is in our history change the page and stepCounter
                if(_this.page.history.indexOf(newPageName) != -1) {
                    _this.page.stepCounter = _this.page.history.indexOf(newPageName) + 1;
                    _this.page.name = newPageName;
                }
            }
        },
        // Change the currently-active page, updating the history and stepCounter as necessary.
        setPage: function(name){
            // while the current page isn't the last page in our history, pop history until it is
            // before advancing.
            while(_this.page.history.indexOf(_this.page.name) != _this.page.history.length - 1){
                _this.page.history.pop();
            }

            _this.page.name = name;
            // call for a reload for any page in setReloadPages.
            if(_this.reloadPages.indexOf(name) != -1){ $window.location = name;}
            else {
                if(_this.page.history.indexOf(name) == -1){
                    _this.page.history.push(name);
                }
                _this.page.stepCounter = _this.page.history.indexOf(name) + 1;
                // this adjusts the url without reloading it.
                $location.path(name);
            }
        },
        // set the pages that, when set, will trigger a reload.
        setReloadPages: function(pages){ _this.reloadPages = pages; },
        // set the initial page, with initialPages being the collection
        // of pages allowed at load time (other than '', our main page).
        init: function(initialPages){
            // remove the leading /
            var locationPath = $location.path().replace(/^\//, '');
            if(initialPages && initialPages.indexOf(locationPath) != -1){
                _this.page.name = locationPath;
            }
            else if (locationPath){
                // if a path other than our initial ones, replace the current
                // url with our base.
                $location.path('').replace();
            }
        },
        // This page represents the currently active page.
        page: _this.page
    };
}]);
<commit_msg>Use $log instead of console.log and $window instead of window to try to remedy IE 9 brokenness.<commit_after>var app = angular.module('identityApp', []);

// disable security checks on html
//app.config(function($sceProvider) {
//	$sceProvider.enabled(false);
//    });

// add xsrf protection as needed
app.config(['$httpProvider', function ($httpProvider) {
	    $httpProvider.defaults.xsrfCookieName = 'csrftoken';
	    $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
	}]);

app.factory('TitleSvc', function(){
  return {
      title: function() {
          if ($('h1') && $('h1')[0]){
              if($('h1')[0].innerText){ return $('h1')[0].innerText;}
              if($('h1')[0].textContent){ return $('h1')[0].textContent;}
          }
          return 'Identity.UW';
      }
  };
});

app.factory('TimeoutSvc', ['$window', function($window){
    var _this = this;
    _this.reloadFunc = function() {$window.location.reload();};

    return {
        setReloadFunc: function(func) { _this.reloadFunc = func; },
        reload: function() {_this.reloadFunc();},
        showTimeout: function() {
            $('#timeoutModal')
                .modal('show')
                .on('shown.bs.modal', function() { $('#timeoutModal').find('button.btn-primary').focus();
            });
        }
    };
}]);

app.controller('TitleCtrl', ['TitleSvc', function(TitleSvc){
    var _this = this;
    this.Page = TitleSvc;
}]);

app.controller('TimeoutCtrl', ['TimeoutSvc', function(TimeoutSvc){
    this.reload = function() {
        TimeoutSvc.reload();
    };
}]);

// Our service to handle general errors (401 - invalid session, 500 - server error)
app.factory('ErrorSvc', ['$log', 'TimeoutSvc', function($log, TimeoutSvc){
    var _this = this;
    this.state = {isErrorSet: false};
    this.handleError = function(data, status) {
        if (status == 401) {
            $log.info('expired session, ', data);
            TimeoutSvc.showTimeout();
        } else if (status == 500) {
            $log.info('500 error from server,', data);
            _this.state.isErrorSet = true;
        }
    };

    return {
        handleError: _this.handleError,
        state: _this.state
    };
}]);

app.controller('ErrorCtrl', ['ErrorSvc', function(ErrorSvc){
    this.errorState = ErrorSvc.state;
}]);

// This only works well when there's only one set-focus directive in the DOM (which we do through ng-if)
// This could probably be made better or supposedly get declared differently with HTML5.
app.directive('setFocus', function($timeout) {
  return function(scope, element, attrs) {
    scope.$watch(attrs.setFocus,
      function (conditional) {
        $timeout(function() {
            conditional && element[0].focus();
        });
      },true);
  };
});

app.factory('LocationTabSvc', ['$location', '$window', function($location, $window){
    var _this = this;
    this.page = {name: '', history: [''], stepCounter: 1};
    this.reloadPages = [];
    return {
        // Call this on successful location change. We inspect the url to see if
        // it's one of the pages in our history, and set the view accordingly.
        locationChange: function(newUrl){
            // this swallows up everything up to the last slash
            newPageName = newUrl.replace(/^.*\//, '');

            // this picks up events not handled by setPage (back/forward button or
            // visiting the link directly.
            if(newPageName != _this.page.name){
                // if the new page is in our history change the page and stepCounter
                if(_this.page.history.indexOf(newPageName) != -1) {
                    _this.page.stepCounter = _this.page.history.indexOf(newPageName) + 1;
                    _this.page.name = newPageName;
                }
            }
        },
        // Change the currently-active page, updating the history and stepCounter as necessary.
        setPage: function(name){
            // while the current page isn't the last page in our history, pop history until it is
            // before advancing.
            while(_this.page.history.indexOf(_this.page.name) != _this.page.history.length - 1){
                _this.page.history.pop();
            }

            _this.page.name = name;
            // call for a reload for any page in setReloadPages.
            if(_this.reloadPages.indexOf(name) != -1){ $window.location = name;}
            else {
                if(_this.page.history.indexOf(name) == -1){
                    _this.page.history.push(name);
                }
                _this.page.stepCounter = _this.page.history.indexOf(name) + 1;
                // this adjusts the url without reloading it.
                $location.path(name);
            }
        },
        // set the pages that, when set, will trigger a reload.
        setReloadPages: function(pages){ _this.reloadPages = pages; },
        // set the initial page, with initialPages being the collection
        // of pages allowed at load time (other than '', our main page).
        init: function(initialPages){
            // remove the leading /
            var locationPath = $location.path().replace(/^\//, '');
            if(initialPages && initialPages.indexOf(locationPath) != -1){
                _this.page.name = locationPath;
            }
            else if (locationPath){
                // if a path other than our initial ones, replace the current
                // url with our base.
                $location.path('').replace();
            }
        },
        // This page represents the currently active page.
        page: _this.page
    };
}]);
<|endoftext|>


<commit_before>import * as blueslip from "./blueslip";

export class LazySet {
    /*
        This class is optimized for a very
        particular use case.

        We often have lots of subscribers on
        a stream.  We get an array from the
        backend, because it's JSON.

        Often the only operation we need
        on subscribers is to get the length,
        which is plenty cheap as an array.

        Making an array from a set is cheap
        for one stream, but it's expensive
        for all N streams at page load.

        Once somebody does an operation
        where sets are useful, such
        as has/add/delete, we convert it over
        to a set for a one-time cost.
    */

    constructor(vals) {
        this.data = {
            arr: vals,
            set: undefined,
        };
    }

    get size() {
        const {data} = this;
        if (data.set !== undefined) {
            return data.set.size;
        }

        return data.arr.length;
    }

    keys() {
        const {data} = this;
        if (data.set !== undefined) {
            return data.set.keys();
        }
        return data.arr.values();
    }

    _make_set() {
        if (this.data.set !== undefined) {
            return;
        }

        this.data = {
            arr: undefined,
            set: new Set(this.data.arr),
        };
    }

    map(f) {
        return Array.from(this.keys(), f);
    }

    has(v) {
        this._make_set();
        const val = this._clean(v);
        return this.data.set.has(val);
    }

    add(v) {
        this._make_set();
        const val = this._clean(v);
        this.data.set.add(val);
    }

    delete(v) {
        this._make_set();
        const val = this._clean(v);
        return this.data.set.delete(val);
    }

    _clean(v) {
        if (typeof v !== "number") {
            blueslip.error("not a number");
            return Number.parseInt(v, 10);
        }
        return v;
    }
}
<commit_msg>lazy_set: Return set from the _make_set method.<commit_after>import * as blueslip from "./blueslip";

export class LazySet {
    /*
        This class is optimized for a very
        particular use case.

        We often have lots of subscribers on
        a stream.  We get an array from the
        backend, because it's JSON.

        Often the only operation we need
        on subscribers is to get the length,
        which is plenty cheap as an array.

        Making an array from a set is cheap
        for one stream, but it's expensive
        for all N streams at page load.

        Once somebody does an operation
        where sets are useful, such
        as has/add/delete, we convert it over
        to a set for a one-time cost.
    */

    constructor(vals) {
        this.data = {
            arr: vals,
            set: undefined,
        };
    }

    get size() {
        const {data} = this;
        if (data.set !== undefined) {
            return data.set.size;
        }

        return data.arr.length;
    }

    keys() {
        const {data} = this;
        if (data.set !== undefined) {
            return data.set.keys();
        }
        return data.arr.values();
    }

    _make_set() {
        if (this.data.set !== undefined) {
            return this.data.set;
        }

        this.data = {
            arr: undefined,
            set: new Set(this.data.arr),
        };

        return this.data.set;
    }

    map(f) {
        return Array.from(this.keys(), f);
    }

    has(v) {
        this._make_set();
        const val = this._clean(v);
        return this.data.set.has(val);
    }

    add(v) {
        const set = this._make_set();
        const val = this._clean(v);
        set.add(val);
    }

    delete(v) {
        const set = this._make_set();
        const val = this._clean(v);
        return set.delete(val);
    }

    _clean(v) {
        if (typeof v !== "number") {
            blueslip.error("not a number");
            return Number.parseInt(v, 10);
        }
        return v;
    }
}
<|endoftext|>


<commit_before>"use strict";

function FIXMEmockMessageId(idx) {
  return btoa(String(idx));
}
function getMockMessage(ref, offset) {
  // Some bogus "hiding" of message ids. base64-encode their ascii
  // representation.
  ref = Number(atob(ref));
  ref += offset;
  var idx = ref % SAMPLE_MSGS.length;
  if (idx < 0)
    idx += SAMPLE_MSGS.length;

  var msg = { };
  for (var key in SAMPLE_MSGS[idx]) {
    msg[key] = SAMPLE_MSGS[idx][key];
  }
  msg.number = ref;
  msg.id = FIXMEmockMessageId(ref);
  return msg;
}

function MockMessageModel() {
}
// TODO(davidben): Just make the API take the options dict along with
// an initial count parameter?
MockMessageModel.prototype.newTailInclusive = function(start, cb) {
  return new MockMessageTail(start, cb, {
    inclusive: true
  });
};
MockMessageModel.prototype.newTail = function(start, cb) {
  return new MockMessageTail(start, cb, {});
};
MockMessageModel.prototype.newReverseTail = function(start, cb) {
  return new MockMessageTail(start, cb, {
    reverse: true
  });
};

function MockMessageTail(start, cb, opts) {
  this.start_ = start;
  this.reverse_ = opts.reverse;
  this.cb_ = cb;

  // Simulate doing only one XHR at a time.
  this.requestPending_ = false;
  this.lastRequested_ = opts.inclusive ? -1 : 0;
  this.count_ = 0;
  // We intentionally do not fire a request and assume the user will
  // immediately call expandTo.
  //
  // this.fireRequest_();
}
MockMessageTail.prototype.expandTo = function(count) {
  this.count_ = Math.max(this.count_, count);
  this.fireRequest_();
};
MockMessageTail.prototype.close = function() {
  this.cb_ = null;
};
MockMessageTail.prototype.fireRequest_ = function() {
  if (this.requestPending_)
    return;
  if (this.lastRequested_ >= this.count_)
    return;

  var requestStart = this.lastRequested_;
  var requestEnd = this.count_;

  this.lastRequested_ = this.count_;
  this.requestPending_ = true;

  window.setTimeout(function() {
    // Fake some messages.
    var msgs = [];
    if (this.reverse_) {
      for (var i = requestEnd; i > requestStart; i--) {
        msgs.push(getMockMessage(this.start_, -i));
      }
    } else {
      for (var i = requestStart + 1; i <= requestEnd; i++) {
        msgs.push(getMockMessage(this.start_, i));
      }
    }
    // For now, say we're never at the end...
    if (this.cb_)
      this.cb_(msgs, false);

    this.requestPending_ = false;
    this.fireRequest_();
  }.bind(this), 500);
};

// If the number of messages outside the scroll goes outside
// [MIN_BUFFER, MAX_BUFFER], request/retire messages to reach
// TARGET_BUFFER. That there is a buffer between TARGET_BUFFER and our
// endpoints ensures that spurious scrolling will not repeatedly
// request and retire the same message. Also that we do our requests
// in large-ish batches.
var MIN_BUFFER = 50;
var TARGET_BUFFER = MIN_BUFFER * 2;
var MAX_BUFFER = MIN_BUFFER * 3;

function MessageView(model, container) {
  this.model_ = model;
  this.container_ = container;

  this.tailBelow_ = model.newTailInclusive(FIXMEmockMessageId(0),
                                           this.appendMessages_.bind(this));
  this.tailBelowOffset_ = 0;  // The global index of the tail reference.

  this.tailAbove_ = model.newReverseTail(FIXMEmockMessageId(0),
                                         this.prependMessages_.bind(this));
  this.tailAboveOffset_ = 0;  // The global index of the tail reference.

  this.listOffset_ = 0;  // The global index of the top of the list.
  this.messages_ = [];
  this.nodes_ = [];

  this.container_.addEventListener("scroll", this.checkBuffers_.bind(this));
  this.checkBuffers_();
}
MessageView.prototype.appendMessages_ = function(msgs, isDone) {
  for (var i = 0; i < msgs.length; i++) {
    var node = this.formatMessage_(msgs[i]);
    this.nodes_.push(node);
    this.messages_.push(msgs[i]);

    this.container_.appendChild(node);
  }
};
MessageView.prototype.prependMessages_ = function(msgs, isDone) {
  // TODO(davidben): This triggers layout a bunch. Optimize this if needbe.
  var nodes = [];
  var insertReference = this.container_.firstChild;
  var oldHeight = this.container_.scrollHeight;
  for (var i = 0; i < msgs.length; i++) {
    var node = this.formatMessage_(msgs[i]);
    nodes.push(node);

    this.container_.insertBefore(node, insertReference);
  }
  this.container_.scrollTop += (this.container_.scrollHeight - oldHeight);

  this.messages_.unshift.apply(this.messages_, msgs);
  this.nodes_.unshift.apply(this.nodes_, nodes);
  this.listOffset_ -= msgs.length;
};
var COLORS = ["black", "silver", "gray", "white", "maroon", "red",
              "purple", "fuchsia", "green", "lime"];
MessageView.prototype.formatMessage_ = function(msg) {
  var pre = document.createElement("pre");
  var indented = "   " + msg.message.split("\n").join("\n   ");
  pre.textContent =
    msg.number + ": " +
    msg.class + " / " + msg.instance + " / " + msg.sender + "  " +
      new Date(msg.time).toString() + "\n" +
    indented;
  pre.className = "message";
  pre.style.color = COLORS[((msg.number % COLORS.length) + COLORS.length) % COLORS.length];
  return pre;
};
// Return 1 if we need to expand below, -1 if we need to contract, and
// 0 if neither.
MessageView.prototype.checkBelow_ = function(bounds) {
  // Do we need to expand?
  if (this.nodes_.length < MIN_BUFFER)
    return 1;
  var b = this.nodes_[this.nodes_.length - MIN_BUFFER].getBoundingClientRect();
  if (bounds.bottom > b.top)
    return 1;

  // Do we need to contract?
  if (this.nodes_.length < MAX_BUFFER)
    return 0;
  b = this.nodes_[this.nodes_.length - MAX_BUFFER].getBoundingClientRect();
  if (bounds.bottom < b.top)
    return -1;

  return 0;
};
MessageView.prototype.checkAbove_ = function(bounds) {
  // Do we need to expand?
  if (this.nodes_.length < MIN_BUFFER)
    return 1;
  var b = this.nodes_[MIN_BUFFER - 1].getBoundingClientRect();
  if (bounds.top < b.bottom)
    return 1;

  // Do we need to contract?
  if (this.nodes_.length < MAX_BUFFER)
    return 0;
  b = this.nodes_[MAX_BUFFER - 1].getBoundingClientRect();
  if (bounds.top > b.bottom)
    return -1;

  return 0;
};

MessageView.prototype.checkBuffers_ = function() {
  console.log(this.container_.scrollTop, this.container_.scrollHeight);
  var bounds = this.container_.getBoundingClientRect();

  // TODO(davidben): Instead of only ever working 50 messages at a
  // time, it's possibly better to just pay a binary search and figure
  // out exactly how many we need to reach TARGET_BUFFER?
  //
  // TODO(davidben): Trigger removal by receiving messages?
  var below = this.checkBelow_(bounds);
  if (below > 0) {
    if (!this.tailBelow_) {
      this.tailBelow_ = this.model_.newTail(
        this.messages_[this.messages_.length - 1].id,
        this.appendMessages_.bind(this));
      this.tailBelowOffset_ = this.listOffset_ + this.messages_.length - 1;
    }
    this.tailBelow_.expandTo(
      this.listOffset_ + this.nodes_.length - 1 + (TARGET_BUFFER - MIN_BUFFER)
        - this.tailBelowOffset_);
  } else if (below < 0) {
    // Close the current tail.
    if (this.tailBelow_) {
      this.tailBelow_.close();
      this.tailBelow_ = null;
    }

    var num = MAX_BUFFER - TARGET_BUFFER;
    for (var i = 0; i < num; i++) {
      this.container_.removeChild(this.nodes_[this.nodes_.length - i - 1]);
    }
    this.nodes_.splice(this.nodes_.length - num, num);
    this.messages_.splice(this.messages_.length - num, num);
  }

  var above = this.checkAbove_(bounds);
  if (above > 0) {
    if (!this.tailAbove_) {
      this.tailAbove_ = this.model_.newReverseTail(
        this.messages_[0].id,
        this.prependMessages_.bind(this));
      this.tailAboveOffset_ = this.listOffset_;
    }
    this.tailAbove_.expandTo(
      this.tailAboveOffset_ -
        (this.listOffset_ - (TARGET_BUFFER - MIN_BUFFER)));
  } else if (above < 0) {
    // Close the current tail.
    if (this.tailAbove_) {
      this.tailAbove_.close();
      this.tailAbove_ = null;
    }

    var maxRemoved = MAX_BUFFER - TARGET_BUFFER;
    var oldHeight = this.container_.scrollHeight;

    // Limit the nodes removed; if we remove enough that scrollTop has
    // to change, the scroll gets off. Do this in two passes so as not
    // to keep re-triggering layout with
    // getBoundingClientRect. Unfortunately, this isn't the cause of
    // us getting stuck.
    var heightLost = 0;
    var maxHeight = oldHeight - this.container_.scrollTop - bounds.height;
    for (var num = 0; num < maxRemoved; num++) {
      // FIXME: What if there are margins?
      var b = this.nodes_[num].getBoundingClientRect();
      if (heightLost + b.height >= maxHeight)
        break;
      heightLost += b.height;
    }

    for (var i = 0; i < num; i++) {
      this.container_.removeChild(this.nodes_[i]);
    }
    this.container_.scrollTop += (this.container_.scrollHeight - oldHeight);
    this.nodes_.splice(0, num);
    this.messages_.splice(0, num);
    this.listOffset_ += num;
  }
};

$(function() {
  new MessageView(new MockMessageModel(),
                  document.getElementById("messagelist"));
});
<commit_msg>Debug<commit_after>"use strict";

function FIXMEmockMessageId(idx) {
  return btoa(String(idx));
}
function getMockMessage(ref, offset) {
  // Some bogus "hiding" of message ids. base64-encode their ascii
  // representation.
  ref = Number(atob(ref));
  ref += offset;
  var idx = ref % SAMPLE_MSGS.length;
  if (idx < 0)
    idx += SAMPLE_MSGS.length;

  var msg = { };
  for (var key in SAMPLE_MSGS[idx]) {
    msg[key] = SAMPLE_MSGS[idx][key];
  }
  msg.number = ref;
  msg.id = FIXMEmockMessageId(ref);
  return msg;
}

function MockMessageModel() {
}
// TODO(davidben): Just make the API take the options dict along with
// an initial count parameter?
MockMessageModel.prototype.newTailInclusive = function(start, cb) {
  return new MockMessageTail(start, cb, {
    inclusive: true
  });
};
MockMessageModel.prototype.newTail = function(start, cb) {
  return new MockMessageTail(start, cb, {});
};
MockMessageModel.prototype.newReverseTail = function(start, cb) {
  return new MockMessageTail(start, cb, {
    reverse: true
  });
};

function MockMessageTail(start, cb, opts) {
  this.start_ = start;
  this.reverse_ = opts.reverse;
  this.cb_ = cb;

  // Simulate doing only one XHR at a time.
  this.requestPending_ = false;
  this.lastRequested_ = opts.inclusive ? -1 : 0;
  this.count_ = 0;
  // We intentionally do not fire a request and assume the user will
  // immediately call expandTo.
  //
  // this.fireRequest_();
}
MockMessageTail.prototype.expandTo = function(count) {
  this.count_ = Math.max(this.count_, count);
  this.fireRequest_();
};
MockMessageTail.prototype.close = function() {
  this.cb_ = null;
};
MockMessageTail.prototype.fireRequest_ = function() {
  if (this.requestPending_)
    return;
  if (this.lastRequested_ >= this.count_)
    return;

  var requestStart = this.lastRequested_;
  var requestEnd = this.count_;

  this.lastRequested_ = this.count_;
  this.requestPending_ = true;

  window.setTimeout(function() {
    // Fake some messages.
    var msgs = [];
    if (this.reverse_) {
      for (var i = requestEnd; i > requestStart; i--) {
        msgs.push(getMockMessage(this.start_, -i));
      }
    } else {
      for (var i = requestStart + 1; i <= requestEnd; i++) {
        msgs.push(getMockMessage(this.start_, i));
      }
    }
    // For now, say we're never at the end...
    if (this.cb_)
      this.cb_(msgs, false);

    this.requestPending_ = false;
    this.fireRequest_();
  }.bind(this), 500);
};

// If the number of messages outside the scroll goes outside
// [MIN_BUFFER, MAX_BUFFER], request/retire messages to reach
// TARGET_BUFFER. That there is a buffer between TARGET_BUFFER and our
// endpoints ensures that spurious scrolling will not repeatedly
// request and retire the same message. Also that we do our requests
// in large-ish batches.
var MIN_BUFFER = 50;
var TARGET_BUFFER = MIN_BUFFER * 2;
var MAX_BUFFER = MIN_BUFFER * 3;

function MessageView(model, container) {
  this.model_ = model;
  this.container_ = container;

  this.tailBelow_ = model.newTailInclusive(FIXMEmockMessageId(0),
                                           this.appendMessages_.bind(this));
  this.tailBelowOffset_ = 0;  // The global index of the tail reference.

  this.tailAbove_ = model.newReverseTail(FIXMEmockMessageId(0),
                                         this.prependMessages_.bind(this));
  this.tailAboveOffset_ = 0;  // The global index of the tail reference.

  this.listOffset_ = 0;  // The global index of the top of the list.
  this.messages_ = [];
  this.nodes_ = [];

  this.container_.addEventListener("scroll", this.checkBuffers_.bind(this));
  this.checkBuffers_();
}
MessageView.prototype.appendMessages_ = function(msgs, isDone) {
  for (var i = 0; i < msgs.length; i++) {
    var node = this.formatMessage_(msgs[i]);
    this.nodes_.push(node);
    this.messages_.push(msgs[i]);

    this.container_.appendChild(node);
  }
};
MessageView.prototype.prependMessages_ = function(msgs, isDone) {
  // TODO(davidben): This triggers layout a bunch. Optimize this if needbe.
  var nodes = [];
  var insertReference = this.container_.firstChild;
  var oldHeight = this.container_.scrollHeight;
  for (var i = 0; i < msgs.length; i++) {
    var node = this.formatMessage_(msgs[i]);
    nodes.push(node);

    this.container_.insertBefore(node, insertReference);
  }
  this.container_.scrollTop += (this.container_.scrollHeight - oldHeight);

  this.messages_.unshift.apply(this.messages_, msgs);
  this.nodes_.unshift.apply(this.nodes_, nodes);
  this.listOffset_ -= msgs.length;
};
var COLORS = ["black", "silver", "gray", "white", "maroon", "red",
              "purple", "fuchsia", "green", "lime"];
MessageView.prototype.formatMessage_ = function(msg) {
  var pre = document.createElement("pre");
  var indented = "   " + msg.message.split("\n").join("\n   ");
  pre.textContent =
    msg.number + ": " +
    msg.class + " / " + msg.instance + " / " + msg.sender + "  " +
      new Date(msg.time).toString() + "\n" +
    indented;
  pre.className = "message";
  pre.style.color = COLORS[((msg.number % COLORS.length) + COLORS.length) % COLORS.length];
  return pre;
};
// Return 1 if we need to expand below, -1 if we need to contract, and
// 0 if neither.
MessageView.prototype.checkBelow_ = function(bounds) {
  // Do we need to expand?
  if (this.nodes_.length < MIN_BUFFER)
    return 1;
  var b = this.nodes_[this.nodes_.length - MIN_BUFFER].getBoundingClientRect();
  if (bounds.bottom > b.top)
    return 1;

  // Do we need to contract?
  if (this.nodes_.length < MAX_BUFFER)
    return 0;
  b = this.nodes_[this.nodes_.length - MAX_BUFFER].getBoundingClientRect();
  if (bounds.bottom < b.top)
    return -1;

  return 0;
};
MessageView.prototype.checkAbove_ = function(bounds) {
  // Do we need to expand?
  if (this.nodes_.length < MIN_BUFFER)
    return 1;
  var b = this.nodes_[MIN_BUFFER - 1].getBoundingClientRect();
  if (bounds.top < b.bottom)
    return 1;

  // Do we need to contract?
  if (this.nodes_.length < MAX_BUFFER)
    return 0;
  b = this.nodes_[MAX_BUFFER - 1].getBoundingClientRect();
  if (bounds.top > b.bottom)
    return -1;

  return 0;
};

MessageView.prototype.checkBuffers_ = function() {
  var bounds = this.container_.getBoundingClientRect();

  // TODO(davidben): Instead of only ever working 50 messages at a
  // time, it's possibly better to just pay a binary search and figure
  // out exactly how many we need to reach TARGET_BUFFER?
  //
  // TODO(davidben): Trigger removal by receiving messages?
  var below = this.checkBelow_(bounds);
  if (below > 0) {
    if (!this.tailBelow_) {
      this.tailBelow_ = this.model_.newTail(
        this.messages_[this.messages_.length - 1].id,
        this.appendMessages_.bind(this));
      this.tailBelowOffset_ = this.listOffset_ + this.messages_.length - 1;
    }
    this.tailBelow_.expandTo(
      this.listOffset_ + this.nodes_.length - 1 + (TARGET_BUFFER - MIN_BUFFER)
        - this.tailBelowOffset_);
  } else if (below < 0) {
    // Close the current tail.
    if (this.tailBelow_) {
      this.tailBelow_.close();
      this.tailBelow_ = null;
    }

    var num = MAX_BUFFER - TARGET_BUFFER;
    for (var i = 0; i < num; i++) {
      this.container_.removeChild(this.nodes_[this.nodes_.length - i - 1]);
    }
    this.nodes_.splice(this.nodes_.length - num, num);
    this.messages_.splice(this.messages_.length - num, num);
  }

  var above = this.checkAbove_(bounds);
  if (above > 0) {
    if (!this.tailAbove_) {
      this.tailAbove_ = this.model_.newReverseTail(
        this.messages_[0].id,
        this.prependMessages_.bind(this));
      this.tailAboveOffset_ = this.listOffset_;
    }
    this.tailAbove_.expandTo(
      this.tailAboveOffset_ -
        (this.listOffset_ - (TARGET_BUFFER - MIN_BUFFER)));
  } else if (above < 0) {
    // Close the current tail.
    if (this.tailAbove_) {
      this.tailAbove_.close();
      this.tailAbove_ = null;
    }

    var maxRemoved = MAX_BUFFER - TARGET_BUFFER;
    var oldHeight = this.container_.scrollHeight;

    // Limit the nodes removed; if we remove enough that scrollTop has
    // to change, the scroll gets off. Do this in two passes so as not
    // to keep re-triggering layout with
    // getBoundingClientRect. Unfortunately, this isn't the cause of
    // us getting stuck.
    var heightLost = 0;
    var maxHeight = oldHeight - this.container_.scrollTop - bounds.height;
    for (var num = 0; num < maxRemoved; num++) {
      // FIXME: What if there are margins?
      var b = this.nodes_[num].getBoundingClientRect();
      if (heightLost + b.height >= maxHeight)
        break;
      heightLost += b.height;
    }

    for (var i = 0; i < num; i++) {
      this.container_.removeChild(this.nodes_[i]);
    }
    this.container_.scrollTop += (this.container_.scrollHeight - oldHeight);
    this.nodes_.splice(0, num);
    this.messages_.splice(0, num);
    this.listOffset_ += num;
  }
};

$(function() {
  new MessageView(new MockMessageModel(),
                  document.getElementById("messagelist"));
});
<|endoftext|>


<commit_before>function getCacheBuster(bust) {
    return (bust ? "?_t=" + (new Date()).getTime() : "");
}

function isArray(obj) {
    if (Array.isArray) {
        return Array.isArray(obj);
    }
    else {
        return "[object Array]" === Object.prototype.toString.call(obj);
    }
}

function addJsScript(src, id, bust, index, options) {
    var script = document.createElement("script");
    var scripts = document.getElementsByTagName("script");
    var selected = scripts[0];
    var last = false;

    if (!index || 0 > index || index > (scripts.length - 1)) {
        last = true;
    }
    else if (0 < index) {
        selected = scripts[index];
    }

    script.setAttribute("type", "text/javascript");
    script.setAttribute("async", true);
    if (id) {
        script.setAttribute("id", id);
    }
    script.setAttribute("src", src + getCacheBuster(bust));

    if (options) {
        for (var option in options) {
            if (option && options.hasOwnProperty(option)) {
                script.setAttribute(option, options[option]);
            }
        }
    }

    if (!last) {
        scripts[0].parentNode.insertBefore(script, selected);
    }
    else {
        scripts[0].parentNode.appendChild(script);
    }
}

function addCssScript(href, id, bust) {
    var script = document.createElement("link");

    script.setAttribute("rel", "stylesheet");
    script.setAttribute("type", "text/css");
    script.setAttribute("async", true);
    if (id) {
        script.setAttribute("id", id);
    }
    script.setAttribute("href", href + getCacheBuster(bust));

    document.getElementsByTagName("head")[0].appendChild(script);
}

function addScript(type, path, id, bust, index, options) {
    var resolver = {
          css: addCssScript
        , js: addJsScript
    };

    resolver[type.toLowerCase()] && resolver[type.toLowerCase()](path, id, bust, index, options);
}

function getCurrentProtocol() {
    return "file:" === document.location.protocol ? "http:" : ""
}

function addScripts(scripts) {
    var i = 0;
    if (!isArray(scripts)) {
        scripts = [ scripts ];
    }

    for (; i < scripts.length; i++) {
        var spec = scripts[i].split("->");
        spec[1] = getCurrentProtocol() + spec[1];

        addScript.apply(this, spec);
    }
}

function createRevealularDOM() {
    var wrapper = document.createElement("div");
    wrapper.setAttribute("class", "reveal");
    wrapper.setAttribute("ng-app", "revealular");
    wrapper.setAttribute("ng-controller", "RevealularController");

    // Any section element inside of this container is displayed as a slide
    var slides = document.createElement("div");
    slides.setAttribute("slideshow", "slides");

    wrapper.appendChild(slides);

    document.getElementsByTagName("body")[0].appendChild(wrapper);
}

function loadRevealularResources() {
    var always = [
          "css->//gh.itkoren.com/revealular/reveal.js/css/reveal.min.css"
        , "css->//gh.itkoren.com/revealular/reveal.js/css/theme/default.css->theme"
        , "css->//gh.itkoren.com/revealular/reveal.js/lib/css/zenburn.css"
        , "js->//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js"
    ];

    var print = [
        "css->//gh.itkoren.com/revealular/reveal.js/css/print/pdf.css"
    ];

    var ielt9 = [
        "js->//gh.itkoren.com/revealular/reveal.js/lib/js/html5shiv.js"
    ];

    var reveal = [
          "js->//gh.itkoren.com/revealular/reveal.js/lib/js/head.min.js"
        , "js->//gh.itkoren.com/revealular/reveal.js/js/reveal.min.js"
    ];

    createRevealularDOM();

    addScripts(always);

    // If the query includes 'print-pdf', include the PDF print sheet
    if (window.location.search.match(/print-pdf/gi)) {
        addScripts(print);
    }

    // If IE < 9, include the HTML5 shim
    if (!document.addEventListener) { // IE < 9
        addScripts(ielt9);
    }

    addScripts(reveal);
}

function loadSlideshow(options) {
    var src = getCurrentProtocol() + "//gh.itkoren.com/revealular/js/revealular.js";

    addJsScript(src, "revealvular", true, -1, options);
}

function init() {
    // If the query includes '/', change it to '%2F'
    if (window.location.search.match(/\//g)) {
        window.location.search = window.location.search.replace(/\//g, "%2F");
    }

    loadRevealularResources();
}

init();<commit_msg>rearrange loading flow<commit_after>function getCacheBuster(bust) {
    return (bust ? "?_t=" + (new Date()).getTime() : "");
}

function isArray(obj) {
    if (Array.isArray) {
        return Array.isArray(obj);
    }
    else {
        return "[object Array]" === Object.prototype.toString.call(obj);
    }
}

function addJsScript(src, id, bust, index, options) {
    var script = document.createElement("script");
    var scripts = document.getElementsByTagName("script");
    var selected = scripts[0];
    var last = false;

    if (!index || 0 > index || index > (scripts.length - 1)) {
        last = true;
    }
    else if (0 < index) {
        selected = scripts[index];
    }

    script.setAttribute("type", "text/javascript");
    script.setAttribute("async", true);
    if (id) {
        script.setAttribute("id", id);
    }
    script.setAttribute("src", src + getCacheBuster(bust));

    if (options) {
        for (var option in options) {
            if (option && options.hasOwnProperty(option)) {
                script.setAttribute(option, options[option]);
            }
        }
    }

    if (!last) {
        scripts[0].parentNode.insertBefore(script, selected);
    }
    else {
        scripts[0].parentNode.appendChild(script);
    }
}

function addCssScript(href, id, bust) {
    var script = document.createElement("link");

    script.setAttribute("rel", "stylesheet");
    script.setAttribute("type", "text/css");
    script.setAttribute("async", true);
    if (id) {
        script.setAttribute("id", id);
    }
    script.setAttribute("href", href + getCacheBuster(bust));

    document.getElementsByTagName("head")[0].appendChild(script);
}

function addScript(type, path, id, bust, index, options) {
    var resolver = {
          css: addCssScript
        , js: addJsScript
    };

    resolver[type.toLowerCase()] && resolver[type.toLowerCase()](path, id, bust, index, options);
}

function getCurrentProtocol() {
    return "file:" === document.location.protocol ? "http:" : ""
}

function addScripts(scripts) {
    var i = 0;
    if (!isArray(scripts)) {
        scripts = [ scripts ];
    }

    for (; i < scripts.length; i++) {
        var spec = scripts[i].split("->");
        spec[1] = getCurrentProtocol() + spec[1];

        addScript.apply(this, spec);
    }
}

function createRevealularDOM() {
    var wrapper = document.createElement("div");
    wrapper.setAttribute("class", "reveal");
    wrapper.setAttribute("ng-app", "revealular");
    wrapper.setAttribute("ng-controller", "RevealularController");

    // Any section element inside of this container is displayed as a slide
    var slides = document.createElement("div");
    slides.setAttribute("slideshow", "slides");

    wrapper.appendChild(slides);

    document.getElementsByTagName("body")[0].appendChild(wrapper);
}

function loadRevealularResources() {
    var always = [
          "css->//gh.itkoren.com/revealular/reveal.js/css/reveal.min.css"
        , "css->//gh.itkoren.com/revealular/reveal.js/css/theme/default.css->theme"
        , "css->//gh.itkoren.com/revealular/reveal.js/lib/css/zenburn.css"
        , "js->//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js"
    ];

    var print = [
        "css->//gh.itkoren.com/revealular/reveal.js/css/print/pdf.css"
    ];

    var ielt9 = [
        "js->//gh.itkoren.com/revealular/reveal.js/lib/js/html5shiv.js"
    ];

    addScripts(always);

    // If the query includes 'print-pdf', include the PDF print sheet
    if (window.location.search.match(/print-pdf/gi)) {
        addScripts(print);
    }

    // If IE < 9, include the HTML5 shim
    if (!document.addEventListener) { // IE < 9
        addScripts(ielt9);
    }
}

function loadSlideshow(options) {
    var reveal = [
        "js->//gh.itkoren.com/revealular/reveal.js/lib/js/head.min.js"
        , "js->//gh.itkoren.com/revealular/reveal.js/js/reveal.min.js"
    ];
    var src = getCurrentProtocol() + "//gh.itkoren.com/revealular/js/revealular.js";

    addScripts(reveal);
    createRevealularDOM();
    addJsScript(src, "revealvular", true, -1, options);
}

function init() {
    // If the query includes '/', change it to '%2F'
    if (window.location.search.match(/\//g)) {
        window.location.search = window.location.search.replace(/\//g, "%2F");
    }

    loadRevealularResources();
}

init();<|endoftext|>


<commit_before>/**
 * Returns a new input guard.
 *
 * An input guard guards an input node that is associated with a set of other input nodes.
 * If all of the associated input nodes are empty, i. e. there's no
 * input in them, the guarded input is disabled. Otherwise it is enabled.
 *
 * @param  {Function} $			The jQuery function object
 * @param  {jQuery}   btn_node 		The input node
 * @param  {jQUery}   assoc_nodes	The associated input node set
 */
function get_input_guard($, btn_node, assoc_nodes) {
	/**
	 * Input helper.
	 *
	 * Calls given callbacks when an input is
	 * edited or cleared.
	 *
	 * @param {Function} cb_is_empty 	 Called with the input nodes. Returns true if nodes are empty.
	 * @param {string}   event_name 	 Name of the event to listen for
	 */
	function _Input_helper(cb_is_empty, event_name) {
		this._nodes = {
			/**
			 * The input node set that is being listened to
			 * @type {jQuery}
			 */
			inputs : null
		};

		this._event_name = event_name;

		/**
		 * Begin to listen
		 * @param  {jQuery} nodes 		 An input node set
	 	 * @param  {Function} cb_cleared Called when input is cleared
	 	 * @param  {Function} cb_edited  Called when input is edited (but not cleared)
		 */
		this.init = function(nodes, cb_cleared, cb_edited) {
			var self = this;
			self._nodes.inputs = nodes;
			self._nodes.inputs.on(self._event_name, function() {
				if (self._is_empty()) {
					cb_cleared();
				} else {
					cb_edited();
				}
			});
		};

		/**
		 * Returns true if the input is empty.
		 *
		 * @return {Boolean}
		 */
		this._is_empty = function() {
			return cb_is_empty(this._nodes.inputs);
		};
	};

	var text_input = new _Input_helper(function(nodes) {
		// _is_empty()

		return nodes.filter(function() {
			return $(this).val().length > 0;
		}).length === 0;

	}, "keyup");

	var checkbox_input = new _Input_helper(function(nodes) {
		// _is_empty()

		return !nodes.is(":checked");
	}, "click");

	return {
		_nodes : {
			btn : btn_node
		},

		_input : null,

		/**
		 * Begin to listen
		 */
		init : function() {
			var self = this;
			var node_type = assoc_nodes.attr("type");

			if ($.inArray(node_type, ["text", "number", "checkbox"]) !== -1) {
				if ($.inArray(node_type, ["text", "number"]) !== -1) {
					self._input = text_input;
				} else {
					self._input = checkbox_input;
				}
			} else {
				throw new Error("Input type '" + node_type + "' not supported.");
			}

			self._input.init(assoc_nodes, function() {
				self._disable_btn();
			}, function() {
				self._enable_btn();
			});
		},

		_disable_btn : function() {
			this._nodes.btn.attr("disabled", true);
		},

		_enable_btn : function() {
			this._nodes.btn.attr("disabled", false);
		}
	};
};
<commit_msg>Semantics and docs<commit_after>/**
 * Returns a new input guard.
 *
 * An input guard guards a set of input nodes that is associated with another set of other input nodes.
 * The associated input nodes are being listened to after init() is called on the guard.
 * If all of the associated input nodes are made empty, i. e. there's no
 * input in them after an input event, the guarded input set is disabled. If there is input
 * after the event, the guarded set is enabled.
 *
 * @param  {Function} $			The jQuery function object
 * @param  {jQuery}   guard_nodes 	The guarded input nodes
 * @param  {jQUery}   assoc_nodes	The associated input node set
 */
function get_input_guard($, guard_nodes, assoc_nodes) {
	/**
	 * Input helper.
	 *
	 * Calls given callbacks when an input is
	 * edited or cleared.
	 *
	 * @param {Function} cb_is_empty 	 Called with the input nodes. Returns true if nodes are empty.
	 * @param {string}   event_name 	 Name of the event to listen for
	 */
	function _Input_helper(cb_is_empty, event_name) {
		this._nodes = {
			/**
			 * The input node set that is being listened to
			 * @type {jQuery}
			 */
			inputs : null
		};

		this._event_name = event_name;

		/**
		 * Begin to listen
		 * @param  {jQuery} nodes 		 An input node set
	 	 * @param  {Function} cb_cleared Called when input is cleared
	 	 * @param  {Function} cb_edited  Called when input is edited (but not cleared)
		 */
		this.init = function(nodes, cb_cleared, cb_edited) {
			var self = this;
			self._nodes.inputs = nodes;
			self._nodes.inputs.on(self._event_name, function() {
				if (self._is_empty()) {
					cb_cleared();
				} else {
					cb_edited();
				}
			});
		};

		/**
		 * Returns true if the input is empty.
		 *
		 * @return {Boolean}
		 */
		this._is_empty = function() {
			return cb_is_empty(this._nodes.inputs);
		};
	};

	var text_input = new _Input_helper(function(nodes) {
		return nodes.filter(function() {
			return $(this).val().length > 0;
		}).length === 0;

	}, "keyup");

	var checkbox_input = new _Input_helper(function(nodes) {
		return !nodes.is(":checked");
	}, "click");

	return {
		_nodes : {
			nodes : guard_nodes
		},

		_assoc_inputs : null,

		/**
		 * Begin to listen
		 */
		init : function() {
			var self = this;
			var node_type = assoc_nodes.attr("type");

			if ($.inArray(node_type, ["text", "number", "checkbox"]) !== -1) {
				if ($.inArray(node_type, ["text", "number"]) !== -1) {
					self._assoc_inputs = text_input;
				} else {
					self._assoc_inputs = checkbox_input;
				}
			} else {
				throw new Error("Input type '" + node_type + "' not supported.");
			}

			self._assoc_inputs.init(assoc_nodes, function() {
				self._disable_nodes();
			}, function() {
				self._enable_nodes();
			});
		},

		_disable_nodes : function() {
			this._nodes.nodes.attr("disabled", true);
		},

		_enable_nodes : function() {
			this._nodes.nodes.attr("disabled", false);
		}
	};
};
<|endoftext|>


<commit_before>var TelegramBot = require('node-telegram-bot-api');
var getCurrency = require('./getCurrency');

// get from Heroku config variable
var token = process.env.token;
var channelID = process.env.channelID;
var port = process.env.PORT || 8443;
var host = process.env.HOST;

var webhook = {
	webHook: {
		port: port,
		host: host
	}
}

var bot = new TelegramBot(token, webhook);
var runCurrency = getCurrency(function(err, currency){
	return currency;
});

console.log('app start!');

// keep your server awake. run every 5 minutes.
setInterval(function(){
    console.log('Keep server awake.', runCurrency());
}, 5 * 60 * 1000);

// bot send message every hours.
setInterval(function(){
	bot.sendMessage(channelID, runCurrency());
}, 3600 * 1000);<commit_msg>fix type error<commit_after>var TelegramBot = require('node-telegram-bot-api');
var getCurrency = require('./getCurrency');

// get from Heroku config variable
var token = process.env.token;
var channelID = process.env.channelID;
var port = process.env.PORT || 8443;
var host = process.env.HOST;

var webhook = {
	webHook: {
		port: port,
		host: host
	}
}

var bot = new TelegramBot(token, webhook);
getCurrency(function(err, currency){
	console.log(currency);
});

console.log('app start!');

// keep your server awake. run every 5 minutes.
setInterval(function(){
	getCurrency(function(err, currency){
		console.log('Keep server awake.', currency);
	});
}, 5 * 60 * 1000);

// bot send message every hours.
setInterval(function(){
	getCurrency(function(err, currency){
		console.log('Keep server awake.', );
		bot.sendMessage(channelID, currency);
	});
}, 3600 * 1000);<|endoftext|>