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

var markersGroup = new L.LayerGroup();
var isDispatching = 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 beginDispatching = function () {

    isDispatching = true;
    console.log('Begin dispatching.');

    $('#start_dispatch_button').hide();

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

}

var endDispatching = function () {

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

    markersGroup.clearLayers();

    $('#start_dispatch_button').show();

}

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 btn-success"'
        + ' 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);
    $('#curr-lng').html(currentLocation.lng);

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

    // events
    marker.on('dragend', function(e) {

        // update current location
        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
    form['details'] = $('#comment').val();
    form['priority'] = $('input:radio[name=priority]:checked').val();

    // checks
    if (form["priority"] === undefined) {
        bsalert("Please click one of priorities");
        return;
    }
    if (numberOfDispatchingAmbulances == 0) {
        bsalert("Please dispatch at least one ambulance");
        return;
    }

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

    // ambulances
    var ambulances = [];
    for (var id in dispatchingAmbulances)
        if (dispatchingAmbulances.hasOwnProperty(id))
            ambulances.push(id);
    form['ambulances'] = 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'] = patient[1];
            patients.push(obj);
        }
    form['patients'] = patients;

    // make json call
    let postJsonUrl = 'api/call/';
    console.log("Will post '" + JSON.stringify(form) + "'");

    var CSRFToken = getCookie('csrftoken');

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

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

            var successMsg = 'Success
' +
                +'Ambulance: ' + data['ambulance']
                + ' dispatched to
' + data['residential_unit']
                + ', ' + data['stmain_number'] + ', ' + data['latitude'] + ', ' + data['longitude'];

            // Show modal
            bsalert(sucessMsg, 'alert-success, 'Success');
            // $('.modal-title').html('Success');
            // $('.modal-body').html(successMsg).addClass('alert-success');
            // $("#dispatchModal").modal('show');

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

            // Show modal
            // $('.modal-title').html('Failure');
            // $('.modal-body').html(textStatus + ": " + errorThrown).addClass('alert-danger');
            // $("#dispatchModal").modal('show');
            bsalert(textStatus + ": " + errorThrown);

            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 new-patient" ' +
                       ' type="button" ' +
                       ' id="patient-' + index + '-button">' +
                    '' +
                '</button>' +
            '
' +
        '
';

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

    return html;

}

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

/*
 * getCookie extracts the csrf token for form submit
 */
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = $.trim(cookies[i]);
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
<commit_msg>Bootstrap 4.0<commit_after>/* Dispatch area - Should be eliminate after dispatching */

var markersGroup = new L.LayerGroup();
var isDispatching = 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 beginDispatching = function () {

    isDispatching = true;
    console.log('Begin dispatching.');

    $('#start_dispatch_button').hide();

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

}

var endDispatching = function () {

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

    markersGroup.clearLayers();

    $('#start_dispatch_button').show();

}

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 btn-success"'
        + ' 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);
    $('#curr-lng').html(currentLocation.lng);

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

    // events
    marker.on('dragend', function(e) {

        // update current location
        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
    form['details'] = $('#comment').val();
    form['priority'] = $('input:radio[name=priority]:checked').val();

    // checks
    if (form["priority"] === undefined) {
        bsalert("Please click one of priorities");
        return;
    }
    if (numberOfDispatchingAmbulances == 0) {
        bsalert("Please dispatch at least one ambulance");
        return;
    }

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

    // ambulances
    var ambulances = [];
    for (var id in dispatchingAmbulances)
        if (dispatchingAmbulances.hasOwnProperty(id))
            ambulances.push(id);
    form['ambulances'] = 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'] = patient[1];
            patients.push(obj);
        }
    form['patients'] = patients;

    // make json call
    let postJsonUrl = 'api/call/';
    console.log("Will post '" + JSON.stringify(form) + "'");

    var CSRFToken = getCookie('csrftoken');

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

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

            var successMsg = 'Success
' +
                +'Ambulance: ' + data['ambulance']
                + ' dispatched to
' + data['residential_unit']
                + ', ' + data['stmain_number'] + ', ' + data['latitude'] + ', ' + data['longitude'];

            // Show modal
            bsalert(sucessMsg, 'alert-success', 'Success');
            // $('.modal-title').html('Success');
            // $('.modal-body').html(successMsg).addClass('alert-success');
            // $("#dispatchModal").modal('show');

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

            // Show modal
            // $('.modal-title').html('Failure');
            // $('.modal-body').html(textStatus + ": " + errorThrown).addClass('alert-danger');
            // $("#dispatchModal").modal('show');
            bsalert(textStatus + ": " + errorThrown);

            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 new-patient" ' +
                       ' type="button" ' +
                       ' id="patient-' + index + '-button">' +
                    '' +
                '</button>' +
            '
' +
        '
';

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

    return html;

}

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

/*
 * getCookie extracts the csrf token for form submit
 */
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = $.trim(cookies[i]);
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
<|endoftext|>


<commit_before>/*
* Event
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

/**
 * A collection of Classes that are shared across all the CreateJS libraries.  The classes are included in the minified
 * files of each library and are available on the createsjs namespace directly.
 *
 *
Example

 *      myObject.addEventListener("change", createjs.proxy(myMethod, scope));
 *
 * @module CreateJS
 * @main CreateJS
 */

// namespace:
this.createjs = this.createjs||{};

(function() {
	"use strict";

/**
 * Contains properties and methods shared by all events for use with
 * {{#crossLink "EventDispatcher"}}{{/crossLink}}.
 *
 * Note that Event objects are often reused, so you should never
 * rely on an event object's state outside of the call stack it was received in.
 * @class Event
 * @param {String} type The event type.
 * @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
 * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
 * @constructor
 **/
var Event = function(type, bubbles, cancelable) {
  this.initialize(type, bubbles, cancelable);
};
var p = Event.prototype;

// events:

// public properties:

	/**
	 * The type of event.
	 * @property type
	 * @type String
	 **/
	p.type = null;

	/**
	 * The object that generated an event.
	 * @property target
	 * @type Object
	 * @default null
	 * @readonly
	*/
	p.target = null;

	/**
	 * The current target that a bubbling event is being dispatched from. For non-bubbling events, this will
	 * always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event
	 * is generated from childObj, then a listener on parentObj would receive the event with
	 * target=childObj (the original target) and currentTarget=parentObj (where the listener was added).
	 * @property currentTarget
	 * @type Object
	 * @default null
	 * @readonly
	*/
	p.currentTarget = null;

	/**
	 * For bubbling events, this indicates the current event phase:

	 *
 capture phase: starting from the top parent to the target

	 *
 at target phase: currently being dispatched from the target

	 *
 bubbling phase: from the target to the top parent

	 *

	 * @property eventPhase
	 * @type Number
	 * @default 0
	 * @readonly
	*/
	p.eventPhase = 0;

	/**
	 * Indicates whether the event will bubble through the display list.
	 * @property bubbles
	 * @type Boolean
	 * @default false
	 * @readonly
	*/
	p.bubbles = false;

	/**
	 * Indicates whether the default behaviour of this event can be cancelled via
	 * {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor.
	 * @property cancelable
	 * @type Boolean
	 * @default false
	 * @readonly
	*/
	p.cancelable = false;

	/**
	 * The epoch time at which this event was created.
	 * @property timeStamp
	 * @type Number
	 * @default 0
	 * @readonly
	*/
	p.timeStamp = 0;

	/**
	 * Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called
	 * on this event.
	 * @property defaultPrevented
	 * @type Boolean
	 * @default false
	 * @readonly
	*/
	p.defaultPrevented = false;

	/**
	 * Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or
	 * {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event.
	 * @property propagationStopped
	 * @type Boolean
	 * @default false
	 * @readonly
	*/
	p.propagationStopped = false;

	/**
	 * Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called
	 * on this event.
	 * @property immediatePropagationStopped
	 * @type Boolean
	 * @default false
	 * @readonly
	*/
	p.immediatePropagationStopped = false;

	/**
	 * Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event.
	 * @property removed
	 * @type Boolean
	 * @default false
	 * @readonly
	*/
	p.removed = false;

// constructor:
	/**
	 * Initialization method.
	 * @method initialize
	 * @param {String} type The event type.
	 * @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
	 * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
	 * @protected
	 **/
	p.initialize = function(type, bubbles, cancelable) {
		this.type = type;
		this.bubbles = bubbles;
		this.cancelable = cancelable;
		this.timeStamp = (new Date()).getTime();
	};

// public methods:

	/**
	 * Sets {{#crossLink "Event/defaultPrevented"}}{{/crossLink}} to true.
	 * Mirrors the DOM event standard.
	 * @method preventDefault
	 **/
	p.preventDefault = function() {
		this.defaultPrevented = true;
	};

	/**
	 * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true.
	 * Mirrors the DOM event standard.
	 * @method stopPropagation
	 **/
	p.stopPropagation = function() {
		this.propagationStopped = true;
	};

	/**
	 * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and
	 * {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true.
	 * Mirrors the DOM event standard.
	 * @method stopImmediatePropagation
	 **/
	p.stopImmediatePropagation = function() {
		this.immediatePropagationStopped = this.propagationStopped = true;
	};

	/**
	 * Causes the active listener to be removed via removeEventListener();
	 *
	 * 		myBtn.addEventListener("click", function(evt) {
	 * 			// do stuff...
	 * 			evt.remove(); // removes this listener.
	 * 		});
	 *
	 * @method remove
	 **/
	p.remove = function() {
		this.removed = true;
	};

	/**
	 * Returns a clone of the Event instance.
	 * @method clone
	 * @return {Event} a clone of the Event instance.
	 **/
	p.clone = function() {
		return new Event(this.type, this.bubbles, this.cancelable);
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Event (type="+this.type+")]";
	};

createjs.Event = Event;
}());
<commit_msg>Added .set() method to Event.<commit_after>/*
* Event
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

/**
 * A collection of Classes that are shared across all the CreateJS libraries.  The classes are included in the minified
 * files of each library and are available on the createsjs namespace directly.
 *
 *
Example

 *      myObject.addEventListener("change", createjs.proxy(myMethod, scope));
 *
 * @module CreateJS
 * @main CreateJS
 */

// namespace:
this.createjs = this.createjs||{};

(function() {
	"use strict";

/**
 * Contains properties and methods shared by all events for use with
 * {{#crossLink "EventDispatcher"}}{{/crossLink}}.
 *
 * Note that Event objects are often reused, so you should never
 * rely on an event object's state outside of the call stack it was received in.
 * @class Event
 * @param {String} type The event type.
 * @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
 * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
 * @constructor
 **/
var Event = function(type, bubbles, cancelable) {
  this.initialize(type, bubbles, cancelable);
};
var p = Event.prototype;

// events:

// public properties:

	/**
	 * The type of event.
	 * @property type
	 * @type String
	 **/
	p.type = null;

	/**
	 * The object that generated an event.
	 * @property target
	 * @type Object
	 * @default null
	 * @readonly
	*/
	p.target = null;

	/**
	 * The current target that a bubbling event is being dispatched from. For non-bubbling events, this will
	 * always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event
	 * is generated from childObj, then a listener on parentObj would receive the event with
	 * target=childObj (the original target) and currentTarget=parentObj (where the listener was added).
	 * @property currentTarget
	 * @type Object
	 * @default null
	 * @readonly
	*/
	p.currentTarget = null;

	/**
	 * For bubbling events, this indicates the current event phase:

	 *
 capture phase: starting from the top parent to the target

	 *
 at target phase: currently being dispatched from the target

	 *
 bubbling phase: from the target to the top parent

	 *

	 * @property eventPhase
	 * @type Number
	 * @default 0
	 * @readonly
	*/
	p.eventPhase = 0;

	/**
	 * Indicates whether the event will bubble through the display list.
	 * @property bubbles
	 * @type Boolean
	 * @default false
	 * @readonly
	*/
	p.bubbles = false;

	/**
	 * Indicates whether the default behaviour of this event can be cancelled via
	 * {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor.
	 * @property cancelable
	 * @type Boolean
	 * @default false
	 * @readonly
	*/
	p.cancelable = false;

	/**
	 * The epoch time at which this event was created.
	 * @property timeStamp
	 * @type Number
	 * @default 0
	 * @readonly
	*/
	p.timeStamp = 0;

	/**
	 * Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called
	 * on this event.
	 * @property defaultPrevented
	 * @type Boolean
	 * @default false
	 * @readonly
	*/
	p.defaultPrevented = false;

	/**
	 * Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or
	 * {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event.
	 * @property propagationStopped
	 * @type Boolean
	 * @default false
	 * @readonly
	*/
	p.propagationStopped = false;

	/**
	 * Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called
	 * on this event.
	 * @property immediatePropagationStopped
	 * @type Boolean
	 * @default false
	 * @readonly
	*/
	p.immediatePropagationStopped = false;

	/**
	 * Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event.
	 * @property removed
	 * @type Boolean
	 * @default false
	 * @readonly
	*/
	p.removed = false;

// constructor:
	/**
	 * Initialization method.
	 * @method initialize
	 * @param {String} type The event type.
	 * @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
	 * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
	 * @protected
	 **/
	p.initialize = function(type, bubbles, cancelable) {
		this.type = type;
		this.bubbles = bubbles;
		this.cancelable = cancelable;
		this.timeStamp = (new Date()).getTime();
	};

// public methods:

	/**
	 * Sets {{#crossLink "Event/defaultPrevented"}}{{/crossLink}} to true.
	 * Mirrors the DOM event standard.
	 * @method preventDefault
	 **/
	p.preventDefault = function() {
		this.defaultPrevented = true;
	};

	/**
	 * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true.
	 * Mirrors the DOM event standard.
	 * @method stopPropagation
	 **/
	p.stopPropagation = function() {
		this.propagationStopped = true;
	};

	/**
	 * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and
	 * {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true.
	 * Mirrors the DOM event standard.
	 * @method stopImmediatePropagation
	 **/
	p.stopImmediatePropagation = function() {
		this.immediatePropagationStopped = this.propagationStopped = true;
	};

	/**
	 * Causes the active listener to be removed via removeEventListener();
	 *
	 * 		myBtn.addEventListener("click", function(evt) {
	 * 			// do stuff...
	 * 			evt.remove(); // removes this listener.
	 * 		});
	 *
	 * @method remove
	 **/
	p.remove = function() {
		this.removed = true;
	};

	/**
	 * Returns a clone of the Event instance.
	 * @method clone
	 * @return {Event} a clone of the Event instance.
	 **/
	p.clone = function() {
		return new Event(this.type, this.bubbles, this.cancelable);
	};

	/**
	 * Provides a chainable shortcut method for setting a number of properties on the instance.
	 *
	 * @method set
	 * @param {Object} props A generic object containing properties to copy to the instance.
	 * @return {Event} Returns the instance the method is called on (useful for chaining calls.)
	*/
	p.set = function(props) {
		for (var n in props) { this[n] = props[n]; }
		return this;
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Event (type="+this.type+")]";
	};

createjs.Event = Event;
}());
<|endoftext|>


<commit_before>const fs = require('fs')
const path = require('path')
const read = require('fs-readdir-recursive')
const postcss = require('postcss')
const modules = require('postcss-modules')
const Promise = require('bluebird')

const discardEmpty = require('./postcss/discard-empty')
const discardDuplicates = require('./postcss/discard-duplicates')
const discardComments = require('./postcss/discard-comments')

let styles
let map

const filter = (file) => file.endsWith('css')

const getJSON = (source) => (cssFileName, json) => map[cssFileName] = json

const store = (result) => styles.push(result.css)

const process = (source, plugins) => (file) => {
  const fromPath = path.join(source, file)
  const css = fs.readFileSync(fromPath)

  return postcss(...plugins, modules({ getJSON: getJSON(source) }))
    .process(css, { from: fromPath })
    .then(store)
}

const result = () => ({ styles: optimize(styles.join('')), map })

const extract = (source, ...plugins) => {
  styles = []
  map = {}

  const files = read(source).filter(filter)
  const processes = files
    .map(process(source, plugins))

  return Promise.all(processes).then(result)
}

const optimize = (css) => {
  const plugins = [discardComments(), discardEmpty()]
  return postcss(...plugins)
    .process(css)
}

module.exports = { extract, optimize }
<commit_msg>fix(css-extractor): Restored css dedupe plugin after css-module compilation<commit_after>const fs = require('fs')
const path = require('path')
const read = require('fs-readdir-recursive')
const postcss = require('postcss')
const modules = require('postcss-modules')
const Promise = require('bluebird')

const discardEmpty = require('./postcss/discard-empty')
const discardDuplicates = require('./postcss/discard-duplicates')
const discardComments = require('./postcss/discard-comments')

let styles
let map

const filter = (file) => file.endsWith('css')

const getJSON = (source) => (cssFileName, json) => map[cssFileName] = json

const store = (result) => styles.push(result.css)

const process = (source, plugins) => (file) => {
  const fromPath = path.join(source, file)
  const css = fs.readFileSync(fromPath)

  return postcss(...plugins, modules({ getJSON: getJSON(source) }))
    .process(css, { from: fromPath })
    .then(store)
}

const result = () => ({ styles: optimize(styles.join('')), map })

const extract = (source, ...plugins) => {
  styles = []
  map = {}

  const files = read(source).filter(filter)
  const processes = files
    .map(process(source, plugins))

  return Promise.all(processes).then(result)
}

const optimize = (css) => {
  const plugins = [discardComments(), discardDuplicates(), discardEmpty()]
  return postcss(...plugins)
    .process(css)
}

module.exports = { extract, optimize }
<|endoftext|>


<commit_before>#!/usr/bin/env node
(function () {
    'use strict';

    var childProcess = require('child_process'),
        inquirer = require('inquirer'),
        configParser = require('../bin/config-parser'),
        messageHandler = require('../bin/message-handler');

    require('colors');

    init();
    //SHOW TAG LIST FOR SELECTION
    function init() {
        //Exec git branch to check if exist .git files
        var checkRepoExistenceCommand = childProcess.exec('git branch');

        checkRepoExistenceCommand.stderr.on('data', function (data) {
            messageHandler.showError(data);
        });

        checkRepoExistenceCommand.on('close', function (code) {
            if (code === 0) { //0 means ok, 128 means error
                inquirer
                    .prompt([
                        {
                            type: 'list',
                            name: 'tag',
                            message: 'Select tag for your turbo-commit',
                            choices: configParser.getTagsFormat()
                        }])
                    .then(function (answers) {
                        var tagTitle = answers.tag;

                        showCommitPrompt(tagTitle);
                    });
            }
        });
    }

    function showCommitPrompt(tagTitle) {
        var commitText, parseDesc;

        function onTitlePrompt() {
            onAskFor('title').then(function (commitTitle) {
                if (!commitTitle) {
                    messageHandler.showError('Title is mandatory');
                    return onTitlePrompt();
                }
                //parse mandatory title for commit
                commitText = tagTitle + ' ' + commitTitle;
                //parse optional description fot the commit
                onAskFor('desc').then(function (commitDesc) {
                    if (commitDesc) {
                        parseDesc = '\n\n';
                        parseDesc += new Array(tagTitle.length).join(' ');
                        parseDesc += ' - ' + commitDesc;
                        commitText += parseDesc;
                    }
                    execCommit(commitText);
                });
            });
        }

        onTitlePrompt();
    }

    function onAskFor(field) {
        var titleText = 'Type the commit title:',
            descText = 'Commit description (leave it empty for omit):';

        return inquirer
            .prompt([
                {
                    type: 'input',
                    name: field,
                    message: field === 'desc' ?  descText : titleText
                }])
            .then(function (answers) {
                return answers[field];
            });
    }

    function execCommit(commitMessage) {
        childProcess.exec('git commit -m "' + commitMessage + '"', function (error) {
            if (error) {
                messageHandler.showError('Nothing to commit, you need to do a `git add` before commit');
            }
        });
    }
})();

<commit_msg>[MOD] turbo-commit: replace messageHandler with utils<commit_after>#!/usr/bin/env node
(function () {
    'use strict';

    var childProcess = require('child_process'),
        inquirer = require('inquirer'),
        configParser = require('../bin/config-parser'),
        messageHandler = require('../bin/message-handler');

    require('colors');

    init();
    //SHOW TAG LIST FOR SELECTION
    function init() {
        //Exec git branch to check if exist .git files
        var checkRepoExistenceCommand = childProcess.exec('git branch');

        checkRepoExistenceCommand.stderr.on('data', function (data) {
            messageHandler.showError(data);
        });

        checkRepoExistenceCommand.on('close', function (code) {
            if (code === 0) { //0 means ok, 128 means error
                inquirer
                    .prompt([
                        {
                            type: 'list',
                            name: 'tag',
                            message: 'Select tag for your turbo-commit',
                            choices: configParser.getTagsFormat()
                        }])
                    .then(function (answers) {
                        var tagTitle = answers.tag;

                        showCommitPrompt(tagTitle);
                    });
            }
        });
    }

    function showCommitPrompt(tagTitle) {
        var commitText, parseDesc;

        function onTitlePrompt() {
            onAskFor('title').then(function (commitTitle) {
                if (!commitTitle) {
                    utils.showError('Title is mandatory');
                    return onTitlePrompt();
                }
                //parse mandatory title for commit
                commitText = tagTitle + ' ' + commitTitle;
                //parse optional description fot the commit
                onAskFor('desc').then(function (commitDesc) {
                    if (commitDesc) {
                        parseDesc = '\n\n';
                        parseDesc += new Array(tagTitle.length).join(' ');
                        parseDesc += ' - ' + commitDesc;
                        commitText += parseDesc;
                    }
                    execCommit(commitText);
                });
            });
        }

        onTitlePrompt();
    }

    function onAskFor(field) {
        var titleText = 'Type the commit title:',
            descText = 'Commit description (leave it empty for omit):';

        return inquirer
            .prompt([
                {
                    type: 'input',
                    name: field,
                    message: field === 'desc' ?  descText : titleText
                }])
            .then(function (answers) {
                return answers[field];
            });
    }

    function execCommit(commitMessage) {
        childProcess.exec('git commit -m "' + commitMessage + '"', function (error) {
            if (error) {
                utils.showError('Nothing to commit, you need to do a `git add` before commit');
            }
        });
    }
})();

<|endoftext|>


<commit_before>'use strict'

var include = module.exports = function include (arr, thing) {
  if (thing instanceof Array) {
    include.list(arr, thing)
  } else {
    include.item(arr, thing)
  }
}

include.list = function (arr, list) {
  var result
  for (var i = list.length - 1; i >= 0; i--) {
    var included = include.item(arr, list[i])
    if (!result) result = included
  }
  return result
}

include.item = function (arr, item) {
  return !include.isIncluded(arr, item) && arr.push(including)
}

include.isIncluded = function includes ( arr, item ) {
  for (var j = arr.length - 1; j >= 0; j--) {
    if (arr[j] === item) {
      return true
    }
  }
}
<commit_msg>removed broken funciton in include<commit_after>'use strict'

var include = module.exports = function include (arr, thing) {
  if (thing instanceof Array) {
    include.list(arr, thing)
  } else {
    include.item(arr, thing)
  }
}

include.list = function (arr, list) {
  var result
  for (var i = list.length - 1; i >= 0; i--) {
    var included = include.item(arr, list[i])
    if (!result) result = included
  }
  return result
}

include.isIncluded = function includes (arr, item) {
  for (var j = arr.length - 1; j >= 0; j--) {
    if (arr[j] === item) {
      return true
    }
  }
}
<|endoftext|>


<commit_before>const chalk            = require('chalk');
const ECError          = require('./corber-error');

// supported log level must be sorted by verbosity
const SUPPORTED_LOG_LEVELS = ['verbose', 'info', 'success', 'warn', 'error'];

module.exports = {
  logLevel: 'info',

  info(message) {
    if (!this.shouldLog('info')) {
      return;
    }
    console.log(message);
  },

  success(message) {
    if (!this.shouldLog('success')) {
      return;
    }
    console.log(chalk.green(message));
  },

  warn(content) {
    if (!this.shouldLog('warn')) {
      return;
    }
    let message = 'WARNING: corber \n';
    message += content;
    console.warn(chalk.yellow(message));
  },

  error(content) {
    let error = new ECError(content);
    console.error(chalk.red(error));
    return error;
  },

  getLogLevel() {
    return this.logLevel;
  },

  setLogLevel(logLevel) {
    if (SUPPORTED_LOG_LEVELS.indexOf(logLevel) === -1) {
      this.error(
        'Log level must be one of `info`, `success`, `warn`, `error`. ' .
        logLevel + ' given.');
    }
    this.logLevel = logLevel;
  },

  shouldLog(logLevel) {
    let currentLogLevel = this.getLogLevel();
    let logLevels = SUPPORTED_LOG_LEVELS;
    return logLevels.indexOf(currentLogLevel) <= logLevels.indexOf(logLevel);
  }
};
<commit_msg>feat(logger): add verbose logging method<commit_after>const chalk            = require('chalk');
const ECError          = require('./corber-error');

// supported log level must be sorted by verbosity
const SUPPORTED_LOG_LEVELS = ['verbose', 'info', 'success', 'warn', 'error'];

module.exports = {
  logLevel: 'info',

  verbose(message) {
    if (!this.shouldLog('verbose')) {
      return;
    }
    console.debug(message);
  },

  info(message) {
    if (!this.shouldLog('info')) {
      return;
    }
    console.log(message);
  },

  success(message) {
    if (!this.shouldLog('success')) {
      return;
    }
    console.log(chalk.green(message));
  },

  warn(content) {
    if (!this.shouldLog('warn')) {
      return;
    }
    let message = 'WARNING: corber \n';
    message += content;
    console.warn(chalk.yellow(message));
  },

  error(content) {
    let error = new ECError(content);
    console.error(chalk.red(error));
    return error;
  },

  getLogLevel() {
    return this.logLevel;
  },

  setLogLevel(logLevel) {
    if (SUPPORTED_LOG_LEVELS.indexOf(logLevel) === -1) {
      this.error(
        'Log level must be one of `info`, `success`, `warn`, `error`. ' .
        logLevel + ' given.');
    }
    this.logLevel = logLevel;
  },

  shouldLog(logLevel) {
    let currentLogLevel = this.getLogLevel();
    let logLevels = SUPPORTED_LOG_LEVELS;
    return logLevels.indexOf(currentLogLevel) <= logLevels.indexOf(logLevel);
  }
};
<|endoftext|>


<commit_before>var stringify = require('json-stable-stringify');

VlocityUtils = {
    showLoggingStatements: true,
    fullLog: [],
    namespace: '',
    verboseLogging: false,
    performance: false,
    startTime: Date.now(),
    silence: false,
    quiet: false,
    simpleLogging: false,
    noErrorLogging: false
};

VlocityUtils.log = function() {
    VlocityUtils.output(console.log, '\x1b[0m', arguments);
}

VlocityUtils.report = function() {
    VlocityUtils.output(console.log, '\x1b[36m', arguments);
}

VlocityUtils.success = function() {
    VlocityUtils.output(console.log, '\x1b[32m', arguments);
}

VlocityUtils.warn = function() {
    VlocityUtils.output(console.log, '\x1b[33m', arguments);
}

VlocityUtils.error = function() {
    VlocityUtils.output(console.error, '\x1b[31m', arguments);
}

VlocityUtils.fatal = function() {
    VlocityUtils.output(console.error, '\x1b[31m', arguments);

    throw arguments;
}

VlocityUtils.verbose = function() {
    if (VlocityUtils.verboseLogging) {
        VlocityUtils.output(console.log, '\x1b[0m', arguments);
    }
}

VlocityUtils.getTime = function() {
    var elapsedTime = (Date.now() - VlocityUtils.startTime) / 1000;

    return Math.floor((elapsedTime / 60)) + 'm ' + Math.floor((elapsedTime % 60)) + 's';
}

VlocityUtils.output = function(loggingMethod, color, messageArray) {
    if (VlocityUtils.quiet || (VlocityUtils.silence && !VlocityUtils.verboseLogging)) return;

    if (!messageArray) {
        return;
    }

    if (VlocityUtils.noErrorLogging) {
        loggingMethod = console.log;
    }

    var neutralColor = '\x1b[0m';

    if (VlocityUtils.simpleLogging) {
        color = '';
        neutralColor = '';
    }

    var logSeperator = '>> ' + neutralColor;

    var newMessage;

    for (var i = 0; i < messageArray.length; i++) {

        if (i == 0) {
            newMessage = color + ' ';
        }

        if (i == 1) {
            newMessage += logSeperator;
        }

        if (messageArray[i] instanceof Error) {
            newMessage += messageArray[i].stack;
        } else if (typeof messageArray[i] == 'object') {
            newMessage += JSON.stringify(messageArray[i], null, 4);
        } else {
            newMessage += messageArray[i];
        }

        if ((i - 1) != messageArray.length) {
            newMessage += ' ';
        }
    }

    if (VlocityUtils.namespace == '') {
        newMessage = newMessage.replace(/%vlocity_namespace%__/g, '').replace(/%vlocity_namespace%/g, '');
    } else {
        newMessage = newMessage.replace(/%vlocity_namespace%/g, VlocityUtils.namespace);
    }

    if (VlocityUtils.showLoggingStatements) {
        loggingMethod((VlocityUtils.performance ? VlocityUtils.getTime() + ':' : ''), newMessage, neutralColor);
    }

    if (VlocityUtils.verboseLogging) {
        VlocityUtils.fullLog.push(newMessage.replace(color, '').replace(/\x1b\[0m/g, ''));
    }
}<commit_msg>Never write to console.error to avoid issues with Powershell failing when anything is written to standard error.<commit_after>VlocityUtils = {
    showLoggingStatements: true,
    fullLog: [],
    namespace: '',
    verboseLogging: false,
    performance: false,
    startTime: Date.now(),
    silence: false,
    quiet: false,
    simpleLogging: false,
    noErrorLogging: false
};

VlocityUtils.log = function() {
    VlocityUtils.output(console.log, '\x1b[0m', arguments);
}

VlocityUtils.report = function() {
    VlocityUtils.output(console.log, '\x1b[36m', arguments);
}

VlocityUtils.success = function() {
    VlocityUtils.output(console.log, '\x1b[32m', arguments);
}

VlocityUtils.warn = function() {
    VlocityUtils.output(console.log, '\x1b[33m', arguments);
}

VlocityUtils.error = function() {
    VlocityUtils.output(console.warn, '\x1b[31m', arguments);
}

VlocityUtils.fatal = function() {
    VlocityUtils.output(console.warn, '\x1b[31m', arguments);

    throw arguments;
}

VlocityUtils.verbose = function() {
    if (VlocityUtils.verboseLogging) {
        VlocityUtils.output(console.log, '\x1b[0m', arguments);
    }
}

VlocityUtils.getTime = function() {
    var elapsedTime = (Date.now() - VlocityUtils.startTime) / 1000;

    return Math.floor((elapsedTime / 60)) + 'm ' + Math.floor((elapsedTime % 60)) + 's';
}

VlocityUtils.output = function(loggingMethod, color, messageArray) {
    if (VlocityUtils.quiet || (VlocityUtils.silence && !VlocityUtils.verboseLogging)) return;

    if (!messageArray) {
        return;
    }

    if (VlocityUtils.noErrorLogging) {
        loggingMethod = console.log;
    }

    var neutralColor = '\x1b[0m';

    if (VlocityUtils.simpleLogging) {
        color = '';
        neutralColor = '';
    }

    var logSeperator = '>> ' + neutralColor;

    var newMessage;

    for (var i = 0; i < messageArray.length; i++) {

        if (i == 0) {
            newMessage = color + ' ';
        }

        if (i == 1) {
            newMessage += logSeperator;
        }

        if (messageArray[i] instanceof Error) {
            newMessage += messageArray[i].stack;
        } else if (typeof messageArray[i] == 'object') {
            newMessage += JSON.stringify(messageArray[i], null, 4);
        } else {
            newMessage += messageArray[i];
        }

        if ((i - 1) != messageArray.length) {
            newMessage += ' ';
        }
    }

    if (VlocityUtils.namespace == '') {
        newMessage = newMessage.replace(/%vlocity_namespace%__/g, '').replace(/%vlocity_namespace%/g, '');
    } else {
        newMessage = newMessage.replace(/%vlocity_namespace%/g, VlocityUtils.namespace);
    }

    if (VlocityUtils.showLoggingStatements) {
        loggingMethod((VlocityUtils.performance ? VlocityUtils.getTime() + ':' : ''), newMessage, neutralColor);
    }

    if (VlocityUtils.verboseLogging) {
        VlocityUtils.fullLog.push(newMessage.replace(color, '').replace(/\x1b\[0m/g, ''));
    }
}<|endoftext|>


<commit_before>"use strict"

var debug = require('debug')('heyu:store:wechat-reply');
var wechat = require('co-wechat');

exports = module.exports = function(token, domain) {

    return wechat(token).middleware(function*() {
        console.log(2123123);
        var message = this.weixin;
        console.log(message);
        if (message.MsgType === 'event') {
            console.log(message);
            if (message.Event.toLowerCase() === "subscribe") {
                // 用户关注时自动回复
                this.body = {
                    content: '感谢关注禾煜南北货！南北货也可以很新鲜，在这里你可以挑选让你称心如意的优质南北货~[微笑]',
                    type: 'text'
                };
                // return this.status = 200;
            }
            if (message.Event.toLowerCase() === 'scan' || message.Event.toLowerCase() === "subscribe") {

                //用户通过QR Code 扫描关注
                this.body = {
                    content: '欢迎扫码关注！\n点此扫描袋内二维码领取优惠：\n马上扫描领取！',
                    type: 'text'
                };
                // return this.status = 200;

            }

            if(message.Event.toLowerCase() === 'location') {

            }

        } else if (message.MsgType === 'text') {
            /*  假如服务器无法保证在五秒内处理并回复，必须直接回复空串
             （ 是指回复一个空字符串，而不是一个XML结构体中content字段的内容为空，请切勿误解 ) */
            this.body = '欢迎光临禾煜旗舰店';
            // return this.stauts;
        }
    });
};<commit_msg>remove location event reply<commit_after>"use strict"

var debug = require('debug')('heyu:store:wechat-reply');
var wechat = require('co-wechat');

exports = module.exports = function(token, domain) {

    return wechat(token).middleware(function*() {
        console.log(2123123);
        var message = this.weixin;
        console.log(message);
        if (message.MsgType === 'event') {
            console.log(message);
            if (message.Event.toLowerCase() === "subscribe") {
                // 用户关注时自动回复
                this.body = {
                    content: '感谢关注禾煜南北货！南北货也可以很新鲜，在这里你可以挑选让你称心如意的优质南北货~[微笑]',
                    type: 'text'
                };
                // return this.status = 200;
            }
            if (message.Event.toLowerCase() === 'scan' || message.Event.toLowerCase() === "subscribe") {

                //用户通过QR Code 扫描关注
                this.body = {
                    content: '欢迎扫码关注！\n点此扫描袋内二维码领取优惠：\n马上扫描领取！',
                    type: 'text'
                };
                // return this.status = 200;

            }

            if(message.Event.toLowerCase() === 'location') {

            }

        } else if (message.MsgType === 'text') {
            /*  假如服务器无法保证在五秒内处理并回复，必须直接回复空串
             （ 是指回复一个空字符串，而不是一个XML结构体中content字段的内容为空，请切勿误解 ) */
            this.body = '欢迎光临禾煜旗舰店';
            // return this.stauts;
        }
    });
};<|endoftext|>


<commit_before>/* global KeyboardEvent */ // TODO: jshint doesn't know about KeyboardEvent
"use strict";

const DomUtil = require('./dom-util');
const EventEmitter = require('events').EventEmitter;

const _el = Symbol('element');
const _registered = Symbol('registeredEvents');
const _addDOMListener = Symbol('addDOMListener');
const _removeDOMListener = Symbol('removeDOMListener');
const _removeAllDOMListeners = Symbol('removeAllDOMListeners');

// List of modifiers in events
const Modifiers = [ 'alt', 'ctrl', 'shift', 'meta' ];

// DOM event mapping

const EventMap = {
	mouse: {
		'click': 'click',
		'dblclick': 'dblclick',
		'down': 'mousedown',
		'up': 'mouseup',
		'enter': 'mouseenter',
		'leave': 'mouseleave',
		'move': 'mousemove',
		'out': 'mouseout',
		'over': 'mouseover',
		'wheel': 'wheel',
		'contextmenu': 'contextmenu',
	},
	key: {
		'press': 'keypress',
		'down': 'keydown',
		'up': 'keyup',
	},
	form: {
		'blur': 'blur',
		'focus': 'focus',
		'focusin': 'focusin',
		'focusout': 'focusout',
		'input': 'input',
		'search': 'search',
		'select': 'select',
	},
	drag: {
		'drag': 'drag',
		'start': 'dragstart',
		'enter': 'dragenter',
		'over': 'dragover',
		'leave': 'dragleave',
		'end': 'dragend',
		'drop': 'drop'
	},
	view: {
		'scroll': 'scroll'
	},
	media: {
		'abort': 'abort',
		'canplay': 'canplay',
		'canplaythrough': 'canplaythrough',
		'duration': 'durationchange',
		'empty': 'emptied',
		'end': 'ended',
		'error': 'error',
		'load': 'loadeddata',
		'metadata': 'loadedmetadata',
		'loading': 'loadstart',
		'pause': 'pause',
		'play': 'play',
		'playing': 'playing',
		'progress': 'progress',
		'ratechange': 'ratechange',
		'seek': 'seeked',
		'seeking': 'seeking',
		'stalled': 'stalled',
		'suspend': 'suspend',
		'time': 'timeupdate',
		'volume': 'volumechange',
		'waiting': 'waiting'
	},
};

/**
 * Make event listener selector name.
 * @param  {Object}  event   Event selector object
 * @param  {Boolean} capture Capture flag
 * @return {String}          Event selector string
 */
function makeEventSelector(event, capture) {
	let selector = event.type + ':' + event.name;
	if (capture) {
		selector += '-capture';
	}
	if (event.selector) {
		selector += ':' + event.selector;
	}
	return selector;
}

/**
 * Event listener for Widget.
 *
 * Adds a modular consistent event naming scheme for DOM events.
 * Adds and removes event listeners from HTMLElement as needed.
 */
class WidgetEvent extends EventEmitter {

	/**
	 * Create WidgetEvent wrapper for HTMLElement
	 * @param  {HTMLElement} element HTML element
	 */
	constructor(element) {
		super();

		if (!DomUtil.isElement(element)) {
			throw new Error('Element must be instance of HTMLElement');
		}

		this[_el] = element;
		this[_registered] = {};
	}

	/**
	 * Adds the given listener for events named `selector`.
	 * If the selector matches a DOM event name then the listener is added also
	 * to the DOM element.
	 * Only one DOM `addEventListener` call is made for given event selector,
	 * WidgetEvent keeps track of event listener counts internally. And
	 * redirects emitted events for propriate listeners, ie. listeners with key
	 * code selectors.
	 *
	 * Event selector string is constructed from multiple parts which are separated
	 * with `:`, ie. `key:press:ctrl+space`.
	 * First part is for the DOM event type, ie. `key` or `mouse`, full list of
	 * event types can be queried with `WidgetEvent.getEventTypes()`.
	 * Second part is for the DOM event name, ie. `press` or `click`, full list of
	 * available names for given type can be queried with
	 * `WidgetEvent.getEventNames(<type>)`. Third part is an optional event
	 * filtering selector, ie. `ctrl+space` or `Alt+Shift+KeyD`.
	 *
	 * @param  {String}   selector Event selector string
	 * @param  {Function} listener Listener callback function
	 * @param  {Boolean}  capture  Capture flag for DOM `addEventListener`
	 */
	on(selector, listener, capture) {
		this.addListener(selector, listener, capture);
	}

	/**
	 * Alias for `on(selector, listener, capture)`.
	 * @param {String}   selector Event selector string
	 * @param {Function} listener Listener callback function
	 * @param {Boolean}  capture  Capture flag for DOM `addEventListener`
	 */
	addListener(selector, listener, capture) {
		// normalize `capture` to boolean
		capture = !!capture;

		let event = WidgetEvent.parseEventSelector(selector);

		// if selector isn't for DOM event, no magic is needed and listener is added
		// as a normal listener only
		if (event.event) {
			// construct proper normalized event name to add as listener
			selector = makeEventSelector(event, capture);

			// add DOM event listener for the event
			this[_addDOMListener](event, capture);
		}

		// add listener to this EventEmitter
		super.addListener(selector, listener);
	}

	/**
	 * Add DOM listener.
	 *
	 * DOM listener is only added once for the same event. Any subsequent calls
	 * will only increase the registered counter.
	 * DOM listener is bound to emit EventEmitter event using event name mapping.
	 *
	 * @param {Object}  event   Event object from `parseEventSelector`
	 * @param {Boolean} capture Capture flag for DOM `addEventListener`
	 */
	[_addDOMListener](event, capture) {
		let eventName = event.event;
		if (capture) {
			// differentiate listeners with and without capture flag
			eventName += '-capture';
		}
		// create cache for the event emitter, includes a counter so
		// that we know when we should remove the listener.
		this[_registered][eventName] = this[_registered][eventName] || {
			count: 0,
			emitter: null,
			event: event.event,
			capture: capture,
		};
		let registered = this[_registered][eventName];

		// if registered counter is zero, we need to create a emitter and add that
		// listener to the element.
		if (registered.count === 0) {
			// create and cache event emitter
			registered.emitter = this.emit.bind(this, makeEventSelector(event, capture));
			this[_el].addEventListener(event.event, registered.emitter, capture);
		}
		registered.count += 1; // increase registered counter
	}

	/**
	 * Removes the given listener with event named `selector`.
	 * If the selector matches a DOM event name and this is the last listener for
	 * that DOM event, the listener is removed from the DOM.
	 *
	 * If `listener` is not a function, then all listeners matching `selector` are
	 * removed and DOM event listeners with that selector is removed.
	 *
	 * @param  {String}   selector Event selector string
	 * @param  {Function} listener Listener callback function, can be omitted
	 * @param  {Boolean}  capture  Capture flag for DOM `addEventListener`
	 */
	removeListener(selector, listener, capture) {
		if (typeof listener !== 'function') {
			// remove listener only if listener is given
			return;
		}

		// normalize `capture` to boolean
		capture = !!capture;

		let event = WidgetEvent.parseEventSelector(selector);

		// if selector isn't for DOM event, no magic is needed and listener is added
		// as a normal listener only.
		if (event.event) {

			// construct proper normalized event name to add as listener
			selector = makeEventSelector(event, capture);

			// check that the given listener is actually added to this selector
			if (this.listeners(selector).includes(listener)) {
				// remove listener from DOM
				this[_removeDOMListener](event, capture);
			}
		}

		// remove listener from this EventEmitter
		super.removeListener(selector, listener);
	}

	/**
	 * Remove DOM listener.
	 *
	 * DOM listener is only removed if the given listeners is actually added as
	 * listener. DOM listener is only removed if the associated registered counter
	 * goes to zero, this indicates that there is no more listeners listening
	 * the given event selector.
	 *
	 * @param {Object}  event   Event object from `parseEventSelector`
	 * @param {Boolean} capture Capture flag for DOM `addEventListener`
	 */
	[_removeDOMListener](event, capture) {
		let eventName = event.event;
		if (capture) {
			// differentiate listeners with and without capture flag
			eventName += '-capture';
		}

		let registered = this[_registered][eventName];
		// check if DOM event has been registered
		if (registered) {
			// decrement registered counter
			registered.count -= 1;
		}

		// check if there are still listeners registered for this selector
		// if not then we can remove listener form DOM
		if (registered.count <= 0) {
			this[_el].removeEventListener(registered.event, registered.emitter, registered.capture);
			// delete registered object from cache
			delete this[_registered][eventName];
		}
	}

	emit(type, ...args) {
		throw new Error('NOT IMPLEMENTED');
	}

	once(selector, listener, capture) {
		throw new Error('NOT IMPLEMENTED');
	}

	prependListener(selector, listener, capture) {
		throw new Error('NOT IMPLEMENTED');
	}

	prependOnceListener(selector, listener, capture) {
		throw new Error('NOT IMPLEMENTED');
	}

	/**
	 * Removes all listeners, or those of the specified event name.
	 * Remove also all matching DOM event listeners.
	 * @param  {String} selector Event selector string
	 */
	removeAllListeners(selector) {
		if (typeof selector === 'string') {
			// parse event selector object from selector string
			let event = WidgetEvent.parseEventSelector(selector);

			if (event.event) {
				// remove all DOM listeners for the given selector
				this[_removeAllDOMListeners](event);
				// remove both capture and non-capture listeners
				super.removeAllListeners(makeEventSelector(event, false));
				super.removeAllListeners(makeEventSelector(event, true));
			} else {
				// this is a non-DOM event selector
				super.removeAllListeners(selector);
			}
		} else {
			// remove ALL DOM listeners and EventEmitter listeners
			this[_removeAllDOMListeners]();
			super.removeAllListeners();
		}
	}

	[_removeAllDOMListeners](event) {
		let remove = (registered) => {
			if (registered) {
				this[_el].removeEventListener(registered.event, registered.emitter, registered.capture);
			}
		};

		if (event) {
			// remove only one DOM listener, both capture and non-capture variant
			remove(this[_registered][event.event]);
			remove(this[_registered][event.event + '-capture']);
		} else {
			// remove ALL DOM listeners
			for (let eventName in this[_registered]) {
				remove(this[_registered][eventName]);
			}
		}
	}

	/**
	 * Parse selector into individual parts.
	 *
	 * Case of the selector parts and extra white spaces are ignored.
	 *
	 * Result object properties:
	 *  - `which`: key code or mouse button
	 *  - `alt`: `true` if selector has Alt modifier
	 *  - `ctrl`: `true` if selector has Ctrl modifier
	 *  - `shift`: `true` if selector has Shift modifier
	 *  - `meta`: `true` if selector has Meta modifier
	 *
	 * @param  {String} selector Event selector string
	 * @return {Object}          Object with `which` and mods
	 */
	static parseSelector(selector) {
		if (typeof selector !== 'string') {
			throw new Error('Event selector must be string, got: ' + (typeof selector));
		}

		// result object template with default values
		let tmpl = {
			which: undefined,
			alt: false,
			ctrl: false,
			shift: false,
			meta: false,
		};

		let parts = selector.split('+');
		let res = parts.reduce((o, current) => {
			// normalize
			current = current.toLowerCase().trim();

			if (Modifiers.includes(current)) {
				// current part is a modifier
				o[current] = true;
			} else {
				o.which = current;
			}

			return o;
		}, tmpl);

		return res;
	}

	/**
	 * Format selector object to normalized selector string.
	 * This ensures that the selector string has all the parts in a
	 * consistent order.
	 *
	 * @param  {String} selector Selector string
	 * @return {String}          Normalized selector
	 */
	static normalizeSelector(selector) {
		// parse selector string
		let obj = this.parseSelector(selector);

		// construct selector form parsed selector
		// using mods array
		let res = Modifiers.filter(m => obj[m]);
		// add `which` to end of the selector
		res.push(obj.which);
		// resulting selector has all the components joined with `+`
		return res.join('+');
	}

	/**
	 * Parse event selector string.
	 *
	 * Result object properties:
	 *  - `type`: Type of the event, ie. `key` or `mouse`
	 *  - `name`: Name of the event, ie. `press` or `click`
	 *  - `selector`: Event selector string, ie. `ctrl+space`
	 *  - `event`: DOM event name
	 *
	 * @param  {String} event Event selector string
	 * @return {Object}       Event selector object
	 */
	static parseEventSelector(event) {
		let parts = event.split(':');

		// initial event object
		let obj = {
			type: parts[0], // event type, ie. `key` or `mouse`
			name: parts[1], // event name, ie. `press` or `click`
			selector: undefined,
			event: undefined,
		};

		if (parts.length > 2) {
			obj.selector = this.normalizeSelector(parts.slice(2).join(':'));
		}

		if (obj.type in EventMap && obj.name in EventMap[obj.type]) {
			obj.event = EventMap[obj.type][obj.name];
		}

		return obj;
	}

	/**
	 * Get list of available event types
	 * @return {Array} Array of event types
	 */
	static getEventTypes() {
		return Object.keys(EventMap);
	}

	/**
	 * Get list of available event names in type
	 * @param  {String} type Event type
	 * @return {Array}       Array of event names in type
	 */
	static getEventNames(type) {
		return Object.keys(EventMap[type]).map(name => type + ':' + name);
	}

	/**
	 * Get mapped DOM event name
	 * @param  {String} type Event type
	 * @param  {String} name Event name
	 * @return {String}      DOM event name or null if not found
	 */
	static getDOMEventName(type, name) {
		if (type in EventMap && name in EventMap[type]) {
			return EventMap[type][name];
		}
		return null;
	}
}
module.exports = WidgetEvent;
<commit_msg>Remove selector string capture part when parsing<commit_after>/* global KeyboardEvent */ // TODO: jshint doesn't know about KeyboardEvent
"use strict";

const DomUtil = require('./dom-util');
const EventEmitter = require('events').EventEmitter;

const _el = Symbol('element');
const _registered = Symbol('registeredEvents');
const _addDOMListener = Symbol('addDOMListener');
const _removeDOMListener = Symbol('removeDOMListener');
const _removeAllDOMListeners = Symbol('removeAllDOMListeners');

// List of modifiers in events
const Modifiers = [ 'alt', 'ctrl', 'shift', 'meta' ];

// DOM event mapping

const EventMap = {
	mouse: {
		'click': 'click',
		'dblclick': 'dblclick',
		'down': 'mousedown',
		'up': 'mouseup',
		'enter': 'mouseenter',
		'leave': 'mouseleave',
		'move': 'mousemove',
		'out': 'mouseout',
		'over': 'mouseover',
		'wheel': 'wheel',
		'contextmenu': 'contextmenu',
	},
	key: {
		'press': 'keypress',
		'down': 'keydown',
		'up': 'keyup',
	},
	form: {
		'blur': 'blur',
		'focus': 'focus',
		'focusin': 'focusin',
		'focusout': 'focusout',
		'input': 'input',
		'search': 'search',
		'select': 'select',
	},
	drag: {
		'drag': 'drag',
		'start': 'dragstart',
		'enter': 'dragenter',
		'over': 'dragover',
		'leave': 'dragleave',
		'end': 'dragend',
		'drop': 'drop'
	},
	view: {
		'scroll': 'scroll'
	},
	media: {
		'abort': 'abort',
		'canplay': 'canplay',
		'canplaythrough': 'canplaythrough',
		'duration': 'durationchange',
		'empty': 'emptied',
		'end': 'ended',
		'error': 'error',
		'load': 'loadeddata',
		'metadata': 'loadedmetadata',
		'loading': 'loadstart',
		'pause': 'pause',
		'play': 'play',
		'playing': 'playing',
		'progress': 'progress',
		'ratechange': 'ratechange',
		'seek': 'seeked',
		'seeking': 'seeking',
		'stalled': 'stalled',
		'suspend': 'suspend',
		'time': 'timeupdate',
		'volume': 'volumechange',
		'waiting': 'waiting'
	},
};

/**
 * Make event listener selector name.
 * @param  {Object}  event   Event selector object
 * @param  {Boolean} capture Capture flag
 * @return {String}          Event selector string
 */
function makeEventSelector(event, capture) {
	let selector = event.type + ':' + event.name;
	if (capture) {
		selector += '-capture';
	}
	if (event.selector) {
		selector += ':' + event.selector;
	}
	return selector;
}

/**
 * Event listener for Widget.
 *
 * Adds a modular consistent event naming scheme for DOM events.
 * Adds and removes event listeners from HTMLElement as needed.
 */
class WidgetEvent extends EventEmitter {

	/**
	 * Create WidgetEvent wrapper for HTMLElement
	 * @param  {HTMLElement} element HTML element
	 */
	constructor(element) {
		super();

		if (!DomUtil.isElement(element)) {
			throw new Error('Element must be instance of HTMLElement');
		}

		this[_el] = element;
		this[_registered] = {};
	}

	/**
	 * Adds the given listener for events named `selector`.
	 * If the selector matches a DOM event name then the listener is added also
	 * to the DOM element.
	 * Only one DOM `addEventListener` call is made for given event selector,
	 * WidgetEvent keeps track of event listener counts internally. And
	 * redirects emitted events for propriate listeners, ie. listeners with key
	 * code selectors.
	 *
	 * Event selector string is constructed from multiple parts which are separated
	 * with `:`, ie. `key:press:ctrl+space`.
	 * First part is for the DOM event type, ie. `key` or `mouse`, full list of
	 * event types can be queried with `WidgetEvent.getEventTypes()`.
	 * Second part is for the DOM event name, ie. `press` or `click`, full list of
	 * available names for given type can be queried with
	 * `WidgetEvent.getEventNames(<type>)`. Third part is an optional event
	 * filtering selector, ie. `ctrl+space` or `Alt+Shift+KeyD`.
	 *
	 * @param  {String}   selector Event selector string
	 * @param  {Function} listener Listener callback function
	 * @param  {Boolean}  capture  Capture flag for DOM `addEventListener`
	 */
	on(selector, listener, capture) {
		this.addListener(selector, listener, capture);
	}

	/**
	 * Alias for `on(selector, listener, capture)`.
	 * @param {String}   selector Event selector string
	 * @param {Function} listener Listener callback function
	 * @param {Boolean}  capture  Capture flag for DOM `addEventListener`
	 */
	addListener(selector, listener, capture) {
		// normalize `capture` to boolean
		capture = !!capture;

		let event = WidgetEvent.parseEventSelector(selector);

		// if selector isn't for DOM event, no magic is needed and listener is added
		// as a normal listener only
		if (event.event) {
			// construct proper normalized event name to add as listener
			selector = makeEventSelector(event, capture);

			// add DOM event listener for the event
			this[_addDOMListener](event, capture);
		}

		// add listener to this EventEmitter
		super.addListener(selector, listener);
	}

	/**
	 * Add DOM listener.
	 *
	 * DOM listener is only added once for the same event. Any subsequent calls
	 * will only increase the registered counter.
	 * DOM listener is bound to emit EventEmitter event using event name mapping.
	 *
	 * @param {Object}  event   Event object from `parseEventSelector`
	 * @param {Boolean} capture Capture flag for DOM `addEventListener`
	 */
	[_addDOMListener](event, capture) {
		let eventName = event.event;
		if (capture) {
			// differentiate listeners with and without capture flag
			eventName += '-capture';
		}
		// create cache for the event emitter, includes a counter so
		// that we know when we should remove the listener.
		this[_registered][eventName] = this[_registered][eventName] || {
			count: 0,
			emitter: null,
			event: event.event,
			capture: capture,
		};
		let registered = this[_registered][eventName];

		// if registered counter is zero, we need to create a emitter and add that
		// listener to the element.
		if (registered.count === 0) {
			// create and cache event emitter
			registered.emitter = this.emit.bind(this, makeEventSelector(event, capture));
			this[_el].addEventListener(event.event, registered.emitter, capture);
		}
		registered.count += 1; // increase registered counter
	}

	/**
	 * Removes the given listener with event named `selector`.
	 * If the selector matches a DOM event name and this is the last listener for
	 * that DOM event, the listener is removed from the DOM.
	 *
	 * If `listener` is not a function, then all listeners matching `selector` are
	 * removed and DOM event listeners with that selector is removed.
	 *
	 * @param  {String}   selector Event selector string
	 * @param  {Function} listener Listener callback function, can be omitted
	 * @param  {Boolean}  capture  Capture flag for DOM `addEventListener`
	 */
	removeListener(selector, listener, capture) {
		if (typeof listener !== 'function') {
			// remove listener only if listener is given
			return;
		}

		// normalize `capture` to boolean
		capture = !!capture;

		let event = WidgetEvent.parseEventSelector(selector);

		// if selector isn't for DOM event, no magic is needed and listener is added
		// as a normal listener only.
		if (event.event) {

			// construct proper normalized event name to add as listener
			selector = makeEventSelector(event, capture);

			// check that the given listener is actually added to this selector
			if (this.listeners(selector).includes(listener)) {
				// remove listener from DOM
				this[_removeDOMListener](event, capture);
			}
		}

		// remove listener from this EventEmitter
		super.removeListener(selector, listener);
	}

	/**
	 * Remove DOM listener.
	 *
	 * DOM listener is only removed if the given listeners is actually added as
	 * listener. DOM listener is only removed if the associated registered counter
	 * goes to zero, this indicates that there is no more listeners listening
	 * the given event selector.
	 *
	 * @param {Object}  event   Event object from `parseEventSelector`
	 * @param {Boolean} capture Capture flag for DOM `addEventListener`
	 */
	[_removeDOMListener](event, capture) {
		let eventName = event.event;
		if (capture) {
			// differentiate listeners with and without capture flag
			eventName += '-capture';
		}

		let registered = this[_registered][eventName];
		// check if DOM event has been registered
		if (registered) {
			// decrement registered counter
			registered.count -= 1;
		}

		// check if there are still listeners registered for this selector
		// if not then we can remove listener form DOM
		if (registered.count <= 0) {
			this[_el].removeEventListener(registered.event, registered.emitter, registered.capture);
			// delete registered object from cache
			delete this[_registered][eventName];
		}
	}

	emit(type, ...args) {
		throw new Error('NOT IMPLEMENTED');
	}

	once(selector, listener, capture) {
		throw new Error('NOT IMPLEMENTED');
	}

	prependListener(selector, listener, capture) {
		throw new Error('NOT IMPLEMENTED');
	}

	prependOnceListener(selector, listener, capture) {
		throw new Error('NOT IMPLEMENTED');
	}

	/**
	 * Removes all listeners, or those of the specified event name.
	 * Remove also all matching DOM event listeners.
	 * @param  {String} selector Event selector string
	 */
	removeAllListeners(selector) {
		if (typeof selector === 'string') {
			// parse event selector object from selector string
			let event = WidgetEvent.parseEventSelector(selector);

			if (event.event) {
				// remove all DOM listeners for the given selector
				this[_removeAllDOMListeners](event);
				// remove both capture and non-capture listeners
				super.removeAllListeners(makeEventSelector(event, false));
				super.removeAllListeners(makeEventSelector(event, true));
			} else {
				// this is a non-DOM event selector
				super.removeAllListeners(selector);
			}
		} else {
			// remove ALL DOM listeners and EventEmitter listeners
			this[_removeAllDOMListeners]();
			super.removeAllListeners();
		}
	}

	[_removeAllDOMListeners](event) {
		let remove = (registered) => {
			if (registered) {
				this[_el].removeEventListener(registered.event, registered.emitter, registered.capture);
			}
		};

		if (event) {
			// remove only one DOM listener, both capture and non-capture variant
			remove(this[_registered][event.event]);
			remove(this[_registered][event.event + '-capture']);
		} else {
			// remove ALL DOM listeners
			for (let eventName in this[_registered]) {
				remove(this[_registered][eventName]);
			}
		}
	}

	/**
	 * Parse selector into individual parts.
	 *
	 * Case of the selector parts and extra white spaces are ignored.
	 *
	 * Result object properties:
	 *  - `which`: key code or mouse button
	 *  - `alt`: `true` if selector has Alt modifier
	 *  - `ctrl`: `true` if selector has Ctrl modifier
	 *  - `shift`: `true` if selector has Shift modifier
	 *  - `meta`: `true` if selector has Meta modifier
	 *
	 * @param  {String} selector Event selector string
	 * @return {Object}          Object with `which` and mods
	 */
	static parseSelector(selector) {
		if (typeof selector !== 'string') {
			throw new Error('Event selector must be string, got: ' + (typeof selector));
		}

		// result object template with default values
		let tmpl = {
			which: undefined,
			alt: false,
			ctrl: false,
			shift: false,
			meta: false,
		};

		let parts = selector.split('+');
		let res = parts.reduce((o, current) => {
			// normalize
			current = current.toLowerCase().trim();

			if (Modifiers.includes(current)) {
				// current part is a modifier
				o[current] = true;
			} else {
				o.which = current;
			}

			return o;
		}, tmpl);

		return res;
	}

	/**
	 * Format selector object to normalized selector string.
	 * This ensures that the selector string has all the parts in a
	 * consistent order.
	 *
	 * @param  {String} selector Selector string
	 * @return {String}          Normalized selector
	 */
	static normalizeSelector(selector) {
		// parse selector string
		let obj = this.parseSelector(selector);

		// construct selector form parsed selector
		// using mods array
		let res = Modifiers.filter(m => obj[m]);
		// add `which` to end of the selector
		res.push(obj.which);
		// resulting selector has all the components joined with `+`
		return res.join('+');
	}

	/**
	 * Parse event selector string.
	 *
	 * Result object properties:
	 *  - `type`: Type of the event, ie. `key` or `mouse`
	 *  - `name`: Name of the event, ie. `press` or `click`
	 *  - `selector`: Event selector string, ie. `ctrl+space`
	 *  - `event`: DOM event name
	 *
	 * @param  {String} event Event selector string
	 * @return {Object}       Event selector object
	 */
	static parseEventSelector(event) {
		let parts = event.split(':');

		// initial event object
		let obj = {
			type: parts[0], // event type, ie. `key` or `mouse`
			name: parts[1], // event name, ie. `press` or `click`
			selector: undefined,
			event: undefined,
		};
		if (obj.name) {
			obj.name = obj.name.replace(/-capture$/, '');
		}

		if (parts.length > 2) {
			obj.selector = this.normalizeSelector(parts.slice(2).join(':'));
		}

		if (obj.type in EventMap && obj.name in EventMap[obj.type]) {
			obj.event = EventMap[obj.type][obj.name];
		}

		return obj;
	}

	/**
	 * Get list of available event types
	 * @return {Array} Array of event types
	 */
	static getEventTypes() {
		return Object.keys(EventMap);
	}

	/**
	 * Get list of available event names in type
	 * @param  {String} type Event type
	 * @return {Array}       Array of event names in type
	 */
	static getEventNames(type) {
		return Object.keys(EventMap[type]).map(name => type + ':' + name);
	}

	/**
	 * Get mapped DOM event name
	 * @param  {String} type Event type
	 * @param  {String} name Event name
	 * @return {String}      DOM event name or null if not found
	 */
	static getDOMEventName(type, name) {
		if (type in EventMap && name in EventMap[type]) {
			return EventMap[type][name];
		}
		return null;
	}
}
module.exports = WidgetEvent;
<|endoftext|>