<commit_before>if (typeof diff_match_patch !== 'undefined') {
    diff_match_patch.prototype.diff_prettyHtml = function(diffs) {
        /* An override of prettyHthml from diff_match_patch. This
           one will not put any style attrs in the ins or del. */
        var html = [];
        var k = 1, // Line numbers of existing or added lines.
            dk = 1; // Line numbers of deleted lines.
        for (var x = 0; x < diffs.length; x++) {
            var op = diffs[x][0];    // Operation (insert, delete, equal)
            var data = diffs[x][1];  // Text of change.
            var text = data.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
            /* As as side effect, add in line numbers */
            var lines = text.split('\n');
            if (lines[lines.length-1] === '') {
                lines.pop();
            }
            if (lines[0] === '') {
                lines.splice(0, 1);
            }
            for (var t = 0; t < lines.length; t++) {
                switch (op) {
                    case DIFF_INSERT:
                        // TODO (andym): templates might work here as suggested by cvan
                        html.push(format('
{0} +' +
                                         '{1}
', k++, lines[t]));
                        break;
                    case DIFF_DELETE:
                        html.push(format('
 -' +
                                         '{1}
', dk++, lines[t]));
                        break;
                    case DIFF_EQUAL:
                        html.push(format('
{0}  ' +
                                         '{1}
', k++, lines[t]));
                        break;
                }
            }
        }
        return html.join('');
    };
}

function bind_viewer(nodes) {
    function Viewer() {
        this.nodes = nodes;
        this.wrapped = true;
        this.hidden = false;
        this.fix_vertically = function($inner, $outer) {
            function update() {
                var $sb_top = $outer.position().top,
                    $sb_bottom = $sb_top + $outer.height() - $inner.height();

                if ($(window).scrollTop() > $sb_bottom) {
                    $inner.css({'position': 'absolute', 'top': $sb_bottom});
                } else if ($(window).scrollTop() > $sb_top) {
                    $inner.css({'position': 'fixed', 'top': 0});
                } else {
                    $inner.css({'position': 'absolute', 'top': $sb_top});
                }
            }
            $(window).scroll(debounce(update), 200);
            update();
        };
        this.compute = function(node) {
            var $content = node.find('#content'),
                $diff = node.find('#diff');
            if ($content.length) {
                var splitted = $content.text().split('\n'),
                    length = splitted.length,
                    html = [];
                if (splitted.slice(length-1) == '') {
                    length = length - 1;
                }
                for (var k = 0; k < length; k++) {
                    if (splitted[k] !== undefined) {
                        var text = splitted[k].replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
                        html.push(format('
{0}' +
                                         ' {1}
', k+1, text));
                    }
                }
                $content.html(html.join('')).show();
            }

            if ($diff.length) {
                var dmp = new diff_match_patch();
                // Line diffs http://code.google.com/p/google-diff-match-patch/wiki/LineOrWordDiffs
                var a = dmp.diff_linesToChars_($diff.siblings('.left').text(), $diff.siblings('.right').text());
                var diffs = dmp.diff_main(a[0], a[1], false);
                dmp.diff_charsToLines_(diffs, a[2]);
                $diff.html(dmp.diff_prettyHtml(diffs)).show();

                var $sb = $diff.siblings('.diff-bar').eq(0);
                var $lines = $diff.find('.line');
                var state = {'start':1, 'type':$lines.eq(0).attr('class'),
                             'href':$lines.eq(0).find('a').attr('href')};
                for (var j = 1; j < $lines.length; j++) {
                    var $node = $lines.eq(j);
                    if ($node.attr('class') != state.type) {
                        this.side_bar_append($sb, state, j, $lines.length);
                        state = {'start': j, 'type': $node.attr('class'),
                                 'href': $node.find('a').attr('href')};
                    }
                }
                this.side_bar_append($sb, state, j, $lines.length);
                this.fix_vertically($sb, $diff);
                $sb.show();
            }

            if (!window.location.hash) {
                window.location.hash = 'top';
            }
            window.location = window.location;
        };
        this.side_bar_append = function($sb, state, k, total) {
            $sb.append($('', {'href': state.href, 'class': state.type,
                                 'css': {'height': (((k-state.start)/total) * 100) + '%' }}));
        };
        this.toggle_leaf = function($leaf) {
            if ($leaf.hasClass('open')) {
                this.hide_leaf($leaf);
            } else {
                this.show_leaf($leaf);
            }
        };
        this.hide_leaf = function($leaf) {
            $leaf.removeClass('open').addClass('closed')
                 .closest('li').next('ul').hide();
        };
        this.show_leaf = function($leaf) {
            /* Exposes the leaves for a given set of node. */
            $leaf.removeClass('closed').addClass('open')
                 .closest('li').next('ul').show();
        };
        this.selected = function($link) {
            /* Exposes all the leaves to an element */
            $link.parentsUntil('ul.root').filter('ul').show()
                 .each(function() {
                        $(this).prev('li').find('a:first')
                               .removeClass('closed').addClass('open');
            });
            if ($('.breadcrumbs li').length > 2) {
                $('.breadcrumbs li').eq(2).text($link.attr('data-short'));
            } else {
                $('.breadcrumbs').append(format('
{0}
', $link.attr('data-short')));
            }
        };
        this.load = function($link) {
            /* Accepts a jQuery wrapped node, which is part of the tree.
               Hides content, shows spinner, gets the content and then
               shows it all. Then alters the title. */
            var self = this,
                $old_wrapper = $('#content-wrapper');
            $old_wrapper.hide();
            this.nodes.$thinking.show();
            if (history.pushState !== undefined) {
                history.pushState({ path: $link.text() }, '', $link.attr('href'));
            }
            $old_wrapper.load($link.attr('href') + ' #content-wrapper', function() {
                $(this).children().unwrap();
                var $new_wrapper = $('#content-wrapper');
                self.compute($new_wrapper);
                self.nodes.$thinking.hide();
                $new_wrapper.slideDown();
                if (self.hidden) {
                    self.toggle_files('hide');
                }
            });
        };
        this.select = function($link) {
            /* Given a node, alters the tree and then loads the content. */
            this.nodes.$files.find('a.selected').each(function() {
                $(this).removeClass('selected');
            });
            $link.addClass('selected');
            this.selected($link);
            this.load($link);
        };
        this.get_selected = function() {
            var k = 0;
            $.each(this.nodes.$files.find('a.file'), function(i, el) {
                if ($(el).hasClass("selected")) {
                   k = i;
                }
            });
            return k;
        };
        this.toggle_wrap = function(state) {
            this.wrapped = (state == 'wrap' || !this.wrapped);
            $('pre').toggleClass('wrapped', this.wrapped);
        };
        this.toggle_files = function(state) {
            this.hidden = (state == 'hide' || !this.hidden);
            if (this.hidden) {
                this.nodes.$files.hide();
                this.nodes.$commands.detach().appendTo('div.featured-inner:first');
                this.nodes.$thinking.addClass('full');
            } else {
                this.nodes.$files.show();
                this.nodes.$commands.detach().appendTo('#files');
                this.nodes.$thinking.removeClass('full');
            }
            $('#content-wrapper').toggleClass('full');
        };
    }

    var viewer = new Viewer();

    if (viewer.nodes.$files.find('li').length == 1) {
        viewer.toggle_files();
        $('#files-prev').parent().hide();
        $('#files-next').parent().hide();
        $('#files-expand-all').parent().hide();
    }

    viewer.nodes.$files.find('.directory').click(_pd(function() {
        viewer.toggle_leaf($(this));
    }));

    $('#files-prev').click(_pd(function() {
        var prev = viewer.get_selected() - 1;
        if (prev >= 0) {
            viewer.select(viewer.nodes.$files.find('a.file').eq(prev));
        }
    }));

    $('#files-next').click(_pd(function() {
        var next = viewer.nodes.$files.find('a.file').eq(viewer.get_selected() + 1);
        if (next.length) {
            viewer.select(next);
        }
    }));

    $('#files-wrap').click(_pd(function() {
        viewer.toggle_wrap();
    }));

    $('#files-hide').click(_pd(function() {
        viewer.toggle_files();
    }));

    $('#files-expand-all').click(_pd(function() {
        viewer.nodes.$files.find('a.closed').each(function() {
            viewer.show_leaf($(this));
        });
    }));

    viewer.nodes.$files.find('.file').click(_pd(function() {
        viewer.select($(this));
        viewer.toggle_wrap('wrap');
    }));

    $(document).bind('keyup', _pd(function(e) {
        if (e.keyCode == 72) {
            $('#files-hide').trigger('click');
        } else if (e.keyCode == 75) {
            $('#files-next').trigger('click');
        } else if (e.keyCode == 74) {
            $('#files-prev').trigger('click');
        } else if (e.keyCode == 87) {
            $('#files-wrap').trigger('click');
        } else if (e.keyCode == 69) {
            $('#files-expand-all').trigger('click');
        }
    }));
    return viewer;
}

$(document).ready(function() {
    var viewer = null;
    var nodes = {
        $files: $('#files'),
        $thinking: $('#thinking'),
        $commands: $('#commands')
    };
    function poll_file_extraction() {
        $.getJSON($('#extracting').attr('data-url'), function(json) {
            if (json && json.status) {
                $('#file-viewer').load(window.location.pathname + ' #file-viewer', function() {
                    nodes.$files = $('#files'); // rebind
                    viewer = bind_viewer(nodes);
                    viewer.selected(viewer.nodes.$files.find('a.selected'));
                    viewer.compute($('#content-wrapper'));
                });
            } else if (json) {
                var errors = false;
                $.each(json.msg, function(k) {
                    if (json.msg[k] !== null) {
                        errors = true;
                        $('
').text(json.msg[k]).appendTo($('#file-viewer div.error'));
                    }
                });
                if (errors) {
                    $('#file-viewer div.error').show();
                    $('#extracting').hide();
                } else {
                    setTimeout(poll_file_extraction, 2000);
                }
            }
        });
    }

    if ($('#extracting').length) {
        poll_file_extraction();
    } else if ($('#file-viewer').length) {
        viewer = bind_viewer(nodes);
        viewer.selected(viewer.nodes.$files.find('a.selected'));
        viewer.compute($('#content-wrapper'));
    }
});
<commit_msg>getting different answers for .position, but only need to calculate it once anyway<commit_after>if (typeof diff_match_patch !== 'undefined') {
    diff_match_patch.prototype.diff_prettyHtml = function(diffs) {
        /* An override of prettyHthml from diff_match_patch. This
           one will not put any style attrs in the ins or del. */
        var html = [];
        var k = 1, // Line numbers of existing or added lines.
            dk = 1; // Line numbers of deleted lines.
        for (var x = 0; x < diffs.length; x++) {
            var op = diffs[x][0];    // Operation (insert, delete, equal)
            var data = diffs[x][1];  // Text of change.
            var text = data.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
            /* As as side effect, add in line numbers */
            var lines = text.split('\n');
            if (lines[lines.length-1] === '') {
                lines.pop();
            }
            if (lines[0] === '') {
                lines.splice(0, 1);
            }
            for (var t = 0; t < lines.length; t++) {
                switch (op) {
                    case DIFF_INSERT:
                        // TODO (andym): templates might work here as suggested by cvan
                        html.push(format('
{0} +' +
                                         '{1}
', k++, lines[t]));
                        break;
                    case DIFF_DELETE:
                        html.push(format('
 -' +
                                         '{1}
', dk++, lines[t]));
                        break;
                    case DIFF_EQUAL:
                        html.push(format('
{0}  ' +
                                         '{1}
', k++, lines[t]));
                        break;
                }
            }
        }
        return html.join('');
    };
}

function bind_viewer(nodes) {
    function Viewer() {
        this.nodes = nodes;
        this.wrapped = true;
        this.hidden = false;
        this.top = null;
        this.fix_vertically = function($inner, $outer) {
            var $self = this;
            if (!$self.top) {
                $self.top = $outer.position().top;
            }
            function update() {
                var sb_bottom = $self.top + $outer.height() - $inner.height();
                if ($(window).scrollTop() > sb_bottom) {
                    $inner.css({'position': 'absolute', 'top': sb_bottom});
                } else if ($(window).scrollTop() > $self.top) {
                    $inner.css({'position': 'fixed', 'top': 0});
                } else {
                    $inner.css({'position': 'absolute', 'top': $self.top});
                }
            }
            $(window).scroll(debounce(update), 200);
            update();
        };
        this.compute = function(node) {
            var $content = node.find('#content'),
                $diff = node.find('#diff');
            if ($content.length) {
                var splitted = $content.text().split('\n'),
                    length = splitted.length,
                    html = [];
                if (splitted.slice(length-1) == '') {
                    length = length - 1;
                }
                for (var k = 0; k < length; k++) {
                    if (splitted[k] !== undefined) {
                        var text = splitted[k].replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
                        html.push(format('
{0}' +
                                         ' {1}
', k+1, text));
                    }
                }
                $content.html(html.join('')).show();
            }

            if ($diff.length) {
                var dmp = new diff_match_patch();
                // Line diffs http://code.google.com/p/google-diff-match-patch/wiki/LineOrWordDiffs
                var a = dmp.diff_linesToChars_($diff.siblings('.left').text(), $diff.siblings('.right').text());
                var diffs = dmp.diff_main(a[0], a[1], false);
                dmp.diff_charsToLines_(diffs, a[2]);
                $diff.html(dmp.diff_prettyHtml(diffs)).show();

                var $sb = $diff.siblings('.diff-bar').eq(0);
                var $lines = $diff.find('.line');
                var state = {'start':0, 'type':$lines.eq(0).attr('class'),
                             'href':$lines.eq(0).find('a').attr('href')};
                for (var j = 1; j < $lines.length; j++) {
                    var $node = $lines.eq(j);
                    if ($node.attr('class') != state.type) {
                        this.side_bar_append($sb, state, j, $lines.length);
                        state = {'start': j, 'type': $node.attr('class'),
                                 'href': $node.find('a').attr('href')};
                    }
                }
                this.side_bar_append($sb, state, j, $lines.length);
                this.fix_vertically($sb, $diff);
                $sb.show();
            }

            if (!window.location.hash) {
                window.location.hash = 'top';
            }
            window.location = window.location;
        };
        this.side_bar_append = function($sb, state, k, total) {
            $sb.append($('', {'href': state.href, 'class': state.type,
                                 'css': {'height': (((k-state.start)/total) * 100) + '%' }}));
        };
        this.toggle_leaf = function($leaf) {
            if ($leaf.hasClass('open')) {
                this.hide_leaf($leaf);
            } else {
                this.show_leaf($leaf);
            }
        };
        this.hide_leaf = function($leaf) {
            $leaf.removeClass('open').addClass('closed')
                 .closest('li').next('ul').hide();
        };
        this.show_leaf = function($leaf) {
            /* Exposes the leaves for a given set of node. */
            $leaf.removeClass('closed').addClass('open')
                 .closest('li').next('ul').show();
        };
        this.selected = function($link) {
            /* Exposes all the leaves to an element */
            $link.parentsUntil('ul.root').filter('ul').show()
                 .each(function() {
                        $(this).prev('li').find('a:first')
                               .removeClass('closed').addClass('open');
            });
            if ($('.breadcrumbs li').length > 2) {
                $('.breadcrumbs li').eq(2).text($link.attr('data-short'));
            } else {
                $('.breadcrumbs').append(format('
{0}
', $link.attr('data-short')));
            }
        };
        this.load = function($link) {
            /* Accepts a jQuery wrapped node, which is part of the tree.
               Hides content, shows spinner, gets the content and then
               shows it all. Then alters the title. */
            var self = this,
                $old_wrapper = $('#content-wrapper');
            $old_wrapper.hide();
            this.nodes.$thinking.show();
            if (history.pushState !== undefined) {
                history.pushState({ path: $link.text() }, '', $link.attr('href'));
            }
            $old_wrapper.load($link.attr('href') + ' #content-wrapper', function() {
                $(this).children().unwrap();
                var $new_wrapper = $('#content-wrapper');
                self.compute($new_wrapper);
                self.nodes.$thinking.hide();
                $new_wrapper.slideDown();
                if (self.hidden) {
                    self.toggle_files('hide');
                }
            });
        };
        this.select = function($link) {
            /* Given a node, alters the tree and then loads the content. */
            this.nodes.$files.find('a.selected').each(function() {
                $(this).removeClass('selected');
            });
            $link.addClass('selected');
            this.selected($link);
            this.load($link);
        };
        this.get_selected = function() {
            var k = 0;
            $.each(this.nodes.$files.find('a.file'), function(i, el) {
                if ($(el).hasClass("selected")) {
                   k = i;
                }
            });
            return k;
        };
        this.toggle_wrap = function(state) {
            this.wrapped = (state == 'wrap' || !this.wrapped);
            $('pre').toggleClass('wrapped', this.wrapped);
        };
        this.toggle_files = function(state) {
            this.hidden = (state == 'hide' || !this.hidden);
            if (this.hidden) {
                this.nodes.$files.hide();
                this.nodes.$commands.detach().appendTo('div.featured-inner:first');
                this.nodes.$thinking.addClass('full');
            } else {
                this.nodes.$files.show();
                this.nodes.$commands.detach().appendTo('#files');
                this.nodes.$thinking.removeClass('full');
            }
            $('#content-wrapper').toggleClass('full');
        };
    }

    var viewer = new Viewer();

    if (viewer.nodes.$files.find('li').length == 1) {
        viewer.toggle_files();
        $('#files-prev').parent().hide();
        $('#files-next').parent().hide();
        $('#files-expand-all').parent().hide();
    }

    viewer.nodes.$files.find('.directory').click(_pd(function() {
        viewer.toggle_leaf($(this));
    }));

    $('#files-prev').click(_pd(function() {
        var prev = viewer.get_selected() - 1;
        if (prev >= 0) {
            viewer.select(viewer.nodes.$files.find('a.file').eq(prev));
        }
    }));

    $('#files-next').click(_pd(function() {
        var next = viewer.nodes.$files.find('a.file').eq(viewer.get_selected() + 1);
        if (next.length) {
            viewer.select(next);
        }
    }));

    $('#files-wrap').click(_pd(function() {
        viewer.toggle_wrap();
    }));

    $('#files-hide').click(_pd(function() {
        viewer.toggle_files();
    }));

    $('#files-expand-all').click(_pd(function() {
        viewer.nodes.$files.find('a.closed').each(function() {
            viewer.show_leaf($(this));
        });
    }));

    viewer.nodes.$files.find('.file').click(_pd(function() {
        viewer.select($(this));
        viewer.toggle_wrap('wrap');
    }));

    $(document).bind('keyup', _pd(function(e) {
        if (e.keyCode == 72) {
            $('#files-hide').trigger('click');
        } else if (e.keyCode == 75) {
            $('#files-next').trigger('click');
        } else if (e.keyCode == 74) {
            $('#files-prev').trigger('click');
        } else if (e.keyCode == 87) {
            $('#files-wrap').trigger('click');
        } else if (e.keyCode == 69) {
            $('#files-expand-all').trigger('click');
        }
    }));
    return viewer;
}

$(document).ready(function() {
    var viewer = null;
    var nodes = {
        $files: $('#files'),
        $thinking: $('#thinking'),
        $commands: $('#commands')
    };
    function poll_file_extraction() {
        $.getJSON($('#extracting').attr('data-url'), function(json) {
            if (json && json.status) {
                $('#file-viewer').load(window.location.pathname + ' #file-viewer', function() {
                    nodes.$files = $('#files'); // rebind
                    viewer = bind_viewer(nodes);
                    viewer.selected(viewer.nodes.$files.find('a.selected'));
                    viewer.compute($('#content-wrapper'));
                });
            } else if (json) {
                var errors = false;
                $.each(json.msg, function(k) {
                    if (json.msg[k] !== null) {
                        errors = true;
                        $('
').text(json.msg[k]).appendTo($('#file-viewer div.error'));
                    }
                });
                if (errors) {
                    $('#file-viewer div.error').show();
                    $('#extracting').hide();
                } else {
                    setTimeout(poll_file_extraction, 2000);
                }
            }
        });
    }

    if ($('#extracting').length) {
        poll_file_extraction();
    } else if ($('#file-viewer').length) {
        viewer = bind_viewer(nodes);
        viewer.selected(viewer.nodes.$files.find('a.selected'));
        viewer.compute($('#content-wrapper'));
    }
});
<|endoftext|>


<commit_before>module.exports = function(grunt) {
    return {
        toolkit: {
            options: {
                config: 'app/src/sass/config.rb',
                sassDir: 'app/src/sass/',
                cssDir: 'dist/stylesheets/',
                imagesDir: 'app/src/icon/',
                outputStyle: grunt.option('beautify') ? "expanded" : "compressed" ,
                noLineComments: true,
                trace: true,
                bundleExec: true,
                relativeAssets: true,
                importPath: ['bower_components']
            }
        }
    };
};
<commit_msg>st: compass fix<commit_after>module.exports = function(grunt) {
    return {
        toolkit: {
            options: {
                config: 'app/src/sass/config.rb',
                sassDir: 'app/src/sass/',
                cssDir: 'dist/stylesheets/',
                imagesDir: 'app/src/icon/',
                outputStyle: grunt.option('beautify') ? "expanded" : "compressed" ,
                noLineComments: true,
                trace: true,
                bundleExec: true,
                relativeAssets: true,
                importPath: ['bower_components', '.']
            }
        }
    };
};
<|endoftext|>


<commit_before>"use strict";

//////////////////////////////////////////////
/////////////// __LIBRARY__///////////////////
//////////////////////////////////////////////

var fallbackForm = {};
var authenticated;

/**
 *
 */
function escapeHtml(text) {

	  var map = {
	    '&': '&',
	    '<': '<',
	    '>': '>',
	    '"': '"',
	    "'": '''
	  };

	  return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}

/**
 * Cookies generator from object array {name: x, value: y}[]
 * @param cookies
 */
function cookiesGenerator ( cookies ) {
	if ( cookies.length > 0 ) {
		$.each(cookies, function (i, v){
				// create/update all cookies one by one
				var name	= typeof v.name !== 'undefined'		? v.name : false;
				var content	= typeof v.value !== 'undefined'	? v.value : false;

				$.cookie(name, content, { expires: cookieExpires, path: cookiePath });
			});
	}
}

/////////////////////////////////////////////
//////__LOAD EXTERNAL SNIPPET CODE__ ////////
/////////////////////////////////////////////

var nestedLoading = 0;

/**
 *
 * @param scope
 * @param nested
 */
function loadFullDoc( scope, nested ) {

	nested = typeof nested !== 'undefined' ? nested : false;

	if (nested)
	{
		initScope ( scope );
	}

	var $snippets = $(scope).find("[data-include]");
	if ($snippets.length > 0) {

		$snippets.each(

			function(i, v){
				// fix from http://www.scratchyourself.com/jquery-load-complete-callback-function-doesnt-work-properly/
				// and from http://stackoverflow.com/questions/8905143/jquery-load-doesnt-work-with-a-defined-function-as-a-callback
				$(this).load($(this).data('include'), null, function(){loadFullDoc(this, true);});
			}
		);
	} else
	{
		initScope ( scope );
	}
}

/////////////////////////////////////////////
////////////__INITIALIZATION__ //////////////
/////////////////////////////////////////////

/**
* Function to activate all jquery items in the scope
*/
function initScope ( scope ) {

	/////////////////////////////////////////////
	///////////// __CONFIRMATION__ //////////////
	/////////////////////////////////////////////

    $( scope ).find('[data-toggle="confirmation"][data-href]').has('span.glyphicon-trash').confirmation(optConfDelete);

	/////////////////////////////////////////////
	///////////// __FORM SUBMIT__ ///////////////
	/////////////////////////////////////////////

    $( scope ).find('form[data-async]').not('form[data-validate]').submit (
    		function(event){
    			formSubmit(this);
    			event.preventDefault();
    		}
    	);

	/////////////////////////////////////////////
	/////////// __FORM VALIDATION__ /////////////
	/////////////////////////////////////////////

	// http://formvalidator.net/index.html#configuration_setup
	// http://formvalidator.net/#reg-form
	// https://github.com/victorjonsson/jQuery-Form-Validator/tree/master/form-validator

	$.formUtils.addValidator({
		name : 'domain',
		validatorFunction : function(value, $el, config, language, $form) {
			var domains = ['iper.it','ortofin.it','unes.it', 'zenatek'];
			var valid = false;
			while (domains.length > 0)
			{
				valid |= value.indexOf(domains.shift()) > -1;
			}
			return valid;
		},
		errorMessage : 'La mail deve essere di lavoro: @iper.it @ortofin.it @unes.it etc..', /* non usato */
		errorMessageKey: 'badDomain'
	});

	$.validate({
		form: 'form[data-validate]',
		modules : 'date, security',
		dateFormat: 'dd/mm/yyyy',
		decimalSeparator: ',',
		onSuccess: function(form){formSubmit(form); return false;},
		onError: function(){return false;}, // Stop the submission
	});

    $( scope ).find('form[data-validate]').closest('.modal').on('click', 'button[data-dismiss="modal"]', function(){
    	// clear form and alerts
    	$('form', $(this).closest('.modal')).get(0).reset();
    });
}

/////////////////////////////////////////////
//////////__WAITING FUNCTIONS__ /////////////
/////////////////////////////////////////////

/**
 *
 */
function startWait () {
	$("body").addClass("loading");
}

/**
 *
 */
function stopWait () {
	$("body").removeClass("loading");
}

/////////////////////////////////////////////
//////__CUSTOM STRING JS EXTENSION _ ////////
// http://stackoverflow.com/questions/4637942/how-can-i-truncate-a-string-in-jquery
/////////////////////////////////////////////

String.prototype.trimToLength = function(m) {
	  return (this.length > m)
	    ? $.trim(this).substring(0, m).split(" ").slice(0, -1).join(" ") + "..."
	    : this;
	};

/////////////////////////////////////////////
//////// __DETECT CLIENT BROWSER__ //////////
/////////////////////////////////////////////

/**
 *
 */
function get_browser() {
    var ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
    if (/trident/i.test(M[1])) {
        tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
        return 'IE ' + (tem[1] || '');
    }
    if (M[1] == 'Chrome') {
        tem = ua.match(/\bOPR\/(\d+)/);
        if (tem != null) { return 'Opera ' + tem[1]; }
    }
    M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
    if ((tem = ua.match(/version\/(\d+)/i)) != null) { M.splice(1, 1, tem[1]); }
    return M[0];
}

/**
 *
 * @returns
 */
function get_browser_version() {
    var ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
    if (/trident/i.test(M[1])) {
        tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
        //return 'IE '+(tem[1]||'');
        return (tem[1] || '');
    }
    if (M[1] == 'Chrome') {
        tem = ua.match(/\bOPR\/(\d+)/);
        //if(tem!=null)   {return 'Opera '+tem[1];}
        if (tem != null) { return tem[1]; }
    }
    M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
    if ((tem = ua.match(/version\/(\d+)/i)) != null) { M.splice(1, 1, tem[1]); }
    return M[1];
}

/////////////////////////////////////////////
///////////// __CONFIRMATION__ //////////////
/////////////////////////////////////////////

/**
 *
 */
function closeConf(e) {
	$(this).closest('div.popover').siblings('[data-toggle="confirmation"]').confirmation('hide');
}

/**
 *
 * @param e
 */
function submitConf(e) {
	$(this).closest('div.popover').siblings('[data-toggle="confirmation"]').siblings('form').submit();
}

/////////////////////////////////////////////
///////////// __FORM SUBMIT__ ///////////////
/////////////////////////////////////////////

/**
 *
 */
function formSubmit ( form )
{
	var $form = $(form);
    var $target = $($form.attr('data-target'));

    $.ajax({
        type: $form.attr('method'),
        url: $form.attr('action'),
        data: $form.serialize(),
        success: function (data, status) {
        	window.console&&console.log(data);
            return formResponce (data, status, $target, $form );
        }
    });
}

/**
 *
 * @param data
 * @param status
 * @param $msgBox
 * @param $form
 */
function formResponce ( data, status, $msgBox, $form )
{
	// display feedback cleaning previous

	alertHint( $msgBox, data, $form );

	// curstom actions
	if (typeof $form.attr('id') !== 'undefined')
	{
		var id = $form.attr('id');
		if (typeof fallbackForm[id] !== 'undefined')
		{
			fallbackForm[$form.attr('id')].call(document, data, status, $msgBox, $form);
		}
	}

	if (status=='success' && !('alert-warning' in data) && !('alert-danger' in data)){
		// update page
		//loadFullPage (data['email']);
		setTimeout(function() {
				$msgBox.closest('.modal').modal('hide');
				$('.alert', $msgBox ).alert('close');
				var refresh = parseInt($form.data('refresh'));
		    	//if(refresh >= 0) startWait();
		    	setTimeout(content,refresh);
			}, 1500);
	}
}

/**
 * Manage error messages
 *
 * @param $target
 * @param data
 * @param $form
 */
function alertHint ( $target, data, $form )
{
	var hint_box = '
';
	hint_box += '<button type="button" class="close" data-dismiss="alert">×</button>';
	hint_box += '%hint%';
	hint_box += '
';

	$('.alert', $target ).alert('close');
	$.each(data, function (index, value){
		if ( $.inArray(index, ['alert-warning','alert-info','alert-danger','alert-success']) > -1 )
		{
			$target.prepend ( hint_box.replace ( /%hint%/g, value ).replace ( /%type%/g, index ));
			return true;
		}

		// $form can be undefined only if no index lack of undescore separator
		var paramId = (index.indexOf("_") >= 0)? index : $form.attr('id') +'_'+ index;

		if ($('#'+paramId).length)
		{
			$('#'+paramId).val( value );
		} else {
			$target.before(
					$('<input>').attr('type','hidden')
						.attr('id', paramId)
						.attr('name', index)
						.val( value )
				);
		}
		$('#'+paramId).trigger('change');
	});
	$target.alert();
}

/////////////////////////////////////////////
///////////// __CONFIRMATION__ //////////////
/////////////////////////////////////////////

var optConfBase = {
		singleton: true,
		popout: true,
		//template: '',
		html: true,
		onCancel: closeConf,
};

var optConfDelete = $.extend({
		btnOkIcon: 'glyphicon glyphicon-trash',
		btnOkLabel: 'Cancella',
		btnOkClass: 'btn btn-xs btn-danger',
		btnCancelIcon: '',
		btnCancelLabel: 'Annulla',
		btnCancelClass: 'btn btn-xs btn-default pull-right',

	}, optConfBase);

var optConfForm = $.extend({onConfirm : submitConf,}, optConfBase);
<commit_msg>library fix<commit_after>"use strict";

//////////////////////////////////////////////
/////////////// __LIBRARY__///////////////////
//////////////////////////////////////////////

var fallbackForm = {};
var authenticated;

/**
 *
 */
function escapeHtml(text) {
	  if (typeof text === 'undefined')
		  return '';

	  var map = {
	    '&': '&',
	    '<': '<',
	    '>': '>',
	    '"': '"',
	    "'": '''
	  };

	  return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}

/**
 * Cookies generator from object array {name: x, value: y}[]
 * @param cookies
 */
function cookiesGenerator ( cookies ) {
	if ( cookies.length > 0 ) {
		$.each(cookies, function (i, v){
				// create/update all cookies one by one
				var name	= typeof v.name !== 'undefined'		? v.name : false;
				var content	= typeof v.value !== 'undefined'	? v.value : false;

				$.cookie(name, content, { expires: cookieExpires, path: cookiePath });
			});
	}
}

/////////////////////////////////////////////
//////__LOAD EXTERNAL SNIPPET CODE__ ////////
/////////////////////////////////////////////

var nestedLoading = 0;

/**
 *
 * @param scope
 * @param nested
 */
function loadFullDoc( scope, nested ) {

	nested = typeof nested !== 'undefined' ? nested : false;

	if (nested)
	{
		initScope ( scope );
	}

	var $snippets = $(scope).find("[data-include]");
	if ($snippets.length > 0) {

		$snippets.each(

			function(i, v){
				// fix from http://www.scratchyourself.com/jquery-load-complete-callback-function-doesnt-work-properly/
				// and from http://stackoverflow.com/questions/8905143/jquery-load-doesnt-work-with-a-defined-function-as-a-callback
				$(this).load($(this).data('include'), null, function(){loadFullDoc(this, true);});
			}
		);
	} else
	{
		initScope ( scope );
	}
}

/////////////////////////////////////////////
////////////__INITIALIZATION__ //////////////
/////////////////////////////////////////////

/**
* Function to activate all jquery items in the scope
*/
function initScope ( scope ) {

	/////////////////////////////////////////////
	///////////// __CONFIRMATION__ //////////////
	/////////////////////////////////////////////

    $( scope ).find('[data-toggle="confirmation"][data-href]').has('span.glyphicon-trash').confirmation(optConfDelete);

	/////////////////////////////////////////////
	///////////// __FORM SUBMIT__ ///////////////
	/////////////////////////////////////////////

    $( scope ).find('form[data-async]').not('form[data-validate]').submit (
    		function(event){
    			formSubmit(this);
    			event.preventDefault();
    		}
    	);

	/////////////////////////////////////////////
	/////////// __FORM VALIDATION__ /////////////
	/////////////////////////////////////////////

	// http://formvalidator.net/index.html#configuration_setup
	// http://formvalidator.net/#reg-form
	// https://github.com/victorjonsson/jQuery-Form-Validator/tree/master/form-validator

	$.formUtils.addValidator({
		name : 'domain',
		validatorFunction : function(value, $el, config, language, $form) {
			var domains = ['iper.it','ortofin.it','unes.it', 'zenatek'];
			var valid = false;
			while (domains.length > 0)
			{
				valid |= value.indexOf(domains.shift()) > -1;
			}
			return valid;
		},
		errorMessage : 'La mail deve essere di lavoro: @iper.it @ortofin.it @unes.it etc..', /* non usato */
		errorMessageKey: 'badDomain'
	});

	$.validate({
		form: 'form[data-validate]',
		modules : 'date, security',
		dateFormat: 'dd/mm/yyyy',
		decimalSeparator: ',',
		onSuccess: function(form){formSubmit(form); return false;},
		onError: function(){return false;}, // Stop the submission
	});

    $( scope ).find('form[data-validate]').closest('.modal').on('click', 'button[data-dismiss="modal"]', function(){
    	// clear form and alerts
    	$('form', $(this).closest('.modal')).get(0).reset();
    });
}

/////////////////////////////////////////////
//////////__WAITING FUNCTIONS__ /////////////
/////////////////////////////////////////////

/**
 *
 */
function startWait () {
	$("body").addClass("loading");
}

/**
 *
 */
function stopWait () {
	$("body").removeClass("loading");
}

/////////////////////////////////////////////
//////__CUSTOM STRING JS EXTENSION _ ////////
// http://stackoverflow.com/questions/4637942/how-can-i-truncate-a-string-in-jquery
/////////////////////////////////////////////

String.prototype.trimToLength = function(m) {
	  return (this.length > m)
	    ? $.trim(this).substring(0, m).split(" ").slice(0, -1).join(" ") + "..."
	    : this;
	};

/////////////////////////////////////////////
//////// __DETECT CLIENT BROWSER__ //////////
/////////////////////////////////////////////

/**
 *
 */
function get_browser() {
    var ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
    if (/trident/i.test(M[1])) {
        tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
        return 'IE ' + (tem[1] || '');
    }
    if (M[1] == 'Chrome') {
        tem = ua.match(/\bOPR\/(\d+)/);
        if (tem != null) { return 'Opera ' + tem[1]; }
    }
    M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
    if ((tem = ua.match(/version\/(\d+)/i)) != null) { M.splice(1, 1, tem[1]); }
    return M[0];
}

/**
 *
 * @returns
 */
function get_browser_version() {
    var ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
    if (/trident/i.test(M[1])) {
        tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
        //return 'IE '+(tem[1]||'');
        return (tem[1] || '');
    }
    if (M[1] == 'Chrome') {
        tem = ua.match(/\bOPR\/(\d+)/);
        //if(tem!=null)   {return 'Opera '+tem[1];}
        if (tem != null) { return tem[1]; }
    }
    M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
    if ((tem = ua.match(/version\/(\d+)/i)) != null) { M.splice(1, 1, tem[1]); }
    return M[1];
}

/////////////////////////////////////////////
///////////// __CONFIRMATION__ //////////////
/////////////////////////////////////////////

/**
 *
 */
function closeConf(e) {
	$(this).closest('div.popover').siblings('[data-toggle="confirmation"]').confirmation('hide');
}

/**
 *
 * @param e
 */
function submitConf(e) {
	$(this).closest('div.popover').siblings('[data-toggle="confirmation"]').siblings('form').submit();
}

/////////////////////////////////////////////
///////////// __FORM SUBMIT__ ///////////////
/////////////////////////////////////////////

/**
 *
 */
function formSubmit ( form )
{
	var $form = $(form);
    var $target = $($form.attr('data-target'));

    $.ajax({
        type: $form.attr('method'),
        url: $form.attr('action'),
        data: $form.serialize(),
        success: function (data, status) {
        	window.console&&console.log(data);
            return formResponce (data, status, $target, $form );
        }
    });
}

/**
 *
 * @param data
 * @param status
 * @param $msgBox
 * @param $form
 */
function formResponce ( data, status, $msgBox, $form )
{
	// display feedback cleaning previous

	alertHint( $msgBox, data, $form );

	// curstom actions
	if (typeof $form.attr('id') !== 'undefined')
	{
		var id = $form.attr('id');
		if (typeof fallbackForm[id] !== 'undefined')
		{
			fallbackForm[$form.attr('id')].call(document, data, status, $msgBox, $form);
		}
	}

	if (status=='success' && !('alert-warning' in data) && !('alert-danger' in data)){
		// update page
		//loadFullPage (data['email']);
		setTimeout(function() {
				$msgBox.closest('.modal').modal('hide');
				$('.alert', $msgBox ).alert('close');
				var refresh = parseInt($form.data('refresh'));
		    	//if(refresh >= 0) startWait();
		    	setTimeout(content,refresh);
			}, 1500);
	}
}

/**
 * Manage error messages
 *
 * @param $target
 * @param data
 * @param $form
 */
function alertHint ( $target, data, $form )
{
	var hint_box = '
';
	hint_box += '<button type="button" class="close" data-dismiss="alert">×</button>';
	hint_box += '%hint%';
	hint_box += '
';

	$('.alert', $target ).alert('close');
	$.each(data, function (index, value){
		if ( $.inArray(index, ['alert-warning','alert-info','alert-danger','alert-success']) > -1 )
		{
			$target.prepend ( hint_box.replace ( /%hint%/g, value ).replace ( /%type%/g, index ));
			return true;
		}

		// $form can be undefined only if no index lack of undescore separator
		var paramId = (index.indexOf("_") >= 0)? index : $form.attr('id') +'_'+ index;

		if ($('#'+paramId).length)
		{
			$('#'+paramId).val( value );
		} else {
			$target.before(
					$('<input>').attr('type','hidden')
						.attr('id', paramId)
						.attr('name', index)
						.val( value )
				);
		}
		$('#'+paramId).trigger('change');
	});
	$target.alert();
}

/////////////////////////////////////////////
///////////// __CONFIRMATION__ //////////////
/////////////////////////////////////////////

var optConfBase = {
		singleton: true,
		popout: true,
		//template: '',
		html: true,
		onCancel: closeConf,
};

var optConfDelete = $.extend({
		btnOkIcon: 'glyphicon glyphicon-trash',
		btnOkLabel: 'Cancella',
		btnOkClass: 'btn btn-xs btn-danger',
		btnCancelIcon: '',
		btnCancelLabel: 'Annulla',
		btnCancelClass: 'btn btn-xs btn-default pull-right',

	}, optConfBase);

var optConfForm = $.extend({onConfirm : submitConf,}, optConfBase);
<|endoftext|>


<commit_before>import { moduleForComponent, test } from 'ember-qunit';
// import hbs from 'htmlbars-inline-precompile';

moduleForComponent('fd-tab-dropdown', 'Integration | Component | fd tab dropdown', {
  integration: true
});

test('it renders', function(assert) {
  // Set any properties with this.set('myProperty', 'value');
  // Handle any actions with this.on('myAction', function(val) { ... });

  // this.render(hbs`{{fd-tab-dropdown}}`);

  // assert.equal(this.$().text().trim(), '');

  // Template block usage:
  // this.render(hbs`
  //   {{#fd-tab-dropdown}}
  //     template block text
  //   {{/fd-tab-dropdown}}
  // `);

  // assert.equal(this.$().text().trim(), 'template block text');
  assert.equal('', '');
});
<commit_msg>JSHint part2<commit_after>import { moduleForComponent, test } from 'ember-qunit';

// import hbs from 'htmlbars-inline-precompile';

moduleForComponent('fd-tab-dropdown', 'Integration | Component | fd tab dropdown', {
  integration: true
});

test('it renders', function(assert) {
  // Set any properties with this.set('myProperty', 'value');
  // Handle any actions with this.on('myAction', function(val) { ... });

  // this.render(hbs`{{fd-tab-dropdown}}`);

  // assert.equal(this.$().text().trim(), '');

  // Template block usage:
  // this.render(hbs`
  //   {{#fd-tab-dropdown}}
  //     template block text
  //   {{/fd-tab-dropdown}}
  // `);

  // assert.equal(this.$().text().trim(), 'template block text');
  assert.equal('', '');
});
<|endoftext|>


<commit_before>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 * @format
 */

'use strict';

import AnimatedImplementation from '../../Animated/AnimatedImplementation';
import * as React from 'react';
import StyleSheet from '../../StyleSheet/StyleSheet';
import View from '../View/View';
import Platform from '../../Utilities/Platform';

import type {LayoutEvent} from '../../Types/CoreEventTypes';

const AnimatedView = AnimatedImplementation.createAnimatedComponent(View);

export type Props = {
  children?: React.Element<any>,
  nextHeaderLayoutY: ?number,
  onLayout: (event: LayoutEvent) => void,
  scrollAnimatedValue: AnimatedImplementation.Value,
  // Will cause sticky headers to stick at the bottom of the ScrollView instead
  // of the top.
  inverted: ?boolean,
  // The height of the parent ScrollView. Currently only set when inverted.
  scrollViewHeight: ?number,
  nativeID?: ?string,
  ...
};

type State = {
  measured: boolean,
  layoutY: number,
  layoutHeight: number,
  nextHeaderLayoutY: ?number,
  translateY: ?number,
  ...
};

class ScrollViewStickyHeader extends React.Component<Props, State> {
  state: State = {
    measured: false,
    layoutY: 0,
    layoutHeight: 0,
    nextHeaderLayoutY: this.props.nextHeaderLayoutY,
    translateY: null,
  };

  _translateY: ?AnimatedImplementation.Interpolation = null;
  _shouldRecreateTranslateY: boolean = true;
  _haveReceivedInitialZeroTranslateY: boolean = true;
  _ref: any; // TODO T53738161: flow type this, and the whole file

  // Fabric-only:
  _timer: ?TimeoutID;
  _animatedValueListenerId: string;
  _animatedValueListener: (valueObject: $ReadOnly<{|value: number|}>) => void;
  _debounceTimeout: number = Platform.OS === 'android' ? 15 : 64;

  setNextHeaderY(y: number) {
    this.setState({nextHeaderLayoutY: y});
  }

  UNSAFE_componentWillReceiveProps(nextProps: Props) {
    if (
      nextProps.scrollViewHeight !== this.props.scrollViewHeight ||
      nextProps.scrollAnimatedValue !== this.props.scrollAnimatedValue ||
      nextProps.inverted !== this.props.inverted
    ) {
      this._shouldRecreateTranslateY = true;
    }
  }

  updateTranslateListener(
    translateY: AnimatedImplementation.Interpolation,
    isFabric: boolean,
  ) {
    if (this._translateY != null && this._animatedValueListenerId != null) {
      this._translateY.removeListener(this._animatedValueListenerId);
    }

    this._translateY = translateY;
    this._shouldRecreateTranslateY = false;

    if (!isFabric) {
      return;
    }

    if (!this._animatedValueListener) {
      // This is called whenever the (Interpolated) Animated Value
      // updates, which is several times per frame during scrolling.
      // To ensure that the Fabric ShadowTree has the most recent
      // translate style of this node, we debounce the value and then
      // pass it through to the underlying node during render.
      // This is:
      // 1. Only an issue in Fabric.
      // 2. Worse in Android than iOS. In Android, but not iOS, you
      //    can touch and move your finger slightly and still trigger
      //    a "tap" event. In iOS, moving will cancel the tap in
      //    both Fabric and non-Fabric. On Android when you move
      //    your finger, the hit-detection moves from the Android
      //    platform to JS, so we need the ShadowTree to have knowledge
      //    of the current position.
      this._animatedValueListener = ({value}) => {
        // When the AnimatedInterpolation is recreated, it always initializes
        // to a value of zero and emits a value change of 0 to its listeners.
        if (value === 0 && !this._haveReceivedInitialZeroTranslateY) {
          this._haveReceivedInitialZeroTranslateY = true;
          return;
        }
        if (this._timer) {
          clearTimeout(this._timer);
        }
        this._timer = setTimeout(() => {
          if (value !== this.state.translateY) {
            this.setState({
              translateY: value,
            });
          }
        }, this._debounceTimeout);
      };
    }
    if (this.state.translateY !== 0 && this.state.translateY != null) {
      this._haveReceivedInitialZeroTranslateY = false;
    }
    this._animatedValueListenerId = translateY.addListener(
      this._animatedValueListener,
    );
  }

  _onLayout = event => {
    const layoutY = event.nativeEvent.layout.y;
    const layoutHeight = event.nativeEvent.layout.height;
    const measured = true;

    if (
      layoutY !== this.state.layoutY ||
      layoutHeight !== this.state.layoutHeight ||
      measured !== this.state.measured
    ) {
      this._shouldRecreateTranslateY = true;
    }

    this.setState({
      measured,
      layoutY,
      layoutHeight,
    });

    this.props.onLayout(event);
    const child = React.Children.only(this.props.children);
    if (child.props.onLayout) {
      child.props.onLayout(event);
    }
  };

  _setComponentRef = ref => {
    this._ref = ref;
  };

  render(): React.Node {
    // Fabric Detection
    // eslint-disable-next-line dot-notation
    const isFabric = !!(
      this._ref && this._ref['_internalInstanceHandle']?.stateNode?.canonical
    );

    // Initially and in the case of updated props or layout, we
    // recreate this interpolated value. Otherwise, we do not recreate
    // when there are state changes.
    if (this._shouldRecreateTranslateY) {
      const {inverted, scrollViewHeight} = this.props;
      const {measured, layoutHeight, layoutY, nextHeaderLayoutY} = this.state;
      const inputRange: Array<number> = [-1, 0];
      const outputRange: Array<number> = [0, 0];

      if (measured) {
        if (inverted) {
          // The interpolation looks like:
          // - Negative scroll: no translation
          // - `stickStartPoint` is the point at which the header will start sticking.
          //   It is calculated using the ScrollView viewport height so it is a the bottom.
          // - Headers that are in the initial viewport will never stick, `stickStartPoint`
          //   will be negative.
          // - From 0 to `stickStartPoint` no translation. This will cause the header
          //   to scroll normally until it reaches the top of the scroll view.
          // - From `stickStartPoint` to when the next header y hits the bottom edge of the header: translate
          //   equally to scroll. This will cause the header to stay at the top of the scroll view.
          // - Past the collision with the next header y: no more translation. This will cause the
          //   header to continue scrolling up and make room for the next sticky header.
          //   In the case that there is no next header just translate equally to
          //   scroll indefinitely.
          if (scrollViewHeight != null) {
            const stickStartPoint = layoutY + layoutHeight - scrollViewHeight;
            if (stickStartPoint > 0) {
              inputRange.push(stickStartPoint);
              outputRange.push(0);
              inputRange.push(stickStartPoint + 1);
              outputRange.push(1);
              // If the next sticky header has not loaded yet (probably windowing) or is the last
              // we can just keep it sticked forever.
              const collisionPoint =
                (nextHeaderLayoutY || 0) - layoutHeight - scrollViewHeight;
              if (collisionPoint > stickStartPoint) {
                inputRange.push(collisionPoint, collisionPoint + 1);
                outputRange.push(
                  collisionPoint - stickStartPoint,
                  collisionPoint - stickStartPoint,
                );
              }
            }
          }
        } else {
          // The interpolation looks like:
          // - Negative scroll: no translation
          // - From 0 to the y of the header: no translation. This will cause the header
          //   to scroll normally until it reaches the top of the scroll view.
          // - From header y to when the next header y hits the bottom edge of the header: translate
          //   equally to scroll. This will cause the header to stay at the top of the scroll view.
          // - Past the collision with the next header y: no more translation. This will cause the
          //   header to continue scrolling up and make room for the next sticky header.
          //   In the case that there is no next header just translate equally to
          //   scroll indefinitely.
          inputRange.push(layoutY);
          outputRange.push(0);
          // If the next sticky header has not loaded yet (probably windowing) or is the last
          // we can just keep it sticked forever.
          const collisionPoint = (nextHeaderLayoutY || 0) - layoutHeight;
          if (collisionPoint >= layoutY) {
            inputRange.push(collisionPoint, collisionPoint + 1);
            outputRange.push(
              collisionPoint - layoutY,
              collisionPoint - layoutY,
            );
          } else {
            inputRange.push(layoutY + 1);
            outputRange.push(1);
          }
        }
      }

      this.updateTranslateListener(
        this.props.scrollAnimatedValue.interpolate({
          inputRange,
          outputRange,
        }),
        isFabric,
      );
    }

    const child = React.Children.only(this.props.children);

    // TODO T68319535: remove this if NativeAnimated is rewritten for Fabric
    const passthroughAnimatedPropExplicitValues =
      isFabric && this.state.translateY != null
        ? {
            style: {transform: [{translateY: this.state.translateY}]},
          }
        : null;

    return (
      <AnimatedView
        collapsable={false}
        nativeID={this.props.nativeID}
        onLayout={this._onLayout}
        ref={this._setComponentRef}
        style={[
          child.props.style,
          styles.header,
          {transform: [{translateY: this._translateY}]},
        ]}
        passthroughAnimatedPropExplicitValues={
          passthroughAnimatedPropExplicitValues
        }>
        {React.cloneElement(child, {
          style: styles.fill, // We transfer the child style to the wrapper.
          onLayout: undefined, // we call this manually through our this._onLayout
        })}
      </AnimatedView>
    );
  }
}

const styles = StyleSheet.create({
  header: {
    zIndex: 10,
    position: 'relative',
  },
  fill: {
    flex: 1,
  },
});

module.exports = ScrollViewStickyHeader;
<commit_msg>Fix memory leak in ScrollViewStickyHeader<commit_after>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 * @format
 */

'use strict';

import AnimatedImplementation from '../../Animated/AnimatedImplementation';
import * as React from 'react';
import StyleSheet from '../../StyleSheet/StyleSheet';
import View from '../View/View';
import Platform from '../../Utilities/Platform';

import type {LayoutEvent} from '../../Types/CoreEventTypes';

const AnimatedView = AnimatedImplementation.createAnimatedComponent(View);

export type Props = {
  children?: React.Element<any>,
  nextHeaderLayoutY: ?number,
  onLayout: (event: LayoutEvent) => void,
  scrollAnimatedValue: AnimatedImplementation.Value,
  // Will cause sticky headers to stick at the bottom of the ScrollView instead
  // of the top.
  inverted: ?boolean,
  // The height of the parent ScrollView. Currently only set when inverted.
  scrollViewHeight: ?number,
  nativeID?: ?string,
  ...
};

type State = {
  measured: boolean,
  layoutY: number,
  layoutHeight: number,
  nextHeaderLayoutY: ?number,
  translateY: ?number,
  ...
};

class ScrollViewStickyHeader extends React.Component<Props, State> {
  state: State = {
    measured: false,
    layoutY: 0,
    layoutHeight: 0,
    nextHeaderLayoutY: this.props.nextHeaderLayoutY,
    translateY: null,
  };

  _translateY: ?AnimatedImplementation.Interpolation = null;
  _shouldRecreateTranslateY: boolean = true;
  _haveReceivedInitialZeroTranslateY: boolean = true;
  _ref: any; // TODO T53738161: flow type this, and the whole file

  // Fabric-only:
  _timer: ?TimeoutID;
  _animatedValueListenerId: string;
  _animatedValueListener: (valueObject: $ReadOnly<{|value: number|}>) => void;
  _debounceTimeout: number = Platform.OS === 'android' ? 15 : 64;

  setNextHeaderY(y: number) {
    this.setState({nextHeaderLayoutY: y});
  }

  componentWillUnmount() {
    if (this._translateY != null && this._animatedValueListenerId != null) {
      this._translateY.removeListener(this._animatedValueListenerId);
    }
    if (this._timer) {
      clearTimeout(this._timer);
    }
  }

  UNSAFE_componentWillReceiveProps(nextProps: Props) {
    if (
      nextProps.scrollViewHeight !== this.props.scrollViewHeight ||
      nextProps.scrollAnimatedValue !== this.props.scrollAnimatedValue ||
      nextProps.inverted !== this.props.inverted
    ) {
      this._shouldRecreateTranslateY = true;
    }
  }

  updateTranslateListener(
    translateY: AnimatedImplementation.Interpolation,
    isFabric: boolean,
  ) {
    if (this._translateY != null && this._animatedValueListenerId != null) {
      this._translateY.removeListener(this._animatedValueListenerId);
    }

    this._translateY = translateY;
    this._shouldRecreateTranslateY = false;

    if (!isFabric) {
      return;
    }

    if (!this._animatedValueListener) {
      // This is called whenever the (Interpolated) Animated Value
      // updates, which is several times per frame during scrolling.
      // To ensure that the Fabric ShadowTree has the most recent
      // translate style of this node, we debounce the value and then
      // pass it through to the underlying node during render.
      // This is:
      // 1. Only an issue in Fabric.
      // 2. Worse in Android than iOS. In Android, but not iOS, you
      //    can touch and move your finger slightly and still trigger
      //    a "tap" event. In iOS, moving will cancel the tap in
      //    both Fabric and non-Fabric. On Android when you move
      //    your finger, the hit-detection moves from the Android
      //    platform to JS, so we need the ShadowTree to have knowledge
      //    of the current position.
      this._animatedValueListener = ({value}) => {
        // When the AnimatedInterpolation is recreated, it always initializes
        // to a value of zero and emits a value change of 0 to its listeners.
        if (value === 0 && !this._haveReceivedInitialZeroTranslateY) {
          this._haveReceivedInitialZeroTranslateY = true;
          return;
        }
        if (this._timer) {
          clearTimeout(this._timer);
        }
        this._timer = setTimeout(() => {
          if (value !== this.state.translateY) {
            this.setState({
              translateY: value,
            });
          }
        }, this._debounceTimeout);
      };
    }
    if (this.state.translateY !== 0 && this.state.translateY != null) {
      this._haveReceivedInitialZeroTranslateY = false;
    }
    this._animatedValueListenerId = translateY.addListener(
      this._animatedValueListener,
    );
  }

  _onLayout = event => {
    const layoutY = event.nativeEvent.layout.y;
    const layoutHeight = event.nativeEvent.layout.height;
    const measured = true;

    if (
      layoutY !== this.state.layoutY ||
      layoutHeight !== this.state.layoutHeight ||
      measured !== this.state.measured
    ) {
      this._shouldRecreateTranslateY = true;
    }

    this.setState({
      measured,
      layoutY,
      layoutHeight,
    });

    this.props.onLayout(event);
    const child = React.Children.only(this.props.children);
    if (child.props.onLayout) {
      child.props.onLayout(event);
    }
  };

  _setComponentRef = ref => {
    this._ref = ref;
  };

  render(): React.Node {
    // Fabric Detection
    // eslint-disable-next-line dot-notation
    const isFabric = !!(
      this._ref && this._ref['_internalInstanceHandle']?.stateNode?.canonical
    );

    // Initially and in the case of updated props or layout, we
    // recreate this interpolated value. Otherwise, we do not recreate
    // when there are state changes.
    if (this._shouldRecreateTranslateY) {
      const {inverted, scrollViewHeight} = this.props;
      const {measured, layoutHeight, layoutY, nextHeaderLayoutY} = this.state;
      const inputRange: Array<number> = [-1, 0];
      const outputRange: Array<number> = [0, 0];

      if (measured) {
        if (inverted) {
          // The interpolation looks like:
          // - Negative scroll: no translation
          // - `stickStartPoint` is the point at which the header will start sticking.
          //   It is calculated using the ScrollView viewport height so it is a the bottom.
          // - Headers that are in the initial viewport will never stick, `stickStartPoint`
          //   will be negative.
          // - From 0 to `stickStartPoint` no translation. This will cause the header
          //   to scroll normally until it reaches the top of the scroll view.
          // - From `stickStartPoint` to when the next header y hits the bottom edge of the header: translate
          //   equally to scroll. This will cause the header to stay at the top of the scroll view.
          // - Past the collision with the next header y: no more translation. This will cause the
          //   header to continue scrolling up and make room for the next sticky header.
          //   In the case that there is no next header just translate equally to
          //   scroll indefinitely.
          if (scrollViewHeight != null) {
            const stickStartPoint = layoutY + layoutHeight - scrollViewHeight;
            if (stickStartPoint > 0) {
              inputRange.push(stickStartPoint);
              outputRange.push(0);
              inputRange.push(stickStartPoint + 1);
              outputRange.push(1);
              // If the next sticky header has not loaded yet (probably windowing) or is the last
              // we can just keep it sticked forever.
              const collisionPoint =
                (nextHeaderLayoutY || 0) - layoutHeight - scrollViewHeight;
              if (collisionPoint > stickStartPoint) {
                inputRange.push(collisionPoint, collisionPoint + 1);
                outputRange.push(
                  collisionPoint - stickStartPoint,
                  collisionPoint - stickStartPoint,
                );
              }
            }
          }
        } else {
          // The interpolation looks like:
          // - Negative scroll: no translation
          // - From 0 to the y of the header: no translation. This will cause the header
          //   to scroll normally until it reaches the top of the scroll view.
          // - From header y to when the next header y hits the bottom edge of the header: translate
          //   equally to scroll. This will cause the header to stay at the top of the scroll view.
          // - Past the collision with the next header y: no more translation. This will cause the
          //   header to continue scrolling up and make room for the next sticky header.
          //   In the case that there is no next header just translate equally to
          //   scroll indefinitely.
          inputRange.push(layoutY);
          outputRange.push(0);
          // If the next sticky header has not loaded yet (probably windowing) or is the last
          // we can just keep it sticked forever.
          const collisionPoint = (nextHeaderLayoutY || 0) - layoutHeight;
          if (collisionPoint >= layoutY) {
            inputRange.push(collisionPoint, collisionPoint + 1);
            outputRange.push(
              collisionPoint - layoutY,
              collisionPoint - layoutY,
            );
          } else {
            inputRange.push(layoutY + 1);
            outputRange.push(1);
          }
        }
      }

      this.updateTranslateListener(
        this.props.scrollAnimatedValue.interpolate({
          inputRange,
          outputRange,
        }),
        isFabric,
      );
    }

    const child = React.Children.only(this.props.children);

    // TODO T68319535: remove this if NativeAnimated is rewritten for Fabric
    const passthroughAnimatedPropExplicitValues =
      isFabric && this.state.translateY != null
        ? {
            style: {transform: [{translateY: this.state.translateY}]},
          }
        : null;

    return (
      <AnimatedView
        collapsable={false}
        nativeID={this.props.nativeID}
        onLayout={this._onLayout}
        ref={this._setComponentRef}
        style={[
          child.props.style,
          styles.header,
          {transform: [{translateY: this._translateY}]},
        ]}
        passthroughAnimatedPropExplicitValues={
          passthroughAnimatedPropExplicitValues
        }>
        {React.cloneElement(child, {
          style: styles.fill, // We transfer the child style to the wrapper.
          onLayout: undefined, // we call this manually through our this._onLayout
        })}
      </AnimatedView>
    );
  }
}

const styles = StyleSheet.create({
  header: {
    zIndex: 10,
    position: 'relative',
  },
  fill: {
    flex: 1,
  },
});

module.exports = ScrollViewStickyHeader;
<|endoftext|>


<commit_before>/* eslint-env mocha */
/* globals expect */
/* eslint no-unused-vars: 0 */

import {getCredentials, api, login, request, credentials, message, log, apiPrivateChannelName } from '../../data/api-data.js';
import {adminEmail, password} from '../../data/user.js';
import supertest from 'supertest';

describe('[Chat]', function() {
	this.retries(0);

	before(done => getCredentials(done));

	it('/chat.postMessage', (done) => {
		request.post(api('chat.postMessage'))
			.set(credentials)
			.send({
				channel: 'general',
				text: 'Sample message',
				alias: 'Gruggy',
				emoji: ':smirk:',
				avatar: 'http://res.guggy.com/logo_128.png',
				attachments: [{
					color: '#ff0000',
					text: 'Yay for gruggy!',
					ts: '2016-12-09T16:53:06.761Z',
					thumb_url: 'http://res.guggy.com/logo_128.png',
					message_link: 'https://google.com',
					collapsed: false,
					author_name: 'Bradley Hilton',
					author_link: 'https://rocket.chat/',
					author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
					title: 'Attachment Example',
					title_link: 'https://youtube.com',
					title_link_download: 'https://rocket.chat/download',
					image_url: 'http://res.guggy.com/logo_128.png',
					audio_url: 'http://www.w3schools.com/tags/horse.mp3',
					video_url: 'http://www.w3schools.com/tags/movie.mp4',
					fields: [{
						short: true,
						title: 'Test',
						value: 'Testing out something or other'
					}, {
						short: true,
						title: 'Another Test',
						value: '[Link](https://google.com/) something and this and that.'
					}]
				}]
			})
			.expect('Content-Type', 'application/json')
			.expect(200)
			.expect((res) => {
				expect(res.body).to.have.property('success', true);
				expect(res.body).to.have.nested.property('message.msg', 'Sample message');
				message._id = res.body.message._id;
			})
			.end(done);
	});

	it('/chat.getMessage', (done) => {
		request.get(api('chat.getMessage'))
			.set(credentials)
			.query({
				msgId: message._id
			})
			.expect('Content-Type', 'application/json')
			.expect(200)
			.expect((res) => {
				expect(res.body).to.have.property('success', true);
				expect(res.body).to.have.nested.property('message._id', message._id);
			})
			.end(done);
	});

	it('/chat.sendMessage', (done) => {
		message._id = `id-${ Date.now() }`;
		request.post(api('chat.sendMessage'))
			.set(credentials)
			.send({
				message: {
					_id: message._id,
					rid: 'GENERAL',
					msg: 'Sample message',
					alias: 'Gruggy',
					emoji: ':smirk:',
					avatar: 'http://res.guggy.com/logo_128.png',
					attachments: [{
						color: '#ff0000',
						text: 'Yay for gruggy!',
						ts: '2016-12-09T16:53:06.761Z',
						thumb_url: 'http://res.guggy.com/logo_128.png',
						message_link: 'https://google.com',
						collapsed: false,
						author_name: 'Bradley Hilton',
						author_link: 'https://rocket.chat/',
						author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
						title: 'Attachment Example',
						title_link: 'https://youtube.com',
						title_link_download: 'https://rocket.chat/download',
						image_url: 'http://res.guggy.com/logo_128.png',
						audio_url: 'http://www.w3schools.com/tags/horse.mp3',
						video_url: 'http://www.w3schools.com/tags/movie.mp4',
						fields: [{
							short: true,
							title: 'Test',
							value: 'Testing out something or other'
						}, {
							short: true,
							title: 'Another Test',
							value: '[Link](https://google.com/) something and this and that.'
						}]
					}]
				}
			})
			.expect('Content-Type', 'application/json')
			.expect(200)
			.expect((res) => {
				expect(res.body).to.have.property('success', true);
				expect(res.body).to.have.nested.property('message.msg', 'Sample message');
			})
			.end(done);
	});

	it('/chat.getMessage', (done) => {
		request.get(api('chat.getMessage'))
			.set(credentials)
			.query({
				msgId: message._id
			})
			.expect('Content-Type', 'application/json')
			.expect(200)
			.expect((res) => {
				expect(res.body).to.have.property('success', true);
				expect(res.body).to.have.nested.property('message._id', message._id);
			})
			.end(done);
	});

	it('/chat.update', (done) => {
		request.post(api('chat.update'))
			.set(credentials)
			.send({
				roomId: 'GENERAL',
				msgId: message._id,
				text: 'This message was edited via API'
			})
			.expect('Content-Type', 'application/json')
			.expect(200)
			.expect((res) => {
				expect(res.body).to.have.property('success', true);
				expect(res.body).to.have.nested.property('message.msg', 'This message was edited via API');
			})
			.end(done);
	});

	it('/chat.search', (done) => {
		request.get(api('chat.search'))
			.set(credentials)
			.query({
				roomId: 'GENERAL',
				searchText: 'This message was edited via API'
			})
			.expect('Content-Type', 'application/json')
			.expect(200)
			.expect((res) => {
				expect(res.body).to.have.property('success', true);
				expect(res.body).to.have.property('messages');
			})
			.end(done);
	});

	describe('/chat.react', () => {
		it('should return statusCode: 200 when the emoji is valid', (done) => {
			request.post(api('chat.react'))
				.set(credentials)
				.send({
					emoji: ':squid:',
					messageId: message._id
				})
				.expect('Content-Type', 'application/json')
				.expect(200)
				.expect((res) => {
					expect(res.body).to.have.property('success', true);
				})
				.end(done);
		});
	});

});
<commit_msg><commit_after>/* eslint-env mocha */
/* globals expect */
/* eslint no-unused-vars: 0 */

import { getCredentials, api, login, request, credentials, message, log, apiPrivateChannelName } from '../../data/api-data.js';
import { adminEmail, password } from '../../data/user.js';
import supertest from 'supertest';

describe('[Chat]', function() {
	this.retries(0);

	before(done => getCredentials(done));

	it('/chat.postMessage', (done) => {
		request.post(api('chat.postMessage'))
			.set(credentials)
			.send({
				channel: 'general',
				text: 'Sample message',
				alias: 'Gruggy',
				emoji: ':smirk:',
				avatar: 'http://res.guggy.com/logo_128.png',
				attachments: [{
					color: '#ff0000',
					text: 'Yay for gruggy!',
					ts: '2016-12-09T16:53:06.761Z',
					thumb_url: 'http://res.guggy.com/logo_128.png',
					message_link: 'https://google.com',
					collapsed: false,
					author_name: 'Bradley Hilton',
					author_link: 'https://rocket.chat/',
					author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
					title: 'Attachment Example',
					title_link: 'https://youtube.com',
					title_link_download: 'https://rocket.chat/download',
					image_url: 'http://res.guggy.com/logo_128.png',
					audio_url: 'http://www.w3schools.com/tags/horse.mp3',
					video_url: 'http://www.w3schools.com/tags/movie.mp4',
					fields: [{
						short: true,
						title: 'Test',
						value: 'Testing out something or other'
					}, {
						short: true,
						title: 'Another Test',
						value: '[Link](https://google.com/) something and this and that.'
					}]
				}]
			})
			.expect('Content-Type', 'application/json')
			.expect(200)
			.expect((res) => {
				expect(res.body).to.have.property('success', true);
				expect(res.body).to.have.nested.property('message.msg', 'Sample message');
				message._id = res.body.message._id;
			})
			.end(done);
	});

	it('/chat.getMessage', (done) => {
		request.get(api('chat.getMessage'))
			.set(credentials)
			.query({
				msgId: message._id
			})
			.expect('Content-Type', 'application/json')
			.expect(200)
			.expect((res) => {
				expect(res.body).to.have.property('success', true);
				expect(res.body).to.have.nested.property('message._id', message._id);
			})
			.end(done);
	});

	it('/chat.sendMessage', (done) => {
		message._id = `id-${ Date.now() }`;
		request.post(api('chat.sendMessage'))
			.set(credentials)
			.send({
				message: {
					_id: message._id,
					rid: 'GENERAL',
					msg: 'Sample message',
					alias: 'Gruggy',
					emoji: ':smirk:',
					avatar: 'http://res.guggy.com/logo_128.png',
					attachments: [{
						color: '#ff0000',
						text: 'Yay for gruggy!',
						ts: '2016-12-09T16:53:06.761Z',
						thumb_url: 'http://res.guggy.com/logo_128.png',
						message_link: 'https://google.com',
						collapsed: false,
						author_name: 'Bradley Hilton',
						author_link: 'https://rocket.chat/',
						author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
						title: 'Attachment Example',
						title_link: 'https://youtube.com',
						title_link_download: 'https://rocket.chat/download',
						image_url: 'http://res.guggy.com/logo_128.png',
						audio_url: 'http://www.w3schools.com/tags/horse.mp3',
						video_url: 'http://www.w3schools.com/tags/movie.mp4',
						fields: [{
							short: true,
							title: 'Test',
							value: 'Testing out something or other'
						}, {
							short: true,
							title: 'Another Test',
							value: '[Link](https://google.com/) something and this and that.'
						}]
					}]
				}
			})
			.expect('Content-Type', 'application/json')
			.expect(200)
			.expect((res) => {
				expect(res.body).to.have.property('success', true);
				expect(res.body).to.have.nested.property('message.msg', 'Sample message');
			})
			.end(done);
	});

	it('/chat.getMessage', (done) => {
		request.get(api('chat.getMessage'))
			.set(credentials)
			.query({
				msgId: message._id
			})
			.expect('Content-Type', 'application/json')
			.expect(200)
			.expect((res) => {
				expect(res.body).to.have.property('success', true);
				expect(res.body).to.have.nested.property('message._id', message._id);
			})
			.end(done);
	});

	it('/chat.update', (done) => {
		request.post(api('chat.update'))
			.set(credentials)
			.send({
				roomId: 'GENERAL',
				msgId: message._id,
				text: 'This message was edited via API'
			})
			.expect('Content-Type', 'application/json')
			.expect(200)
			.expect((res) => {
				expect(res.body).to.have.property('success', true);
				expect(res.body).to.have.nested.property('message.msg', 'This message was edited via API');
			})
			.end(done);
	});

	it('/chat.search', (done) => {
		request.get(api('chat.search'))
			.set(credentials)
			.query({
				roomId: 'GENERAL',
				searchText: 'This message was edited via API'
			})
			.expect('Content-Type', 'application/json')
			.expect(200)
			.expect((res) => {
				expect(res.body).to.have.property('success', true);
				expect(res.body).to.have.property('messages');
			})
			.end(done);
	});

	describe('/chat.react', () => {
		it('should return statusCode: 200 when the emoji is valid', (done) => {
			request.post(api('chat.react'))
				.set(credentials)
				.send({
					emoji: ':squid:',
					messageId: message._id
				})
				.expect('Content-Type', 'application/json')
				.expect(200)
				.expect((res) => {
					expect(res.body).to.have.property('success', true);
				})
				.end(done);
		});
	});
});
<|endoftext|>


<commit_before>describe('context', function() {

  var Observable = require('../../../../../lib/observable')

  function contextObservable() {

    var cnt = {
      total: 0
    }

    var a = new Observable({
      $key:'a',
      $trackInstances: true,
      //no on so on default no trackinstances
      b: {
        $on: {
          $change:function() {
            var key = this.$path[0]
            cnt[key] = cnt[key] ? cnt[key]+1 :1
            cnt.total++
          }
        }
      }
    })

    var b = new a.$Constructor({
      $key:'b'
    })

    return {
      cnt: cnt,
      a: a,
      b: b
    }
  }

  describe( 'emit on instance', function() {
    console.clear()
    var test = contextObservable()
    //dit is resolve context shit
    test.b.b.$emit('$change') // = 'b change'
    it( 'should fire once for "b" context' , function() {
      expect( test.cnt.b ).to.equal( 1 )
    })
    it( 'should fire once in total' , function() {
      expect( test.cnt.total ).to.equal( 1 )
    })
  })

  describe( 'set on instance', function() {
    console.clear()
    var test = contextObservable()
    //dit is resolve context shit
    test.b.b.$val = 'b change'
    it( 'should fire once for "b" context' , function() {
      expect( test.cnt.b ).to.equal( 1 )
    })
    it( 'should fire once in total' , function() {
      expect( test.cnt.total ).to.equal( 1 )
    })
  })

  describe.skip( 'multiple instances', function() {
    var test = contextObservable()

    var c = new test.a.$Constructor({
      $key:'c'
    })

    test.a.b.$val = 'a change'
    it( 'should fire once for "a" context' , function() {
      expect( test.cnt.a ).to.equal( 1 )
    })
    it( 'should fire once for "b" context' , function() {
      expect( test.cnt.b ).to.equal( 1 )
    })
    it( 'should fire once for "c" context' , function() {
      expect( test.cnt.c ).to.equal( 1 )
    })
    it( 'should fire 3 times in total' , function() {
      expect( test.cnt.total ).to.equal( 3 )
    })
  })

})
<commit_msg>more tests<commit_after>describe('context', function() {

  var Observable = require('../../../../../lib/observable')

  function contextObservable() {
    var cnt = {
      total: 0
    }

    var a = new Observable({
      $key:'a',
      $trackInstances: true,
      //no on so on default no trackinstances
      b: {
        $on: {
          $change:function() {
            var key = this.$path[0]
            cnt[key] = cnt[key] ? cnt[key]+1 :1
            cnt.total++
          }
        }
      }
    })

    var b = new a.$Constructor({
      $key:'b'
    })

    return {
      cnt: cnt,
      a: a,
      b: b
    }
  }

  describe( 'emit on instance', function() {
    console.clear()
    var test = contextObservable()
    //dit is resolve context shit
    test.b.b.$emit('$change') // = 'b change'
    it( 'should fire once for "b" context' , function() {
      expect( test.cnt.b ).to.equal( 1 )
    })
    it( 'should fire once in total' , function() {
      expect( test.cnt.total ).to.equal( 1 )
    })
  })

  describe( 'set on instance', function() {
    console.clear()
    var test = contextObservable()
    //dit is resolve context shit
    test.b.b.$val = 'b change'
    it( 'should fire once for "b" context' , function() {
      expect( test.cnt.b ).to.equal( 1 )
    })
    it( 'should fire once in total' , function() {
      expect( test.cnt.total ).to.equal( 1 )
    })
  })

  describe( 'multiple instances', function() {
    var test = contextObservable()

    var c = new test.a.$Constructor({
      $key:'c'
    })

    test.a.b.$val = 'a change'
    it( 'should fire once for "a" context' , function() {
      expect( test.cnt.a ).to.equal( 1 )
    })
    it( 'should fire once for "b" context' , function() {
      expect( test.cnt.b ).to.equal( 1 )
    })
    it( 'should fire once for "c" context' , function() {
      expect( test.cnt.c ).to.equal( 1 )
    })
    it( 'should fire 3 times in total' , function() {
      expect( test.cnt.total ).to.equal( 3 )
    })
  })

})
<|endoftext|>


<commit_before>/*
 * The MIT License.
 * Copyright (c) 2015, 2016 Bruce Schubert
 */

/*global define, $, WorldWind */

/**
 * The WeatherMapSymbol module renders a composite WorldWind.Renderable representing a weather station.
 * A WeatherMapSymbol object is a "view" of a WeatherScout "model" object.
 *
 * @param {type} AirTemperature
 * @param {type} controller
 * @param {type} ForecastTime
 * @param {type} RelativeHumidity
 * @param {type} SkyCover
 * @param {type} WindBarb
 * @param {type} wmt
 * @param {type} ww
 * @returns {WeatherMapSymbol}
 *
 * @author Bruce Schubert
 */
define(['model/weather/symbols/AirTemperature',
        'model/weather/symbols/Background',
        'model/weather/symbols/ForecastTime',
        'model/weather/symbols/RelativeHumidity',
        'model/weather/symbols/SkyCover',
        'model/weather/symbols/WindBarb',
        'model/Config',
        'model/Constants',
        'model/Events',
        'worldwind'],
    function (
              AirTemperature,
              Background,
              ForecastTime,
              RelativeHumidity,
              SkyCover,
              WindBarb,
              config,
              constants,
              events,
              ww) {
        "use strict";

        var WeatherMapSymbol = function (wxScout) {

            // Inherit Renderable properties
            WorldWind.Renderable.call(this);

            var self = this,
                wx, i, max,
                timeOptions = {"hour": "2-digit", "minute": "2-digit", "timeZoneName": "short"},
                eyeDistanceScalingThreshold = 2500000;

            // Maintain a reference to the weather object this symbol represents
            this.wxScout = wxScout;

            // Create the composite weather map symbol components
            this.background = new Background(wxScout.latitude(), wxScout.longitude(), eyeDistanceScalingThreshold);
            this.skyCover = new SkyCover(wxScout.latitude(), wxScout.longitude(), null, eyeDistanceScalingThreshold);
            this.windBarb = new WindBarb(wxScout.latitude(), wxScout.longitude(), null, null, eyeDistanceScalingThreshold);
            this.airTemperature = new AirTemperature(wxScout.latitude(), wxScout.longitude(), 'F');
            this.relHumidity = new RelativeHumidity(wxScout.latitude(), wxScout.longitude(), '%');
            this.forecastTime = new ForecastTime(wxScout.latitude(), wxScout.longitude(), ' ');

            // Add a reference to our wx model object to the principle renderables.
            // The "movable" wxScoute will generate EVENT_OBJECT_MOVED events.
            // See the PickController.
            this.background.pickDelegate = wxScout;
            this.skyCover.pickDelegate = wxScout;
            this.windBarb.pickDelegate = wxScout;
            this.airTemperature.pickDelegate = wxScout;
            this.relHumidity.pickDelegate = wxScout;
            this.forecastTime.pickDelegate = wxScout;

            this.skyCover.label = wxScout.name();   // TODO: could be configured for place or lat/lon

            // EVENT_OBJECT_MOVED handler that synchronizes the composite renderables with the model's location
            this.handleObjectMovedEvent = function (wxScout) {
                self.background.position.latitude = wxScout.latitude();
                self.background.position.longitude = wxScout.longitude();
                self.skyCover.position.latitude = wxScout.latitude();
                self.skyCover.position.longitude = wxScout.longitude();
                self.windBarb.position.latitude = wxScout.latitude();
                self.windBarb.position.longitude = wxScout.longitude();
                self.airTemperature.position.latitude = wxScout.latitude();
                self.airTemperature.position.longitude = wxScout.longitude();
                self.relHumidity.position.latitude = wxScout.latitude();
                self.relHumidity.position.longitude = wxScout.longitude();
                self.forecastTime.position.latitude = wxScout.latitude();
                self.forecastTime.position.longitude = wxScout.longitude();
            };

            // EVENT_WEATHER_CHANGED handler that updates the symbology
            this.handleWeatherChangedEvent = function (wxScout) {
                wx = wxScout.getForecastAtTime(wxScout.globe.dateTime());
                // Update the values
                self.skyCover.updateSkyCoverImage(wx.skyCoverPct);
                self.windBarb.updateWindBarbImage(wx.windSpeedKts, wx.windDirectionDeg);
                self.airTemperature.text = wx.airTemperatureF + 'F';
                self.relHumidity.text = wx.relaltiveHumidityPct + '%';
                self.forecastTime.text = '@ ' + wx.time.toLocaleTimeString('en', timeOptions);
            };

            // EVENT_PLACE_CHANGED handler that updates the label
            this.handlePlaceChangedEvent = function (wxScout) {

                if (config.weatherScoutLabels === constants.WEATHER_SCOUT_LABEL_PLACE) {
                    // Display the place name
                    self.skyCover.label = wxScout.toponym || null;
                } else if (config.weatherScoutLabels === constants.WEATHER_SCOUT_LABEL_LATLON) {
                    // Display "Lat Lon"
                    self.skyCover.label = wxScout.latitude.toFixed(3) + ' ' + wxScout.longitude.toFixed(3);
                }
            };

            //EVENT_TIME_CHANGED handler that updates the label and symbology
            this.handleTimeChangedEvent = function (time) {
                wx = self.wxScout.getForecastAtTime(time);
                // Update the values
                self.skyCover.updateSkyCoverImage(wx.skyCoverPct);
                self.windBarb.updateWindBarbImage(wx.windSpeedKts, wx.windDirectionDeg);
                self.airTemperature.text = wx.airTemperatureF + 'F';
                self.relHumidity.text = wx.relaltiveHumidityPct + '%';
                if (wx.time.getTime() === 0) { // See: WeatherScout.INVALID_WX.time
                    self.forecastTime.text = '-'; // empty labels not allowed
                } else {
                    self.forecastTime.text = '@ ' + wx.time.toLocaleTimeString('en', timeOptions);
                }
            };

            // Establish the Publisher/Subscriber relationship between this symbol and the wx scout
            wxScout.on(events.EVENT_OBJECT_MOVED, this.handleObjectMovedEvent, this);
            wxScout.on(events.EVENT_PLACE_CHANGED, this.handlePlaceChangedEvent, this);
            wxScout.on(events.EVENT_WEATHER_CHANGED, this.handleWeatherChangedEvent, this);
            wxScout.on(events.EVENT_TIME_CHANGED, this.handleTimeChangedEvent, this);

            // Set the initial values to the current application time
            this.handleTimeChangedEvent(wxScout.globe.dateTime());
        };
        // Inherit Renderable functions.
        WeatherMapSymbol.prototype = Object.create(WorldWind.Renderable.prototype);

        /**
         * Render this WeatherMapSymbol.
         * @param {DrawContext} dc The current draw context.
         */
        WeatherMapSymbol.prototype.render = function (dc) {

            // Rotate the wind barb to match the view
//            this.windBarb.imageRotation = dc.navigatorState.heading;
            this.windBarb.imageRotation = dc.navigator.heading;
            // Tilt the wind barb to match the view
            //this.windBarb.imageTilt = dc.navigator.tilt; -- Disabled: visbility diminished when tilted

            //this.background.enabled = this.highlighted;
            this.background.highlighted = this.highlighted;

            this.background.render(dc);
            this.skyCover.render(dc);
            this.airTemperature.render(dc);
            this.relHumidity.render(dc);
            this.forecastTime.render(dc);
            this.windBarb.render(dc);
        };

        return WeatherMapSymbol;
    }
);
<commit_msg>Fixed the heading for the wind barb.<commit_after>/*
 * The MIT License.
 * Copyright (c) 2015, 2016 Bruce Schubert
 */

/*global define, $, WorldWind */

/**
 * The WeatherMapSymbol module renders a composite WorldWind.Renderable representing a weather station.
 * A WeatherMapSymbol object is a "view" of a WeatherScout "model" object.
 *
 * @param {type} AirTemperature
 * @param {type} controller
 * @param {type} ForecastTime
 * @param {type} RelativeHumidity
 * @param {type} SkyCover
 * @param {type} WindBarb
 * @param {type} wmt
 * @param {type} ww
 * @returns {WeatherMapSymbol}
 *
 * @author Bruce Schubert
 */
define(['model/weather/symbols/AirTemperature',
        'model/weather/symbols/Background',
        'model/weather/symbols/ForecastTime',
        'model/weather/symbols/RelativeHumidity',
        'model/weather/symbols/SkyCover',
        'model/weather/symbols/WindBarb',
        'model/Config',
        'model/Constants',
        'model/Events',
        'worldwind'],
    function (
              AirTemperature,
              Background,
              ForecastTime,
              RelativeHumidity,
              SkyCover,
              WindBarb,
              config,
              constants,
              events,
              ww) {
        "use strict";

        var WeatherMapSymbol = function (wxScout) {

            // Inherit Renderable properties
            WorldWind.Renderable.call(this);

            var self = this,
                wx, i, max,
                timeOptions = {"hour": "2-digit", "minute": "2-digit", "timeZoneName": "short"},
                eyeDistanceScalingThreshold = 2500000;

            // Maintain a reference to the weather object this symbol represents
            this.wxScout = wxScout;

            // Create the composite weather map symbol components
            this.background = new Background(wxScout.latitude(), wxScout.longitude(), eyeDistanceScalingThreshold);
            this.skyCover = new SkyCover(wxScout.latitude(), wxScout.longitude(), null, eyeDistanceScalingThreshold);
            this.windBarb = new WindBarb(wxScout.latitude(), wxScout.longitude(), null, null, eyeDistanceScalingThreshold);
            this.airTemperature = new AirTemperature(wxScout.latitude(), wxScout.longitude(), 'F');
            this.relHumidity = new RelativeHumidity(wxScout.latitude(), wxScout.longitude(), '%');
            this.forecastTime = new ForecastTime(wxScout.latitude(), wxScout.longitude(), ' ');

            // Add a reference to our wx model object to the principle renderables.
            // The "movable" wxScoute will generate EVENT_OBJECT_MOVED events.
            // See the PickController.
            this.background.pickDelegate = wxScout;
            this.skyCover.pickDelegate = wxScout;
            this.windBarb.pickDelegate = wxScout;
            this.airTemperature.pickDelegate = wxScout;
            this.relHumidity.pickDelegate = wxScout;
            this.forecastTime.pickDelegate = wxScout;

            this.skyCover.label = wxScout.name();   // TODO: could be configured for place or lat/lon

            // EVENT_OBJECT_MOVED handler that synchronizes the composite renderables with the model's location
            this.handleObjectMovedEvent = function (wxScout) {
                self.background.position.latitude = wxScout.latitude();
                self.background.position.longitude = wxScout.longitude();
                self.skyCover.position.latitude = wxScout.latitude();
                self.skyCover.position.longitude = wxScout.longitude();
                self.windBarb.position.latitude = wxScout.latitude();
                self.windBarb.position.longitude = wxScout.longitude();
                self.airTemperature.position.latitude = wxScout.latitude();
                self.airTemperature.position.longitude = wxScout.longitude();
                self.relHumidity.position.latitude = wxScout.latitude();
                self.relHumidity.position.longitude = wxScout.longitude();
                self.forecastTime.position.latitude = wxScout.latitude();
                self.forecastTime.position.longitude = wxScout.longitude();
            };

            // EVENT_WEATHER_CHANGED handler that updates the symbology
            this.handleWeatherChangedEvent = function (wxScout) {
                wx = wxScout.getForecastAtTime(wxScout.globe.dateTime());
                // Update the values
                self.skyCover.updateSkyCoverImage(wx.skyCoverPct);
                self.windBarb.updateWindBarbImage(wx.windSpeedKts, wx.windDirectionDeg);
                self.airTemperature.text = wx.airTemperatureF + 'F';
                self.relHumidity.text = wx.relaltiveHumidityPct + '%';
                self.forecastTime.text = '@ ' + wx.time.toLocaleTimeString('en', timeOptions);
            };

            // EVENT_PLACE_CHANGED handler that updates the label
            this.handlePlaceChangedEvent = function (wxScout) {

                if (config.weatherScoutLabels === constants.WEATHER_SCOUT_LABEL_PLACE) {
                    // Display the place name
                    self.skyCover.label = wxScout.toponym || null;
                } else if (config.weatherScoutLabels === constants.WEATHER_SCOUT_LABEL_LATLON) {
                    // Display "Lat Lon"
                    self.skyCover.label = wxScout.latitude.toFixed(3) + ' ' + wxScout.longitude.toFixed(3);
                }
            };

            //EVENT_TIME_CHANGED handler that updates the label and symbology
            this.handleTimeChangedEvent = function (time) {
                wx = self.wxScout.getForecastAtTime(time);
                // Update the values
                self.skyCover.updateSkyCoverImage(wx.skyCoverPct);
                self.windBarb.updateWindBarbImage(wx.windSpeedKts, wx.windDirectionDeg);
                self.airTemperature.text = wx.airTemperatureF + 'F';
                self.relHumidity.text = wx.relaltiveHumidityPct + '%';
                if (wx.time.getTime() === 0) { // See: WeatherScout.INVALID_WX.time
                    self.forecastTime.text = '-'; // empty labels not allowed
                } else {
                    self.forecastTime.text = '@ ' + wx.time.toLocaleTimeString('en', timeOptions);
                }
            };

            // Establish the Publisher/Subscriber relationship between this symbol and the wx scout
            wxScout.on(events.EVENT_OBJECT_MOVED, this.handleObjectMovedEvent, this);
            wxScout.on(events.EVENT_PLACE_CHANGED, this.handlePlaceChangedEvent, this);
            wxScout.on(events.EVENT_WEATHER_CHANGED, this.handleWeatherChangedEvent, this);
            wxScout.on(events.EVENT_TIME_CHANGED, this.handleTimeChangedEvent, this);

            // Set the initial values to the current application time
            this.handleTimeChangedEvent(wxScout.globe.dateTime());
        };
        // Inherit Renderable functions.
        WeatherMapSymbol.prototype = Object.create(WorldWind.Renderable.prototype);

        /**
         * Render this WeatherMapSymbol.
         * @param {DrawContext} dc The current draw context.
         */
        WeatherMapSymbol.prototype.render = function (dc) {

            // Rotate the wind barb to match the view
            this.windBarb.imageRotation = -dc.navigator.heading;
            // Tilt the wind barb to match the view
            //this.windBarb.imageTilt = dc.navigator.tilt; -- Disabled: visbility diminished when tilted

            //this.background.enabled = this.highlighted;
            this.background.highlighted = this.highlighted;

            this.background.render(dc);
            this.skyCover.render(dc);
            this.airTemperature.render(dc);
            this.relHumidity.render(dc);
            this.forecastTime.render(dc);
            this.windBarb.render(dc);
        };

        return WeatherMapSymbol;
    }
);
<|endoftext|>


<commit_before>(function() {

	var version = process.version.match(/v([0-9]+).([0-9]+).([0-9]+)/);
	if (version[1] != 0 || version[2] < 8 || version[2] > 8 || version[3] < 21) {
		console.error("Ares ide.js only works on Node.js version 0.8.x >= 0.8.21");
		process.exit(1);
	}

})();
<commit_msg>ENYO-2276: Check node version matches package.json. Display module version from package.json<commit_after><|endoftext|>