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

function saveFormPersistence(elemPersist) {
    var store = {};
    var value = null;
    $(elemPersist).find('.persist').each(function(index, element) {
        if ($(element).is('input')) {
            if ($(element).attr('type') === 'checkbox') {
                value = $(element).prop('checked');
            } else if ($(element).attr('type') === 'radio') {
                value = $(element).prop('checked');
            } else if ($(element).attr('type') === 'field') {
                value = $(element).val();
            }
        } else if ($(element).is('select')) {
            value = $(element).val();
        }

        if (value != null) {
            store[$(element).attr('id')] = value;
        }
    });
    var dictName = $(elemPersist).attr('id')
    localStorageSet(dictName, store)
    updateDebug();
}

function restoreFormPersistence(elemPersist) {
    var form_store = localStorageGet($(elemPersist).attr('id'));
    for (key in form_store) {
        element = $(elemPersist).find('#' + key);
        if ($(element).is('input')) {
            if ($(element).attr('type') === 'checkbox') {
                $(element).prop('checked', form_store[key]);
            } else if ($(element).attr('type') === 'radio') {
                $(element).prop('checked', form_store[key]);
            } else if ($(element).attr('type') === 'field') {
                $(element).val(form_store[key]);
            }
        } else if ($(element).is('select')) {
            $(element).val(form_store[key]);
        }
    }
}

// Execute setup code when page loads
$(document).ready(function() {
    /******* AJAX Helpers ******/
    $('body').ajaxStart(function() {
        if ($('#loading').length == 0) {
            $('body').append('<images id="loading" src="/railroad-static/' +
                'images/loading.gif" style="position: absolute;"/>');
        }
    });

    $('body').ajaxStop(function() {
        $('#loading').remove();
    });

    /**** GRAPH SETUP ****/

    // Bind the graph time range selection buttons
    $('.options input[type=button]').live('click', function() {
        var dates = $(this).parent().siblings('.daterange');

        if ($('#sync').prop('checked')) {
            var from = $('input[name=from]');
            var to = $('input[name=to]');
        } else {
            var from = $(dates).children('[name=from]');
            var to = $(dates).children('[name=to]');
        }

        to.datepicker('setDate', new Date());
        from.datepicker('setDate', new Date());

        if ($(this).attr('name') == 'week') {
            from.datepicker('setDate', '-1w');
        } else if ($(this).attr('name') == 'month') {
            from.datepicker('setDate', '-1m');
        } else if ($(this).attr('name') == 'year') {
            from.datepicker('setDate', '-1y');
        }

        to.datepicker('refresh')
        from.datepicker('refresh')
        updateZoom(from,to);
    });

    // Initialize the data for any graphs already on the page
    update_number_graphs();
    fetchAndDrawGraphDataByDiv();

    /**** CONFIGURATOR SETUP ****/

    $('#debug_check').prop('checked', localStorageGet('debug'));
    updateDebug();

    $('#debug_check').change(function () {
        localStorageSet('debug', $('#debug_check').prop('checked'));
        updateDebug();
    });

    $('#debug a').live('click', function() {
        localStorageClear();
    });

    /*** Persistent form settings ***/
    /* Anything in #configurator with a class of "... persist ..."
     * will get persistence.
     */
    $('#configurator').bind('change', function () {
        saveFormPersistence($('#configurator'));
    });

    $('#preference_panel').bind('change', function () {
        saveFormPersistence($('#preference_panel'));
    });

    // Restore persisted objects
    restoreFormPersistence($('#configurator'));
    restoreFormPersistence($('#preference_panel'));

    // Autocomplete anything with class = "... autocomplete ..."
    $('.autocomplete').each(function () {
        $(this).autocomplete ( { source : "/railroad/ajax/autocomplete/" +
            $(this).attr('name' ), minLength : 1, autoFocus: true})
    });

    $('#service_count').click(function () {
        $('.service_row').show();
        console.log(update_hidden_count);
        update_hidden_count();
        $(this).siblings().css({'opacity': 1.0}).data('checkp', false);
    });
    $('#stats .state_count').bind('click', function() {
        var buttonClass = $(this).attr('name');
        var checkp = !$(this).data('checkp');
        $(this).data('checkp', checkp);

        $('.service_row').each(function(index, element) {
            if ($(element).children('.status_text').hasClass(buttonClass)) {
                if (checkp) {
                    $(element).hide();
                } else {
                    $(element).show();
                }
            }
        });
        update_hidden_count();
    });

    $('#cleargraphs').click(function () {
        $('.service_row').remove();
        update_number_graphs()
        update_hidden_count();
    });

    $('.graphcheckbox').change(function () {
        var checkp = true;
        var checkboxes = $('.graphcheckbox');
        checkboxes.each(function (index, element) {
            if (  ! $(element).prop('checked') ) {
                checkp = false;
            }
        });
        $('.checkall').prop('checked', checkp);
    });

    $('#clearform').bind('click', function () {
        $('#host').val("");
        $('#group').val("");
        $('#service').val("");
    });

    $('#localtime, #utc').bind('change', function() {
        // Guarantee that localstorage gets the change before redrawing graphs
        $('#configurator').trigger('change');
        graphs = $('.graph');
        graphs.each(function(index, element) {
            if ($(element).data('data')) {
                redrawGraph(element, $(element).data('data'));
            }
        });
    });

    // Handle configurator form submissions
    $('#configurator').submit(function() {

        fields = $('#configurator').formSerialize();
        $.ajax({
            data: fields,
            dataType: 'json',
            url: '/railroad/graphs',
            success: function(data, textStatus, XMLHttpRequest) {
                //reset_fields();
                $('#clearform').trigger('click');
                createGraphs(data);
            },
            error: function(XMLHttpRequest, textStatus, errorThrown) {
                // TODO: Indicate error somehow, probably
            }
        });
        $('#configurator').data('changed', true);
        return false;
    });

    // *************************** Row manipulations ***************************
    // Expand all/of type buttons
    $('#expansion_by_type').find('input').bind('change', function() {
        //To shorten the else if line below to < 80 chars
        var allCheckboxes = $(this).parent().siblings().children('input');
        if (! $(this).prop('checked')) {
            $('#expandall').prop('checked', false);
        } else if ($(allCheckBoxes).not(':checked').length == 0) {
            // If all inputs are checked
            $('#expandall').prop('checked', true);
        }
    });

    $('#expandall').change(function() {
        var state = $(this).prop('checked');
        $('#expansion_by_type input').prop('checked', state);
    });

    // expand one buttons
    $('.collapse_row').live('click', function() {
        collapse_row($(this).parents().parents().first());
    });
    $('.expand_row').live('click', function() {
        expand_row($(this).parents().parents().first());
    });
    // remove 1 buttons
    $('.remove_row').live('click', function() {
        var tr = $(this).parents().parents().first();
        tr.hide(0, function() {
            $(tr).remove();
        });
    });

    // Start the AJAX graph refreshes
    setTimeout(autoFetchData, 600 * 1000);

    /******* Hint System *******/
    $('.hint').each(function(index, element) {
        var hintText = $(element).text();
        $('

').insertBefore(element)
        .mouseover(function(e) {
            // on hover
            showTooltip(e.pageX, e.pageY, hintText);
        })
        .mouseout(function(e) {
            // on unhover
            $('#tooltip').remove();
        });
        $(element).remove();
    });

    /****** Tool bar stuff *****/
    $('#check_controls input[type=checkbox]').bind('click', function(event) {
        $('.service_row .controls input[type=checkbox]').prop('checked',
            $(this).prop('checked'));
        // Don't trigger the click event of the parent
        event.stopPropagation();
    });

    var allChecked = function(func) {
        $('.service_row').each(function(index, elem) {
            if ($(elem).children('.controls').children('input')
                    .prop('checked')) {
                func(elem);
            }
        });
        $('.service_row .controls input[type=checkbox]').prop('checked', false);
        $('#checkall input').prop('checked', false);
    }

    var bindMenu = function(button, menu) {
        var pos = $(button).offset();
        var height = $(button).height();
        $(menu).position({my: 'left top', at: 'left bottom', of: $(button)}).hide();
        $(button).bind('click', function() {
            $(menu).toggle();
        });
    }

    bindMenu($('#checkall'), $('#selectall.menu'));
    $('#selectall.menu li').bind('click', function(e) {
        switch ($(this).attr('name')) {
            case 'all':
                $('.service_row .controls input[type=checkbox]')
                    .prop('checked', true);
                break;
            case 'none':
                $('.service_row .controls input[type=checkbox]')
                    .prop('checked', false);
                break;
            case 'state_ok':
            case 'state_warning':
            case 'state_critical':
            case 'state_unknown':
                var button = this;
                $('.service_row').each(function(index, elem) {
                    var className = $(button).attr('name');
                    if ($(elem).children('.status_text').hasClass(className)) {
                        $(elem).children('.controls').children('input')
                            .prop('checked', true);
                    }
                });
                break;
        }
        $('.graphcheckbox').trigger('change');
        $('#selectall.menu').hide();
    });

    $('#remove_checked').bind('click', function() {
        allChecked(function(elem) {$(elem).remove();});
        update_number_graphs();
    });
    $('#expand_checked').bind('click', function() {
        allChecked(expand_row);
    });
    $('#collapse_checked').bind('click', function() {
        allChecked(collapse_row);
    });

    bindMenu($('#sortby'), $('#sortbymenu'));
    $('#sortbymenu li').bind('click', function(e) {
        var name = $(this).attr('name');
        sortGraphs(name);
        $('#sortbymenu').hide();
        $('#sortby').data('lastSort', name);
    });
    $('#sortreverse input').bind('change', function(e) {
        var name = $('#sortby').data('lastSort');
        sortGraphs(name, $(this).prop('checked'));
        $('#sortby').data('lastSort', name);
    });

    $('#preferences').bind('click', function() {
        $('#preference_panel').toggle();
    });
    $('#close_preferences').bind('click', function() {
        $('#preference_panel').toggle();
    });

    /******** Quicklook view ********/
    $('#quicklook .status_text, .host').bind('click', function() {
        $(this).siblings('ul').toggle();
    });
    $('#quicklook .status_text.state_ok').siblings('ul').hide();
    $('#quicklook .host').siblings('ul').hide();

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

function saveFormPersistence(elemPersist) {
    var store = {};
    var value = null;
    $(elemPersist).find('.persist').each(function(index, element) {
        if ($(element).is('input')) {
            if ($(element).attr('type') === 'checkbox') {
                value = $(element).prop('checked');
            } else if ($(element).attr('type') === 'radio') {
                value = $(element).prop('checked');
            } else if ($(element).attr('type') === 'field') {
                value = $(element).val();
            }
        } else if ($(element).is('select')) {
            value = $(element).val();
        }

        if (value != null) {
            store[$(element).attr('id')] = value;
        }
    });
    var dictName = $(elemPersist).attr('id')
    localStorageSet(dictName, store)
    updateDebug();
}

function restoreFormPersistence(elemPersist) {
    var form_store = localStorageGet($(elemPersist).attr('id'));
    for (key in form_store) {
        element = $(elemPersist).find('#' + key);
        if ($(element).is('input')) {
            if ($(element).attr('type') === 'checkbox') {
                $(element).prop('checked', form_store[key]);
            } else if ($(element).attr('type') === 'radio') {
                $(element).prop('checked', form_store[key]);
            } else if ($(element).attr('type') === 'field') {
                $(element).val(form_store[key]);
            }
        } else if ($(element).is('select')) {
            $(element).val(form_store[key]);
        }
    }
}

// Execute setup code when page loads
$(document).ready(function() {
    /******* AJAX Helpers ******/
    $('body').ajaxStart(function() {
        if ($('#loading').length == 0) {
            $('body').append('<images id="loading" src="/railroad-static/' +
                'images/loading.gif" style="position: absolute;"/>');
        }
    });

    $('body').ajaxStop(function() {
        $('#loading').remove();
    });

    /**** GRAPH SETUP ****/

    // Bind the graph time range selection buttons
    $('.options input[type=button]').live('click', function() {
        var dates = $(this).parent().siblings('.daterange');

        if ($('#sync').prop('checked')) {
            var from = $('input[name=from]');
            var to = $('input[name=to]');
        } else {
            var from = $(dates).children('[name=from]');
            var to = $(dates).children('[name=to]');
        }

        to.datepicker('setDate', new Date());
        from.datepicker('setDate', new Date());

        if ($(this).attr('name') == 'week') {
            from.datepicker('setDate', '-1w');
        } else if ($(this).attr('name') == 'month') {
            from.datepicker('setDate', '-1m');
        } else if ($(this).attr('name') == 'year') {
            from.datepicker('setDate', '-1y');
        }

        to.datepicker('refresh')
        from.datepicker('refresh')
        updateZoom(from,to);
    });

    // Initialize the data for any graphs already on the page
    update_number_graphs();
    fetchAndDrawGraphDataByDiv();

    /**** CONFIGURATOR SETUP ****/

    $('#debug_check').prop('checked', localStorageGet('debug'));
    updateDebug();

    $('#debug_check').change(function () {
        localStorageSet('debug', $('#debug_check').prop('checked'));
        updateDebug();
    });

    $('#debug a').live('click', function() {
        localStorageClear();
    });

    /*** Persistent form settings ***/
    /* Anything in #configurator with a class of "... persist ..."
     * will get persistence.
     */
    $('#configurator').bind('change', function () {
        saveFormPersistence($('#configurator'));
    });

    $('#preference_panel').bind('change', function () {
        saveFormPersistence($('#preference_panel'));
    });

    // Restore persisted objects
    restoreFormPersistence($('#configurator'));
    restoreFormPersistence($('#preference_panel'));

    // Autocomplete anything with class = "... autocomplete ..."
    $('.autocomplete').each(function () {
        $(this).autocomplete ( { source : "/railroad/ajax/autocomplete/" +
            $(this).attr('name' ), minLength : 1, autoFocus: true})
    });

    $('#service_count').click(function () {
        $('.service_row').show();
        console.log(update_hidden_count);
        update_hidden_count();
        $(this).siblings().css({'opacity': 1.0}).data('checkp', false);
    });
    $('#stats .state_count').bind('click', function() {
        var buttonClass = $(this).attr('name');
        var checkp = !$(this).data('checkp');
        $(this).data('checkp', checkp);

        $('.service_row').each(function(index, element) {
            if ($(element).children('.status_text').hasClass(buttonClass)) {
                if (checkp) {
                    $(element).hide();
                } else {
                    $(element).show();
                }
            }
        });
        update_hidden_count();
    });

    $('#cleargraphs').click(function () {
        $('.service_row').remove();
        update_number_graphs()
        update_hidden_count();
    });

    $('.graphcheckbox').change(function () {
        var checkp = true;
        var checkboxes = $('.graphcheckbox');
        checkboxes.each(function (index, element) {
            if (  ! $(element).prop('checked') ) {
                checkp = false;
            }
        });
        $('.checkall').prop('checked', checkp);
    });

    $('#clearform').bind('click', function () {
        $('#host').val("");
        $('#group').val("");
        $('#service').val("");
    });

    $('#localtime, #utc').bind('change', function() {
        // Guarantee that localstorage gets the change before redrawing graphs
        $('#configurator').trigger('change');
        graphs = $('.graph');
        graphs.each(function(index, element) {
            if ($(element).data('data')) {
                redrawGraph(element, $(element).data('data'));
            }
        });
    });

    // Handle configurator form submissions
    $('#configurator').submit(function() {

        fields = $('#configurator').formSerialize();
        $.ajax({
            data: fields,
            dataType: 'json',
            url: '/railroad/graphs',
            success: function(data, textStatus, XMLHttpRequest) {
                //reset_fields();
                $('#clearform').trigger('click');
                createGraphs(data);
            },
            error: function(XMLHttpRequest, textStatus, errorThrown) {
                // TODO: Indicate error somehow, probably
            }
        });
        $('#configurator').data('changed', true);
        return false;
    });

    // *************************** Row manipulations ***************************
    // Expand all/of type buttons
    $('#expansion_by_type').find('input').bind('change', function() {
        //To shorten the else if line below to < 80 chars
        var allCheckboxes = $(this).parent().siblings().children('input');
        if (! $(this).prop('checked')) {
            $('#expandall').prop('checked', false);
        } else if ($(allCheckboxes).not(':checked').length == 0) {
            // If all inputs are checked
            $('#expandall').prop('checked', true);
        }
    });

    $('#expandall').change(function() {
        var state = $(this).prop('checked');
        $('#expansion_by_type input').prop('checked', state);
    });

    // expand one buttons
    $('.collapse_row').live('click', function() {
        collapse_row($(this).parents().parents().first());
    });
    $('.expand_row').live('click', function() {
        expand_row($(this).parents().parents().first());
    });
    // remove 1 buttons
    $('.remove_row').live('click', function() {
        var tr = $(this).parents().parents().first();
        tr.hide(0, function() {
            $(tr).remove();
        });
    });

    // Start the AJAX graph refreshes
    setTimeout(autoFetchData, 600 * 1000);

    /******* Hint System *******/
    $('.hint').each(function(index, element) {
        var hintText = $(element).text();
        $('

').insertBefore(element)
        .mouseover(function(e) {
            // on hover
            showTooltip(e.pageX, e.pageY, hintText);
        })
        .mouseout(function(e) {
            // on unhover
            $('#tooltip').remove();
        });
        $(element).remove();
    });

    /****** Tool bar stuff *****/
    $('#check_controls input[type=checkbox]').bind('click', function(event) {
        $('.service_row .controls input[type=checkbox]').prop('checked',
            $(this).prop('checked'));
        // Don't trigger the click event of the parent
        event.stopPropagation();
    });

    var allChecked = function(func) {
        $('.service_row').each(function(index, elem) {
            if ($(elem).children('.controls').children('input')
                    .prop('checked')) {
                func(elem);
            }
        });
        $('.service_row .controls input[type=checkbox]').prop('checked', false);
        $('#checkall input').prop('checked', false);
    }

    var bindMenu = function(button, menu) {
        var pos = $(button).offset();
        var height = $(button).height();
        $(menu).position({my: 'left top', at: 'left bottom', of: $(button)}).hide();
        $(button).bind('click', function() {
            $(menu).toggle();
        });
    }

    bindMenu($('#checkall'), $('#selectall.menu'));
    $('#selectall.menu li').bind('click', function(e) {
        switch ($(this).attr('name')) {
            case 'all':
                $('.service_row .controls input[type=checkbox]')
                    .prop('checked', true);
                break;
            case 'none':
                $('.service_row .controls input[type=checkbox]')
                    .prop('checked', false);
                break;
            case 'state_ok':
            case 'state_warning':
            case 'state_critical':
            case 'state_unknown':
                var button = this;
                $('.service_row').each(function(index, elem) {
                    var className = $(button).attr('name');
                    if ($(elem).children('.status_text').hasClass(className)) {
                        $(elem).children('.controls').children('input')
                            .prop('checked', true);
                    }
                });
                break;
        }
        $('.graphcheckbox').trigger('change');
        $('#selectall.menu').hide();
    });

    $('#remove_checked').bind('click', function() {
        allChecked(function(elem) {$(elem).remove();});
        update_number_graphs();
    });
    $('#expand_checked').bind('click', function() {
        allChecked(expand_row);
    });
    $('#collapse_checked').bind('click', function() {
        allChecked(collapse_row);
    });

    bindMenu($('#sortby'), $('#sortbymenu'));
    $('#sortbymenu li').bind('click', function(e) {
        var name = $(this).attr('name');
        sortGraphs(name);
        $('#sortbymenu').hide();
        $('#sortby').data('lastSort', name);
    });
    $('#sortreverse input').bind('change', function(e) {
        var name = $('#sortby').data('lastSort');
        sortGraphs(name, $(this).prop('checked'));
        $('#sortby').data('lastSort', name);
    });

    $('#preferences').bind('click', function() {
        $('#preference_panel').toggle();
    });
    $('#close_preferences').bind('click', function() {
        $('#preference_panel').toggle();
    });

    /******** Quicklook view ********/
    $('#quicklook .status_text, .host').bind('click', function() {
        $(this).siblings('ul').toggle();
    });
    $('#quicklook .status_text.state_ok').siblings('ul').hide();
    $('#quicklook .host').siblings('ul').hide();

});
<|endoftext|>


<commit_before>angular.module('core')
.directive('inlineLookup', ['$timeout', function($timeout) {
	return {
		restrict: 'E',
		replace: true,
		transclude: true,
		template: function(elm, attr) {
			var viewTmpl = attr.hasOwnProperty('multiple')
				? '

'
				: '{{ ' + attr.model + '.text || \'---\' }}';

			var editTmpl = '<input type="text" ng-model="emodel.value" ng-readonly="waiting"' +
				' lookup="' + attr.inputLookup + '"' +
				' class="form-control ' + (attr.inputClass ? attr.inputClass + '"' : '"') +
				(attr.hasOwnProperty('required')
					? (attr.hasOwnProperty('multiple') ? ' required-multiple="true"' : ' ng-required="true"')
					: '') +
				(attr.hasOwnProperty('multiple') ? ' multiple="multiple"' : '') +
				' lookup-options="{openOnEnter: false' +
					(attr.inputLookupOptions ?  + ', ' + attr.inputLookupOptions : '') + '}"></input>';

			//margin-bottom in form is hack - strange margin from top on enter in edit mode
			//can't fix another
			var tmpl =
'
' +
'	' + viewTmpl + '' +
'	<form name="editForm" ng-show="editMode" ng-class="{\'has-error\': editForm.$invalid}" style="width: 100%; margin-bottom: 0px" novalidate>' +
'
' +
editTmpl +
'		<inline-buttons class="input-group-btn"></inline-buttons>' +
'
' +
'	</form>' +
'
'; return tmpl;
		},
		link: function(scope, elm, attr) {
			//select2 not initialized if call without timeout
			$timeout(function() {
				//inplaceEdit use this for focus() when enter in edit mode
				scope.elInput = attr.hasOwnProperty('multiple')
					? $('input.select2-input', elm)
					: $('.select2-focusser', elm);

				var $input = $('input', elm);
				//select2 has self specific blur
				$input.off('blur', scope.onBlur);

				$input.on('select2-blur', scope.onBlur);
				$input.on('select2-opening', function(e) {
					$input.off('select2-blur', scope.onBlur);
				});
				$input.on('select2-close', function(e) {
					//if attach without timeout, blur handled right after close
					$timeout(function() {
						$input.on('select2-blur', scope.onBlur);
						//console.log('lookup focused: %s', $input.is(':focus'));
						if (!attr.hasOwnProperty('multiple')) {
							scope.onBlur();
						} else {
							//console.log('lookup focused: %s', $input.is(':focus'));
						}
					});
				});
				$input.on('select2-focus', function(e) {
					$(this).select2('open');
				});
				$input.on('select2-removing', function(e) {
					console.log('removing: %s', e);
				});
				//enter and esc handling does not work, because of killEvent in select2 code
			}, 50);
		}
	};
}]);
<commit_msg>Bug refs #1455: can not fix blur for multiple lookup<commit_after>angular.module('core')
.directive('inlineLookup', ['$timeout', function($timeout) {
	return {
		restrict: 'E',
		replace: true,
		transclude: true,
		template: function(elm, attr) {
			var viewTmpl = attr.hasOwnProperty('multiple')
				? '

'
				: '{{ ' + attr.model + '.text || \'---\' }}';

			var editTmpl = '<input type="text" ng-model="emodel.value" ng-readonly="waiting"' +
				' lookup="' + attr.inputLookup + '"' +
				' class="form-control ' + (attr.inputClass ? attr.inputClass + '"' : '"') +
				(attr.hasOwnProperty('required')
					? (attr.hasOwnProperty('multiple') ? ' required-multiple="true"' : ' ng-required="true"')
					: '') +
				(attr.hasOwnProperty('multiple') ? ' multiple="multiple"' : '') +
				' lookup-options="{openOnEnter: false' +
					(attr.inputLookupOptions ?  + ', ' + attr.inputLookupOptions : '') + '}"></input>';

			//margin-bottom in form is hack - strange margin from top on enter in edit mode
			//can't fix another
			var tmpl =
'
' +
'	' + viewTmpl + '' +
'	<form name="editForm" ng-show="editMode" ng-class="{\'has-error\': editForm.$invalid}" style="width: 100%; margin-bottom: 0px" novalidate>' +
'
' +
editTmpl +
'		<inline-buttons class="input-group-btn"></inline-buttons>' +
'
' +
'	</form>' +
'
'; return tmpl;
		},
		link: function(scope, elm, attr) {
			//select2 not initialized if call without timeout
			$timeout(function() {
				//inplaceEdit use this for focus() when enter in edit mode
				scope.elInput = attr.hasOwnProperty('multiple')
					? $('input.select2-input', elm)
					: $('.select2-focusser', elm);

				var $input = $('input', elm);
				//select2 has self specific blur
				$input.off('blur', scope.onBlur);

				$input.on('select2-blur', scope.onBlur);
				$input.on('select2-opening', function(e) {
					$input.off('select2-blur', scope.onBlur);
				});
				$input.on('select2-close', function(e) {
					//if attach without timeout, blur handled right after close
					$timeout(function() {
						$input.on('select2-blur', scope.onBlur);
						//console.log('lookup focused: %s', $input.is(':focus'));
						if (!attr.hasOwnProperty('multiple')) {
							scope.onBlur();
						} else {
							// $timeout(function() {
							// 	console.log('close select2Change: %s', scope.select2Change);
							// 	if (!angular.isDefined(scope.select2Change) || scope.select2Change === false) {
							// 		//scope.onBlur();
							// 	}
							// }, 10);
							//console.log('lookup focused: %s', $input.is(':focus'));
						}
					});
					return false;
				});
				$input.on('select2-focus', function(e) {
					$(this).select2('open');
				});
				// $input.on('change', function(e) {
				// 	scope.select2Change = true;
				// 	console.log('change select2Change: %s', scope.select2Change);
				// });
				//enter and esc handling does not work, because of killEvent in select2 code
			}, 50);
		}
	};
}]);
<|endoftext|>


<commit_before>/**
 * A Bot for Slack!
 */

/**
 * Define a function for initiating a conversation on installation
 * With custom integrations, we don't have a way to find out who installed us, so we can't message them :(
 */

function onInstallation(bot, installer) {
    if (installer) {
        bot.startPrivateConversation({user: installer}, function (err, convo) {
            if (err) {
                console.log(err);
            } else {
                convo.say("Hi there, I'm Sara from Springboard.");
            }
        });
    }
}

/**
 * Configure the persistence options
 */

var config = {};
if (process.env.MONGOLAB_URI) {
    var BotkitStorage = require('botkit-storage-mongo');
    config = {
        storage: BotkitStorage({mongoUri: process.env.MONGOLAB_URI}),
    };
} else {
    config = {
        json_file_store: ((process.env.TOKEN)?'./db_sara_ci/':'./db_slack_bot_a/'), //use a different name if an app or CI
    };
}

if (process.env.CLIENT_ID && process.env.CLIENT_SECRET && process.env.PORT) {
    //Treat this as an app
    var app = require('./lib/apps');
    var controller = app.configure(process.env.PORT, process.env.CLIENT_ID,
                              process.env.CLIENT_SECRET, config, onInstallation);
} else {
    console.log('Error: If this is a custom integration, please specify TOKEN in the environment. \
                  If this is an app, please specify CLIENTID, CLIENTSECRET, and PORT in the environment');
    process.exit(1);
}

//var springboard = require('./springboard')

// Handle events related to the websocket connection to Slack
controller.on('rtm_open', function (bot) {
    console.log('** The RTM api just connected!');
});

controller.on('rtm_close', function (bot) {
    console.log('** The RTM api just closed');
    // you may want to attempt to re-open
});

/**
 * Core bot logic goes here!
 */
// BEGIN EDITING HERE!

controller.on('bot_channel_join', function (bot, message) {
    bot.reply(message, "I'm here!")
});

controller.hears(['hello','hi'],'direct_message,direct_mention,mention',function(bot, message) {

    bot.api.reactions.add({
        timestamp: message.ts,
        channel: message.channel,
        name: 'robot_face',
    },function(err, res) {
        if (err) {
            bot.botkit.log('Failed to add emoji reaction :(',err);
        }
    });

    controller.storage.users.get(message.user,function(err, user) {
        if (user && user.name) {
            bot.reply(message,'Hello ' + user.name + '!!');
        } else {
            bot.reply(message,'Hello.');
        }
    });
});

function isDefined(obj) {
    if (typeof obj == 'undefined') {
        return false;
    }

    if (!obj) {
        return false;
    }

    return obj != null;
}

var RestClient = require('node-rest-client').Client;
var restClient = new RestClient();
restClient.registerMethod("jsonMethod", "https://app.knowledgeowl.com/api/head/suggest.json", "GET");
const baseURL = "https://help.springboard.com/help/article/link/"

controller.hears(['owl (.*)'],'direct_message',function(bot, message) {
  searchKnowledgeOwl(bot.replyWithTyping,message);
});

var searchKnowledgeOwl = function(botFunc,message){
  var args = {
    parameters: {
      project_id: process.env.KNOWL_KB_ID,
      _authbykey: process.env.KNOWL_KEY,
      phrase: message.text
    }
  };
  restClient.methods.jsonMethod(args, function (response) {
      console.log(response);
      if (response.valid && response.data.length){
        attach = [];
        for (i=0; i < response.data.length;i++) {
            if (i == 3) {
              break;
            };
            res = response.data[i];
            console.log(res);
            attach.push({
                title: res.name,
                title_link: baseURL + res.url_hash,
                text: res.summary
            });
        };
        responseMessage = {
          text: "Here are a few results that might help:",
          attachments: attach
        };
      } else {
        responseMessage = {
          text: "No results! Try another query, perhaps?",
        };
      }
      console.log(responseMessage);
      botFunc(message,responseMessage);
  });
}

controller.on('slash_command', function (slashCommand, message) {
    switch (message.command) {
        case "/owl":
            // but first, let's make sure the token matches!
            if (message.token !== process.env.SLASH_TOKEN) return; //just ignore it.

            // if no text was supplied, treat it as a help command
            if (message.text === "" || message.text === "help") {
                slashCommand.replyPrivate(message,
                    "Usage: /owl [query]");
                return;
            }

            responseMessage = searchKnowledgeOwl(slashCommand.replyPrivate,message);
            break;
        default:
            slashCommand.replyPrivate(message, "I'm afraid I don't know how to " + message.command + " yet.");
    }
  }
);

/**
API.ai integration for default case.
*/
const uuid = require('node-uuid');

const sessionIds = new Map();
const apiai = require('apiai');
var apiai_app = apiai(
  process.env.APIAI_ACC, process.env.APIAI_SUB
);

controller.hears(['.*'],['direct_message','direct_mention'],
  function(bot,message) {
    if (message.type == "message") {
        if (message.user == bot.identity.id) {
            // message from bot can be skipped
        } else if (message.text.indexOf("<@U") == 0 && message.text.indexOf(bot.identity.id) == -1) {
            // skip other users direct mentions
        } else {
            var requestText = message.text;

            var channel = message.channel;
            var messageType = message.event;
            var botId = '<@' + bot.identity.id + '>';
            console.log(requestText);
            console.log(messageType);
            if (requestText.indexOf(botId) > -1) {
                requestText = requestText.replace(botId, '');
            }
            if (!sessionIds.has(channel)) {
                sessionIds.set(channel, uuid.v1());
            }

            var request = apiai_app.textRequest(requestText, {
                        sessionId: sessionIds.get(channel)
            });
            request.on('response', function (response) {
                if (response.result) {
                    var responseText = response.result.fulfillment.speech;
                    var action = response.result.action;
                    console.log("Response Text: " + responseText);
                    bot.replyWithTyping(message, responseText || "Sorry, I can't answer that right now :(" );
                }
            });
            request.on('error', function(error) {
              console.log(error);
              controller.storage.users.get(message.user,function(err, user) {
                  if (user && user.name) {
                      bot.reply(message,'Sorry, I don\'t understand that yet ' + user.name +'.');
                  } else {
                      bot.reply(message,"Sorry, I don't understand that right now :(");
                  }
              });
            });
            request.end();
        }
    }
  }
);
<commit_msg>Truncate summary to 100<commit_after>/**
 * A Bot for Slack!
 */

/**
 * Define a function for initiating a conversation on installation
 * With custom integrations, we don't have a way to find out who installed us, so we can't message them :(
 */

function onInstallation(bot, installer) {
    if (installer) {
        bot.startPrivateConversation({user: installer}, function (err, convo) {
            if (err) {
                console.log(err);
            } else {
                convo.say("Hi there, I'm Sara from Springboard.");
            }
        });
    }
}

/**
 * Configure the persistence options
 */

var config = {};
if (process.env.MONGOLAB_URI) {
    var BotkitStorage = require('botkit-storage-mongo');
    config = {
        storage: BotkitStorage({mongoUri: process.env.MONGOLAB_URI}),
    };
} else {
    config = {
        json_file_store: ((process.env.TOKEN)?'./db_sara_ci/':'./db_slack_bot_a/'), //use a different name if an app or CI
    };
}

if (process.env.CLIENT_ID && process.env.CLIENT_SECRET && process.env.PORT) {
    //Treat this as an app
    var app = require('./lib/apps');
    var controller = app.configure(process.env.PORT, process.env.CLIENT_ID,
                              process.env.CLIENT_SECRET, config, onInstallation);
} else {
    console.log('Error: If this is a custom integration, please specify TOKEN in the environment. \
                  If this is an app, please specify CLIENTID, CLIENTSECRET, and PORT in the environment');
    process.exit(1);
}

//var springboard = require('./springboard')

// Handle events related to the websocket connection to Slack
controller.on('rtm_open', function (bot) {
    console.log('** The RTM api just connected!');
});

controller.on('rtm_close', function (bot) {
    console.log('** The RTM api just closed');
    // you may want to attempt to re-open
});

/**
 * Core bot logic goes here!
 */
// BEGIN EDITING HERE!

controller.on('bot_channel_join', function (bot, message) {
    bot.reply(message, "I'm here!")
});

controller.hears(['hello','hi'],'direct_message,direct_mention,mention',function(bot, message) {

    bot.api.reactions.add({
        timestamp: message.ts,
        channel: message.channel,
        name: 'robot_face',
    },function(err, res) {
        if (err) {
            bot.botkit.log('Failed to add emoji reaction :(',err);
        }
    });

    controller.storage.users.get(message.user,function(err, user) {
        if (user && user.name) {
            bot.reply(message,'Hello ' + user.name + '!!');
        } else {
            bot.reply(message,'Hello.');
        }
    });
});

// TODO: separate module
var truncate = function(s, n, useWordBoundary){
    var isTooLong = s.length > n;
    var s_ = isTooLong ? s.substr(0,n-1) : s;
    s_ = (useWordBoundary && isTooLong) ? s_.substr(0,s_.lastIndexOf(' ')) : s_;
    return  isTooLong ? s_ + '…' : s_;
};

var RestClient = require('node-rest-client').Client;
var restClient = new RestClient();
restClient.registerMethod("jsonMethod", "https://app.knowledgeowl.com/api/head/suggest.json", "GET");
const baseURL = "https://help.springboard.com/help/article/link/"

controller.hears(['owl (.*)'],'direct_message',function(bot, message) {
  searchKnowledgeOwl(bot.replyWithTyping,message);
});

// TODO: separate module
var searchKnowledgeOwl = function(botFunc,message){
  var args = {
    parameters: {
      project_id: process.env.KNOWL_KB_ID,
      _authbykey: process.env.KNOWL_KEY,
      phrase: message.text
    }
  };
  restClient.methods.jsonMethod(args, function (response) {
      console.log(response);
      if (response.valid && response.data.length){
        attach = [];
        for (i=0; i < response.data.length;i++) {
            if (i == 3) {
              break;
            };
            res = response.data[i];
            console.log(res);
            attach.push({
                title: res.name,
                title_link: baseURL + res.url_hash,
                text: truncate(res.summary, 100, true)
            });
        };
        responseMessage = {
          text: "Here are a few results that might help:",
          attachments: attach
        };
      } else {
        responseMessage = {
          text: "No results! Try another query, perhaps?",
        };
      }
      console.log(responseMessage);
      botFunc(message,responseMessage);
  });
}

controller.on('slash_command', function (slashCommand, message) {
    switch (message.command) {
        case "/owl":
            // but first, let's make sure the token matches!
            if (message.token !== process.env.SLASH_TOKEN) return; //just ignore it.

            // if no text was supplied, treat it as a help command
            if (message.text === "" || message.text === "help") {
                slashCommand.replyPrivate(message,
                    "Usage: /owl [query]");
                return;
            }

            responseMessage = searchKnowledgeOwl(slashCommand.replyPrivate,message);
            break;
        default:
            slashCommand.replyPrivate(message, "I'm afraid I don't know how to " + message.command + " yet.");
    }
  }
);

/**
API.ai integration for default case.
*/
const uuid = require('node-uuid');

const sessionIds = new Map();
const apiai = require('apiai');
var apiai_app = apiai(
  process.env.APIAI_ACC, process.env.APIAI_SUB
);

controller.hears(['.*'],['direct_message','direct_mention'],
  function(bot,message) {
    if (message.type == "message") {
        if (message.user == bot.identity.id) {
            // message from bot can be skipped
        } else if (message.text.indexOf("<@U") == 0 && message.text.indexOf(bot.identity.id) == -1) {
            // skip other users direct mentions
        } else {
            var requestText = message.text;

            var channel = message.channel;
            var messageType = message.event;
            var botId = '<@' + bot.identity.id + '>';
            console.log(requestText);
            console.log(messageType);
            if (requestText.indexOf(botId) > -1) {
                requestText = requestText.replace(botId, '');
            }
            if (!sessionIds.has(channel)) {
                sessionIds.set(channel, uuid.v1());
            }

            var request = apiai_app.textRequest(requestText, {
                        sessionId: sessionIds.get(channel)
            });
            request.on('response', function (response) {
                if (response.result) {
                    var responseText = response.result.fulfillment.speech;
                    var action = response.result.action;
                    console.log("Response Text: " + responseText);
                    bot.replyWithTyping(message, responseText || "Sorry, I can't answer that right now :(" );
                }
            });
            request.on('error', function(error) {
              console.log(error);
              controller.storage.users.get(message.user,function(err, user) {
                  if (user && user.name) {
                      bot.reply(message,'Sorry, I don\'t understand that yet ' + user.name +'.');
                  } else {
                      bot.reply(message,"Sorry, I don't understand that right now :(");
                  }
              });
            });
            request.end();
        }
    }
  }
);
<|endoftext|>


<commit_before>var exec = require('child_process').exec;
var path = require('path');
var log = require('./logger').create('launcher');
var env = process.env;
var fs = require('fs');
var util = require('./util');

var counter = 1;

// inspired by https://github.com/admc/jellyfish/blob/master/lib/browsers.js
var Browser = function(id) {

  var exitCallback = function() {};

  this._getCommand = function() {
    var cmd = env[this.ENV_CMD] || this.DEFAULT_CMD[process.platform];
    return '"' + path.normalize(cmd) + '"';
  };

  this._execCommand = function(cmd) {
    // TODO(vojta): show some errors if cmd does not exists...
    log.debug(cmd);
    this._process = exec(cmd, function(e) {
      if (e && !e.killed) {
        log.error(e);
      }
    });

    var tempDir = this._tempDir;
    this._process.on('exit', function() {
      log.debug('Cleaning the profile at %s', tempDir);
      exec('rm -rf "' + tempDir + '"', exitCallback);
    });
  };

  this._getOptions = function(url) {
    return [url];
  };

  this._tempDir = path.normalize((env.TMPDIR || env.TMP || env.TEMP || '/tmp') + '/testacular-' + id.toString());

  try {
    log.debug('Creating temp dir at ' + this._tempDir);
    fs.mkdirSync(this._tempDir);
  } catch (e) {}

  this.start = function(url) {
    this._execCommand(this._getCommand() + ' ' + this._getOptions(url).join(' '));
  };

  this.kill = function(callback) {
    exitCallback = callback || function() {};

    if (this._process.exitCode === null) {
      this._process.kill();
    } else {
      process.nextTick(exitCallback);
    }
  };
};

var ChromeBrowser = function() {
  Browser.apply(this, arguments);

  this._getOptions = function(url) {
    // Chrome CLI options
    // http://peter.sh/experiments/chromium-command-line-switches/
    return [
      '--user-data-dir=' + this._tempDir,
      '--no-default-browser-check',
      '--no-first-run',
      '--disable-default-apps',
      url
    ];
  };
};

ChromeBrowser.prototype = {
  name: 'Chrome',

  DEFAULT_CMD: {
    linux: 'google-chrome',
    darwin: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
    win32: process.env.LOCALAPPDATA + '\\Google\\Chrome\\Application\\chrome.exe'
  },
  ENV_CMD: 'CHROME_BIN'
};

var ChromeCanaryBrowser = function() {
  ChromeBrowser.apply(this, arguments);

  var parentOptions = this._getOptions;
  this._getOptions = function(url) {
    // disable crankshaft optimizations, as it causes lot of memory leaks (as of Chrome 23.0)
    return parentOptions.call(this, url).concat(['--js-flags="--nocrankshaft --noopt"']);
  };
};

ChromeCanaryBrowser.prototype = {
  name: 'ChromeCanary',

  DEFAULT_CMD: {
    linux: 'google-chrome-canary',
    darwin: '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
    win32: process.env.LOCALAPPDATA + '\\Google\\Chrome SxS\\Application\\chrome.exe'
  },
  ENV_CMD: 'CHROME_CANARY_BIN'
};

var FirefoxBrowser = function() {
  Browser.apply(this, arguments);

  this.start = function(url) {
    var self = this;
    var command = this._getCommand();

    // create profile first
    exec(command + ' -CreateProfile testacular', function(e, stdout, stderr) {
      if (e) {
        log.error(e);
      }

      var match = /at\s\'(.*)\/prefs\.js\'/.exec(stderr.toString());

      if (match) {
        var profile = self._tempDir = match[1];
        var prefs = 'user_pref("browser.shell.checkDefaultBrowser", false);\n' +
                    'user_pref("browser.bookmarks.restore_default_bookmarks", false);\n';

        fs.createWriteStream(profile + '/prefs.js', {flags: 'a'}).write(prefs);
        self._execCommand(command + ' -profile "' + profile + '" ' + url);
      } else {
        log.warn('Cannot create Firefox profile');
        self._execCommand(command + ' ' + url);
      }
    });
  };
};

FirefoxBrowser.prototype = {
  name: 'Firefox',

  DEFAULT_CMD: {
    linux: 'firefox',
    darwin: '/Applications/Firefox.app/Contents/MacOS/firefox-bin',
    win32: process.env.ProgramFiles + '\\Mozilla Firefox\\firefox.exe'
  },
  ENV_CMD: 'FIREFOX_BIN'
};

var OperaBrowser = function() {
  Browser.apply(this, arguments);

  this._getOptions = function(url) {
    // Opera CLI options
    // http://www.opera.com/docs/switches/
    return [
      '-personaldir ' + this._tempDir,
      '-nomail',
      url
    ];
  };
};

OperaBrowser.prototype = {
  name: 'Opera',

  DEFAULT_CMD: {
    linux: 'opera',
    darwin: '/Applications/Opera.app/Contents/MacOS/Opera',
    win32: process.env.ProgramFiles + '\\Opera\\opera.exe'
  },
  ENV_CMD: 'OPERA_BIN'
};

var SafariBrowser = function() {
  Browser.apply(this, arguments);

  this.start = function(url) {
    var HTML_TPL = path.normalize(__dirname + '/../static/safari.html');
    var self = this;

    fs.readFile(HTML_TPL, function(err, data) {
      var content = data.toString().replace('%URL%', url);
      var staticHtmlPath = self._tempDir + '/redirect.html';

      fs.writeFile(staticHtmlPath, content, function(err) {
        self._execCommand(self._getCommand() + ' ' + staticHtmlPath);
      });
    });
  };
};

SafariBrowser.prototype = {
  name: 'Safari',

  DEFAULT_CMD: {
    darwin: '/Applications/Safari.app/Contents/MacOS/Safari'
  },
  ENV_CMD: 'SAFARI_BIN'
};

var PhantomJSBrowser = function() {
  Browser.apply(this, arguments);

  this.start = function(url) {
    // create the js file, that will open testacular
    var captureFile = this._tempDir + '/capture.js';
    var captureCode = '(new WebPage()).open("' + url + '");';
    fs.createWriteStream(captureFile).end(captureCode);

    // and start phantomjs
    this._execCommand(this._getCommand() + ' ' + captureFile);
  };
};

PhantomJSBrowser.prototype = {
  name: 'PhantomJS',

  DEFAULT_CMD: {
    linux: 'phantomjs',
    darwin: '/usr/local/bin/phantomjs'
  },
  ENV_CMD: 'PHANTOMJS_BIN'
};

var IE8Browser = function() {
  Browser.apply(this, arguments);
};

IE8Browser.prototype = {
  name: 'IE8',
  DEFAULT_CMD: {
    // darwin: '/usr/local/bin/ie8',
    // linux: '/usr/bin/ie8'
    // win32: ''
  },
  ENV_CMD: 'IE8_BIN'
};

var IE9Browser = function() {
  Browser.apply(this, arguments);
};

IE9Browser.prototype = {
  name: 'IE9',
  DEFAULT_CMD: {
    // darwin: '/usr/local/bin/ie9',
    // linux: '/usr/bin/ie9'
    // win32: ''
  },
  ENV_CMD: 'IE9_BIN'
};

var ScriptBrowser = function(id, script) {
  Browser.apply(this, arguments);

  this.name = script;

  this._getCommand = function() {
    return '"' + script + '"';
  };
};

var Launcher = function() {
  var browsers = [];

  this.launch = function(names, port, urlRoot) {
    var url = 'http://localhost:' + port + urlRoot;
    var Cls, browser, id;

    names.forEach(function(nameOrCls) {
      id = counter++;

      if (util.isString(nameOrCls)) {
        Cls = exports[nameOrCls + 'Browser'] || ScriptBrowser;
      } else if (util.isFunction(nameOrCls)) {
        Cls = nameOrCls;
        nameOrCls = null;
      }

      browser = new Cls(id, nameOrCls);
      browser.id = id;
      browser.isCaptured = false;

      log.info('Starting  browser "%s"', browser.name || 'Custom');
      browser.start(url + '?id=' + browser.id);
      browsers.push(browser);
    });
  };

  this.kill = function(callback) {
    var remaining = 0;
    var finish = function() {
      remaining--;
      if (!remaining && callback) {
        callback();
      }
    };

    if (!browsers.length) {
      return process.nextTick(callback);
    }

    browsers.forEach(function(browser) {
      remaining++;
      browser.kill(finish);
    });
  };

  this.areAllCaptured = function() {
    return !browsers.some(function(browser) {
      return !browser.isCaptured;
    });
  };

  this.markCaptured = function(id) {
    browsers.forEach(function(browser) {
      if (browser.id === id) {
        browser.isCaptured = true;
      }
    });
  };
};

exports.Launcher = Launcher;
exports.BaseBrowser = Browser;
exports.ChromeBrowser = ChromeBrowser;
exports.ChromeCanaryBrowser = ChromeCanaryBrowser;
exports.FirefoxBrowser = FirefoxBrowser;
exports.OperaBrowser = OperaBrowser;
exports.SafariBrowser = SafariBrowser;
exports.PhantomJSBrowser = PhantomJSBrowser;
exports.IE8Browser = IE8Browser;
exports.IE9Browser = IE9Browser;
<commit_msg>Fix opera launcher to create new prefs with disabling all pop-ups<commit_after>var exec = require('child_process').exec;
var path = require('path');
var log = require('./logger').create('launcher');
var env = process.env;
var fs = require('fs');
var util = require('./util');

var counter = 1;

// inspired by https://github.com/admc/jellyfish/blob/master/lib/browsers.js
var Browser = function(id) {

  var exitCallback = function() {};

  this._getCommand = function() {
    var cmd = env[this.ENV_CMD] || this.DEFAULT_CMD[process.platform];
    return '"' + path.normalize(cmd) + '"';
  };

  this._execCommand = function(cmd) {
    // TODO(vojta): show some errors if cmd does not exists...
    log.debug(cmd);
    this._process = exec(cmd, function(e) {
      if (e && !e.killed) {
        log.error(e);
      }
    });

    var tempDir = this._tempDir;
    this._process.on('exit', function() {
      log.debug('Cleaning the profile at %s', tempDir);
      exec('rm -rf "' + tempDir + '"', exitCallback);
    });
  };

  this._getOptions = function(url) {
    return [url];
  };

  this._tempDir = path.normalize((env.TMPDIR || env.TMP || env.TEMP || '/tmp') + '/testacular-' + id.toString());

  try {
    log.debug('Creating temp dir at ' + this._tempDir);
    fs.mkdirSync(this._tempDir);
  } catch (e) {}

  this.start = function(url) {
    this._execCommand(this._getCommand() + ' ' + this._getOptions(url).join(' '));
  };

  this.kill = function(callback) {
    exitCallback = callback || function() {};

    if (this._process.exitCode === null) {
      this._process.kill();
    } else {
      process.nextTick(exitCallback);
    }
  };
};

var ChromeBrowser = function() {
  Browser.apply(this, arguments);

  this._getOptions = function(url) {
    // Chrome CLI options
    // http://peter.sh/experiments/chromium-command-line-switches/
    return [
      '--user-data-dir=' + this._tempDir,
      '--no-default-browser-check',
      '--no-first-run',
      '--disable-default-apps',
      url
    ];
  };
};

ChromeBrowser.prototype = {
  name: 'Chrome',

  DEFAULT_CMD: {
    linux: 'google-chrome',
    darwin: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
    win32: process.env.LOCALAPPDATA + '\\Google\\Chrome\\Application\\chrome.exe'
  },
  ENV_CMD: 'CHROME_BIN'
};

var ChromeCanaryBrowser = function() {
  ChromeBrowser.apply(this, arguments);

  var parentOptions = this._getOptions;
  this._getOptions = function(url) {
    // disable crankshaft optimizations, as it causes lot of memory leaks (as of Chrome 23.0)
    return parentOptions.call(this, url).concat(['--js-flags="--nocrankshaft --noopt"']);
  };
};

ChromeCanaryBrowser.prototype = {
  name: 'ChromeCanary',

  DEFAULT_CMD: {
    linux: 'google-chrome-canary',
    darwin: '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
    win32: process.env.LOCALAPPDATA + '\\Google\\Chrome SxS\\Application\\chrome.exe'
  },
  ENV_CMD: 'CHROME_CANARY_BIN'
};

var FirefoxBrowser = function() {
  Browser.apply(this, arguments);

  this.start = function(url) {
    var self = this;
    var command = this._getCommand();

    // create profile first
    exec(command + ' -CreateProfile testacular', function(e, stdout, stderr) {
      if (e) {
        log.error(e);
      }

      var match = /at\s\'(.*)\/prefs\.js\'/.exec(stderr.toString());

      if (match) {
        var profile = self._tempDir = match[1];
        var prefs = 'user_pref("browser.shell.checkDefaultBrowser", false);\n' +
                    'user_pref("browser.bookmarks.restore_default_bookmarks", false);\n';

        fs.createWriteStream(profile + '/prefs.js', {flags: 'a'}).write(prefs);
        self._execCommand(command + ' -profile "' + profile + '" ' + url);
      } else {
        log.warn('Cannot create Firefox profile');
        self._execCommand(command + ' ' + url);
      }
    });
  };
};

FirefoxBrowser.prototype = {
  name: 'Firefox',

  DEFAULT_CMD: {
    linux: 'firefox',
    darwin: '/Applications/Firefox.app/Contents/MacOS/firefox-bin',
    win32: process.env.ProgramFiles + '\\Mozilla Firefox\\firefox.exe'
  },
  ENV_CMD: 'FIREFOX_BIN'
};

var OperaBrowser = function() {
  Browser.apply(this, arguments);

  this._getOptions = function(url) {
    // Opera CLI options
    // http://www.opera.com/docs/switches/
    return [
      '-pd ' + this._tempDir,
      '-nomail',
      url
    ];
  };

  this.start = function(url) {
    var self = this;
    var prefs = 'Opera Preferences version 2.1\n' +
      '[User Prefs]\n' +
      'Show Default Browser Dialog=0\n' +
      'Recovery Strategy=2\n' +
      'Home URL=about:blank\n' +
      'Show Close All But Active Dialog=0\n' +
      'Show Close All Dialog=0\n' +
      'Show Crash Log Upload Dialog=0\n' +
      'Show Delete Mail Dialog=0\n' +
      'Show Download Manager Selection Dialog=0\n' +
      'Show Geolocation License Dialog=0\n' +
      'Show Mail Error Dialog=0\n' +
      'Show New Opera Dialog=0\n' +
      'Show Problem Dialog=0\n' +
      'Show Progress Dialog=0\n' +
      'Show Validation Dialog=0\n' +
      'Show Widget Debug Info Dialog=0\n' +
      'Show Startup Dialog=0\n' +
      'Enable Usage Statistics=0\n' +
      '[Install]\n' +
      'Newest Used Version=1.00.0000\n';

    var prefsFile = this._tempDir + '/operaprefs.ini';
    fs.writeFile(prefsFile, prefs, function(err) {
      // TODO(vojta): handle error
      self._execCommand(self._getCommand() + ' ' + self._getOptions(url).join(' '));
    });
  };
};

OperaBrowser.prototype = {
  name: 'Opera',

  DEFAULT_CMD: {
    linux: 'opera',
    darwin: '/Applications/Opera.app/Contents/MacOS/Opera',
    win32: process.env.ProgramFiles + '\\Opera\\opera.exe'
  },
  ENV_CMD: 'OPERA_BIN'
};

var SafariBrowser = function() {
  Browser.apply(this, arguments);

  this.start = function(url) {
    var HTML_TPL = path.normalize(__dirname + '/../static/safari.html');
    var self = this;

    fs.readFile(HTML_TPL, function(err, data) {
      var content = data.toString().replace('%URL%', url);
      var staticHtmlPath = self._tempDir + '/redirect.html';

      fs.writeFile(staticHtmlPath, content, function(err) {
        self._execCommand(self._getCommand() + ' ' + staticHtmlPath);
      });
    });
  };
};

SafariBrowser.prototype = {
  name: 'Safari',

  DEFAULT_CMD: {
    darwin: '/Applications/Safari.app/Contents/MacOS/Safari'
  },
  ENV_CMD: 'SAFARI_BIN'
};

var PhantomJSBrowser = function() {
  Browser.apply(this, arguments);

  this.start = function(url) {
    // create the js file, that will open testacular
    var captureFile = this._tempDir + '/capture.js';
    var captureCode = '(new WebPage()).open("' + url + '");';
    fs.createWriteStream(captureFile).end(captureCode);

    // and start phantomjs
    this._execCommand(this._getCommand() + ' ' + captureFile);
  };
};

PhantomJSBrowser.prototype = {
  name: 'PhantomJS',

  DEFAULT_CMD: {
    linux: 'phantomjs',
    darwin: '/usr/local/bin/phantomjs'
  },
  ENV_CMD: 'PHANTOMJS_BIN'
};

var IE8Browser = function() {
  Browser.apply(this, arguments);
};

IE8Browser.prototype = {
  name: 'IE8',
  DEFAULT_CMD: {
    // darwin: '/usr/local/bin/ie8',
    // linux: '/usr/bin/ie8'
    // win32: ''
  },
  ENV_CMD: 'IE8_BIN'
};

var IE9Browser = function() {
  Browser.apply(this, arguments);
};

IE9Browser.prototype = {
  name: 'IE9',
  DEFAULT_CMD: {
    // darwin: '/usr/local/bin/ie9',
    // linux: '/usr/bin/ie9'
    // win32: ''
  },
  ENV_CMD: 'IE9_BIN'
};

var ScriptBrowser = function(id, script) {
  Browser.apply(this, arguments);

  this.name = script;

  this._getCommand = function() {
    return '"' + script + '"';
  };
};

var Launcher = function() {
  var browsers = [];

  this.launch = function(names, port, urlRoot) {
    var url = 'http://localhost:' + port + urlRoot;
    var Cls, browser, id;

    names.forEach(function(nameOrCls) {
      id = counter++;

      if (util.isString(nameOrCls)) {
        Cls = exports[nameOrCls + 'Browser'] || ScriptBrowser;
      } else if (util.isFunction(nameOrCls)) {
        Cls = nameOrCls;
        nameOrCls = null;
      }

      browser = new Cls(id, nameOrCls);
      browser.id = id;
      browser.isCaptured = false;

      log.info('Starting  browser "%s"', browser.name || 'Custom');
      browser.start(url + '?id=' + browser.id);
      browsers.push(browser);
    });
  };

  this.kill = function(callback) {
    var remaining = 0;
    var finish = function() {
      remaining--;
      if (!remaining && callback) {
        callback();
      }
    };

    if (!browsers.length) {
      return process.nextTick(callback);
    }

    browsers.forEach(function(browser) {
      remaining++;
      browser.kill(finish);
    });
  };

  this.areAllCaptured = function() {
    return !browsers.some(function(browser) {
      return !browser.isCaptured;
    });
  };

  this.markCaptured = function(id) {
    browsers.forEach(function(browser) {
      if (browser.id === id) {
        browser.isCaptured = true;
      }
    });
  };
};

exports.Launcher = Launcher;
exports.BaseBrowser = Browser;
exports.ChromeBrowser = ChromeBrowser;
exports.ChromeCanaryBrowser = ChromeCanaryBrowser;
exports.FirefoxBrowser = FirefoxBrowser;
exports.OperaBrowser = OperaBrowser;
exports.SafariBrowser = SafariBrowser;
exports.PhantomJSBrowser = PhantomJSBrowser;
exports.IE8Browser = IE8Browser;
exports.IE9Browser = IE9Browser;
<|endoftext|>


<commit_before>'use strict';

var BoardIO = require('board-io'),
  i2cBus = require('i2c-bus'),
  Gpio = require('onoff').Gpio,
  util = require('util'),
  Led = require('./led');

var DEFAULT_SAMPLING_INTERVAL = 5;

function LinuxIO(options) {
  var i;

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

  BoardIO.call(this);

  options = options || {};

  this.defaultLed = options.defaultLed;
  this.aref = options.aref;
  this.vref = options.vref;

  this._samplingInterval = typeof(options.samplingInterval) !== 'undefined' ?
    options.samplingInterval : DEFAULT_SAMPLING_INTERVAL;
  this._reports = [];
  this._reportTimeoutId = 0;
  this._analogReports = [];
  this._analogReportsTimeoutId = 0;
  this._addressToBus = {};
  this._defaultI2cBus = options.defaultI2cBus;
  this._i2cBuses = {};
  this._pinsById = {};

  if (options.pins) {
    options.pins.forEach(function (pin, index) {
      var pinData = {
        index: index,
        supportedModes: pin.modes.slice(0),
        mode: this.MODES.UNKNOWN,
        report: 0,
        analogChannel: typeof pin.analogChannel === 'number' ? pin.analogChannel : 127
      }

      this._pins[index] = pinData;

      if (Array.isArray(pin.ids)) {
        pin.ids.forEach(function (id) {
          this._pinsById[id] = pinData;
        }.bind(this));
      }

      if (typeof pin.gpioNo === 'number') {
        pinData.gpioNo = pin.gpioNo;
      }

      if (typeof pin.ledPath === 'string') {
        pinData.ledPath = pin.ledPath;
        pinData.isLed = true;
      } else {
        pinData.isLed = false;
      }
    }.bind(this));
  }
}
util.inherits(LinuxIO, BoardIO);

LinuxIO.prototype.normalize = function(pin) {
  if (typeof pin === 'string') {
    return this._pinsById[pin].index;
  }

  return pin;
};

LinuxIO.prototype.pinMode = function(pin, mode) {
  var pinData = this._pins[this.normalize(pin)];

  if (mode === this.MODES.INPUT) {
    this._pinModeInput(pinData);
  } else if (mode === this.MODES.OUTPUT) {
    if (pinData.isLed) {
      this._pinModeLed(pinData);
    } else {
      this._pinModeOutput(pinData);
    }
  } else if (mode === this.MODES.ANALOG) {
    this._pinModeAnalog(pinData);
  } else if (mode === this.MODES.PWM) {
    this._pinModePwm(pinData);
  } else if (mode === this.MODES.SERVO) {
    this._pinModeServo(pinData);
  } else {
    throw new Error('mode ' + mode + ' is not supported yet');
  }

  pinData.mode = mode;

  return this;
};

LinuxIO.prototype.digitalRead = function(pin, handler) {
  var pinIndex = this.normalize(pin),
    pinData = this._pins[pinIndex],
    event = 'digital-read-' + pinIndex;

  pinData.report = 1;

  this._reports[pinIndex] = {
    pinData: pinData,
    event: event
  }

  this.on(event, handler);

  if (!this._reportTimeoutId) {
    this._reportTimeoutId = setTimeout(this._tick.bind(this), this._samplingInterval);
  }

  return this;
};

LinuxIO.prototype.digitalWrite = function(pin, value) {
  var pinData = this._pins[this.normalize(pin)];

  if (pinData.isLed) {
    this._digitalWriteLedSync(pinData, value);
  } else {
    this._digitalWriteSync(pinData, value);
  }

  pinData.value = value;

  return this;
};

LinuxIO.prototype.analogRead = function(pin, handler) {
  var pinIndex = this.normalize(pin),
    pinData = this._pins[pinIndex],
    event = 'analog-read-' + pinIndex;

  pinData.report = 1;

  this._analogReports[pinIndex] = {
    pinData: pinData,
    event: event
  }

  this.on(event, handler);

  if (!this._analogReportsTimeoutId) {
    this._analogReportsTimeoutId = setTimeout(
      this._analogReportsTick.bind(this),
      this._samplingInterval
    );
  }

  return this;
};

LinuxIO.prototype.pwmWrite = function(pin, value) {
  this._pwmWriteSync(this._pins[this.normalize(pin)], value);

  return this;
};

LinuxIO.prototype.analogWrite = LinuxIO.prototype.pwmWrite;

LinuxIO.prototype.servoConfig = function(pin, min, max) {
  var pinData = this._pins(this.normalize(pin));

  /*if (pinData.mode !== this.MODES.SERVO) { // TODO - Is this ever needed?
    this.pinMode(pin, this.MODES.SERVO);
  }*/

  pinData.servoConfig.min = min;
  pinData.servoConfig.max = max;

  return this;
};

LinuxIO.prototype.servoWrite = function(pin, value) {
  var pinData = this._pins[this.normalize(pin)];

  /*if (pinData.mode !== this.MODES.SERVO) { // TODO - Is this ever needed?
    this.pinMode(pin, this.MODES.SERVO);
  }*/

  if (value < 0) {
    value = 0;
  } else if (value > 180) {
    value = 180;
  }

  this._servoWriteSync(pinData, value);

  return this;
};

LinuxIO.prototype.i2cConfig = function(options) {
  // note that there's a design flaw here
  // two devices with the same address on different buses doesn't work
  // see https://github.com/rwaldron/io-plugins/issues/13

  // options.address is _always_ sent by all I2C component classes in
  // Johnny-Five
  var address = options.address;

  // options.bus is optional
  var bus = typeof(options.bus) !== 'undefined' ? options.bus : this._defaultI2cBus;

  // associate the address to the bus
  if (!this._addressToBus.hasOwnProperty(address)) {
    this._addressToBus[address] = bus;
  }

  // create an i2c-bus object for the I2C bus
  if (!this._i2cBuses.hasOwnProperty(bus)) {
    this._i2cBuses[bus] = i2cBus.openSync(bus);
  }

  return this;
};

LinuxIO.prototype.i2cWrite = function(address, cmdRegOrData, inBytes) {
  var i2c = this._i2cBuses[this._addressToBus[address]];

  // if i2cWrite was used for an i2cWriteReg call...
  if (arguments.length === 3 &&
      !Array.isArray(cmdRegOrData) &&
      !Array.isArray(inBytes)) {
    return this.i2cWriteReg(address, cmdRegOrData, inBytes);
  }

  // fix arguments if called with Firmata.js API
  if (arguments.length === 2) {
    if (Array.isArray(cmdRegOrData)) {
      inBytes = cmdRegOrData.slice();
      cmdRegOrData = inBytes.shift();
    } else {
      inBytes = [];
    }
  }

  var buffer = new Buffer([cmdRegOrData].concat(inBytes));

  // only write if bytes provided
  if (buffer.length) {
    i2c.i2cWriteSync(address, buffer.length, buffer);
  }
  return this;
};

LinuxIO.prototype.i2cWriteReg = function(address, register, byte) {
  var i2c = this._i2cBuses[this._addressToBus[address]];

  i2c.writeByteSync(address, register, byte);

  return this;
};

LinuxIO.prototype.i2cRead = function(address, register, size, handler) {
  var continuousRead = function() {
    this.i2cReadOnce(address, register, size, function(bytes) {
      handler(bytes);
      setTimeout(continuousRead, this._samplingInterval);
    });
  }.bind(this);

  continuousRead();

  return this;
};

LinuxIO.prototype.i2cReadOnce = function(address, register, size, handler) {
  // fix arguments if called with Firmata.js API
  if (arguments.length === 3 &&
      typeof register === "number" &&
      typeof size === "function") {
    handler = size;
    size = register;
    register = null;
  }

  var event = "I2C-reply" + address + "-" + (register !== null ? register : 0);

  var afterRead = function (err, bytesRead, buffer) {
    if (err) {
      return this.emit("error", err);
    }

    // convert buffer to an Array before emit
    this.emit(event, Array.prototype.slice.call(buffer));
  }.bind(this);

  if (typeof handler === "function") {
    this.once(event, handler);
  }

  var i2c = this._i2cBuses[this._addressToBus[address]];
  var data = new Buffer(size);

  if (register !== null) {
    i2c.readI2cBlock(address, register, size, data, afterRead);
  } else {
    i2c.i2cRead(address, size, data, afterRead);
  }

  return this;
};

LinuxIO.prototype._tick = function() {
  this._reports.forEach(function (report) {
    var value = this._digitalReadSync(report.pinData);

    if (value !== report.pinData.value) {
      report.pinData.value = value;
      this.emit(report.event, value);
    }
  }.bind(this));

  this._reportTimeoutId = setTimeout(this._tick.bind(this), this._samplingInterval);
};

LinuxIO.prototype._analogReportsTick = function() {
  var reports = this._analogReports.filter(function (report) {
      return report;
    });
  var reportsProcessed = 0;

  reports.forEach(function (report) {
    this._analogRead(report.pinData, function (err, value) {
      if (err) {
        this.emit('error', err);
      } else {
        if (value !== report.pinData.value) {
          report.pinData.value = value;
          this.emit(report.event, value);
        }
      }

      reportsProcessed += 1;

      if (reportsProcessed === reports.length) {
        this._analogReportsTickTimeoutId = setTimeout(
          this._analogReportsTick.bind(this),
          this._samplingInterval
        );
      }
    }.bind(this));
  }.bind(this));
};

LinuxIO.prototype._pinModeInput = function(pinData) {
  if (!pinData.gpio) {
    pinData.gpio = new Gpio(pinData.gpioNo, 'in');
  } else if (pinData.mode != mode) {
    pinData.gpio.setDirection('in');
  }
};

LinuxIO.prototype._pinModeOutput = function(pinData) {
  if (!pinData.gpio) {
    pinData.gpio = new Gpio(pinData.gpioNo, 'out');
  } else if (pinData.mode != mode) {
    pinData.gpio.setDirection('out');
  }
};

LinuxIO.prototype._pinModeLed = function(pinData) {
  if (!pinData.led) {
    pinData.led = new Led(pinData.ledPath);
  }
};

LinuxIO.prototype._pinModeAnalog = function(pinData) {
  throw new Error('ANALOG mode is not supported yet');
};

LinuxIO.prototype._pinModePwm = function(pinData) {
  throw new Error('PWM mode is not supported yet');
};

LinuxIO.prototype._pinModeServo = function(pinData) {
  throw new Error('SERVO mode is not supported yet');
};

LinuxIO.prototype._digitalReadSync = function(pinData) {
  return pinData.gpio.readSync();
};

LinuxIO.prototype._digitalWriteSync = function(pinData, value) {
  pinData.gpio.writeSync(value);
};

LinuxIO.prototype._digitalWriteLedSync = function(pinData, value) {
  if (value) {
    pinData.led.on();
  } else {
    pinData.led.off();
  }
};

LinuxIO.prototype._analogRead = function(pinData, callback) {
  throw new Error('analogRead is not supported yet');
};

LinuxIO.prototype._pwmWriteSync = function(pinData, value) {
  throw new Error('pwmWrite is not supported yet');
};

LinuxIO.prototype._servoWriteSync = function(pinData, value) {
  throw new Error('servoWrite is not supported yet');
};

module.exports = LinuxIO;

<commit_msg>improved naming for digital reports<commit_after>'use strict';

var BoardIO = require('board-io'),
  i2cBus = require('i2c-bus'),
  Gpio = require('onoff').Gpio,
  util = require('util'),
  Led = require('./led');

var DEFAULT_SAMPLING_INTERVAL = 5;

function LinuxIO(options) {
  var i;

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

  BoardIO.call(this);

  options = options || {};

  this.defaultLed = options.defaultLed;
  this.aref = options.aref;
  this.vref = options.vref;

  this._samplingInterval = typeof(options.samplingInterval) !== 'undefined' ?
    options.samplingInterval : DEFAULT_SAMPLING_INTERVAL;
  this._digitalReports = [];
  this._digitalReportsTimeoutId = 0;
  this._analogReports = [];
  this._analogReportsTimeoutId = 0;
  this._addressToBus = {};
  this._defaultI2cBus = options.defaultI2cBus;
  this._i2cBuses = {};
  this._pinsById = {};

  if (options.pins) {
    options.pins.forEach(function (pin, index) {
      var pinData = {
        index: index,
        supportedModes: pin.modes.slice(0),
        mode: this.MODES.UNKNOWN,
        report: 0,
        analogChannel: typeof pin.analogChannel === 'number' ? pin.analogChannel : 127
      }

      this._pins[index] = pinData;

      if (Array.isArray(pin.ids)) {
        pin.ids.forEach(function (id) {
          this._pinsById[id] = pinData;
        }.bind(this));
      }

      if (typeof pin.gpioNo === 'number') {
        pinData.gpioNo = pin.gpioNo;
      }

      if (typeof pin.ledPath === 'string') {
        pinData.ledPath = pin.ledPath;
        pinData.isLed = true;
      } else {
        pinData.isLed = false;
      }
    }.bind(this));
  }
}
util.inherits(LinuxIO, BoardIO);

LinuxIO.prototype.normalize = function(pin) {
  if (typeof pin === 'string') {
    return this._pinsById[pin].index;
  }

  return pin;
};

LinuxIO.prototype.pinMode = function(pin, mode) {
  var pinData = this._pins[this.normalize(pin)];

  if (mode === this.MODES.INPUT) {
    this._pinModeInput(pinData);
  } else if (mode === this.MODES.OUTPUT) {
    if (pinData.isLed) {
      this._pinModeLed(pinData);
    } else {
      this._pinModeOutput(pinData);
    }
  } else if (mode === this.MODES.ANALOG) {
    this._pinModeAnalog(pinData);
  } else if (mode === this.MODES.PWM) {
    this._pinModePwm(pinData);
  } else if (mode === this.MODES.SERVO) {
    this._pinModeServo(pinData);
  } else {
    throw new Error('mode ' + mode + ' is not supported yet');
  }

  pinData.mode = mode;

  return this;
};

LinuxIO.prototype.digitalRead = function(pin, handler) {
  var pinIndex = this.normalize(pin),
    pinData = this._pins[pinIndex],
    event = 'digital-read-' + pinIndex;

  pinData.report = 1;

  this._digitalReports[pinIndex] = {
    pinData: pinData,
    event: event
  }

  this.on(event, handler);

  if (!this._digitalReportsTimeoutId) {
    this._digitalReportsTimeoutId = setTimeout(this._tick.bind(this), this._samplingInterval);
  }

  return this;
};

LinuxIO.prototype.digitalWrite = function(pin, value) {
  var pinData = this._pins[this.normalize(pin)];

  if (pinData.isLed) {
    this._digitalWriteLedSync(pinData, value);
  } else {
    this._digitalWriteSync(pinData, value);
  }

  pinData.value = value;

  return this;
};

LinuxIO.prototype.analogRead = function(pin, handler) {
  var pinIndex = this.normalize(pin),
    pinData = this._pins[pinIndex],
    event = 'analog-read-' + pinIndex;

  pinData.report = 1;

  this._analogReports[pinIndex] = {
    pinData: pinData,
    event: event
  }

  this.on(event, handler);

  if (!this._analogReportsTimeoutId) {
    this._analogReportsTimeoutId = setTimeout(
      this._analogReportsTick.bind(this),
      this._samplingInterval
    );
  }

  return this;
};

LinuxIO.prototype.pwmWrite = function(pin, value) {
  this._pwmWriteSync(this._pins[this.normalize(pin)], value);

  return this;
};

LinuxIO.prototype.analogWrite = LinuxIO.prototype.pwmWrite;

LinuxIO.prototype.servoConfig = function(pin, min, max) {
  var pinData = this._pins(this.normalize(pin));

  /*if (pinData.mode !== this.MODES.SERVO) { // TODO - Is this ever needed?
    this.pinMode(pin, this.MODES.SERVO);
  }*/

  pinData.servoConfig.min = min;
  pinData.servoConfig.max = max;

  return this;
};

LinuxIO.prototype.servoWrite = function(pin, value) {
  var pinData = this._pins[this.normalize(pin)];

  /*if (pinData.mode !== this.MODES.SERVO) { // TODO - Is this ever needed?
    this.pinMode(pin, this.MODES.SERVO);
  }*/

  if (value < 0) {
    value = 0;
  } else if (value > 180) {
    value = 180;
  }

  this._servoWriteSync(pinData, value);

  return this;
};

LinuxIO.prototype.i2cConfig = function(options) {
  // note that there's a design flaw here
  // two devices with the same address on different buses doesn't work
  // see https://github.com/rwaldron/io-plugins/issues/13

  // options.address is _always_ sent by all I2C component classes in
  // Johnny-Five
  var address = options.address;

  // options.bus is optional
  var bus = typeof(options.bus) !== 'undefined' ? options.bus : this._defaultI2cBus;

  // associate the address to the bus
  if (!this._addressToBus.hasOwnProperty(address)) {
    this._addressToBus[address] = bus;
  }

  // create an i2c-bus object for the I2C bus
  if (!this._i2cBuses.hasOwnProperty(bus)) {
    this._i2cBuses[bus] = i2cBus.openSync(bus);
  }

  return this;
};

LinuxIO.prototype.i2cWrite = function(address, cmdRegOrData, inBytes) {
  var i2c = this._i2cBuses[this._addressToBus[address]];

  // if i2cWrite was used for an i2cWriteReg call...
  if (arguments.length === 3 &&
      !Array.isArray(cmdRegOrData) &&
      !Array.isArray(inBytes)) {
    return this.i2cWriteReg(address, cmdRegOrData, inBytes);
  }

  // fix arguments if called with Firmata.js API
  if (arguments.length === 2) {
    if (Array.isArray(cmdRegOrData)) {
      inBytes = cmdRegOrData.slice();
      cmdRegOrData = inBytes.shift();
    } else {
      inBytes = [];
    }
  }

  var buffer = new Buffer([cmdRegOrData].concat(inBytes));

  // only write if bytes provided
  if (buffer.length) {
    i2c.i2cWriteSync(address, buffer.length, buffer);
  }
  return this;
};

LinuxIO.prototype.i2cWriteReg = function(address, register, byte) {
  var i2c = this._i2cBuses[this._addressToBus[address]];

  i2c.writeByteSync(address, register, byte);

  return this;
};

LinuxIO.prototype.i2cRead = function(address, register, size, handler) {
  var continuousRead = function() {
    this.i2cReadOnce(address, register, size, function(bytes) {
      handler(bytes);
      setTimeout(continuousRead, this._samplingInterval);
    });
  }.bind(this);

  continuousRead();

  return this;
};

LinuxIO.prototype.i2cReadOnce = function(address, register, size, handler) {
  // fix arguments if called with Firmata.js API
  if (arguments.length === 3 &&
      typeof register === "number" &&
      typeof size === "function") {
    handler = size;
    size = register;
    register = null;
  }

  var event = "I2C-reply" + address + "-" + (register !== null ? register : 0);

  var afterRead = function (err, bytesRead, buffer) {
    if (err) {
      return this.emit("error", err);
    }

    // convert buffer to an Array before emit
    this.emit(event, Array.prototype.slice.call(buffer));
  }.bind(this);

  if (typeof handler === "function") {
    this.once(event, handler);
  }

  var i2c = this._i2cBuses[this._addressToBus[address]];
  var data = new Buffer(size);

  if (register !== null) {
    i2c.readI2cBlock(address, register, size, data, afterRead);
  } else {
    i2c.i2cRead(address, size, data, afterRead);
  }

  return this;
};

LinuxIO.prototype._tick = function() {
  this._digitalReports.forEach(function (report) {
    var value = this._digitalReadSync(report.pinData);

    if (value !== report.pinData.value) {
      report.pinData.value = value;
      this.emit(report.event, value);
    }
  }.bind(this));

  this._digitalReportsTimeoutId = setTimeout(this._tick.bind(this), this._samplingInterval);
};

LinuxIO.prototype._analogReportsTick = function() {
  var reports = this._analogReports.filter(function (report) {
      return report;
    });
  var reportsProcessed = 0;

  reports.forEach(function (report) {
    this._analogRead(report.pinData, function (err, value) {
      if (err) {
        this.emit('error', err);
      } else {
        if (value !== report.pinData.value) {
          report.pinData.value = value;
          this.emit(report.event, value);
        }
      }

      reportsProcessed += 1;

      if (reportsProcessed === reports.length) {
        this._analogReportsTickTimeoutId = setTimeout(
          this._analogReportsTick.bind(this),
          this._samplingInterval
        );
      }
    }.bind(this));
  }.bind(this));
};

LinuxIO.prototype._pinModeInput = function(pinData) {
  if (!pinData.gpio) {
    pinData.gpio = new Gpio(pinData.gpioNo, 'in');
  } else if (pinData.mode != mode) {
    pinData.gpio.setDirection('in');
  }
};

LinuxIO.prototype._pinModeOutput = function(pinData) {
  if (!pinData.gpio) {
    pinData.gpio = new Gpio(pinData.gpioNo, 'out');
  } else if (pinData.mode != mode) {
    pinData.gpio.setDirection('out');
  }
};

LinuxIO.prototype._pinModeLed = function(pinData) {
  if (!pinData.led) {
    pinData.led = new Led(pinData.ledPath);
  }
};

LinuxIO.prototype._pinModeAnalog = function(pinData) {
  throw new Error('ANALOG mode is not supported yet');
};

LinuxIO.prototype._pinModePwm = function(pinData) {
  throw new Error('PWM mode is not supported yet');
};

LinuxIO.prototype._pinModeServo = function(pinData) {
  throw new Error('SERVO mode is not supported yet');
};

LinuxIO.prototype._digitalReadSync = function(pinData) {
  return pinData.gpio.readSync();
};

LinuxIO.prototype._digitalWriteSync = function(pinData, value) {
  pinData.gpio.writeSync(value);
};

LinuxIO.prototype._digitalWriteLedSync = function(pinData, value) {
  if (value) {
    pinData.led.on();
  } else {
    pinData.led.off();
  }
};

LinuxIO.prototype._analogRead = function(pinData, callback) {
  throw new Error('analogRead is not supported yet');
};

LinuxIO.prototype._pwmWriteSync = function(pinData, value) {
  throw new Error('pwmWrite is not supported yet');
};

LinuxIO.prototype._servoWriteSync = function(pinData, value) {
  throw new Error('servoWrite is not supported yet');
};

module.exports = LinuxIO;

<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');

var async = require('async');
var dmp_module = require("diff_match_patch");
var DMP = new dmp_module.diff_match_patch();
var _ = require("underscore");

var utils = require("./utils");

var Listener = function (path, conn) {
  var self = this;

  self.path = path;
  self.conn = conn;
  self.watch(function (err, result) {
    console.log("EVERYTHING IS BEING WATCHED");
    conn.listener_ready(self);
  });
  self.bufs = {};
  self.dirs = {};
  self.watchers = {};
  self.expected_changes = {};
};

Listener.prototype.listener = function (_path, is_dir, event, filename) {
  var self = this,
    patches,
    current_text,
    md5,
    buf = self.bufs[_path],
    real_path = path.join(self.path, _path);

  console.log(event, real_path, is_dir);
  if (is_dir || event === 'rename') {
    return;
  }
  if (!buf){
    return console.log('should share ' + real_path + "but I'm too stupid");
  }

  if (!buf.buf){
    return console.log('ignoring for get buf');
  }
  // try{
    current_text = fs.readFileSync(real_path);
    md5 = utils.md5(current_text);
    if (md5 === buf.md5){
      return console.info('got expected change');
    }
    patches = DMP.patch_make(buf.buf.toString(), current_text.toString());
    self.conn.write('patch', {
      'id': buf.id,
      'md5_after': md5,
      'md5_before': buf.md5,
      'path': _path,
      'patch': DMP.patch_toText(patches)
    });

    // self.conn.send_patch()
    // console.log(DMP.patch_toText(patches));
  // }  catch (e){
  //   console.error(e);
  // }

  // conn.write();
};

Listener.prototype.add_listener = function (f, is_dir, cb) {
  var self = this,
    rel_path = path.relative(self.path, f);

  is_dir = is_dir === true ? true : false;

  if (is_dir){
    fs.watch(f, self.listener.bind(self, rel_path, is_dir));
    self.dirs[rel_path] = true;
    return cb();
  }

  fs.readFile(f, function (err, buf) {
    if (err){
      return cb(err);
    }

    self.bufs[rel_path] = {buf: buf, md5: utils.md5(buf.toString())};
    // not sure possibly 50K closures is a good idea, but it works for HN...
    fs.watch(f, self.listener.bind(self, rel_path, is_dir));
    cb();
  });
};

Listener.prototype.watch = function (cb) {
  var self = this;

  async.auto({
    paths: function (cb) {
      utils.walk_dir(self.path, cb);
    },
    dirs: ['paths', function (cb, res) {
      async.eachLimit(_.values(res.paths.dirs), 20, function (filename, cb) {
        self.add_listener(filename, true, cb);
      }, cb);
    }],
    files: ['paths', function (cb, res) {
      async.eachLimit(_.values(res.paths.files), 20, function (filename, cb) {
        self.add_listener(filename, false, cb);
      }, cb);
    }]
  }, function (err, result) {
    if (err) {
      console.error(err);
      return cb(err, result);
    }
    return cb(err, result);
  });
};

Listener.prototype.patch = function(_path, patch_text, md5, id) {
  var self = this,
    res,
    self_md5_after,
    patches,
    buf = self.bufs[_path];

  if (!buf){
    return self.conn.send_get_buf(id);
  }

  try {
    patches = DMP.patch_fromText(patch_text);
  } catch (e) {
    log.error("Couldn't parse patch text:", patch_text, "\nException:", e);
    return self.conn.disconnect("Unable to parse the patch you sent.");
  }
  if (patches.length === 0) {
    return log.log("Patch is empty.");
  }

  res = DMP.patch_apply(patches, buf.buf.toString());
  if (!utils.patched_cleanly(res)){
    return self.conn.send_get_buf(id);
  }

  self_md5_after = utils.md5(res[0]);
  if (self_md5_after !== md5){
    console.log('md5s don\'t match');
    return self.conn.send_get_buf(id);
  }

  self.write(_path, md5, res[0]);

};

Listener.prototype.write = function(_path, md5, buf){
  var self = this,
    existing = self.bufs[_path],
    realpath = path.join(self.path, _path);

  if (existing && existing.md5 === md5){
    return;
  }

  fs.writeFile(realpath, buf, function(err){
    if (err){
      return console.error('could not write file: ' + realpath + ' because ' + toString(err));
    }
    existing.buf = new Buffer(buf);
    existing.md5 = md5;
  });
};
exports.Listener = Listener;
<commit_msg>now setting the current state after sending patches<commit_after>var fs = require('fs');
var path = require('path');

var async = require('async');
var dmp_module = require("diff_match_patch");
var DMP = new dmp_module.diff_match_patch();
var _ = require("underscore");

var utils = require("./utils");

var Listener = function (path, conn) {
  var self = this;

  self.path = path;
  self.conn = conn;
  self.watch(function (err, result) {
    console.log("EVERYTHING IS BEING WATCHED");
    conn.listener_ready(self);
  });
  self.bufs = {};
  self.dirs = {};
  self.watchers = {};
  self.expected_changes = {};
};

Listener.prototype.listener = function (_path, is_dir, event, filename) {
  var self = this,
    patches,
    current,
    md5,
    buf = self.bufs[_path],
    real_path = path.join(self.path, _path);

  console.log(event, real_path, is_dir);
  if (is_dir || event === 'rename') {
    return;
  }
  if (!buf){
    return console.log('should share ' + real_path + "but I'm too stupid");
  }

  if (!buf.buf){
    return console.log('ignoring for get buf');
  }
  // try{
    current = fs.readFileSync(real_path);
    md5 = utils.md5(current);
    if (md5 === buf.md5){
      return console.info('got expected change');
    }
    patches = DMP.patch_make(buf.buf.toString(), current.toString());
    self.conn.write('patch', {
      'id': buf.id,
      'md5_after': md5,
      'md5_before': buf.md5,
      'path': _path,
      'patch': DMP.patch_toText(patches)
    });
    buf.buf = current;
    buf.md5 = md5;

    // self.conn.send_patch()
    // console.log(DMP.patch_toText(patches));
  // }  catch (e){
  //   console.error(e);
  // }

  // conn.write();
};

Listener.prototype.add_listener = function (f, is_dir, cb) {
  var self = this,
    rel_path = path.relative(self.path, f);

  is_dir = is_dir === true ? true : false;

  if (is_dir){
    fs.watch(f, self.listener.bind(self, rel_path, is_dir));
    self.dirs[rel_path] = true;
    return cb();
  }

  fs.readFile(f, function (err, buf) {
    if (err){
      return cb(err);
    }

    self.bufs[rel_path] = {buf: buf, md5: utils.md5(buf.toString())};
    // not sure possibly 50K closures is a good idea, but it works for HN...
    fs.watch(f, self.listener.bind(self, rel_path, is_dir));
    cb();
  });
};

Listener.prototype.watch = function (cb) {
  var self = this;

  async.auto({
    paths: function (cb) {
      utils.walk_dir(self.path, cb);
    },
    dirs: ['paths', function (cb, res) {
      async.eachLimit(_.values(res.paths.dirs), 20, function (filename, cb) {
        self.add_listener(filename, true, cb);
      }, cb);
    }],
    files: ['paths', function (cb, res) {
      async.eachLimit(_.values(res.paths.files), 20, function (filename, cb) {
        self.add_listener(filename, false, cb);
      }, cb);
    }]
  }, function (err, result) {
    if (err) {
      console.error(err);
      return cb(err, result);
    }
    return cb(err, result);
  });
};

Listener.prototype.patch = function(_path, patch_text, md5, id) {
  var self = this,
    res,
    self_md5_after,
    patches,
    buf = self.bufs[_path];

  if (!buf){
    return self.conn.send_get_buf(id);
  }

  try {
    patches = DMP.patch_fromText(patch_text);
  } catch (e) {
    log.error("Couldn't parse patch text:", patch_text, "\nException:", e);
    return self.conn.disconnect("Unable to parse the patch you sent.");
  }
  if (patches.length === 0) {
    return log.log("Patch is empty.");
  }

  res = DMP.patch_apply(patches, buf.buf.toString());
  if (!utils.patched_cleanly(res)){
    return self.conn.send_get_buf(id);
  }

  self_md5_after = utils.md5(res[0]);
  if (self_md5_after !== md5){
    console.log('md5s don\'t match');
    return self.conn.send_get_buf(id);
  }

  self.write(_path, md5, res[0]);

};

Listener.prototype.write = function(_path, md5, buf){
  var self = this,
    existing = self.bufs[_path],
    realpath = path.join(self.path, _path);

  if (existing && existing.md5 === md5){
    return;
  }

  fs.writeFile(realpath, buf, function(err){
    if (err){
      return console.error('could not write file: ' + realpath + ' because ' + toString(err));
    }
    existing.buf = new Buffer(buf);
    existing.md5 = md5;
  });
};
exports.Listener = Listener;
<|endoftext|>


<commit_before>import React from 'react';

import DescriptionList from './DescriptionList';
import MarathonStore from '../stores/MarathonStore';

class MarathonTaskDetailsList extends React.Component {
  constructor() {
    super();
  }

  getTaskEndpoints(task) {
    if ((task.ports == null || task.ports.length === 0) &&
        (task.ipAddresses == null || task.ipAddresses.length === 0)) {
      return 'None';
    }

    let service = MarathonStore.getServiceFromTaskID(task.id);

    if (service != null &&
      service.ipAddress != null &&
      service.ipAddress.discovery != null &&
      service.ipAddress.discovery.ports != null &&
      task.ipAddresses != null &&
      task.ipAddresses.length > 0) {

      let ports = service.ipAddress.discovery.ports;
      let endpoints = task.ipAddresses.reduce(function (memo, address) {
        ports.forEach(function (port) {
          memo.push(`${address.ipAddress}:${port.number}`);
        });

        return memo;
      }, []);

      if (endpoints.length) {
        return endpoints.map(function (endpoint) {
          return (
            <a href={`//${endpoint}`} target="_blank">{endpoint}
          );
        });
      }

      return 'n/a';
    }

    return task.ports.map(function (port) {
      let endpoint = `${task.host}:${port}`;
      return (
        <a href={`//${endpoint}`} target="_blank">{endpoint}
      );
    });
  }

  getTaskStatus(task) {
    if (task == null || task.status == null) {
      return 'Unknown';
    }
    return task.status;
  }

  getMarathonTaskDetailsDescriptionList(task) {
    if (task == null) {
      return null;
    }

    let headerValueMapping = {
      'Host': task.host,
      'Ports': task.ports,
      'Endpoints': this.getTaskEndpoints(task),
      'Status': this.getTaskStatus(task),
      'Staged at': task.stagedAt,
      'Started at': task.startedAt,
      'Version': task.version
    };

    return (
      <DescriptionList
        className="container container-fluid flush container-pod container-pod-super-short flush-top"
        hash={headerValueMapping}
        headline="Marathon Task Configuration" />
    );
  }

  render() {
    const marathonTask = MarathonStore.getTaskFromTaskID(this.props.taskID);

    return this.getMarathonTaskDetailsDescriptionList(marathonTask);
  }
};

MarathonTaskDetailsList.propTypes = {
  taskID: React.PropTypes.string.isRequired
};

module.exports = MarathonTaskDetailsList;
<commit_msg>Use timefield for date<commit_after>import React from 'react';

import DescriptionList from './DescriptionList';
import MarathonStore from '../stores/MarathonStore';

class MarathonTaskDetailsList extends React.Component {
  constructor() {
    super();
  }

  getTaskEndpoints(task) {
    if ((task.ports == null || task.ports.length === 0) &&
        (task.ipAddresses == null || task.ipAddresses.length === 0)) {
      return 'None';
    }

    let service = MarathonStore.getServiceFromTaskID(task.id);

    if (service != null &&
      service.ipAddress != null &&
      service.ipAddress.discovery != null &&
      service.ipAddress.discovery.ports != null &&
      task.ipAddresses != null &&
      task.ipAddresses.length > 0) {

      let ports = service.ipAddress.discovery.ports;
      let endpoints = task.ipAddresses.reduce(function (memo, address) {
        ports.forEach(function (port) {
          memo.push(`${address.ipAddress}:${port.number}`);
        });

        return memo;
      }, []);

      if (endpoints.length) {
        return endpoints.map(function (endpoint) {
          return (
            <a href={`//${endpoint}`} target="_blank">{endpoint}
          );
        });
      }

      return 'n/a';
    }

    return task.ports.map(function (port) {
      let endpoint = `${task.host}:${port}`;
      return (
        <a href={`//${endpoint}`} target="_blank">{endpoint}
      );
    });
  }

  getTaskStatus(task) {
    if (task == null || task.status == null) {
      return 'Unknown';
    }
    return task.status;
  }

  getTimeField(time) {
    return (
      <time dateTime={time} title={time}>
        {new Date(time).toLocaleString()}
      </time>
    );
  }

  getMarathonTaskDetailsDescriptionList(task) {
    if (task == null) {
      return null;
    }

    let headerValueMapping = {
      'Host': task.host,
      'Ports': task.ports,
      'Endpoints': this.getTaskEndpoints(task),
      'Status': this.getTaskStatus(task),
      'Staged at': this.getTimeField(task.stagedAt),
      'Started at': this.getTimeField(task.startedAt),
      'Version': task.version
    };

    return (
      <DescriptionList
        className="container container-fluid flush container-pod container-pod-super-short flush-top"
        hash={headerValueMapping}
        headline="Marathon Task Configuration" />
    );
  }

  render() {
    const marathonTask = MarathonStore.getTaskFromTaskID(this.props.taskID);

    return this.getMarathonTaskDetailsDescriptionList(marathonTask);
  }
};

MarathonTaskDetailsList.propTypes = {
  taskID: React.PropTypes.string.isRequired
};

module.exports = MarathonTaskDetailsList;
<|endoftext|>