<commit_before>/* global contentTypes, taxonomies */

// SBTODO: Multiple conditions
// SBTODO: < and > condition functionality
// SBTODO: Make condition: yaml use the field name instead of the backend html class/id (kind of does this already, but taxonomies and relations get prepended)
// SBTODO: On hide, set value to null / empty so they don't still submit (Should clear: true | false be a setting?)

(function ($) {
    'use strict';

    // Content Types
    var contentType = $('#contenttype').val();
    var ctConfig    = contentTypes[contentType];

    var allFields = null;
    var fieldName = '';

    // Repeaters
    var allRepeaterFields = null;
    var repeaterToAdd     = null;
    var regexPattern;

    // Relations
    var allRelations  = null;
    var relationToAdd = null;

    // Taxonomies
    var allTaxonomies = taxonomies;
    var taxonomyToAdd = null;

    // Templatefields
    var $templateSelect    = $('.bolt-field-templateselect select');
    var templatefieldToAdd = null;

    // Conditional Arrays
    var fieldsToWatch         = [];
    var repeaterFieldsToWatch = [];
    var conditionalFields     = [];
    var repeaterFields        = [];

    if (ctConfig !== undefined) {
        initContenttypeFields();

        initTemplatefieldFields();

        initRelationFields();

        initTaxonomyFields();

        $(document).on('ready', function () {
            watchFieldsForChange();

            var i;
            var len = fieldsToWatch.length;
            var $fieldToWatch;
            var watchVal;

            // Check conditional fields
            for (i = 0; i < len; i += 1) {
                console.log('# Checking fields that have conditionals on: "#' + fieldsToWatch[i] + '"');

                $fieldToWatch = $('#' + fieldsToWatch[i]);

                if (!$fieldToWatch.length) {
                    $fieldToWatch = $('[name=' + fieldsToWatch[i] + ']');
                }

                watchVal = $fieldToWatch.val();

                if ($fieldToWatch.is('input[type="checkbox"]') && !$fieldToWatch.is(':checked')) {
                    watchVal = '0';
                }

                checkConditionalFields($fieldToWatch.attr('id'), watchVal);
            }

            // Check conditional fields inside repeaters
            runRepeaterCheck();

            // On new repeater item, check for conditional fields
            $('.repeater-add').on('click', function () {
                runRepeaterCheck();
            });
        });
    }

    function checkConditionalFields (id, watchedValue) {
        // Check the watched value against fields with conditions
        for (var name in conditionalFields) {
            if (conditionalFields.hasOwnProperty(name)) {
                if (conditionalFields[name]['condition']['field'] !== id) {
                    continue;
                }

                // Find the bolt fieldset container
                var $field;

                if (conditionalFields[name].hasOwnProperty('type')) {
                    switch (conditionalFields[name]['type']) {
                        case 'repeater':
                            $field = $('[name*="' + name + '[0]"]');

                            break;
                        case 'relation':
                            $field = $('#relation-' + name);

                            break;
                        case 'taxonomy':
                            $field = $('#taxonomy-' + name);

                            break;
                        case 'filelist':
                        case 'textarea':
                        case 'checkbox':
                            $field = $('[name=' + name + ']');

                            break;
                        case 'image':
                            $field = $('#field-' + name);

                            break;
                        case 'geolocation':
                            $field = $('[name*="' + name + '[address]"]');

                            break;
                        default:
                            $field = $('#' + name);

                            break;
                    }
                }

                if ($field.length === 0) {
                    console.error('Conditional field could not be hidden: ' + name + '. Type = {' + conditionalFields[name]['type'] + '}');
                }

                var $fieldContainer = $field.closest('[data-bolt-fieldset]');

                // Config value whether to show / hide the field on condition matched
                var showField = (conditionalFields[name]['condition']['show'] === undefined || conditionalFields[name]['condition']['show']);

                // Check if the value matches
                var conditionMatched = false;

                // Check if field's value is null (e.g. this occurs for images)
                if (watchedValue === null) {
                    watchedValue = '';
                }

                if (typeof watchedValue === 'object') {
                    var i   = 0;
                    var len = watchedValue.length;

                    for (i = 0; i < len; i += 1) {
                        if (conditionalFields[name]['condition']['value'].indexOf(watchedValue[i]) > -1) {
                            conditionMatched = true;

                            break;
                        }
                    }
                } else {
                    if (conditionalFields[name]['condition']['value'].indexOf(watchedValue) > -1) {
                        conditionMatched = true;
                    }
                }

                // If the condition is a != condition, invert the conditionMatched result
                if (conditionalFields[name]['condition']['operator'] === '!=' && conditionMatched) {
                    conditionMatched = false;
                } else if (conditionalFields[name]['condition']['operator'] === '!=') {
                    conditionMatched = true;
                }

                if ((showField && conditionMatched) || (!showField && !conditionMatched)) {
                    console.log('--> Show ' + name);

                    showFieldset($fieldContainer);
                } else {
                    console.log('--> Hide ' + name);

                    hideFieldset($fieldContainer);
                }
            }
        }

        checkTabPanes();
    }

    function runRepeaterCheck () {
        // Check conditional fields inside repeaters
        var $repeaterFieldToWatch;
        var i;
        var len = repeaterFieldsToWatch.length;

        for (i = 0; i < len; i += 1) {
            console.log('# Checking repeater fields that have conditionals on: "name=/' + repeaterFieldsToWatch[i] + '/"');

            $repeaterFieldToWatch = $('select, input, textarea').filter(function () {
                if (typeof $(this).attr('name') === 'undefined') {
                    return false;
                }

                return $(this).attr('name').match(new RegExp(repeaterFieldsToWatch[i]))
            });

            // If repeater fields have been found, loop through them and check the conditional fields within the repeater-group
            if ($repeaterFieldToWatch.length) {
                $repeaterFieldToWatch.each(function () {
                    checkRepeaterFields($(this).attr('id'), $(this).val());
                })
            }
        }
    }

    function checkRepeaterFields (id, watchedValue) {
        // Check the watched value against fields with conditions
        for (var name in repeaterFields) {
            if (repeaterFields.hasOwnProperty(name)) {
                var repeater          = repeaterFields[name]['repeater'];
                var watchedFieldRegex = new RegExp(repeater + '\\[\\d+\\]\\[' + repeaterFields[name]['condition']['field'] + '\\]');
                var fieldRegex        = new RegExp(repeater + '\\[\\d+\\]\\[' + name + '\\]');
                var $watched_field    = $('#' + id);

                if (!watchedFieldRegex.test($watched_field.attr('name'))) {
                    continue;
                }

                // Find the bolt fieldset container
                var $field = $watched_field.closest('.repeater-group').find('select, input, textarea').filter(function () {
                    if (typeof $(this).attr('name') === 'undefined') {
                        return false;
                    }

                    return $(this).attr('name').match(fieldRegex);
                });

                if ($field.length === 0) {
                    console.error('Conditional repeater field could not be hidden: ' + name + '. Type = {' + repeaterFields[name]['type'] + '}');
                }

                var $fieldContainer = $field.closest('.repeater-field');

                // Config value whether to show / hide the field on condition matched
                var showField = (repeaterFields[name]['condition']['show'] === undefined || repeaterFields[name]['condition']['show']);

                // Check if the value matches
                var conditionMatched = false;

                if (typeof watchedValue === 'object') {
                    var i   = 0;
                    var len = watchedValue.length;

                    for (i = 0; i < len; i += 1) {
                        if (repeaterFields[name]['condition']['value'].indexOf(watchedValue[i]) > -1) {
                            conditionMatched = true;

                            break;
                        }
                    }
                } else {
                    if (repeaterFields[name]['condition']['value'].indexOf(watchedValue) > -1) {
                        conditionMatched = true;
                    }
                }

                // If the condition is a != condition, invert the conditionMatched result
                if (repeaterFields[name]['condition']['operator'] === '!=' && conditionMatched) {
                    conditionMatched = false;
                } else if (repeaterFields[name]['condition']['operator'] === '!=') {
                    conditionMatched = true;
                }

                if ((showField && conditionMatched) || (!showField && !conditionMatched)) {
                    console.log('--> Show ' + repeater + ' field: ' + name);

                    showFieldset($fieldContainer);
                } else {
                    console.log('--> Hide ' + repeater + ' field: ' + name);

                    hideFieldset($fieldContainer);
                }
            }
        }
    }

    function initContenttypeFields () {
        allFields    = ctConfig['fields'];
        allRelations = ctConfig['relations'];

        // Check condition config to see what fields need to be checked
        for (var name in allFields) {
            if (allFields.hasOwnProperty(name) && allFields[name].hasOwnProperty('condition')) {
                if (allFields[name]['condition'].hasOwnProperty('field')) {
                    fieldName = allFields[name]['condition']['field'];

                    if (fieldsToWatch.indexOf(fieldName) === -1) {
                        // Fields to watch the value change of
                        fieldsToWatch.push(fieldName);
                    }

                    // Add to Conditional fields
                    conditionalFields[name] = allFields[name];
                }
            }

            // Check repeater fields and build array of repeaters to watch
            if (allFields.hasOwnProperty(name) && allFields[name].hasOwnProperty('type') && allFields[name]['type'] === 'repeater') {
                allRepeaterFields = allFields[name]['fields'];

                for (var field in allRepeaterFields) {
                    if (allRepeaterFields.hasOwnProperty(field) && allRepeaterFields[field].hasOwnProperty('condition')) {
                        if (allRepeaterFields[field]['condition'].hasOwnProperty('field')) {
                            fieldName = allRepeaterFields[field]['condition']['field'];

                            if (repeaterFieldsToWatch.indexOf(name + '\\[\\d+\\]\\[' + fieldName + '\\]') === -1) {
                                // Fields to watch the value change of
                                repeaterFieldsToWatch.push(name + '\\[\\d+\\]\\[' + fieldName + '\\]');
                            }

                            repeaterToAdd = allRepeaterFields[field];

                            // Flag this entry as a repeater sub field
                            repeaterToAdd['repeater'] = name;

                            // Add this field to the repeaterFields array
                            repeaterFields[field] = repeaterToAdd;
                        }
                    }
                }
            }
        }
    }

    function initRelationFields () {
        // Check Relations for condition config to see what relationships need to be checked
        for (var relName in allRelations) {
            if (allRelations.hasOwnProperty(relName) && allRelations[relName].hasOwnProperty('condition')) {
                if (allRelations[relName]['condition'].hasOwnProperty('field')) {
                    fieldName = allRelations[relName]['condition']['field'];

                    if (fieldsToWatch.indexOf(fieldName) === -1) {
                        // Fields to watch the value change of
                        fieldsToWatch.push(fieldName);
                    }

                    relationToAdd = allRelations[relName];

                    // Force the field type
                    relationToAdd['type'] = 'relation';

                    // Add to Conditional fields
                    conditionalFields[relName] = relationToAdd;
                }
            }
        }
    }

    function initTemplatefieldFields () {
        // Check templatefields for condition config to see what relationships need to be checked
        if ($templateSelect.length && $templateSelect.val().length) {
            var selectedTemplate = $templateSelect.val();

            if (templateFields.hasOwnProperty(selectedTemplate)) {
                var fieldsForTemplate = templateFields[selectedTemplate]['fields'];

                // Check templatefields for condition config to see what templatefields need to be checked
                for (var templatefieldName in fieldsForTemplate) {
                    if (fieldsForTemplate.hasOwnProperty(templatefieldName) && fieldsForTemplate[templatefieldName].hasOwnProperty('condition')) {
                        templatefieldToAdd = fieldsForTemplate[templatefieldName];

                        fieldName = fieldsForTemplate[templatefieldName]['condition']['field'];

                        if (fieldsToWatch.indexOf(fieldName) === -1) {
                            // Fields to watch the value change of
                            fieldsToWatch.push(fieldName);
                        }

                        // Add to Conditional fields
                        conditionalFields['templatefields-' + templatefieldName] = fieldsForTemplate[templatefieldName];
                    }
                }
            }
        }
    }

    function initTaxonomyFields () {
        // Check taxonomies for condition config to see what taxonomies need to be checked
        for (var taxName in allTaxonomies) {
            if (allTaxonomies.hasOwnProperty(taxName) && allTaxonomies[taxName].hasOwnProperty('condition')) {
                taxonomyToAdd = allTaxonomies[taxName];

                if (taxonomyToAdd['condition'].hasOwnProperty(contentType)) {
                    fieldName = allTaxonomies[taxName]['condition'][contentType]['field'];

                    if (fieldsToWatch.indexOf(fieldName) === -1) {
                        // Fields to watch the value change of
                        fieldsToWatch.push(fieldName);
                    }

                    // Force the field type
                    taxonomyToAdd['type'] = 'taxonomy';

                    // Only use the condition for the relevant content type
                    taxonomyToAdd['condition'] = taxonomyToAdd['condition'][contentType];

                    // Add to Conditional fields
                    conditionalFields[taxName] = taxonomyToAdd;
                }
            }
        }
    }

    function showFieldset ($fieldContainer) {

        var $wasRequiredFields = $fieldContainer.find('.was-required');

        if ($wasRequiredFields.length) {
            $wasRequiredFields.each(function () {
                $(this).attr('required', 'required').removeClass('was-required');
            });
        }

        $fieldContainer.show();
    }

    function hideFieldset ($fieldContainer) {

        var $requiredFields = $fieldContainer.find('[required]');

        if ($requiredFields.length) {
            $requiredFields.each(function () {
                $(this).removeAttr('required').addClass('was-required');
            });
        }

        $fieldContainer.hide();
    }

    function checkTabPanes () {
        var $tabPanes = $('.tab-pane');
        var $fields   = null;

        $tabPanes.each(function () {
            // Find bolt fields
            $fields = $(this).find('[data-bolt-fieldset]');

            // Check which of these are display none (can't use hidden because inactive tabs return as hidden)
            $fields = $fields.filter(function () {
                return $(this).css("display") !== "none"
            });

            // Show / Hide the tab if it has visible contents
            if ($fields.length > 0) {
                $('#tabindicator-' + $(this).attr('id')).show();
            } else {
                $('#tabindicator-' + $(this).attr('id')).hide();
            }
        });
    }

    function watchFieldsForChange () {
        // Check for select2 changes
        $(document).on('change', 'select,input', function () {
            var id           = $(this).attr('id');
            var name         = $(this).attr('name');
            var watchedValue = $(this).val();

            if (!id) {
                id = name;
            }

            // Check if the changed select is inside a repeater
            if ($(this).parents('.repeater-field').length) {
                checkRepeaterFields(id, watchedValue);
            }

            // Check the changed field is one of the fieldsToWatch
            if (fieldsToWatch.indexOf(id) > -1) {
                checkConditionalFields(id, watchedValue);
            }
        }).on('click', 'input[type="checkbox"], button', function () {
            var $field = $(this);

            if ($field.is('button')) {
                $field = $field.prev();
            }

            var id           = $field.attr('name');
            var name         = id;
            var watchedValue = $field.val();

            if (!$field.is(':checked')) {
                watchedValue = '0';
            }

            // Check if the changed select is inside a repeater
            if ($field.parents('.repeater-field').length) {
                checkRepeaterFields(id, watchedValue);
            }

            // Check the changed field is one of the fieldsToWatch
            if (fieldsToWatch.indexOf(id) > -1) {
                checkConditionalFields(id, watchedValue);
            }
        });
    }
})(jQuery);
<commit_msg>Bugfix - checkboxes were using the wrong identifier on load.<commit_after>/* global contentTypes, taxonomies */

// SBTODO: Multiple conditions
// SBTODO: < and > condition functionality
// SBTODO: Make condition: yaml use the field name instead of the backend html class/id (kind of does this already, but taxonomies and relations get prepended)
// SBTODO: On hide, set value to null / empty so they don't still submit (Should clear: true | false be a setting?)

(function ($) {
    'use strict';

    // Content Types
    var contentType = $('#contenttype').val();
    var ctConfig    = contentTypes[contentType];

    var allFields = null;
    var fieldName = '';

    // Repeaters
    var allRepeaterFields = null;
    var repeaterToAdd     = null;
    var regexPattern;

    // Relations
    var allRelations  = null;
    var relationToAdd = null;

    // Taxonomies
    var allTaxonomies = taxonomies;
    var taxonomyToAdd = null;

    // Templatefields
    var $templateSelect    = $('.bolt-field-templateselect select');
    var templatefieldToAdd = null;

    // Conditional Arrays
    var fieldsToWatch         = [];
    var repeaterFieldsToWatch = [];
    var conditionalFields     = [];
    var repeaterFields        = [];

    if (ctConfig !== undefined) {
        initContenttypeFields();

        initTemplatefieldFields();

        initRelationFields();

        initTaxonomyFields();

        $(document).on('ready', function () {
            watchFieldsForChange();

            var i;
            var len = fieldsToWatch.length;
            var $fieldToWatch;
            var watchName;
            var watchVal;

            // Check conditional fields
            for (i = 0; i < len; i += 1) {
                console.log('# Checking fields that have conditionals on: "#' + fieldsToWatch[i] + '"');

                $fieldToWatch = $('#' + fieldsToWatch[i]);

                if (!$fieldToWatch.length) {
                    $fieldToWatch = $('[name=' + fieldsToWatch[i] + ']');
                }

                watchName = $fieldToWatch.attr('id');
                watchVal  = $fieldToWatch.val();

                if ($fieldToWatch.is('input[type="checkbox"]') && !$fieldToWatch.is(':checked')) {
                    watchName = $fieldToWatch.attr('name');
                    watchVal  = '0';
                }

                checkConditionalFields(watchName, watchVal);
            }

            // Check conditional fields inside repeaters
            runRepeaterCheck();

            // On new repeater item, check for conditional fields
            $('.repeater-add').on('click', function () {
                runRepeaterCheck();
            });
        });
    }

    function checkConditionalFields (id, watchedValue) {
        // Check the watched value against fields with conditions
        for (var name in conditionalFields) {
            if (conditionalFields.hasOwnProperty(name)) {
                if (conditionalFields[name]['condition']['field'] !== id) {
                    continue;
                }

                // Find the bolt fieldset container
                var $field;

                if (conditionalFields[name].hasOwnProperty('type')) {
                    switch (conditionalFields[name]['type']) {
                        case 'repeater':
                            $field = $('[name*="' + name + '[0]"]');

                            break;
                        case 'relation':
                            $field = $('#relation-' + name);

                            break;
                        case 'taxonomy':
                            $field = $('#taxonomy-' + name);

                            break;
                        case 'filelist':
                        case 'textarea':
                        case 'checkbox':
                            $field = $('[name=' + name + ']');

                            break;
                        case 'image':
                            $field = $('#field-' + name);

                            break;
                        case 'geolocation':
                            $field = $('[name*="' + name + '[address]"]');

                            break;
                        default:
                            $field = $('#' + name);

                            break;
                    }
                }

                if ($field.length === 0) {
                    console.error('Conditional field could not be hidden: ' + name + '. Type = {' + conditionalFields[name]['type'] + '}');
                }

                var $fieldContainer = $field.closest('[data-bolt-fieldset]');

                // Config value whether to show / hide the field on condition matched
                var showField = (conditionalFields[name]['condition']['show'] === undefined || conditionalFields[name]['condition']['show']);

                // Check if the value matches
                var conditionMatched = false;

                // Check if field's value is null (e.g. this occurs for images)
                if (watchedValue === null) {
                    watchedValue = '';
                }

                if (typeof watchedValue === 'object') {
                    var i   = 0;
                    var len = watchedValue.length;

                    for (i = 0; i < len; i += 1) {
                        if (conditionalFields[name]['condition']['value'].indexOf(watchedValue[i]) > -1) {
                            conditionMatched = true;

                            break;
                        }
                    }
                } else {
                    if (conditionalFields[name]['condition']['value'].indexOf(watchedValue) > -1) {
                        conditionMatched = true;
                    }
                }

                // If the condition is a != condition, invert the conditionMatched result
                if (conditionalFields[name]['condition']['operator'] === '!=' && conditionMatched) {
                    conditionMatched = false;
                } else if (conditionalFields[name]['condition']['operator'] === '!=') {
                    conditionMatched = true;
                }

                if ((showField && conditionMatched) || (!showField && !conditionMatched)) {
                    console.log('--> Show ' + name);

                    showFieldset($fieldContainer);
                } else {
                    console.log('--> Hide ' + name);

                    hideFieldset($fieldContainer);
                }
            }
        }

        checkTabPanes();
    }

    function runRepeaterCheck () {
        // Check conditional fields inside repeaters
        var $repeaterFieldToWatch;
        var i;
        var len = repeaterFieldsToWatch.length;

        for (i = 0; i < len; i += 1) {
            console.log('# Checking repeater fields that have conditionals on: "name=/' + repeaterFieldsToWatch[i] + '/"');

            $repeaterFieldToWatch = $('select, input, textarea').filter(function () {
                if (typeof $(this).attr('name') === 'undefined') {
                    return false;
                }

                return $(this).attr('name').match(new RegExp(repeaterFieldsToWatch[i]))
            });

            // If repeater fields have been found, loop through them and check the conditional fields within the repeater-group
            if ($repeaterFieldToWatch.length) {
                $repeaterFieldToWatch.each(function () {
                    checkRepeaterFields($(this).attr('id'), $(this).val());
                })
            }
        }
    }

    function checkRepeaterFields (id, watchedValue) {
        // Check the watched value against fields with conditions
        for (var name in repeaterFields) {
            if (repeaterFields.hasOwnProperty(name)) {
                var repeater          = repeaterFields[name]['repeater'];
                var watchedFieldRegex = new RegExp(repeater + '\\[\\d+\\]\\[' + repeaterFields[name]['condition']['field'] + '\\]');
                var fieldRegex        = new RegExp(repeater + '\\[\\d+\\]\\[' + name + '\\]');
                var $watched_field    = $('#' + id);

                if (!watchedFieldRegex.test($watched_field.attr('name'))) {
                    continue;
                }

                // Find the bolt fieldset container
                var $field = $watched_field.closest('.repeater-group').find('select, input, textarea').filter(function () {
                    if (typeof $(this).attr('name') === 'undefined') {
                        return false;
                    }

                    return $(this).attr('name').match(fieldRegex);
                });

                if ($field.length === 0) {
                    console.error('Conditional repeater field could not be hidden: ' + name + '. Type = {' + repeaterFields[name]['type'] + '}');
                }

                var $fieldContainer = $field.closest('.repeater-field');

                // Config value whether to show / hide the field on condition matched
                var showField = (repeaterFields[name]['condition']['show'] === undefined || repeaterFields[name]['condition']['show']);

                // Check if the value matches
                var conditionMatched = false;

                if (typeof watchedValue === 'object') {
                    var i   = 0;
                    var len = watchedValue.length;

                    for (i = 0; i < len; i += 1) {
                        if (repeaterFields[name]['condition']['value'].indexOf(watchedValue[i]) > -1) {
                            conditionMatched = true;

                            break;
                        }
                    }
                } else {
                    if (repeaterFields[name]['condition']['value'].indexOf(watchedValue) > -1) {
                        conditionMatched = true;
                    }
                }

                // If the condition is a != condition, invert the conditionMatched result
                if (repeaterFields[name]['condition']['operator'] === '!=' && conditionMatched) {
                    conditionMatched = false;
                } else if (repeaterFields[name]['condition']['operator'] === '!=') {
                    conditionMatched = true;
                }

                if ((showField && conditionMatched) || (!showField && !conditionMatched)) {
                    console.log('--> Show ' + repeater + ' field: ' + name);

                    showFieldset($fieldContainer);
                } else {
                    console.log('--> Hide ' + repeater + ' field: ' + name);

                    hideFieldset($fieldContainer);
                }
            }
        }
    }

    function initContenttypeFields () {
        allFields    = ctConfig['fields'];
        allRelations = ctConfig['relations'];

        // Check condition config to see what fields need to be checked
        for (var name in allFields) {
            if (allFields.hasOwnProperty(name) && allFields[name].hasOwnProperty('condition')) {
                if (allFields[name]['condition'].hasOwnProperty('field')) {
                    fieldName = allFields[name]['condition']['field'];

                    if (fieldsToWatch.indexOf(fieldName) === -1) {
                        // Fields to watch the value change of
                        fieldsToWatch.push(fieldName);
                    }

                    // Add to Conditional fields
                    conditionalFields[name] = allFields[name];
                }
            }

            // Check repeater fields and build array of repeaters to watch
            if (allFields.hasOwnProperty(name) && allFields[name].hasOwnProperty('type') && allFields[name]['type'] === 'repeater') {
                allRepeaterFields = allFields[name]['fields'];

                for (var field in allRepeaterFields) {
                    if (allRepeaterFields.hasOwnProperty(field) && allRepeaterFields[field].hasOwnProperty('condition')) {
                        if (allRepeaterFields[field]['condition'].hasOwnProperty('field')) {
                            fieldName = allRepeaterFields[field]['condition']['field'];

                            if (repeaterFieldsToWatch.indexOf(name + '\\[\\d+\\]\\[' + fieldName + '\\]') === -1) {
                                // Fields to watch the value change of
                                repeaterFieldsToWatch.push(name + '\\[\\d+\\]\\[' + fieldName + '\\]');
                            }

                            repeaterToAdd = allRepeaterFields[field];

                            // Flag this entry as a repeater sub field
                            repeaterToAdd['repeater'] = name;

                            // Add this field to the repeaterFields array
                            repeaterFields[field] = repeaterToAdd;
                        }
                    }
                }
            }
        }
    }

    function initRelationFields () {
        // Check Relations for condition config to see what relationships need to be checked
        for (var relName in allRelations) {
            if (allRelations.hasOwnProperty(relName) && allRelations[relName].hasOwnProperty('condition')) {
                if (allRelations[relName]['condition'].hasOwnProperty('field')) {
                    fieldName = allRelations[relName]['condition']['field'];

                    if (fieldsToWatch.indexOf(fieldName) === -1) {
                        // Fields to watch the value change of
                        fieldsToWatch.push(fieldName);
                    }

                    relationToAdd = allRelations[relName];

                    // Force the field type
                    relationToAdd['type'] = 'relation';

                    // Add to Conditional fields
                    conditionalFields[relName] = relationToAdd;
                }
            }
        }
    }

    function initTemplatefieldFields () {
        // Check templatefields for condition config to see what relationships need to be checked
        if ($templateSelect.length && $templateSelect.val().length) {
            var selectedTemplate = $templateSelect.val();

            if (templateFields.hasOwnProperty(selectedTemplate)) {
                var fieldsForTemplate = templateFields[selectedTemplate]['fields'];

                // Check templatefields for condition config to see what templatefields need to be checked
                for (var templatefieldName in fieldsForTemplate) {
                    if (fieldsForTemplate.hasOwnProperty(templatefieldName) && fieldsForTemplate[templatefieldName].hasOwnProperty('condition')) {
                        templatefieldToAdd = fieldsForTemplate[templatefieldName];

                        fieldName = fieldsForTemplate[templatefieldName]['condition']['field'];

                        if (fieldsToWatch.indexOf(fieldName) === -1) {
                            // Fields to watch the value change of
                            fieldsToWatch.push(fieldName);
                        }

                        // Add to Conditional fields
                        conditionalFields['templatefields-' + templatefieldName] = fieldsForTemplate[templatefieldName];
                    }
                }
            }
        }
    }

    function initTaxonomyFields () {
        // Check taxonomies for condition config to see what taxonomies need to be checked
        for (var taxName in allTaxonomies) {
            if (allTaxonomies.hasOwnProperty(taxName) && allTaxonomies[taxName].hasOwnProperty('condition')) {
                taxonomyToAdd = allTaxonomies[taxName];

                if (taxonomyToAdd['condition'].hasOwnProperty(contentType)) {
                    fieldName = allTaxonomies[taxName]['condition'][contentType]['field'];

                    if (fieldsToWatch.indexOf(fieldName) === -1) {
                        // Fields to watch the value change of
                        fieldsToWatch.push(fieldName);
                    }

                    // Force the field type
                    taxonomyToAdd['type'] = 'taxonomy';

                    // Only use the condition for the relevant content type
                    taxonomyToAdd['condition'] = taxonomyToAdd['condition'][contentType];

                    // Add to Conditional fields
                    conditionalFields[taxName] = taxonomyToAdd;
                }
            }
        }
    }

    function showFieldset ($fieldContainer) {

        var $wasRequiredFields = $fieldContainer.find('.was-required');

        if ($wasRequiredFields.length) {
            $wasRequiredFields.each(function () {
                $(this).attr('required', 'required').removeClass('was-required');
            });
        }

        $fieldContainer.show();
    }

    function hideFieldset ($fieldContainer) {

        var $requiredFields = $fieldContainer.find('[required]');

        if ($requiredFields.length) {
            $requiredFields.each(function () {
                $(this).removeAttr('required').addClass('was-required');
            });
        }

        $fieldContainer.hide();
    }

    function checkTabPanes () {
        var $tabPanes = $('.tab-pane');
        var $fields   = null;

        $tabPanes.each(function () {
            // Find bolt fields
            $fields = $(this).find('[data-bolt-fieldset]');

            // Check which of these are display none (can't use hidden because inactive tabs return as hidden)
            $fields = $fields.filter(function () {
                return $(this).css("display") !== "none"
            });

            // Show / Hide the tab if it has visible contents
            if ($fields.length > 0) {
                $('#tabindicator-' + $(this).attr('id')).show();
            } else {
                $('#tabindicator-' + $(this).attr('id')).hide();
            }
        });
    }

    function watchFieldsForChange () {
        // Check for select2 changes
        $(document).on('change', 'select,input', function () {
            var id           = $(this).attr('id');
            var name         = $(this).attr('name');
            var watchedValue = $(this).val();

            if (!id) {
                id = name;
            }

            // Check if the changed select is inside a repeater
            if ($(this).parents('.repeater-field').length) {
                checkRepeaterFields(id, watchedValue);
            }

            // Check the changed field is one of the fieldsToWatch
            if (fieldsToWatch.indexOf(id) > -1) {
                checkConditionalFields(id, watchedValue);
            }
        }).on('click', 'input[type="checkbox"], button', function () {
            var $field = $(this);

            if ($field.is('button')) {
                $field = $field.prev();
            }

            var id           = $field.attr('name');
            var name         = id;
            var watchedValue = $field.val();

            if (!$field.is(':checked')) {
                watchedValue = '0';
            }

            // Check if the changed select is inside a repeater
            if ($field.parents('.repeater-field').length) {
                checkRepeaterFields(id, watchedValue);
            }

            // Check the changed field is one of the fieldsToWatch
            if (fieldsToWatch.indexOf(id) > -1) {
                checkConditionalFields(id, watchedValue);
            }
        });
    }
})(jQuery);
<|endoftext|>


<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 */

var TG = {};

TG.Texture = function ( width, height ) {

	this.width = width;
	this.height = height;

	this.array = new Float32Array( width * height * 4 );
	this.arrayCopy = new Float32Array( width * height * 4 );

	return this;

};

TG.Texture.prototype = {

	constructor: TG.Texture,

	pass: function ( program, operation ) {

		if ( operation === undefined ) operation = '';

		var color = program.getColor();
		var source = program.getSource();

		this.arrayCopy.set( this.array );

		var string = [
			'var x = 0, y = 0;',
			'for ( var i = 0, il = dst.length; i < il; i += 4 ) {',
				'	' + source,
				color[ 0 ] !== 0 ? '	dst[ i + 0 ] ' + operation + '= color * ' + color[ 0 ] + ';' : '',
				color[ 1 ] !== 0 ? '	dst[ i + 1 ] ' + operation + '= color * ' + color[ 1 ] + ';' : '',
				color[ 2 ] !== 0 ? '	dst[ i + 2 ] ' + operation + '= color * ' + color[ 2 ] + ';' : '',
				'	if ( ++x === width ) { x = 0; y ++; }',
			'}'
		].join( '\n' );

		new Function( 'dst, src, width, height', string )( this.array, this.arrayCopy, this.width, this.height );

		return this;

	},

	add: function ( program ) {

		return this.pass( program, '+' );

	},

	sub: function ( program ) {

		return this.pass( program, '-' );

	},

	mul: function ( program ) {

		return this.pass( program, '*' );

	},

	div: function ( program ) {

		return this.pass( program, '/' );

	},

	toImageData: function ( context ) {

		var array = this.array;

		var imagedata = context.createImageData( this.width, this.height );
		var data = imagedata.data;

		for ( var i = 0, il = array.length; i < il; i += 4 ) {

			data[ i		 ] = array[ i		 ] * 255;
			data[ i + 1 ] = array[ i + 1 ] * 255;
			data[ i + 2 ] = array[ i + 2 ] * 255;
			data[ i + 3 ] = 255;

		}

		return imagedata;

	},

	toCanvas: function () {

		var canvas = document.createElement( 'canvas' );
		canvas.width = this.width;
		canvas.height = this.height;

		var context = canvas.getContext( '2d' );
		var imagedata = this.toImageData( context );

		context.putImageData( imagedata, 0, 0 );

		return canvas;

	}

};

TG.Program = function ( object ) {

	var color = [ 1, 1, 1 ];

	object.color = function ( r, g, b ) {
		color = [ r, g, b ];
		return this;
	};

	object.getColor = function () {
		return color;
	};

	return object;

};

TG.Number = function () {

	return new TG.Program( {
		getSource: function () {
			return 'var color = 1;';
		}
	} );

};

TG.SinX = function () {

	var frequency = 1;
	var offset = 0;

	return new TG.Program( {
		frequency: function ( value ) {
			frequency = value * Math.PI;
			return this;
		},
		offset: function ( value ) {
			offset = value;
			return this;
		},
		getSource: function () {
			return 'var color = Math.sin( ( x + ' + offset + ' ) * ' + frequency + ' );';
		}
	} );

};

TG.SinY = function () {

	var frequency = 1;
	var offset = 0;

	return new TG.Program( {
		frequency: function ( value ) {
			frequency = value * Math.PI;
			return this;
		},
		offset: function ( value ) {
			offset = value;
			return this;
		},
		getSource: function () {
			return 'var color = Math.sin( ( y + ' + offset + ' ) * ' + frequency + ' );';
		}
	} );

};

TG.OR = function () {

	return new TG.Program( {
		getSource: function () {
			return 'var color = ( x | y ) / width;';
		}
	} );

};

TG.XOR = function () {

	return new TG.Program( {
		getSource: function () {
			return 'var color = ( x ^ y ) / width;';
		}
	} );

};

TG.Noise = function () {

	return new TG.Program( {
		getSource: function () {
			return 'var color = Math.random();';
		}
	} );

};

TG.CheckerBoard = function () {

	var size = [ 32, 32 ];
	var offset = [ 0, 0 ];

	return new TG.Program( {
		size: function ( x, y ) {
			size = [ x, y ];
			return this;
		},
		offset: function ( x, y ) {
			offset = [ x, y ];
			return this;
		},
		getSource: function () {
			return 'var color = ( ( ( y + ' + offset[ 1 ] + ' ) / ' + size[ 1 ] + ' ) & 1 ) ^ ( ( ( x + ' + offset[ 0 ] + ' ) / ' + size[ 0 ] + ' ) & 1 ) ? 0 : 1';
		}
	} );

};

TG.Rect = function () {

	var position = [ 0, 0 ];
	var size = [ 32, 32 ];

	return new TG.Program( {
		position: function ( x, y ) {
			position = [ x, y ];
			return this;
		},
		size: function ( x, y ) {
			size = [ x, y ];
			return this;
		},
		getSource: function () {

			return 'var color = ( x >= ' + position[ 0 ] + ' && x <= ' + ( position[ 0 ] + size[ 0 ] ) + ' && y <= ' + ( position[ 1 ] + size[ 1 ] ) + ' && y >= ' + position[ 1 ] + ' ) ? 1 : 0;';

		}
	} );

};
<commit_msg>Sinedistort<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 */

var TG = {};

TG.Texture = function ( width, height ) {

	this.width = width;
	this.height = height;

	this.array = new Float32Array( width * height * 4 );
	this.arrayCopy = new Float32Array( width * height * 4 );

	return this;

};

TG.Texture.prototype = {

	constructor: TG.Texture,

	pass: function ( program, operation ) {

		if ( operation === undefined ) operation = '';

		var color = program.getColor();
		var source = program.getSource();

		this.arrayCopy.set( this.array );

		var string = [
			'var x = 0, y = 0;',
			'for ( var i = 0, il = dst.length; i < il; i += 4 ) {',
				'	' + source,
				color[ 0 ] !== 0 ? '	dst[ i + 0 ] ' + operation + '= color * ' + color[ 0 ] + ';' : '',
				color[ 1 ] !== 0 ? '	dst[ i + 1 ] ' + operation + '= color * ' + color[ 1 ] + ';' : '',
				color[ 2 ] !== 0 ? '	dst[ i + 2 ] ' + operation + '= color * ' + color[ 2 ] + ';' : '',
				'	if ( ++x === width ) { x = 0; y ++; }',
			'}'
		].join( '\n' );

		new Function( 'dst, src, width, height', string )( this.array, this.arrayCopy, this.width, this.height );

		return this;

	},

	add: function ( program ) {

		return this.pass( program, '+' );

	},

	sub: function ( program ) {

		return this.pass( program, '-' );

	},

	mul: function ( program ) {

		return this.pass( program, '*' );

	},

	div: function ( program ) {

		return this.pass( program, '/' );

	},

	toImageData: function ( context ) {

		var array = this.array;

		var imagedata = context.createImageData( this.width, this.height );
		var data = imagedata.data;

		for ( var i = 0, il = array.length; i < il; i += 4 ) {

			data[ i		 ] = array[ i		 ] * 255;
			data[ i + 1 ] = array[ i + 1 ] * 255;
			data[ i + 2 ] = array[ i + 2 ] * 255;
			data[ i + 3 ] = 255;

		}

		return imagedata;

	},

	toCanvas: function () {

		var canvas = document.createElement( 'canvas' );
		canvas.width = this.width;
		canvas.height = this.height;

		var context = canvas.getContext( '2d' );
		var imagedata = this.toImageData( context );

		context.putImageData( imagedata, 0, 0 );

		return canvas;

	}

};

TG.Program = function ( object ) {

	var color = [ 1, 1, 1 ];

	object.color = function ( r, g, b ) {
		color = [ r, g, b ];
		return this;
	};

	object.getColor = function () {
		return color;
	};

	return object;

};

TG.Number = function () {

	return new TG.Program( {
		getSource: function () {
			return 'var color = 1;';
		}
	} );

};

TG.SinX = function () {

	var frequency = 1;
	var offset = 0;

	return new TG.Program( {
		frequency: function ( value ) {
			frequency = value * Math.PI;
			return this;
		},
		offset: function ( value ) {
			offset = value;
			return this;
		},
		getSource: function () {
			return 'var color = Math.sin( ( x + ' + offset + ' ) * ' + frequency + ' );';
		}
	} );

};

TG.SinY = function () {

	var frequency = 1;
	var offset = 0;

	return new TG.Program( {
		frequency: function ( value ) {
			frequency = value * Math.PI;
			return this;
		},
		offset: function ( value ) {
			offset = value;
			return this;
		},
		getSource: function () {
			return 'var color = Math.sin( ( y + ' + offset + ' ) * ' + frequency + ' );';
		}
	} );

};

TG.OR = function () {

	return new TG.Program( {
		getSource: function () {
			return 'var color = ( x | y ) / width;';
		}
	} );

};

TG.XOR = function () {

	return new TG.Program( {
		getSource: function () {
			return 'var color = ( x ^ y ) / width;';
		}
	} );

};

TG.Noise = function () {

	return new TG.Program( {
		getSource: function () {
			return 'var color = Math.random();';
		}
	} );

};

TG.CheckerBoard = function () {

	var size = [ 32, 32 ];
	var offset = [ 0, 0 ];

	return new TG.Program( {
		size: function ( x, y ) {
			size = [ x, y ];
			return this;
		},
		offset: function ( x, y ) {
			offset = [ x, y ];
			return this;
		},
		getSource: function () {
			return 'var color = ( ( ( y + ' + offset[ 1 ] + ' ) / ' + size[ 1 ] + ' ) & 1 ) ^ ( ( ( x + ' + offset[ 0 ] + ' ) / ' + size[ 0 ] + ' ) & 1 ) ? 0 : 1';
		}
	} );

};

TG.Rect = function () {

	var position = [ 0, 0 ];
	var size = [ 32, 32 ];

	return new TG.Program( {
		position: function ( x, y ) {
			position = [ x, y ];
			return this;
		},
		size: function ( x, y ) {
			size = [ x, y ];
			return this;
		},
		getSource: function () {

			return 'var color = ( x >= ' + position[ 0 ] + ' && x <= ' + ( position[ 0 ] + size[ 0 ] ) + ' && y <= ' + ( position[ 1 ] + size[ 1 ] ) + ' && y >= ' + position[ 1 ] + ' ) ? 1 : 0;';

		}
	} );

};

TG.SineDistort = function () {

	var sines = [ 1, 1 ];
	var offset = [ 0, 0 ];
	var amplitude = [ 0, 0 ];

	return new TG.Program( {
		sines: function ( x, y ) {
			sines = [ x, y ];
			return this;
		},
		offset: function ( x, y ) {
			offset = [ x, y ];
			return this;
		},
		amplitude: function ( x, y ) {
			amplitude = [ x, y ];
			return this;
		},
		getSource: function () {
			return [
				'var sx = Math.sin(' + sines[ 0 ] / 100 + ' * y + ' + offset[ 0 ] + ') * ' + amplitude[ 0 ] + ' + x;',
				'var sy = Math.sin(' + sines[ 1 ] / 100 + ' * x + ' + offset[ 1 ] + ') * ' + amplitude[ 1 ] + ' + y;',
				'var color = src[ parseInt( sy ) * width * 4 + parseInt( sx ) * 4 ];'
				].join( '\n' );
		}
	} );
}<|endoftext|>


<commit_before>"use strict";

const EventEmitter = require("events").EventEmitter;
const util = require("util");
const rp = require("request-promise");

/**
 * Represents a Marathon Event Listener
 * @constructor
 * @param {object} options - The option map object.
 */
function SlackHandler (options) {

    if (!(this instanceof SlackHandler)) {
        return new SlackHandler(options);
    }

    // Inherit from EventEmitter
    EventEmitter.call(this);

    let self = this;

    self.slackWebHook = options.slackWebHook|| process.env.SLACK_WEBHOOK_URL;
    self.slackChannel = options.slackChannel || process.env.SLACK_CHANNEL || "#marathon";
    self.slackBotName = options.slackBotName || process.env.SLACK_BOT_NAME || "Marathon Event Bot";

}

// Inherit from EventEmitter
util.inherits(SlackHandler, EventEmitter);

SlackHandler.prototype.renderMessage = function (event) {

    let self = this;

    self.emit("received_event", {
        timestamp: event.data.timestamp,
        eventType: event.type,
        data: event.data
    });

    function clearDateString (dateString) {
        return "`" + dateString.replace("Z", "").replace("T", " ") + "`";
    }

    function toUnixTimestamp (dateString) {
        return parseInt((new Date(dateString).getTime() / 1000).toFixed(0));
    }

    function parseEvents (event) {

        let message = {
            "username": self.slackBotName,
            "icon_url": "http://i.imgur.com/5FJDbGz.png",
            "mrkdwn": true
        };

        let attachment = {};

        switch (event.type) {

            case "deployment_info":
                attachment = {
                    "fallback": "Deployment was triggered.",
                    "title": "Deployment info",
                    "text": "The deployment `" + event.data.plan.id + "` triggered the following steps:",
                    "fields": [],
                    "color": "#0066cc",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                let diSteps = 1;
                event.data.plan.steps.forEach(function (step) {
                    step.actions.forEach(function (action) {
                        attachment.fields.push({
                            "title": diSteps.toString() + ". " + (action.action ? action.action : (action.type ? action.type : "Step")),
                            "value": action.app,
                            "short": true
                        });
                        diSteps++;
                    });
                });
                message.attachments = [attachment];
                break;
            case "deployment_success":
                attachment = {
                    "fallback": "Deployment was successful.",
                    "title": "Deployment success",
                    "text": "The deployment `" + event.data.id + "` was completed successfully at " + clearDateString(event.data.timestamp),
                    "color": "#7CD197",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            case "deployment_failed":
                attachment = {
                    "fallback": "Deployment failed.",
                    "title": "Deployment failed",
                    "text": "The deployment `" + event.data.id + "` failed at " + clearDateString(event.data.timestamp),
                    "color": "#ff0000",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            case "deployment_step_success":
                attachment = {
                    "fallback": "Deployment step was completed.",
                    "title": "Deployment step(s) success",
                    "text": "The deployment `" + event.data.plan.id + "` completed the following steps:",
                    "fields": [],
                    "color": "#7CD197",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                let dssSteps = 1;
                event.data.plan.steps.forEach(function (step) {
                    step.actions.forEach(function (action) {
                        attachment.fields.push({
                            "title": dssSteps.toString() + ". " + (action.action ? action.action : (action.type ? action.type : "Step")),
                            "value": action.app,
                            "short": true
                        });
                        dssSteps++;
                    });
                });
                message.attachments = [attachment];
                break;
            case "deployment_step_failure":
                attachment = {
                    "fallback": "Deployment step failed.",
                    "title": "Deployment step(s) failed",
                    "text": "The deployment `" + event.data.plan.id + "` failed at the following steps:",
                    "fields": [],
                    "color": "#ff0000",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                let dsfSteps = 1;
                event.data.plan.steps.forEach(function (step) {
                    step.actions.forEach(function (action) {
                        attachment.fields.push({
                            "title": dsfSteps.toString() + ". " + (action.action ? action.action : (action.type ? action.type : "Step")),
                            "value": action.app,
                            "short": true
                        });
                        dsfSteps++;
                    });
                });
                message.attachments = [attachment];
                break;
            case "group_change_success":
                attachment = {
                    "fallback": "Group change was completed.",
                    "title": "Group change completed",
                    "text": "The group `" + event.data.groupId + "` completed at " + clearDateString(event.data.timestamp) + ".",
                    "color": "#7CD197",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            case "group_change_failed":
                attachment = {
                    "fallback": "Group change failed.",
                    "title": "Group change failed",
                    "text": "The group `" + event.data.groupId + "` failed at " + clearDateString(event.data.timestamp) + ".",
                    "color": "#ff0000",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            case "failed_health_check_event":
                attachment = {
                    "fallback": "App health check failed.",
                    "title": "App health check failed",
                    "text": "The app `" + event.data.appId + "` (with task id `" + event.data.taskId + "`) failed it's health check at " + clearDateString(event.data.timestamp),
                    "color": "#ff9900",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            case "health_status_changed_event":
                attachment = {
                    "fallback": "App health status changed.",
                    "title": "App health check status changed",
                    "text": "The app `" + event.data.appId + "` (with task id `" + event.data.taskId + "`) changed it's health check status at " + clearDateString(event.data.timestamp) + " to " + (event.data.alive ? "*healthy*" : "*unhealthy*"),
                    "color": (event.data.alive ? "#7CD197" : "#ff0000"),
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            case "unhealthy_task_kill_event":
                attachment = {
                    "fallback": "Unhealthy task was killed.",
                    "title": "Unhealthy task was killed",
                    "text": "The app `" + event.data.appId + "` had it's task with id `" + event.data.taskId + "` killed at " + clearDateString(event.data.timestamp) + " due to an '" + event.data.reason + "' error",
                    "color": "#ff0000",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            default:
                attachment = {
                    "fallback": "Event type " + event.type + " received.",
                    "title": "Event type " + event.type + " received.",
                    "text": "An event of type " + event.type + " was received.",
                    "color": "#0066cc",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;

        }

        return message;

    }

    return parseEvents(event);

};

SlackHandler.prototype.sendMessage = function (message) {

    let self = this;

    let options = {
        method: "POST",
        uri: self.slackWebHook,
        body: message,
        json: true // Automatically stringifies the body to JSON
    };

    rp(options)
        .then(function (parsedBody) {
            self.emit("sent_message", message);
            self.emit("received_reply", parsedBody);
        })
        .catch(function (error) {
            self.emit("error", error)
        });

};

module.exports = SlackHandler;
<commit_msg>Bugfix for displaying actions in the case of `deployment_info` events<commit_after>"use strict";

const EventEmitter = require("events").EventEmitter;
const util = require("util");
const rp = require("request-promise");

/**
 * Represents a Marathon Event Listener
 * @constructor
 * @param {object} options - The option map object.
 */
function SlackHandler (options) {

    if (!(this instanceof SlackHandler)) {
        return new SlackHandler(options);
    }

    // Inherit from EventEmitter
    EventEmitter.call(this);

    let self = this;

    self.slackWebHook = options.slackWebHook|| process.env.SLACK_WEBHOOK_URL;
    self.slackChannel = options.slackChannel || process.env.SLACK_CHANNEL || "#marathon";
    self.slackBotName = options.slackBotName || process.env.SLACK_BOT_NAME || "Marathon Event Bot";

}

// Inherit from EventEmitter
util.inherits(SlackHandler, EventEmitter);

SlackHandler.prototype.renderMessage = function (event) {

    let self = this;

    self.emit("received_event", {
        timestamp: event.data.timestamp,
        eventType: event.type,
        data: event.data
    });

    function clearDateString (dateString) {
        return "`" + dateString.replace("Z", "").replace("T", " ") + "`";
    }

    function toUnixTimestamp (dateString) {
        return parseInt((new Date(dateString).getTime() / 1000).toFixed(0));
    }

    function parseEvents (event) {

        let message = {
            "username": self.slackBotName,
            "icon_url": "http://i.imgur.com/5FJDbGz.png",
            "mrkdwn": true
        };

        let attachment = {};

        switch (event.type) {

            case "deployment_info":
                attachment = {
                    "fallback": "Deployment was triggered.",
                    "title": "Deployment info",
                    "text": "The deployment `" + event.data.plan.id + "` triggered step `" + event.data.currentStep.actions[0].action + "` of the following steps:",
                    "fields": [],
                    "color": "#0066cc",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                let diSteps = 1;
                event.data.plan.steps.forEach(function (step) {
                    step.actions.forEach(function (action) {
                        attachment.fields.push({
                            "title": diSteps.toString() + ". " + (action.action ? action.action : (action.type ? action.type : "Step")),
                            "value": action.app,
                            "short": true
                        });
                        diSteps++;
                    });
                });
                message.attachments = [attachment];
                break;
            case "deployment_success":
                attachment = {
                    "fallback": "Deployment was successful.",
                    "title": "Deployment success",
                    "text": "The deployment `" + event.data.id + "` was completed successfully at " + clearDateString(event.data.timestamp),
                    "color": "#7CD197",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            case "deployment_failed":
                attachment = {
                    "fallback": "Deployment failed.",
                    "title": "Deployment failed",
                    "text": "The deployment `" + event.data.id + "` failed at " + clearDateString(event.data.timestamp),
                    "color": "#ff0000",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            case "deployment_step_success":
                attachment = {
                    "fallback": "Deployment step was completed.",
                    "title": "Deployment step(s) success",
                    "text": "The deployment `" + event.data.plan.id + "` completed the following steps:",
                    "fields": [],
                    "color": "#7CD197",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                let dssSteps = 1;
                event.data.plan.steps.forEach(function (step) {
                    step.actions.forEach(function (action) {
                        attachment.fields.push({
                            "title": dssSteps.toString() + ". " + (action.action ? action.action : (action.type ? action.type : "Step")),
                            "value": action.app,
                            "short": true
                        });
                        dssSteps++;
                    });
                });
                message.attachments = [attachment];
                break;
            case "deployment_step_failure":
                attachment = {
                    "fallback": "Deployment step failed.",
                    "title": "Deployment step(s) failed",
                    "text": "The deployment `" + event.data.plan.id + "` failed at the following steps:",
                    "fields": [],
                    "color": "#ff0000",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                let dsfSteps = 1;
                event.data.plan.steps.forEach(function (step) {
                    step.actions.forEach(function (action) {
                        attachment.fields.push({
                            "title": dsfSteps.toString() + ". " + (action.action ? action.action : (action.type ? action.type : "Step")),
                            "value": action.app,
                            "short": true
                        });
                        dsfSteps++;
                    });
                });
                message.attachments = [attachment];
                break;
            case "group_change_success":
                attachment = {
                    "fallback": "Group change was completed.",
                    "title": "Group change completed",
                    "text": "The group `" + event.data.groupId + "` completed at " + clearDateString(event.data.timestamp) + ".",
                    "color": "#7CD197",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            case "group_change_failed":
                attachment = {
                    "fallback": "Group change failed.",
                    "title": "Group change failed",
                    "text": "The group `" + event.data.groupId + "` failed at " + clearDateString(event.data.timestamp) + ".",
                    "color": "#ff0000",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            case "failed_health_check_event":
                attachment = {
                    "fallback": "App health check failed.",
                    "title": "App health check failed",
                    "text": "The app `" + event.data.appId + "` (with task id `" + event.data.taskId + "`) failed it's health check at " + clearDateString(event.data.timestamp),
                    "color": "#ff9900",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            case "health_status_changed_event":
                attachment = {
                    "fallback": "App health status changed.",
                    "title": "App health check status changed",
                    "text": "The app `" + event.data.appId + "` (with task id `" + event.data.taskId + "`) changed it's health check status at " + clearDateString(event.data.timestamp) + " to " + (event.data.alive ? "*healthy*" : "*unhealthy*"),
                    "color": (event.data.alive ? "#7CD197" : "#ff0000"),
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            case "unhealthy_task_kill_event":
                attachment = {
                    "fallback": "Unhealthy task was killed.",
                    "title": "Unhealthy task was killed",
                    "text": "The app `" + event.data.appId + "` had it's task with id `" + event.data.taskId + "` killed at " + clearDateString(event.data.timestamp) + " due to an '" + event.data.reason + "' error",
                    "color": "#ff0000",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;
            default:
                attachment = {
                    "fallback": "Event type " + event.type + " received.",
                    "title": "Event type " + event.type + " received.",
                    "text": "An event of type " + event.type + " was received.",
                    "color": "#0066cc",
                    "mrkdwn_in": ["text"],
                    "ts": toUnixTimestamp(event.data.timestamp)
                };
                message.attachments = [attachment];
                break;

        }

        return message;

    }

    return parseEvents(event);

};

SlackHandler.prototype.sendMessage = function (message) {

    let self = this;

    let options = {
        method: "POST",
        uri: self.slackWebHook,
        body: message,
        json: true // Automatically stringifies the body to JSON
    };

    rp(options)
        .then(function (parsedBody) {
            self.emit("sent_message", message);
            self.emit("received_reply", parsedBody);
        })
        .catch(function (error) {
            self.emit("error", error)
        });

};

module.exports = SlackHandler;
<|endoftext|>


<commit_before>var _Action = new function () {
    var availableActions = new Array('none', 'addpoint', 'removepoint', 'addconnection', 'removeconnection');
    this.current = 'none'
    this.change = function (action) {
        if (typeof action != 'undefined') {
            action = action.toLowerCase();

            if (availableActions.indexOf(action) != -1) {
                var body = $('body');

                body.removeClass(this.current);
                if (action != 'none') {
                    body.addClass(action);
                }

                this.current = action;
            } else {
                _Action.toDefault();
                throw 'Action not exists!';
            }
        }
    };
    this.toDefault = function () {
        this.change('none');
    };
    this.currentIs = function (action) {
        return (this.current == action.toLowerCase());
    };
    this.getCurrent = function () {
        return this.current;
    };
};

var _Stage = new function () {
    this.stage = undefined;
    this.create = function () {
        if (typeof this.stage == 'undefined') {
            this.stage = new Kinetic.Stage({
                container: 'canvas-area',
                height: 500,
                width: 1000
            });

            var baseLayer = new Kinetic.Layer();

            var connectionsLayer = new Kinetic.Layer();

            var pointsLayer = new Kinetic.Layer();

            this.stage
                .add(baseLayer)
                .add(connectionsLayer)
                .add(pointsLayer)
                .draw();
        } else {
            throw 'Stage exists!';
        }
    };
    this.getConnectionsLayer = function () {
        return this.stage
                   .children[1];
    };
    this.refreshConnectionsLayer = function () {
        this.getConnectionsLayer()
            .draw();
    };
    this.getPointsLayer = function () {
        return this.stage
                   .children[2];
    };
    this.refreshPointsLayer = function () {
        this.getPointsLayer()
            .draw();
    };
    this.clear = function () {
        if (typeof this.stage != 'undefined') {
            this.getConnectionsLayer()
                .removeChildren()
                .draw();
            _Connections.clear();

            this.getPointsLayer()
                .removeChildren()
                .draw();
            _Points.clear();
        }
    };
    this.reload = function (graphName) {
        if (graphName == '') {
            throw 'Graph name is empty!';
        } else {
            this.clear();

            var pointsLayer = this.getPointsLayer();

            this.loadGraph(graphName);
            _Points.eachOne(function (point) {
                pointsLayer.add(_Points[a]);
            });
            pointsLayer.draw();
        }
    };
    this.loadGraph = function (name) {
        $.ajax({
            data: 'graphName=' + name,
            dataType: 'json',
            method: 'post',
            url: '/load-graph',
            success: function (data) {
                if (data.success) {
                    for (var a = 0; a < data.pointscount; a++) {
                        _Points.add(data.points[a].x, data.points[a].y);
                    }

                    for (var a = 0; a < data.connectionscount; a++) {
                        try {
                            _Points.getPoint(data.connections[a].from);
                            _Points.getPoint(data.connections[a].to);

                            _Connection.add(data.connections[a].from);
                            _Connection.add(data.connections[a].to);
                        } catch (e) {
                            console.log(e);
                        }
                    }
                }
            }
        });
    };
    this.add = function (element) {
        return this.stage
                   .add(element);
    };
    this.draw = function () {
        return this.stage
                   .draw();
    };
};

var _Points = new function (points) {
    this.points = new Array();
    this.getPoint = function(n) {
        if (this.points.length == 0) {
            throw 'Points array is clear!';
        } else {
            if ((n < this.points.length) && (n >= 0)) {
                return this.points[n];
            } else {
                throw 'Point not exists!';
            }
        }
    };
    this.eachOne = function (func) {
        if (this.points.length > 0) {
            for (var a = 0; a < this.points.length; a++) {
                func(this.points[a]);
            }
        }
    };
    this.check = function (x, y) {
        if (this.points.length > 0) {
            for (var a = 0; a < this.points.length; a++) {
                if ((this.points[a].getX() == x) && (this.points[a].getY() == y)) {
                    return false;
                }
            }

            return true;
        } else {
            return true;
        }
    };
    this.clear = function () {
        if (typeof this.points != 'undefined') {
            delete this.points;
            this.points = new Array();
        }
    };
    this.add = function (x, y) {
        if (this.check(x, y)) {
            var point = new Kinetic.Circle({
                fill: '#fff',
                radius: 6,
                stroke: '#000',
                strokeWidth: 1,
                x: x,
                y: y
            });

            point.on('click', function () {
                switch (_Action.getCurrent()) {
                    case 'removepoint':
                        _Points.remove(this.getX(), this.getY());

                        _Stage.refreshPointsLayer();
                        _Stage.refreshConnectionsLayer();
                        break;
                    case 'addconnection':
                        _Connection.add(_Points.points
                                               .indexOf(this));
                        break;
                }
            });

            this.points
                .push(point);

            _Stage.getPointsLayer()
                  .add(point)
                  .draw();
        } else {
            alert('Point on position (' + x + ', ' + y + ') exists!');
        }
    };
    this.remove = function (x, y) {
        if (this.points.length > 0) {
            for (var a = 0; a < this.points.length; a++) {
                if ((this.points[a].getX() == x) && (this.points[a].getY() == y)) {
                    this.points[a]
                        .remove();
                    delete this.points[a];

                    this.points = this.points.slice(0, a)
                                      .concat(this.points
                                                  .slice(a + 1));

                    _Connections.removeFromPoint(a);
                    break;
                }
            }
        }
    };
    this.toSave = function () {
        var saveString = '';
        for (var a = 0; a < this.points.length; a++) {
            if (saveString != '') {
                saveString += ',';
            }

            saveString += this.points[a]
                              .getX() +
                          ':' +
                          this.points[a]
                              .getY();
        }
        return saveString;
    };
    this.getID = function (x, y) {
        if (this.points.length > 0) {
            for (var a = 0; a < this.points.length; a++) {
                if ((this.points[a].getX() == x) && (this.points[a].getY() == y)) {
                    return a;
                }
            }

            return -1;
        } else {
            return -1;
        }
    };
};

var _Connections = new function () {
    this.connections = new Array();
    this.add = function (connection) {
        _Action.toDefault();

        try {
            if ((connection.getEnd(0) != -1) && (connection.getEnd(1) != -1) && (connection.getEnd(0) != connection.getEnd(1))) {
                var first = _Points.getPoint(connection.getEnd(0)),
                    second = _Points.getPoint(connection.getEnd(1));

                var line = new Kinetic.Line({
                    points: [first.getX(), first.getY(), second.getX(), second.getY()],
                    stroke: '#000',
                    strokeWidth: 1.2
                });

                line.idxs = new Array(connection.getEnd(0), connection.getEnd(1));

                line.on('click', function () {
                    if (_Action.getCurrent() == 'removeconnection') {
                        _Connections.remove(this.idxs);

                        _Stage.refreshConnectionsLayer();
                    }
                });

                this.connections
                    .push(line);

                _Stage.getConnectionsLayer()
                      .add(line)
                      .draw();
            }

            _Connection.clear();
        } catch (e) {
            _Connection.clear();
            throw 'Wrong connection object!';
        }
    };
    this.clear = function () {
        if (typeof this.connections != 'undefined') {
            delete this.connections;
            this.connections = new Array();
        }
    };
    this.toSave = function () {
        var saveString = '';
        for (var a = 0; a < this.connections.length; a++) {
            if (saveString != '') {
                saveString += ',';
            }

            saveString += this.connections[a]
                              .idxs[0] +
                          ':' +
                          this.connections[a]
                              .idxs[1];
        }
        return saveString;
    };
    this.removeFromPoint = function (pointN) {
        var newConnections = new Array();

        for (var a = 0; a < this.connections.length; a++) {
            if ((this.connections[a].idxs[0] == pointN) || (this.connections[a].idxs[1] == pointN)) {
                this.connections[a]
                    .remove();
                delete this.connections[a];
            } else {
                if (this.connections[a].idxs[0] > pointN) {
                    this.connections[a]
                        .idxs[0]--;
                }
                if (this.connections[a].idxs[1] > pointN) {
                    this.connections[a]
                        .idxs[1]--;
                }

                newConnections.push(this.connections[a]);
            }
        }

        this.connections = newConnections;
    };
    this.remove = function (idxs) {
        for (var a = 0; a < this.connections.length; a++) {
            if ((this.connections[a].idxs[0] == idxs[0]) && (this.connections[a].idxs[1] == idxs[1])) {
                this.connections[a]
                    .remove();
                delete this.connections[a];

                this.connections = this.connections.slice(0, a)
                                       .concat(this.connections
                                                   .slice(a + 1));
            }
        }
    };
    this.checkConnection = function (id1, id2) {
        for (var a = 0; a < this.connections.length; a++) {
            if (((this.connections[a].idxs[0] == id1) && (this.connections[a].idxs[1] == id2)) ||
                ((this.connections[a].idxs[0] == id2) && (this.connections[a].idxs[1] == id1))) {
                return false;
            }
        }

        return true;
    };
};

var _Connection = new function () {
    this.ends = new Array(-1, -1);
    this.add = function (id) {
        if (this.ends[0] == -1) {
            this.ends[0] = id;
        } else {
            _Action.toDefault();

            if (id == this.ends[0]) {
                this.clear();
            } else {
                this.ends[1] = id;

                if (_Connections.checkConnection(this.ends[0], this.ends[1])) {
                    _Connections.add(this);
                } else {
                    this.clear();
                }
            }
        }
    };
    this.clear = function () {
        this.ends[0] = this.ends[1]
                     = -1;
        _Action.toDefault();
    };
    this.getEnd = function (n) {
        return this.ends[n];
    };
};
<commit_msg>Drag points with connections added<commit_after>var _Action = new function () {
    var availableActions = new Array('none', 'addpoint', 'removepoint', 'addconnection', 'removeconnection');
    this.current = 'none'
    this.change = function (action) {
        if (typeof action != 'undefined') {
            action = action.toLowerCase();

            if (availableActions.indexOf(action) != -1) {
                var body = $('body');

                body.removeClass(this.current);
                if (action != 'none') {
                    body.addClass(action);
                }

                this.current = action;
                console.log('Changed to ' + action);
            } else {
                _Action.toDefault();
                throw 'Action not exists!';
            }
        }
    };
    this.toDefault = function () {
        this.change('none');
    };
    this.currentIs = function (action) {
        return (this.current == action.toLowerCase());
    };
    this.getCurrent = function () {
        return this.current;
    };
};

var _Stage = new function () {
    this.stage = undefined;
    this.create = function () {
        if (typeof this.stage == 'undefined') {
            this.stage = new Kinetic.Stage({
                container: 'canvas-area',
                height: 500,
                width: 1000
            });

            var baseLayer = new Kinetic.Layer();

            var connectionsLayer = new Kinetic.Layer();

            var pointsLayer = new Kinetic.Layer();

            this.stage
                .add(baseLayer)
                .add(connectionsLayer)
                .add(pointsLayer)
                .draw();
        } else {
            throw 'Stage exists!';
        }
    };
    this.getConnectionsLayer = function () {
        return this.stage
                   .children[1];
    };
    this.refreshConnectionsLayer = function () {
        this.getConnectionsLayer()
            .draw();
    };
    this.getPointsLayer = function () {
        return this.stage
                   .children[2];
    };
    this.refreshPointsLayer = function () {
        this.getPointsLayer()
            .draw();
    };
    this.clear = function () {
        if (typeof this.stage != 'undefined') {
            this.getConnectionsLayer()
                .removeChildren()
                .draw();
            _Connections.clear();

            this.getPointsLayer()
                .removeChildren()
                .draw();
            _Points.clear();
        }
    };
    this.reload = function (graphName) {
        if (graphName == '') {
            throw 'Graph name is empty!';
        } else {
            this.clear();

            var pointsLayer = this.getPointsLayer();

            this.loadGraph(graphName);
            _Points.eachOne(function (point) {
                pointsLayer.add(_Points[a]);
            });
            pointsLayer.draw();
        }
    };
    this.loadGraph = function (name) {
        $.ajax({
            data: 'graphName=' + name,
            dataType: 'json',
            method: 'post',
            url: '/load-graph',
            success: function (data) {
                if (data.success) {
                    for (var a = 0; a < data.pointscount; a++) {
                        _Points.add(data.points[a].x, data.points[a].y);
                    }

                    for (var a = 0; a < data.connectionscount; a++) {
                        try {
                            _Points.getPoint(data.connections[a].from);
                            _Points.getPoint(data.connections[a].to);

                            _Connection.add(data.connections[a].from);
                            _Connection.add(data.connections[a].to);
                        } catch (e) {
                            console.log(e);
                        }
                    }
                }
            }
        });
    };
    this.add = function (element) {
        return this.stage
                   .add(element);
    };
    this.draw = function () {
        return this.stage
                   .draw();
    };
};

var _Points = new function (points) {
    this.points = new Array();
    this.getPoint = function(n) {
        if (this.points.length == 0) {
            throw 'Points array is clear!';
        } else {
            if ((n < this.points.length) && (n >= 0)) {
                return this.points[n];
            } else {
                throw 'Point not exists!';
            }
        }
    };
    this.eachOne = function (func) {
        if (this.points.length > 0) {
            for (var a = 0; a < this.points.length; a++) {
                func(this.points[a]);
            }
        }
    };
    this.check = function (x, y) {
        if (this.points.length > 0) {
            for (var a = 0; a < this.points.length; a++) {
                if ((this.points[a].getX() == x) && (this.points[a].getY() == y)) {
                    return false;
                }
            }

            return true;
        } else {
            return true;
        }
    };
    this.clear = function () {
        if (typeof this.points != 'undefined') {
            delete this.points;
            this.points = new Array();
        }
    };
    this.add = function (x, y) {
        if (this.check(x, y)) {
            var point = new Kinetic.Circle({
                draggable: true,
                fill: '#fff',
                radius: 6,
                stroke: '#000',
                strokeWidth: 1,
                x: x,
                y: y
            });

            point.attrs.x = parseInt(point.attrs.x);
            point.attrs.y = parseInt(point.attrs.y);

            point.on('click', function () {
                switch (_Action.getCurrent()) {
                    case 'removepoint':
                        _Points.remove(this.getX(), this.getY());

                        _Stage.refreshPointsLayer();
                        _Stage.refreshConnectionsLayer();
                        break;
                    case 'addconnection':
                        console.log('Add ' + _Points.points
                                               .indexOf(this));
                        _Connection.add(_Points.points
                                               .indexOf(this));
                        break;
                }
            })
            .on('dragstart', function () {
                _Action.toDefault();
            })
            .on('dragmove', function () {
                var pointIndex = _Points.points
                                        .indexOf(this);
                var connections = _Connections.getConnections(pointIndex);
                for (var a = 0; a < connections.length; a++) {
                    if (connections[a].idxs[0] == pointIndex) {
                        connections[a].attrs.points[0] = this.getX();
                        connections[a].attrs.points[1] = this.getY();
                    } else {
                        connections[a].attrs.points[2] = this.getX();
                        connections[a].attrs.points[3] = this.getY();
                    }
                }

                _Stage.getConnectionsLayer()
                      .draw();
            });

            this.points
                .push(point);

            _Stage.getPointsLayer()
                  .add(point)
                  .draw();
        } else {
            alert('Point on position (' + x + ', ' + y + ') exists!');
        }
    };
    this.remove = function (x, y) {
        if (this.points.length > 0) {
            for (var a = 0; a < this.points.length; a++) {
                if ((this.points[a].getX() == x) && (this.points[a].getY() == y)) {
                    this.points[a]
                        .remove();
                    delete this.points[a];

                    this.points = this.points.slice(0, a)
                                      .concat(this.points
                                                  .slice(a + 1));

                    _Connections.removeFromPoint(a);
                    break;
                }
            }
        }
    };
    this.toSave = function () {
        var saveString = '';
        for (var a = 0; a < this.points.length; a++) {
            if (saveString != '') {
                saveString += ',';
            }

            saveString += this.points[a]
                              .getX() +
                          ':' +
                          this.points[a]
                              .getY();
        }
        return saveString;
    };
    this.getID = function (x, y) {
        if (this.points.length > 0) {
            for (var a = 0; a < this.points.length; a++) {
                if ((this.points[a].getX() == x) && (this.points[a].getY() == y)) {
                    return a;
                }
            }

            return -1;
        } else {
            return -1;
        }
    };
};

var _Connections = new function () {
    this.connections = new Array();
    this.add = function (connection) {
        _Action.toDefault();

        try {
            if ((connection.getEnd(0) != -1) && (connection.getEnd(1) != -1) && (connection.getEnd(0) != connection.getEnd(1))) {
                var first = _Points.getPoint(connection.getEnd(0)),
                    second = _Points.getPoint(connection.getEnd(1));

                var line = new Kinetic.Line({
                    points: [first.getX(), first.getY(), second.getX(), second.getY()],
                    stroke: '#000',
                    strokeWidth: 1.4
                });

                line.idxs = new Array(connection.getEnd(0), connection.getEnd(1));

                line.on('click', function () {
                    if (_Action.getCurrent() == 'removeconnection') {
                        _Connections.remove(this.idxs);

                        _Stage.refreshConnectionsLayer();
                    }
                });

                this.connections
                    .push(line);

                _Stage.getConnectionsLayer()
                      .add(line)
                      .draw();
            }

            _Connection.clear();
        } catch (e) {
            _Connection.clear();
            throw 'Wrong connection object!';
        }
    };
    this.clear = function () {
        if (typeof this.connections != 'undefined') {
            delete this.connections;
            this.connections = new Array();
        }
    };
    this.toSave = function () {
        var saveString = '';
        for (var a = 0; a < this.connections.length; a++) {
            if (saveString != '') {
                saveString += ',';
            }

            saveString += this.connections[a]
                              .idxs[0] +
                          ':' +
                          this.connections[a]
                              .idxs[1];
        }
        return saveString;
    };
    this.removeFromPoint = function (pointN) {
        var newConnections = new Array();

        for (var a = 0; a < this.connections.length; a++) {
            if ((this.connections[a].idxs[0] == pointN) || (this.connections[a].idxs[1] == pointN)) {
                this.connections[a]
                    .remove();
                delete this.connections[a];
            } else {
                if (this.connections[a].idxs[0] > pointN) {
                    this.connections[a]
                        .idxs[0]--;
                }
                if (this.connections[a].idxs[1] > pointN) {
                    this.connections[a]
                        .idxs[1]--;
                }

                newConnections.push(this.connections[a]);
            }
        }

        this.connections = newConnections;
    };
    this.remove = function (idxs) {
        for (var a = 0; a < this.connections.length; a++) {
            if ((this.connections[a].idxs[0] == idxs[0]) && (this.connections[a].idxs[1] == idxs[1])) {
                this.connections[a]
                    .remove();
                delete this.connections[a];

                this.connections = this.connections.slice(0, a)
                                       .concat(this.connections
                                                   .slice(a + 1));
            }
        }
    };
    this.checkConnection = function (id1, id2) {
        for (var a = 0; a < this.connections.length; a++) {
            if (((this.connections[a].idxs[0] == id1) && (this.connections[a].idxs[1] == id2)) ||
                ((this.connections[a].idxs[0] == id2) && (this.connections[a].idxs[1] == id1))) {
                return false;
            }
        }

        return true;
    };
    this.getConnections = function (pointN) {
        var returnedConnetions = new Array();
        for (var a = 0; a < this.connections.length; a++) {
            if ((this.connections[a].idxs[0] == pointN) || (this.connections[a].idxs[1] == pointN)) {
                returnedConnetions.push(this.connections[a]);
            }
        }
        return returnedConnetions;
    };
};

var _Connection = new function () {
    this.ends = new Array(-1, -1);
    this.add = function (id) {
        if (this.ends[0] == -1) {
            this.ends[0] = id;
        } else {
            _Action.toDefault();

            if (id == this.ends[0]) {
                this.clear();
            } else {
                this.ends[1] = id;

                if (_Connections.checkConnection(this.ends[0], this.ends[1])) {
                    _Connections.add(this);
                } else {
                    this.clear();
                }
            }
        }
    };
    this.clear = function () {
        this.ends[0] = this.ends[1]
                     = -1;
        _Action.toDefault();
    };
    this.getEnd = function (n) {
        return this.ends[n];
    };
};
<|endoftext|>


<commit_before>'use strict';

/*!
 * clack: a node.js websocket server
 * Copyright(c) 2016 Suhail Gupta <suhailgupta03@gmail.com>
 * MIT Licensed
 */

var WebSocketServer = require("ws").Server
  , EventEmitter = require("events").EventEmitter
  , util = require("util")
  , Logger = require("./Logger");

/**
 * Socket Server implementation
 *
 * @constructor
 * @param {Integer} port Port to bind the WebSocket Server
 * @api public
 */

function SocketServer(port) {
    this.socketPort = port;
    // Starts WebSocket Server
    this.socketServer = new WebSocketServer({port: this.socketPort});
    var logger = new Logger(`Socket server started on ${this.socketPort}`,0).log();
}

/**
 * Inherits from WebSocketServer
 */
util.inherits(SocketServer,WebSocketServer);

/**
 * Activate the listeners to be associated with SocketServer class
 */
SocketServer.prototype.activateListeners = function() {
    var that = this;
    this.socketServer.on("connection",function(socket) {
        that.emit("newsocket",socket,200);
        socket.on("message",function(message) {
           that.emit("newmessage",message,200);
        });
    });
};

/**
 * Starts a socket server on a specified port.
 * The server is started on the port supplied (by default) when the object
 * is initialized.
 * Call this function to start the socket server on a different port.
 * It first closes and then starts the server on a different port
 */
SocketServer.prototype.restart = function(port) {
    return new SocketServer(port);
};

/**
 * Stops the WebSocket Server
 */
SocketServer.prototype.stop = function() {

}

// Exports
module.exports = SocketServer;<commit_msg>added a broadcast method<commit_after>'use strict';

/*!
 * clack: a node.js websocket server
 * Copyright(c) 2016 Suhail Gupta <suhailgupta03@gmail.com>
 * MIT Licensed
 */

var WebSocketServer = require("ws").Server
  , EventEmitter = require("events").EventEmitter
  , util = require("util")
  , Logger = require("./Logger");

/**
 * Socket Server implementation
 *
 * @constructor
 * @param {Integer} port Port to bind the WebSocket Server
 * @api public
 */

function SocketServer(port) {
    this.socketPort = port;
    // Starts WebSocket Server
    this.socketServer = new WebSocketServer({port: this.socketPort});
    var logger = new Logger(`Socket server started on ${this.socketPort}`,0).log();
}

/**
 * Inherits from WebSocketServer
 */
util.inherits(SocketServer,WebSocketServer);

/**
 * Activate the listeners to be associated with SocketServer class
 */
SocketServer.prototype.activateListeners = function() {
    var that = this;
    this.socketServer.on("connection",function(socket) {
        that.emit("newsocket",socket,200);
        socket.on("message",function(message) {
           that.emit("newmessage",message,200);
        });
    });
};

/**
 * Starts a socket server on a specified port.
 * The server is started on the port supplied (by default) when the object
 * is initialized.
 * Call this function to start the socket server on a different port.
 * It first closes and then starts the server on a different port
 */
SocketServer.prototype.restart = function(port) {
    return new SocketServer(port);
};

/**
 * Stops the WebSocket Server
 */
SocketServer.prototype.stop = function() {

}

/**
 * Broadcast the message to all connected to the WebSocket Server
 * @param {String} message Message that has to be broadcasted
 * @param {String} groupName Name of the group to which the message has to be broadcasted
 * @return {Error|Boolean}
 */
SocketServer.prototype.broadcast = function(message, groupName) {
    if(!message && "string" !== typeof message.trim())
        return new Error("Cannot broadcast an empty string");
    if(!groupName) {
        // Broadcast to all connected to the WebSocket Server
        this.socketServer.forEach(function(client) {
           client.send(`~Broadcast: ${message}`);
        });
        // Emit a beamed event, when the broadcast has been made
        this.emit("beamed",this.socketServer,200);
    }else {
        // Broadcast to the particular group
    }
    return true;
};

SocketServer.prototype.createBroadcastGroup = function(groupName) {

};

// Exports
module.exports = SocketServer;<|endoftext|>