<commit_before>$(function () {

    /**
     * Search plugin logic
     * ===================
     */

    var $body = $("body");

    // Search form
    var $form = $('#search-form'),
        $results = $('#search-results'),
        resultsTemplate = Handlebars.compile($('script[type="text/x-handlebars-template"]', $results).html()),
        $phraseInput = $('input[name=phrase]', $form),
        $filterInput = $('input[name=provider]', $form),
        $cachedInput = $('select[name=cached]', $form),
        $resultLimitInput = $('input[name=resultLimit]', $form),
        $progressSpinner = $('.progress .spinner', $form),
        $progressText = $('.progress .text', $form),
        searchXhr = null;

    function search() {
    if (!$phraseInput.val().length) {
        return;
    }

    if (searchXhr) {
        searchXhr.abort();
    }

    searchXhr = $.ajax({
        url: pluginRoot + '/by-phrase',
        type: 'GET',
        data: $form.serializeArray(),
        beforeSend: function () {
            $results.empty();
            $progressText.text('Please wait...');
            $progressSpinner.removeClass('done');

            if (history.pushState) {
                var formParams = $form.serializeObject();
                var uri = new URI().search(formParams).toString();
                history.pushState({}, document.title, uri)
            }
        },
        complete: function () {
            $progressSpinner.addClass('done');
            $progressText.text('Completed');
        },
        success: function (response) {
            var vars = $.extend({
                url: this.url
            }, response.data);

            $results.data(vars);
            $results.html(resultsTemplate(vars));
        },
        error: function (xhr) {
            if (xhr.statusText != 'abort') {
                openAlert('Cannot perform search by phrase. Internal server error.');
            }
        }
    });
    };

    var formParams = new URI().search(true);
    populate($form, formParams);

    $phraseInput.on('keyup', _.debounce(search, 1000));
    $filterInput.on('change', search);
    $cachedInput.on('change', search);
    $resultLimitInput.on('change', search);

    search();

    $body.delegate('.class-decompile', 'click', function () {
        var $link = $(this);

        classDecompile($link.attr('href'));

        return false;
    });

    // Viewer for decompiled class source
    var classDecompileTemplate = Handlebars.compile($('#class-decompile-template').html());
    function classDecompile(url) {
        $.ajax({
            url: url,
            type: 'GET',
            success: function (response) {
                var html = classDecompileTemplate(response.data);
                var title = response.data.className + " (" + response.data.bundleId + ")";

                closeDialog('class-bundle');
                openDialog(html, title);
                classSourcePrettyPrint();
            },
            error: function () {
                openAlert('Cannot decompile class. Internal server error.');
            }
        });
    }

    // Chooser for classes found in more than one bundle
    var classBundleTemplate = Handlebars.compile($('#class-bundle-template').html());
    function classSourcePrettyPrint() {
        $('.class-source').each(function () {
            var $source = $(this);

            if ($source.hasClass('prettyprinted')) {
                return;
            } else {
                $source.addClass('prettyprinted');
            }

            var classSource = $source.html();
            var highlightedSource = PR.prettyPrintOne(classSource, 'lang-java' /*, 'line-nums'*/);

            $source.data('source', classSource);
            $source.html(highlightedSource);

            var package = getMatches(classSource, /package (.*);/gi, 1)[0];
            var imports = _.uniq(getMatches(classSource, /import (.*);/gi, 1));

            $('span.typ', $source).each(function () {
                var $link = $(this);
                var type = $(this).text();
                var className = package + "." + type;

                for (var i = 0; i < imports.length; i++) {
                    if (_.last(imports[i].split('.')) == type) {
                        className = imports[i];
                        break;
                    }
                }

                $link.attr({
                    title: 'Click to view source of this type.',
                });

                $link.on('click', function () {
                    $.ajax({
                        type: 'GET',
                        url: pluginRoot + '/by-phrase',
                        data: {
                            phrase: className,
                            provider: 'class',
                            cached: false
                        },
                        success: function (response) {
                            var results = _.filter(response.data.results, function (result) {
                                return result.context.className == className;
                            });

                            if (!results.length) {
                                openAlert('Class cannot be found. It is probably class available only at compile time (e.g. part of JDK) or private inner class.');
                            } else if (results.length == 1) {
                                classDecompile(_.first(results).context.classDecompileUrl)
                            } else {
                                openDialog(classBundleTemplate({
                                    className,
                                    results: results
                                }), "Choose appropriate class");
                            }
                        },
                        error: function () {
                            openAlert('Cannot find referenced class. Internal server error.');
                        },
                    });

                    return false;
                });
            });
        });
    }

    // Viewer for classes in concrete bundle
    var bundleClassesTemplate = Handlebars.compile($('#bundle-classes-template').html());
    $body.delegate('.bundle-classes', 'click', function () {
        var $link = $(this);

        $.ajax({
            url: $link.attr('href'),
            type: 'GET',
            success: function (response) {
                var html = bundleClassesTemplate(response);
                var title = response.bundleSymbolicName + " (" + response.bundleId + ")";

                var $dialog = openDialog(html, title);
                var $tree = $dialog.find('.tree')

                $tree.tree({
                    data: response.classes.tree,
                    autoOpen: response.classes.count < 200 ? true : 2
                }).bind('tree.click', function (e) {
                    var node = e.node;

                    if (node.decompileUrl) {
                        classDecompile(node.decompileUrl);
                    }
                });
            },
            error: function () {
                openAlert('Cannot compose bundle class tree. Internal server error.');
            }
        });

        return false;
    });

    // Search for text in class sources from concrete bundle
    $body.delegate('.select-all', 'click', function () {
        $('.result', $results).each(function () {
            $('[name=result]', this).prop('checked', 'checked');
        });
    });

    $body.delegate('.deselect-all', 'click', function () {
        $('.result', $results).each(function () {
            $('[name=result]', this).prop('checked', '');
        });
    });

    function getSelectedResults() {
        var $results = $('.result', $results).filter(function () {
            return $('[name=result]', this).prop('checked');
        });

        return $results.map(function () {
            var $result = $(this);
            var config = $result.data('result');

            return config;
        }).get();
    }

    function getSelectedBundleData() {
        var data = {
            bundleIds: [],
            bundleClasses: []
        };

        _.each(getSelectedResults(), function (result) {
            var ctx = result.context;

            if (ctx.bundleId && ctx.className) {
                data.bundleClasses.push(ctx.bundleId + ',' + ctx.className);
            } else if (ctx.bundleId) {
                data.bundleIds.push(ctx.bundleId);
            }
        });

        data.total = data.bundleIds.length + data.bundleClasses.length;

        return data;
    }

    // Search classes form
    (function () {
        var searchClassesResultsTemplate = Handlebars.compile($('#search-classes-results-template').html());
        var jobCurrent = null;
        var jobPoll = null;

        function start() {
            if (jobPoll != null) {
                return;
            }

            var $form = $('.dialog-search-classes');
            var $progressSpinner = $('.progress .spinner', $form);
            var $progressText = $('.progress .text', $form);
            var $startButton = $('.class-decompile-start', $form);
            var $stopButton = $('.class-decompile-stop', $form);

            var bundleData = getSelectedBundleData();
            var $results = $('#search-classes-results');

            $.ajax({
                type: 'POST',
                url: pluginRoot + '/class-search',
                data: {
                    phrase: $('input[name=text]', $form).val(),
                    bundleId: bundleData.bundleIds,
                    bundleClass: bundleData.bundleClasses
                },
                beforeSend: function () {
                    $results.empty();
                    $progressText.text('Please wait...');
                    $progressSpinner.removeClass('done');
                },
                success: function (response) {
                    var job = response.data;

                    $stopButton.show();
                    $startButton.hide();

                    jobCurrent = job;
                    jobPoll = setInterval(function () {
                        $.ajax({
                            type: 'GET',
                            url: pluginRoot + '/class-search?jobId=' + job.id,
                            success: function (response) {
                                var job = response.data;

                                // Append partial results
                                if (job.partialResults.length) {
                                    var $result = $(searchClassesResultsTemplate(job)).tabs();
                                    $results.append($result);
                                    classSourcePrettyPrint();
                                }

                                if (job.progress == 100) {
                                    stop();
                                } else {
                                    // Display percentage only it total is calculated
                                    if (job.total <= 0) {
                                        $progressText.text(job.step);
                                    } else {
                                        $progressText.text(job.step + ' ' + job.progress.toFixed(2) + '% (' + job.count + ' / ' + job.total + ')');
                                    }
                                }
                            },
                            error: function () {
                                openAlert('Cannot poll class search. Internal server error.');
                            },
                        });
                    }, 1000);
                },
                error: function () {
                    openAlert('Cannot start class search. Internal server error.');
                },
            });
        }

        function stop() {
            if (jobCurrent == null || jobPoll == null) {
                return;
            }

            var $form = $('.dialog-search-classes');
            var $progressSpinner = $('.progress .spinner', $form);
            var $progressText = $('.progress .text', $form);
            var $startButton = $('.class-decompile-start', $form);
            var $stopButton = $('.class-decompile-stop', $form);

            $.ajax({
                type: 'DELETE',
                url: pluginRoot + '/class-search?jobId=' + jobCurrent.id,
                success: function (response) {
                    var job = response.data;

                    clearInterval(jobPoll);
                    $progressSpinner.addClass('done');
                    $progressText.text('Completed');
                    $stopButton.hide();
                    $startButton.show();

                    jobPoll = null;
                    jobCurrent = null;
                },
                error: function () {
                    openAlert('Cannot stop class search. Internal server error.');
                }
            });

            return false;
        }

        var searchClassesTemplate = Handlebars.compile($('#search-classes-template').html());
        $body.delegate('.search-classes', 'click', function () {
            var results = getSelectedResults();
            if (!results.length) {
                openAlert("Please select elements in which classes will be searched.", "Error");
                return;
            }

            var elements = results.length == 1 ? ("'" + results[0].label + "'") : results.length + " elements";
            openDialog(searchClassesTemplate(), "Decompile classes & search in " + elements, {
                modal: true,
                close: function () {
                    stop();
                }
            });
        });

        $body.delegate('.class-decompile-start', 'click', function () {
            start();

            return false;
        });

        $body.delegate('.class-decompile-stop', 'click', function () {
            stop();
            return false;
        });

        $body.delegate('.dialog-search-classes form', 'submit', function () {
            start();
        });
    }());

    // Generate sources
   (function () {
        var resultsTemplate = Handlebars.compile($('#source-generate-results-template').html());
        var jobCurrent = null;
        var jobPoll = null;

        function start() {
            if (jobPoll != null) {
                return;
            }

            var $form = $('.dialog-source-generate');
            var $progressSpinner = $('.progress .spinner', $form);
            var $progressText = $('.progress .text', $form);
            var $startButton = $('.source-generate-start', $form);
            var $stopButton = $('.source-generate-stop', $form);

            var bundleData = getSelectedBundleData();
            var $results = $('#source-generate-results');

            $.ajax({
                type: 'POST',
                url: pluginRoot + '/source-generate',
                data: {
                    bundleId: bundleData.bundleIds,
                    bundleClass: bundleData.bundleClasses
                },
                beforeSend: function () {
                    $results.empty();
                    $progressText.text('Please wait...');
                    $progressSpinner.removeClass('done');
                },
                success: function (response) {
                    var job = response.data;

                    $stopButton.show();
                    $startButton.hide();

                    jobCurrent = job;
                    jobPoll = setInterval(function () {
                        $.ajax({
                            type: 'GET',
                            url: pluginRoot + '/source-generate?jobId=' + job.id,
                            success: function (response) {
                                var job = response.data;

                                if (job.progress == 100) {
                                    stop();

                                    $results.html(resultsTemplate(job));
                                } else {
                                    // Display percentage only it total is calculated
                                    if (job.total <= 0) {
                                        $progressText.text(job.step);
                                    } else {
                                        $progressText.text(job.step + ' ' + job.progress.toFixed(2) + '% (' + job.count + ' / ' + job.total + ')');
                                    }
                                }
                            },
                            error: function () {
                                openAlert('Cannot poll source generation. Internal server error.');
                            },
                        });
                    }, 1000);
                },
                error: function () {
                    openAlert('Cannot start source generation. Internal server error.');
                },
            });
        }

        function stop() {
            if (jobCurrent == null || jobPoll == null) {
                return;
            }

            var $form = $('.dialog-source-generate');
            var $progressSpinner = $('.progress .spinner', $form);
            var $progressText = $('.progress .text', $form);
            var $startButton = $('.source-generate-start', $form);
            var $stopButton = $('.source-generate-stop', $form);

            $.ajax({
                type: 'DELETE',
                url: pluginRoot + '/source-generate?jobId=' + jobCurrent.id,
                success: function (response) {
                    var job = response.data;

                    clearInterval(jobPoll);
                    $progressSpinner.addClass('done');
                    $progressText.text('Completed');
                    $stopButton.hide();
                    $startButton.show();

                    jobPoll = null;
                    jobCurrent = null;
                },
                error: function () {
                    openAlert('Cannot stop source generation. Internal server error.');
                }
            });

            return false;
        }

        var generateSourcesTemplate = Handlebars.compile($('#source-generate-template').html());
        $body.delegate('.source-generate', 'click', function () {
            var results = getSelectedResults();
            if (!results.length) {
                openAlert("Please select elements for which sources will be generated.", "Error");
                return;
            }

            var elements = results.length == 1 ? ("'" + results[0].label + "'") : results.length + " elements";
            openDialog(generateSourcesTemplate(), "Generate sources for " + elements, {
                modal: true,
                width: 480,
                height: 200,
                close: function () {
                    stop();
                }
            });

            start();
        });

        $body.delegate('.source-generate-start', 'click', function () {
            start();

            return false;
        });

        $body.delegate('.source-generate-stop', 'click', function () {
            stop();

            return false;
        });

        $body.delegate('.dialog-source-generate form', 'submit', function () {
            start();
        });
    }());
});
<commit_msg>#1 | Bug fix<commit_after>$(function () {

    /**
     * Search plugin logic
     * ===================
     */

    var $body = $("body");

    // Search form
    var $form = $('#search-form'),
        $results = $('#search-results'),
        resultsTemplate = Handlebars.compile($('script[type="text/x-handlebars-template"]', $results).html()),
        $phraseInput = $('input[name=phrase]', $form),
        $filterInput = $('input[name=provider]', $form),
        $cachedInput = $('select[name=cached]', $form),
        $resultLimitInput = $('input[name=resultLimit]', $form),
        $progressSpinner = $('.progress .spinner', $form),
        $progressText = $('.progress .text', $form),
        searchXhr = null;

    function search() {
    if (!$phraseInput.val().length) {
        return;
    }

    if (searchXhr) {
        searchXhr.abort();
    }

    searchXhr = $.ajax({
        url: pluginRoot + '/by-phrase',
        type: 'GET',
        data: $form.serializeArray(),
        beforeSend: function () {
            $results.empty();
            $progressText.text('Please wait...');
            $progressSpinner.removeClass('done');

            if (history.pushState) {
                var formParams = $form.serializeObject();
                var uri = new URI().search(formParams).toString();
                history.pushState({}, document.title, uri)
            }
        },
        complete: function () {
            $progressSpinner.addClass('done');
            $progressText.text('Completed');
        },
        success: function (response) {
            var vars = $.extend({
                url: this.url
            }, response.data);

            $results.data(vars);
            $results.html(resultsTemplate(vars));
        },
        error: function (xhr) {
            if (xhr.statusText != 'abort') {
                openAlert('Cannot perform search by phrase. Internal server error.');
            }
        }
    });
    };

    var formParams = new URI().search(true);
    populate($form, formParams);

    $phraseInput.on('keyup', _.debounce(search, 1000));
    $filterInput.on('change', search);
    $cachedInput.on('change', search);
    $resultLimitInput.on('change', search);

    search();

    $body.delegate('.class-decompile', 'click', function () {
        var $link = $(this);

        classDecompile($link.attr('href'));

        return false;
    });

    // Viewer for decompiled class source
    var classDecompileTemplate = Handlebars.compile($('#class-decompile-template').html());
    function classDecompile(url) {
        $.ajax({
            url: url,
            type: 'GET',
            success: function (response) {
                var html = classDecompileTemplate(response.data);
                var title = response.data.className + " (" + response.data.bundleId + ")";

                closeDialog('class-bundle');
                openDialog(html, title);
                classSourcePrettyPrint();
            },
            error: function () {
                openAlert('Cannot decompile class. Internal server error.');
            }
        });
    }

    // Chooser for classes found in more than one bundle
    var classBundleTemplate = Handlebars.compile($('#class-bundle-template').html());
    function classSourcePrettyPrint() {
        $('.class-source').each(function () {
            var $source = $(this);

            if ($source.hasClass('prettyprinted')) {
                return;
            } else {
                $source.addClass('prettyprinted');
            }

            var classSource = $source.html();
            var highlightedSource = PR.prettyPrintOne(classSource, 'lang-java' /*, 'line-nums'*/);

            $source.data('source', classSource);
            $source.html(highlightedSource);

            var package = getMatches(classSource, /package (.*);/gi, 1)[0];
            var imports = _.uniq(getMatches(classSource, /import (.*);/gi, 1));

            $('span.typ', $source).each(function () {
                var $link = $(this);
                var type = $(this).text();
                var className = package + "." + type;

                for (var i = 0; i < imports.length; i++) {
                    if (_.last(imports[i].split('.')) == type) {
                        className = imports[i];
                        break;
                    }
                }

                $link.attr({
                    title: 'Click to view source of this type.',
                });

                $link.on('click', function () {
                    $.ajax({
                        type: 'GET',
                        url: pluginRoot + '/by-phrase',
                        data: {
                            phrase: className,
                            provider: 'class',
                            cached: false
                        },
                        success: function (response) {
                            var results = _.filter(response.data.results, function (result) {
                                return result.context.className == className;
                            });

                            if (!results.length) {
                                openAlert('Class cannot be found. It is probably class available only at compile time (e.g. part of JDK) or private inner class.');
                            } else if (results.length == 1) {
                                classDecompile(_.first(results).context.classDecompileUrl)
                            } else {
                                openDialog(classBundleTemplate({
                                    className,
                                    results: results
                                }), "Choose appropriate class");
                            }
                        },
                        error: function () {
                            openAlert('Cannot find referenced class. Internal server error.');
                        },
                    });

                    return false;
                });
            });
        });
    }

    // Viewer for classes in concrete bundle
    var bundleClassesTemplate = Handlebars.compile($('#bundle-classes-template').html());
    $body.delegate('.bundle-classes', 'click', function () {
        var $link = $(this);

        $.ajax({
            url: $link.attr('href'),
            type: 'GET',
            success: function (response) {
                var html = bundleClassesTemplate(response);
                var title = response.bundleSymbolicName + " (" + response.bundleId + ")";

                var $dialog = openDialog(html, title);
                var $tree = $dialog.find('.tree')

                $tree.tree({
                    data: response.classes.tree,
                    autoOpen: response.classes.count < 200 ? true : 2
                }).bind('tree.click', function (e) {
                    var node = e.node;

                    if (node.decompileUrl) {
                        classDecompile(node.decompileUrl);
                    }
                });
            },
            error: function () {
                openAlert('Cannot compose bundle class tree. Internal server error.');
            }
        });

        return false;
    });

    // Search for text in class sources from concrete bundle
    $body.delegate('.select-all', 'click', function () {
        $('.result', $results).each(function () {
            $('[name=result]', this).prop('checked', 'checked');
        });
    });

    $body.delegate('.deselect-all', 'click', function () {
        $('.result', $results).each(function () {
            $('[name=result]', this).prop('checked', '');
        });
    });

    function getSelectedResults() {
        var $results = $('.result', $results).filter(function () {
            return $('[name=result]', this).prop('checked');
        });

        return $results.map(function () {
            var $result = $(this);
            var config = $result.data('result');

            return config;
        }).get();
    }

    function getSelectedBundleData() {
        var data = {
            bundleIds: [],
            bundleClasses: []
        };

        _.each(getSelectedResults(), function (result) {
            var ctx = result.context;

            if (ctx.bundleId && ctx.className) {
                data.bundleClasses.push(ctx.bundleId + ',' + ctx.className);
            } else if (ctx.bundleId) {
                data.bundleIds.push(ctx.bundleId);
            }
        });

        data.total = data.bundleIds.length + data.bundleClasses.length;

        return data;
    }

    // Search classes form
    (function () {
        var searchClassesResultsTemplate = Handlebars.compile($('#search-classes-results-template').html());
        var jobCurrent = null;
        var jobPoll = null;

        function start() {
            if (jobPoll != null) {
                return;
            }

            var $form = $('.dialog-search-classes');
            var $progressSpinner = $('.progress .spinner', $form);
            var $progressText = $('.progress .text', $form);
            var $startButton = $('.class-decompile-start', $form);
            var $stopButton = $('.class-decompile-stop', $form);

            var bundleData = getSelectedBundleData();
            var $results = $('#search-classes-results');

            $.ajax({
                type: 'POST',
                url: pluginRoot + '/class-search',
                data: {
                    phrase: $('input[name=text]', $form).val(),
                    bundleId: bundleData.bundleIds,
                    bundleClass: bundleData.bundleClasses
                },
                beforeSend: function () {
                    $results.empty();
                    $progressText.text('Please wait...');
                    $progressSpinner.removeClass('done');
                },
                success: function (response) {
                    var job = response.data;

                    $stopButton.show();
                    $startButton.hide();

                    jobCurrent = job;
                    jobPoll = setInterval(function () {
                        $.ajax({
                            type: 'GET',
                            url: pluginRoot + '/class-search?jobId=' + job.id,
                            success: function (response) {
                                var job = response.data;

                                // Append partial results
                                if (job.partialResults.length) {
                                    var $result = $(searchClassesResultsTemplate(job)).tabs();
                                    $results.append($result);
                                    classSourcePrettyPrint();
                                }

                                if (job.progress == 100) {
                                    stop();
                                } else {
                                    // Display percentage only it total is calculated
                                    if (job.total <= 0) {
                                        $progressText.text(job.step);
                                    } else {
                                        $progressText.text(job.step + ' ' + job.progress.toFixed(2) + '% (' + job.count + ' / ' + job.total + ')');
                                    }
                                }
                            },
                            error: function () {
                                openAlert('Cannot poll class search. Internal server error.');
                            },
                        });
                    }, 1000);
                },
                error: function () {
                    openAlert('Cannot start class search. Internal server error.');
                },
            });
        }

        function stop() {
            if (jobCurrent == null || jobPoll == null) {
                return;
            }

            var $form = $('.dialog-search-classes');
            var $progressSpinner = $('.progress .spinner', $form);
            var $progressText = $('.progress .text', $form);
            var $startButton = $('.class-decompile-start', $form);
            var $stopButton = $('.class-decompile-stop', $form);

            $.ajax({
                type: 'DELETE',
                url: pluginRoot + '/class-search?jobId=' + jobCurrent.id,
                success: function (response) {
                    var job = response.data;

                    clearInterval(jobPoll);
                    $progressSpinner.addClass('done');
                    $progressText.text('Completed');
                    $stopButton.hide();
                    $startButton.show();

                    jobPoll = null;
                    jobCurrent = null;
                },
                error: function () {
                    openAlert('Cannot stop class search. Internal server error.');
                }
            });

            return false;
        }

        var searchClassesTemplate = Handlebars.compile($('#search-classes-template').html());
        $body.delegate('.search-classes', 'click', function () {
            var results = getSelectedResults();
            if (!results.length) {
                openAlert("Please select elements in which classes will be searched.", "Error");
                return;
            }

            var elements = results.length == 1 ? ("'" + results[0].label + "'") : results.length + " elements";

            openDialog(searchClassesTemplate(), "Decompile classes & search in " + elements, {
                modal: true,
                close: function () {
                    $(this).dialog('destroy').remove();
                    stop();
                }
            });
        });

        $body.delegate('.class-decompile-start', 'click', function () {
            start();

            return false;
        });

        $body.delegate('.class-decompile-stop', 'click', function () {
            stop();
            return false;
        });

        $body.delegate('.dialog-search-classes form', 'submit', function () {
            start();
        });
    }());

    // Generate sources
   (function () {
        var resultsTemplate = Handlebars.compile($('#source-generate-results-template').html());
        var jobCurrent = null;
        var jobPoll = null;

        function start() {
            if (jobPoll != null) {
                return;
            }

            var $form = $('.dialog-source-generate');
            var $progressSpinner = $('.progress .spinner', $form);
            var $progressText = $('.progress .text', $form);
            var $startButton = $('.source-generate-start', $form);
            var $stopButton = $('.source-generate-stop', $form);

            var bundleData = getSelectedBundleData();
            var $results = $('#source-generate-results');

            $.ajax({
                type: 'POST',
                url: pluginRoot + '/source-generate',
                data: {
                    bundleId: bundleData.bundleIds,
                    bundleClass: bundleData.bundleClasses
                },
                beforeSend: function () {
                    $results.empty();
                    $progressText.text('Please wait...');
                    $progressSpinner.removeClass('done');
                },
                success: function (response) {
                    var job = response.data;

                    $stopButton.show();
                    $startButton.hide();

                    jobCurrent = job;
                    jobPoll = setInterval(function () {
                        $.ajax({
                            type: 'GET',
                            url: pluginRoot + '/source-generate?jobId=' + job.id,
                            success: function (response) {
                                var job = response.data;

                                if (job.progress == 100) {
                                    $results.html(resultsTemplate(job));
                                    stop();
                                } else {
                                    // Display percentage only it total is calculated
                                    if (job.total <= 0) {
                                        $progressText.text(job.step);
                                    } else {
                                        $progressText.text(job.step + ' ' + job.progress.toFixed(2) + '% (' + job.count + ' / ' + job.total + ')');
                                    }
                                }
                            },
                            error: function () {
                                openAlert('Cannot poll source generation. Internal server error.');
                            },
                        });
                    }, 1000);
                },
                error: function () {
                    openAlert('Cannot start source generation. Internal server error.');
                },
            });
        }

        function stop() {
            if (jobCurrent == null || jobPoll == null) {
                return;
            }

            var $form = $('.dialog-source-generate');
            var $progressSpinner = $('.progress .spinner', $form);
            var $progressText = $('.progress .text', $form);
            var $startButton = $('.source-generate-start', $form);
            var $stopButton = $('.source-generate-stop', $form);

            $.ajax({
                type: 'DELETE',
                url: pluginRoot + '/source-generate?jobId=' + jobCurrent.id,
                success: function (response) {
                    var job = response.data;

                    clearInterval(jobPoll);
                    $progressSpinner.addClass('done');
                    $progressText.text('Completed');
                    $stopButton.hide();
                    $startButton.show();

                    jobPoll = null;
                    jobCurrent = null;
                },
                error: function () {
                    openAlert('Cannot stop source generation. Internal server error.');
                }
            });

            return false;
        }

        var generateSourcesTemplate = Handlebars.compile($('#source-generate-template').html());
        $body.delegate('.source-generate', 'click', function () {
            var results = getSelectedResults();
            if (!results.length) {
                openAlert("Please select elements for which sources will be generated.", "Error");
                return;
            }

            var elements = results.length == 1 ? ("'" + results[0].label + "'") : results.length + " elements";

            openDialog(generateSourcesTemplate(), "Generate sources for " + elements, {
                modal: true,
                width: 480,
                height: 200,
                close: function () {
                    $(this).dialog('destroy').remove();
                    stop();
                }
            });

            start();
        });

        $body.delegate('.source-generate-start', 'click', function () {
            start();

            return false;
        });

        $body.delegate('.source-generate-stop', 'click', function () {
            stop();

            return false;
        });

        $body.delegate('.dialog-source-generate form', 'submit', function () {
            start();
        });
    }());
});
<|endoftext|>


<commit_before>/**
* Copyright 2012-2019, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var fontAttrs = require('../../plots/font_attributes');
var colorAttrs = require('../color/attributes');

module.exports = {
    bgcolor: {
        valType: 'color',
        role: 'style',
        editType: 'legend',
        description: 'Sets the legend background color.'
    },
    bordercolor: {
        valType: 'color',
        dflt: colorAttrs.defaultLine,
        role: 'style',
        editType: 'legend',
        description: 'Sets the color of the border enclosing the legend.'
    },
    borderwidth: {
        valType: 'number',
        min: 0,
        dflt: 0,
        role: 'style',
        editType: 'legend',
        description: 'Sets the width (in px) of the border enclosing the legend.'
    },
    font: fontAttrs({
        editType: 'legend',
        description: 'Sets the font used to text the legend items.'
    }),
    orientation: {
        valType: 'enumerated',
        values: ['v', 'h'],
        dflt: 'v',
        role: 'info',
        editType: 'legend',
        description: 'Sets the orientation of the legend.'
    },
    traceorder: {
        valType: 'flaglist',
        flags: ['reversed', 'grouped'],
        extras: ['normal'],
        role: 'style',
        editType: 'legend',
        description: [
            'Determines the order at which the legend items are displayed.',

            'If *normal*, the items are displayed top-to-bottom in the same',
            'order as the input data.',

            'If *reversed*, the items are displayed in the opposite order',
            'as *normal*.',

            'If *grouped*, the items are displayed in groups',
            '(when a trace `legendgroup` is provided).',

            'if *grouped+reversed*, the items are displayed in the opposite order',
            'as *grouped*.'
        ].join(' ')
    },
    tracegroupgap: {
        valType: 'number',
        min: 0,
        dflt: 10,
        role: 'style',
        editType: 'legend',
        description: [
            'Sets the amount of vertical space (in px) between legend groups.'
        ].join(' ')
    },
    itemsizing: {
        valType: 'enumerated',
        values: ['trace', 'constant'],
        dflt: 'trace',
        role: 'style',
        editType: 'legend',
        description: [
            'Determines if the legend items symbols scale with their corresponding *trace* attributes',
            'or remain *constant* independent of the symbol size on the graph.'
        ].join(' ')
    },

    itemclick: {
        valType: 'enumerated',
        values: ['toggle', 'toggleothers', false],
        dflt: 'toggle',
        role: 'info',
        editType: 'legend',
        description: [
            'Determines the behavior on legend item click.',
            '*toggle* toggles the visibility of the item clicked on the graph.',
            '*toggleothers* makes the clicked item the sole visible item on the graph.',
            '*false* disable legend item click interactions.'
        ].join(' ')
    },
    itemdoubleclick: {
        valType: 'enumerated',
        values: ['toggle', 'toggleothers', false],
        dflt: 'toggleothers',
        role: 'info',
        editType: 'legend',
        description: [
            'Determines the behavior on legend item double-click.',
            '*toggle* toggles the visibility of the item clicked on the graph.',
            '*toggleothers* makes the clicked item the sole visible item on the graph.',
            '*false* disable legend item double-click interactions.'
        ].join(' ')
    },

    x: {
        valType: 'number',
        min: -2,
        max: 3,
        role: 'style',
        editType: 'legend',
        description: [
            'Sets the x position (in normalized coordinates) of the legend.',
            'Defaults to *1.02* for vertical legends and',
            'defaults to *0* for horizontal legends.'
        ].join(' ')
    },
    xanchor: {
        valType: 'enumerated',
        values: ['auto', 'left', 'center', 'right'],
        dflt: 'left',
        role: 'info',
        editType: 'legend',
        description: [
            'Sets the legend\'s horizontal position anchor.',
            'This anchor binds the `x` position to the *left*, *center*',
            'or *right* of the legend.',
            'Value *auto* anchors legends to the right for `x` values greater than or equal to 2/3,',
            'anchors legends to the left for `x` values less than or equal to 1/3 and',
            'anchors legends with respect to their center otherwise.'
        ].join(' ')
    },
    y: {
        valType: 'number',
        min: -2,
        max: 3,
        role: 'style',
        editType: 'legend',
        description: [
            'Sets the y position (in normalized coordinates) of the legend.',
            'Defaults to *1* for vertical legends,',
            'defaults to *-0.1* for horizontal legends on graphs w/o range sliders and',
            'defaults to *1.1* for horizontal legends on graph with one or multiple range sliders.'
        ].join(' ')
    },
    yanchor: {
        valType: 'enumerated',
        values: ['auto', 'top', 'middle', 'bottom'],
        role: 'info',
        editType: 'legend',
        description: [
            'Sets the legend\'s vertical position anchor',
            'This anchor binds the `y` position to the *top*, *middle*',
            'or *bottom* of the legend.',
            'Value *auto* anchors legends at their bottom for `y` values less than or equal to 1/3,',
            'anchors legends to at their top for `y` values greater than or equal to 2/3 and',
            'anchors legends with respect to their middle otherwise.'
        ].join(' ')
    },
    uirevision: {
        valType: 'any',
        role: 'info',
        editType: 'none',
        description: [
            'Controls persistence of legend-driven changes in trace and pie label',
            'visibility. Defaults to `layout.uirevision`.'
        ].join(' ')
    },
    valign: {
        valType: 'enumerated',
        values: ['top', 'middle', 'bottom'],
        dflt: 'middle',
        role: 'style',
        editType: 'legend',
        description: [
            'Sets the vertical alignment of the symbols with respect to their associated text.',
        ].join(' ')
    },
    editType: 'legend'
};
<commit_msg>mention legend.bgcolor default in documentation<commit_after>/**
* Copyright 2012-2019, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var fontAttrs = require('../../plots/font_attributes');
var colorAttrs = require('../color/attributes');

module.exports = {
    bgcolor: {
        valType: 'color',
        role: 'style',
        editType: 'legend',
        description: [
            'Sets the legend background color.',
            'Defaults to `layout.paper_bgcolor`.'
        ].join(' ')
    },
    bordercolor: {
        valType: 'color',
        dflt: colorAttrs.defaultLine,
        role: 'style',
        editType: 'legend',
        description: 'Sets the color of the border enclosing the legend.'
    },
    borderwidth: {
        valType: 'number',
        min: 0,
        dflt: 0,
        role: 'style',
        editType: 'legend',
        description: 'Sets the width (in px) of the border enclosing the legend.'
    },
    font: fontAttrs({
        editType: 'legend',
        description: 'Sets the font used to text the legend items.'
    }),
    orientation: {
        valType: 'enumerated',
        values: ['v', 'h'],
        dflt: 'v',
        role: 'info',
        editType: 'legend',
        description: 'Sets the orientation of the legend.'
    },
    traceorder: {
        valType: 'flaglist',
        flags: ['reversed', 'grouped'],
        extras: ['normal'],
        role: 'style',
        editType: 'legend',
        description: [
            'Determines the order at which the legend items are displayed.',

            'If *normal*, the items are displayed top-to-bottom in the same',
            'order as the input data.',

            'If *reversed*, the items are displayed in the opposite order',
            'as *normal*.',

            'If *grouped*, the items are displayed in groups',
            '(when a trace `legendgroup` is provided).',

            'if *grouped+reversed*, the items are displayed in the opposite order',
            'as *grouped*.'
        ].join(' ')
    },
    tracegroupgap: {
        valType: 'number',
        min: 0,
        dflt: 10,
        role: 'style',
        editType: 'legend',
        description: [
            'Sets the amount of vertical space (in px) between legend groups.'
        ].join(' ')
    },
    itemsizing: {
        valType: 'enumerated',
        values: ['trace', 'constant'],
        dflt: 'trace',
        role: 'style',
        editType: 'legend',
        description: [
            'Determines if the legend items symbols scale with their corresponding *trace* attributes',
            'or remain *constant* independent of the symbol size on the graph.'
        ].join(' ')
    },

    itemclick: {
        valType: 'enumerated',
        values: ['toggle', 'toggleothers', false],
        dflt: 'toggle',
        role: 'info',
        editType: 'legend',
        description: [
            'Determines the behavior on legend item click.',
            '*toggle* toggles the visibility of the item clicked on the graph.',
            '*toggleothers* makes the clicked item the sole visible item on the graph.',
            '*false* disable legend item click interactions.'
        ].join(' ')
    },
    itemdoubleclick: {
        valType: 'enumerated',
        values: ['toggle', 'toggleothers', false],
        dflt: 'toggleothers',
        role: 'info',
        editType: 'legend',
        description: [
            'Determines the behavior on legend item double-click.',
            '*toggle* toggles the visibility of the item clicked on the graph.',
            '*toggleothers* makes the clicked item the sole visible item on the graph.',
            '*false* disable legend item double-click interactions.'
        ].join(' ')
    },

    x: {
        valType: 'number',
        min: -2,
        max: 3,
        role: 'style',
        editType: 'legend',
        description: [
            'Sets the x position (in normalized coordinates) of the legend.',
            'Defaults to *1.02* for vertical legends and',
            'defaults to *0* for horizontal legends.'
        ].join(' ')
    },
    xanchor: {
        valType: 'enumerated',
        values: ['auto', 'left', 'center', 'right'],
        dflt: 'left',
        role: 'info',
        editType: 'legend',
        description: [
            'Sets the legend\'s horizontal position anchor.',
            'This anchor binds the `x` position to the *left*, *center*',
            'or *right* of the legend.',
            'Value *auto* anchors legends to the right for `x` values greater than or equal to 2/3,',
            'anchors legends to the left for `x` values less than or equal to 1/3 and',
            'anchors legends with respect to their center otherwise.'
        ].join(' ')
    },
    y: {
        valType: 'number',
        min: -2,
        max: 3,
        role: 'style',
        editType: 'legend',
        description: [
            'Sets the y position (in normalized coordinates) of the legend.',
            'Defaults to *1* for vertical legends,',
            'defaults to *-0.1* for horizontal legends on graphs w/o range sliders and',
            'defaults to *1.1* for horizontal legends on graph with one or multiple range sliders.'
        ].join(' ')
    },
    yanchor: {
        valType: 'enumerated',
        values: ['auto', 'top', 'middle', 'bottom'],
        role: 'info',
        editType: 'legend',
        description: [
            'Sets the legend\'s vertical position anchor',
            'This anchor binds the `y` position to the *top*, *middle*',
            'or *bottom* of the legend.',
            'Value *auto* anchors legends at their bottom for `y` values less than or equal to 1/3,',
            'anchors legends to at their top for `y` values greater than or equal to 2/3 and',
            'anchors legends with respect to their middle otherwise.'
        ].join(' ')
    },
    uirevision: {
        valType: 'any',
        role: 'info',
        editType: 'none',
        description: [
            'Controls persistence of legend-driven changes in trace and pie label',
            'visibility. Defaults to `layout.uirevision`.'
        ].join(' ')
    },
    valign: {
        valType: 'enumerated',
        values: ['top', 'middle', 'bottom'],
        dflt: 'middle',
        role: 'style',
        editType: 'legend',
        description: [
            'Sets the vertical alignment of the symbols with respect to their associated text.',
        ].join(' ')
    },
    editType: 'legend'
};
<|endoftext|>


<commit_before><commit_msg>fix(reconciler): minor fixes to handle null<commit_after><|endoftext|>


<commit_before>import React from 'react'
import { IndexLink, Link } from 'react-router'
import classes from './Header.scss'

export const Header = () => (

    <Link to='/list' activeClassName={classes.activeRoute}>
      List
    </Link>

)

export default Header
<commit_msg>rename link header<commit_after>import React from 'react'
import { IndexLink, Link } from 'react-router'
import classes from './Header.scss'

export const Header = () => (

    <Link to='/list' activeClassName={classes.activeRoute}>
      Shops
    </Link>

)

export default Header
<|endoftext|>


<commit_before>import detectPrefixes from './detect-prefixes.js'

const { slice } = Array.prototype
const prefixes = detectPrefixes()

const LINEAR_ANIMATION = 'linear'
const SLIDES_TO_SCROLL = 1
const VALID_SWIPE_DISTANCE = 25
const {abs, min, max, round} = Math
const windowDOM = window

export function slidy (slider, options) {
  const {
    ease,
    frameDOMEl,
    infinite,
    items,
    itemsPreloaded,
    rewind,
    rewindSpeed,
    slideSpeed,
    tailArrowClass
  } = options

  // if frameDOMEl is null, then we do nothing
  if (frameDOMEl === null) return
  // DOM elements
  const slideContainerDOMEl = frameDOMEl.getElementsByClassName(options.classNameSlideContainer)[0]

  const prevArrow = frameDOMEl.getElementsByClassName(options.classNamePrevCtrl)[0]
  const nextArrow = frameDOMEl.getElementsByClassName(options.classNameNextCtrl)[0]

  // initialize some variables
  let frameWidth = 0
  let index = 0
  let loadedIndex = []
  let maxOffset = 0
  let position = 0
  let slides = []
  let transitionEndCallback

  let itemsPreloadedCount = itemsPreloaded
  while (itemsPreloadedCount--) loadedIndex[itemsPreloadedCount] = 1

  // event handling
  let touchOffset = { pageX: 0, pageY: 0 }
  let currentTouchOffset = { pageX: 0, pageY: 0 }
  let delta = { x: 0, y: 0 }
  let isScrolling = false

  // clamp a number between two min and max values
  function _clampNumber (x, minValue, maxValue) {
    return min(max(x, minValue), maxValue)
  }

  // get the width from a DOM element
  function _getWidthFromDOMEl (el) {
    return el.getBoundingClientRect().width
  }

  // calculate the offset with the width of the frame and the desired position
  function _getOffsetLeft (slidePosition) {
    return frameWidth * slidePosition
  }

  /**
   * private
   * _setupInfinite: function to setup if infinite is set
   *
   * @param  {array} slideArray
   * @return {array} array of updated slideContainer elements
   */
  function _setupInfinite (slideArray) {
    const { infinite } = options

    const totalSlides = slideArray.length
    const front = slideArray.slice(0, infinite)
    const back = slideArray.slice(totalSlides - infinite, totalSlides)
    const { firstChild } = slideContainerDOMEl

    front.forEach(function (el) {
      const cloned = el.cloneNode(true)
      slideContainerDOMEl.appendChild(cloned)
    })

    back.reverse()
      .forEach(function (el) {
        const cloned = el.cloneNode(true)
        slideContainerDOMEl.insertBefore(cloned, firstChild)
      })

    return slice.call(slideContainerDOMEl.children)
  }

  /**
   * translates to a given position in a given time in milliseconds
   *
   * @to        {number} number in pixels where to translate to
   * @duration  {number} time in milliseconds for the transistion
   * @ease      {string} easing css property
   */
  function _translate (to, duration, ease) {
    const easeCssText = ease ? `${prefixes.transitionTiming}: ${ease};` : ''
    const durationCssText = duration ? `${prefixes.transitionDuration}: ${duration}ms;` : ''
    const cssText = `${easeCssText}${durationCssText}
      ${prefixes.transform}: ${prefixes.translate(to)};`

    slideContainerDOMEl.style.cssText = cssText
  }

  function _setTailArrowClasses () {
    if (infinite === true) { return }
    if (prevArrow !== null && typeof prevArrow.classList === 'object') {
      let action = index < 1 ? 'add' : 'remove'
      prevArrow.classList[action](tailArrowClass)
    }
    if (nextArrow !== null && typeof nextArrow.classList !== 'object') {
      let action = index > options.items.length - 2 ? 'add' : 'remove'
      nextArrow.classList[action](tailArrowClass)
    }
  }

  /**
   * slidefunction called by prev, next & touchend
   *
   * determine nextIndex and slide to next postion
   * under restrictions of the defined options
   *
   * @direction  {boolean} 'true' for right, 'false' for left
   */
  function slide (direction) {
    let duration = slideSpeed

    const movement = direction === true ? 1 : -1
    const totalSlides = slides.length

    // calculate the nextIndex according to the movement
    let nextIndex = index + (SLIDES_TO_SCROLL * movement)

    // nextIndex should be between 0 and totalSlides minus 1
    nextIndex = _clampNumber(nextIndex, 0, totalSlides - 1)

    if (infinite === true && direction === undefined) {
      nextIndex += infinite
    }

    let nextOffset = _clampNumber(_getOffsetLeft(nextIndex) * -1, maxOffset * -1, 0)

    if (rewind && direction && abs(position) === maxOffset) {
      nextOffset = 0
      nextIndex = 0
      duration = rewindSpeed
    }

    const needToSlide = nextIndex !== index
    if (!needToSlide) return

    // translate to the nextOffset by a defined duration and ease function
    _translate(nextOffset, duration, ease)

    // update the position with the next position
    position = nextOffset

    // if the nextIndex is possible according to totalSlides, then use it
    if (nextIndex <= totalSlides) {
      options.doBeforeSlide({ currentSlide: index, nextSlide: nextIndex })
      index = nextIndex
    }

    if (infinite && (nextIndex === totalSlides - infinite || nextIndex === 0)) {
      index = direction ? infinite : totalSlides - (infinite * 2)

      position = _getOffsetLeft(index) * -1

      transitionEndCallback = function () {
        _translate(_getOffsetLeft(index) * -1, 0)
      }
    } else {
      const indexToLoad = index + (SLIDES_TO_SCROLL * movement) - 1
      // check if the slide has been loaded before
      const indexLoaded = !!loadedIndex[indexToLoad]
      if (indexToLoad < totalSlides && indexToLoad >= 0 && !indexLoaded) {
        // insert in the correct position
        slideContainerDOMEl.appendChild(slides[indexToLoad])
        loadedIndex[indexToLoad] = 1
      }
    }

    // Checking wheter to paint or hide the arrows.
    _setTailArrowClasses()

    options.doAfterSlide({ currentSlide: index })
  }

  function _removeTouchEventsListeners (all = false) {
    frameDOMEl.removeEventListener('touchmove', onTouchmove)
    frameDOMEl.removeEventListener('touchend', onTouchend)
    frameDOMEl.removeEventListener('touchcancel', onTouchend)
    if (all === true) {
      frameDOMEl.removeEventListener('touchstart', onTouchstart)
    }
  }

  function _removeAllEventsListeners () {
    _removeTouchEventsListeners(true)
    slideContainerDOMEl.removeEventListener(prefixes.transitionEnd, onTransitionEnd)
    windowDOM.removeEventListener('resize', onResize)
  }

  function onTransitionEnd () {
    if (transitionEndCallback) {
      transitionEndCallback()
      transitionEndCallback = undefined
    }
  }

  function onTouchstart (event) {
    const { pageX, pageY } = event.targetTouches ? event.targetTouches[0] : event
    touchOffset = currentTouchOffset = { pageX, pageY }
    frameDOMEl.addEventListener('touchmove', onTouchmove)
    frameDOMEl.addEventListener('touchend', onTouchend)
    frameDOMEl.addEventListener('touchcancel', onTouchend)
  }

  function onTouchmove (event) {
    const { pageX, pageY } = event.targetTouches ? event.targetTouches[0] : event

    delta = {
      x: pageX - touchOffset.pageX,
      y: pageY - touchOffset.pageY
    }

    const deltaNow = {
      x: pageX - currentTouchOffset.pageX,
      y: pageY - currentTouchOffset.pageY
    }

    currentTouchOffset = { pageX, pageY }

    const isScrollingNow = abs(deltaNow.x) < abs(deltaNow.y)
    isScrolling = !!(isScrolling || isScrollingNow)

    if (navigator.userAgent.indexOf('Android 4.3') >= 0 && !isScrollingNow) {
      event.preventDefault()
    }

    if (isScrolling === false && delta.x !== 0) {
      _translate(Math.round(position + delta.x), 0)
    } else if (isScrolling === true) {
      onTouchend(event)
    }
  }

  function onTouchend (event) {
    /**
     * is valid if:
     * -> swipe distance is greater than the specified valid swipe distance
     * -> swipe distance is more then a third of the swipe area
     * @isValidSlide {Boolean}
     */
    const absoluteX = abs(delta.x)
    const isValid = absoluteX > VALID_SWIPE_DISTANCE || absoluteX > frameWidth / 3

    /**
     * is out of bounds if:
     * -> index is 0 and delta x is greater than 0
     * -> index is the last slide and delta is smaller than 0
     * @isOutOfBounds {Boolean}
     */
    const direction = delta.x < 0
    const isOutOfBounds = (!index && !direction) ||
        (index === slides.length - 1 && direction)

    if (isValid === true && !isOutOfBounds) {
      slide(direction)
    } else {
      _translate(position, options.snapBackSpeed, LINEAR_ANIMATION)
    }

    delta = {}
    touchOffset = {}
    isScrolling = false

    _removeTouchEventsListeners()
  }

  function _convertItemToDOM (string) {
    const wrappedString = `<li class='${options.classNameItem}'>${string}</li>`
    const el = document.createElement('template')
    if (el.content) {
      el.innerHTML = wrappedString
      return el.content
    } else {
      const container = document.createElement('ul')
      const fragment = document.createDocumentFragment()
      container.innerHTML = wrappedString
      while (container.firstChild !== null) {
        fragment.appendChild(container.firstChild)
      }
      return fragment
    }
  }

  function onResize (event) {
    reset()
  }

  /**
   * public
   * setup function
   */
  function _setup () {
    const { infinite } = options
    const slidesArray = slice.call(items.map(i => _convertItemToDOM(i)))
    position = slideContainerDOMEl.offsetLeft

    slides = infinite ? _setupInfinite(slidesArray) : slice.call(slidesArray)

    _setTailArrowClasses()
    reset()

    slideContainerDOMEl.addEventListener(prefixes.transitionEnd, onTransitionEnd)
    frameDOMEl.addEventListener('touchstart', onTouchstart)
    windowDOM.addEventListener('resize', onResize)
  }

  /**
   * public
   * reset function: called on resize
   */
  function reset () {
    const {infinite, rewindOnResize} = options
    let { ease, rewindSpeed } = options

    frameWidth = _getWidthFromDOMEl(frameDOMEl)
    maxOffset = round((frameWidth * slides.length) - frameWidth)

    if (rewindOnResize === true) {
      index = 0
    } else {
      ease = null
      rewindSpeed = 0
    }

    const offsetIndex = infinite ? index + infinite : index
    const newX = _getOffsetLeft(offsetIndex) * -1
    if (infinite === true) {
      _translate(newX, 0)
    } else {
      _translate(newX, rewindSpeed, ease)
    }
    index = offsetIndex
    position = newX
  }

  /**
   * public
   * returnIndex function: called on clickhandler
   */
  function returnIndex () {
    return index - options.infinite || 0
  }

  /**
   * public
   * prev function: called on clickhandler
   */
  function prev () {
    slide(false)
  }

  /**
   * public
   * next function: called on clickhandler
   */
  function next () {
    slide(true)
  }

  /**
   * public
   * destroy function: called to gracefully destroy the slidy instance
   */
  function destroy () {
    const { infinite } = options
    _removeAllEventsListeners()
    // remove cloned slides if infinite is set
    if (infinite === true) {
      const {firstChild, lastChild} = slideContainerDOMEl
      Array.apply(null, Array(infinite)).forEach(function () {
        slideContainerDOMEl.removeChild(firstChild)
        slideContainerDOMEl.removeChild(lastChild)
      })
    }
  }

  // trigger initial setup
  _setup()

  // expose public api
  return {
    reset,
    slide,
    returnIndex,
    prev,
    next,
    destroy
  }
}
<commit_msg>Fix isomorphism<commit_after>import detectPrefixes from './detect-prefixes.js'

const { slice } = Array.prototype
const prefixes = detectPrefixes()

const LINEAR_ANIMATION = 'linear'
const SLIDES_TO_SCROLL = 1
const VALID_SWIPE_DISTANCE = 25
const {abs, min, max, round} = Math

export function slidy (slider, options) {
  const {
    ease,
    frameDOMEl,
    infinite,
    items,
    itemsPreloaded,
    rewind,
    rewindSpeed,
    slideSpeed,
    tailArrowClass
  } = options

  // if frameDOMEl is null, then we do nothing
  if (frameDOMEl === null) return
  // DOM elements
  const slideContainerDOMEl = frameDOMEl.getElementsByClassName(options.classNameSlideContainer)[0]

  const prevArrow = frameDOMEl.getElementsByClassName(options.classNamePrevCtrl)[0]
  const nextArrow = frameDOMEl.getElementsByClassName(options.classNameNextCtrl)[0]

  // initialize some variables
  let frameWidth = 0
  let index = 0
  let loadedIndex = []
  let maxOffset = 0
  let position = 0
  let slides = []
  let transitionEndCallback

  let itemsPreloadedCount = itemsPreloaded
  while (itemsPreloadedCount--) loadedIndex[itemsPreloadedCount] = 1

  // event handling
  let touchOffset = { pageX: 0, pageY: 0 }
  let currentTouchOffset = { pageX: 0, pageY: 0 }
  let delta = { x: 0, y: 0 }
  let isScrolling = false

  // clamp a number between two min and max values
  function _clampNumber (x, minValue, maxValue) {
    return min(max(x, minValue), maxValue)
  }

  // get the width from a DOM element
  function _getWidthFromDOMEl (el) {
    return el.getBoundingClientRect().width
  }

  // calculate the offset with the width of the frame and the desired position
  function _getOffsetLeft (slidePosition) {
    return frameWidth * slidePosition
  }

  /**
   * private
   * _setupInfinite: function to setup if infinite is set
   *
   * @param  {array} slideArray
   * @return {array} array of updated slideContainer elements
   */
  function _setupInfinite (slideArray) {
    const { infinite } = options

    const totalSlides = slideArray.length
    const front = slideArray.slice(0, infinite)
    const back = slideArray.slice(totalSlides - infinite, totalSlides)
    const { firstChild } = slideContainerDOMEl

    front.forEach(function (el) {
      const cloned = el.cloneNode(true)
      slideContainerDOMEl.appendChild(cloned)
    })

    back.reverse()
      .forEach(function (el) {
        const cloned = el.cloneNode(true)
        slideContainerDOMEl.insertBefore(cloned, firstChild)
      })

    return slice.call(slideContainerDOMEl.children)
  }

  /**
   * translates to a given position in a given time in milliseconds
   *
   * @to        {number} number in pixels where to translate to
   * @duration  {number} time in milliseconds for the transistion
   * @ease      {string} easing css property
   */
  function _translate (to, duration, ease) {
    const easeCssText = ease ? `${prefixes.transitionTiming}: ${ease};` : ''
    const durationCssText = duration ? `${prefixes.transitionDuration}: ${duration}ms;` : ''
    const cssText = `${easeCssText}${durationCssText}
      ${prefixes.transform}: ${prefixes.translate(to)};`

    slideContainerDOMEl.style.cssText = cssText
  }

  function _setTailArrowClasses () {
    if (infinite === true) { return }
    if (prevArrow !== null && typeof prevArrow.classList === 'object') {
      let action = index < 1 ? 'add' : 'remove'
      prevArrow.classList[action](tailArrowClass)
    }
    if (nextArrow !== null && typeof nextArrow.classList !== 'object') {
      let action = index > options.items.length - 2 ? 'add' : 'remove'
      nextArrow.classList[action](tailArrowClass)
    }
  }

  /**
   * slidefunction called by prev, next & touchend
   *
   * determine nextIndex and slide to next postion
   * under restrictions of the defined options
   *
   * @direction  {boolean} 'true' for right, 'false' for left
   */
  function slide (direction) {
    let duration = slideSpeed

    const movement = direction === true ? 1 : -1
    const totalSlides = slides.length

    // calculate the nextIndex according to the movement
    let nextIndex = index + (SLIDES_TO_SCROLL * movement)

    // nextIndex should be between 0 and totalSlides minus 1
    nextIndex = _clampNumber(nextIndex, 0, totalSlides - 1)

    if (infinite === true && direction === undefined) {
      nextIndex += infinite
    }

    let nextOffset = _clampNumber(_getOffsetLeft(nextIndex) * -1, maxOffset * -1, 0)

    if (rewind && direction && abs(position) === maxOffset) {
      nextOffset = 0
      nextIndex = 0
      duration = rewindSpeed
    }

    const needToSlide = nextIndex !== index
    if (!needToSlide) return

    // translate to the nextOffset by a defined duration and ease function
    _translate(nextOffset, duration, ease)

    // update the position with the next position
    position = nextOffset

    // if the nextIndex is possible according to totalSlides, then use it
    if (nextIndex <= totalSlides) {
      options.doBeforeSlide({ currentSlide: index, nextSlide: nextIndex })
      index = nextIndex
    }

    if (infinite && (nextIndex === totalSlides - infinite || nextIndex === 0)) {
      index = direction ? infinite : totalSlides - (infinite * 2)

      position = _getOffsetLeft(index) * -1

      transitionEndCallback = function () {
        _translate(_getOffsetLeft(index) * -1, 0)
      }
    } else {
      const indexToLoad = index + (SLIDES_TO_SCROLL * movement) - 1
      // check if the slide has been loaded before
      const indexLoaded = !!loadedIndex[indexToLoad]
      if (indexToLoad < totalSlides && indexToLoad >= 0 && !indexLoaded) {
        // insert in the correct position
        slideContainerDOMEl.appendChild(slides[indexToLoad])
        loadedIndex[indexToLoad] = 1
      }
    }

    // Checking wheter to paint or hide the arrows.
    _setTailArrowClasses()

    options.doAfterSlide({ currentSlide: index })
  }

  function _removeTouchEventsListeners (all = false) {
    frameDOMEl.removeEventListener('touchmove', onTouchmove)
    frameDOMEl.removeEventListener('touchend', onTouchend)
    frameDOMEl.removeEventListener('touchcancel', onTouchend)
    if (all === true) {
      frameDOMEl.removeEventListener('touchstart', onTouchstart)
    }
  }

  function _removeAllEventsListeners () {
    _removeTouchEventsListeners(true)
    slideContainerDOMEl.removeEventListener(prefixes.transitionEnd, onTransitionEnd)
    window.removeEventListener('resize', onResize)
  }

  function onTransitionEnd () {
    if (transitionEndCallback) {
      transitionEndCallback()
      transitionEndCallback = undefined
    }
  }

  function onTouchstart (event) {
    const { pageX, pageY } = event.targetTouches ? event.targetTouches[0] : event
    touchOffset = currentTouchOffset = { pageX, pageY }
    frameDOMEl.addEventListener('touchmove', onTouchmove)
    frameDOMEl.addEventListener('touchend', onTouchend)
    frameDOMEl.addEventListener('touchcancel', onTouchend)
  }

  function onTouchmove (event) {
    const { pageX, pageY } = event.targetTouches ? event.targetTouches[0] : event

    delta = {
      x: pageX - touchOffset.pageX,
      y: pageY - touchOffset.pageY
    }

    const deltaNow = {
      x: pageX - currentTouchOffset.pageX,
      y: pageY - currentTouchOffset.pageY
    }

    currentTouchOffset = { pageX, pageY }

    const isScrollingNow = abs(deltaNow.x) < abs(deltaNow.y)
    isScrolling = !!(isScrolling || isScrollingNow)

    if (navigator.userAgent.indexOf('Android 4.3') >= 0 && !isScrollingNow) {
      event.preventDefault()
    }

    if (isScrolling === false && delta.x !== 0) {
      _translate(Math.round(position + delta.x), 0)
    } else if (isScrolling === true) {
      onTouchend(event)
    }
  }

  function onTouchend (event) {
    /**
     * is valid if:
     * -> swipe distance is greater than the specified valid swipe distance
     * -> swipe distance is more then a third of the swipe area
     * @isValidSlide {Boolean}
     */
    const absoluteX = abs(delta.x)
    const isValid = absoluteX > VALID_SWIPE_DISTANCE || absoluteX > frameWidth / 3

    /**
     * is out of bounds if:
     * -> index is 0 and delta x is greater than 0
     * -> index is the last slide and delta is smaller than 0
     * @isOutOfBounds {Boolean}
     */
    const direction = delta.x < 0
    const isOutOfBounds = (!index && !direction) ||
        (index === slides.length - 1 && direction)

    if (isValid === true && !isOutOfBounds) {
      slide(direction)
    } else {
      _translate(position, options.snapBackSpeed, LINEAR_ANIMATION)
    }

    delta = {}
    touchOffset = {}
    isScrolling = false

    _removeTouchEventsListeners()
  }

  function _convertItemToDOM (string) {
    const wrappedString = `<li class='${options.classNameItem}'>${string}</li>`
    const el = document.createElement('template')
    if (el.content) {
      el.innerHTML = wrappedString
      return el.content
    } else {
      const container = document.createElement('ul')
      const fragment = document.createDocumentFragment()
      container.innerHTML = wrappedString
      while (container.firstChild !== null) {
        fragment.appendChild(container.firstChild)
      }
      return fragment
    }
  }

  function onResize (event) {
    reset()
  }

  /**
   * public
   * setup function
   */
  function _setup () {
    const { infinite } = options
    const slidesArray = slice.call(items.map(i => _convertItemToDOM(i)))
    position = slideContainerDOMEl.offsetLeft

    slides = infinite ? _setupInfinite(slidesArray) : slice.call(slidesArray)

    _setTailArrowClasses()
    reset()

    slideContainerDOMEl.addEventListener(prefixes.transitionEnd, onTransitionEnd)
    frameDOMEl.addEventListener('touchstart', onTouchstart)
    window.addEventListener('resize', onResize)
  }

  /**
   * public
   * reset function: called on resize
   */
  function reset () {
    const {infinite, rewindOnResize} = options
    let { ease, rewindSpeed } = options

    frameWidth = _getWidthFromDOMEl(frameDOMEl)
    maxOffset = round((frameWidth * slides.length) - frameWidth)

    if (rewindOnResize === true) {
      index = 0
    } else {
      ease = null
      rewindSpeed = 0
    }

    const offsetIndex = infinite ? index + infinite : index
    const newX = _getOffsetLeft(offsetIndex) * -1
    if (infinite === true) {
      _translate(newX, 0)
    } else {
      _translate(newX, rewindSpeed, ease)
    }
    index = offsetIndex
    position = newX
  }

  /**
   * public
   * returnIndex function: called on clickhandler
   */
  function returnIndex () {
    return index - options.infinite || 0
  }

  /**
   * public
   * prev function: called on clickhandler
   */
  function prev () {
    slide(false)
  }

  /**
   * public
   * next function: called on clickhandler
   */
  function next () {
    slide(true)
  }

  /**
   * public
   * destroy function: called to gracefully destroy the slidy instance
   */
  function destroy () {
    const { infinite } = options
    _removeAllEventsListeners()
    // remove cloned slides if infinite is set
    if (infinite === true) {
      const {firstChild, lastChild} = slideContainerDOMEl
      Array.apply(null, Array(infinite)).forEach(function () {
        slideContainerDOMEl.removeChild(firstChild)
        slideContainerDOMEl.removeChild(lastChild)
      })
    }
  }

  // trigger initial setup
  _setup()

  // expose public api
  return {
    reset,
    slide,
    returnIndex,
    prev,
    next,
    destroy
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 Mats & Myles
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
if (!String.prototype.format) {
    String.prototype.format = function () {
        var args = arguments;
        return this.replace(/{(\d+)}/g, function (match, number) {
            return typeof args[number] != 'undefined' ? args[number] : match;
        });
    };
}
String.prototype.formatArg = function (args) {
    return this.replace(/{(\d+)}/g, function (match, number) {
        return typeof args[number] != 'undefined' ? args[number] : match;
    });
};

// TODO MAKE THIS WHOLE PART BETTER, I COPIED THIS FROM THE PROTOTYPE.
var web = {
    isHidden: false,
    registerListeners: function () {
        $('#compare').click(function () {
            var compare = $("#compare");
            compare.html(" Compare versions"); // TODO FIND A BETTER WAY FOR THIS
            compare.prop("disabled", true);

            var oldV = $("#old").find('option:selected').val();
            var newV = $("#new").find('option:selected').val();

            dataHandler.requestCompare(oldV, newV);
        })
    },
    addInfo: function (oldVersion, newVersion) {
        var title = "INFORMATION";
        var template = "Minecraft Version: {0}
 \
        Version Type: {1}
 \
        Version release time: {2}
 \
        Protocol id: Unknown";

        this.addHtml
        (title,
            template.format(oldVersion.id, oldVersion.type, oldVersion.releaseTime, "pidOld"),
            title,
            template.format(newVersion.id, newVersion.type, newVersion.releaseTime, "pidNew"));
    },
    addPacket: function (oldVersion, newVersion) {
        var oldTitle, oldData, newTitle, newData;
        if (typeof oldVersion === 'undefined') {
            oldTitle = "NON-EXISTENT";
            oldData = "";
        } else {
            oldTitle = this.getPacketTitle(oldVersion);
            oldData = htmlParser.getInstructions(oldVersion.instructions, 0);
        }
        if (typeof newVersion === 'undefined') {
            newTitle = "REMOVED";
            newData = "";
        } else {
            newTitle = this.getPacketTitle(oldVersion);
            newData = htmlParser.getInstructions(oldVersion.instructions, 0);
        }

        this.addHtml(
            oldTitle,
            oldData,
            newTitle,
            newData
        );
    },
    getPacketTitle: function (packet) {
        console.log(packet);
        return "" + packet.state + ": 0x" + Number(packet.id).toString(16) + " (" + packet.class + ") - " + packet.direction
    },
    setProtocolId: function (oldId, newId) {
        $("#pidOld").html(oldId);
        $("#pidNew").html(newId);
    },
    addHtml: function (title, footer, newTitle, newFooter) {
        $("#data")
            .append("
"
                + "
"
                + "
"
                + "
" + title + "
"
                + "
" + footer + "
"
                + "
"
                + "
"
                + "
"
                + "
"
                + "
" + newTitle + "
"
                + "
" + newFooter + "
"
                + "
"
                + "
");
    },
    addMetadata: function (oldV, newV) {
        $("#data")
            .append("
"
                + "
"
                + "
"
                + "
Metadata
"
                + "

"
                + "
"
                + "
"
                + "
"
                + "
"
                + "
Metadata
"
                + "

"
                + "
"
                + "
");
        web.generateTree(oldV, newV);
    },

    generateTree: function (oldV, newV) {
        var oldTree = oldV;
        var newTree = newV;
        web.convertTree(oldTree);
        web.convertTree(newTree);
        console.info([newTree]);
        $('.oldMeta #tree').treeview({data: [JSON.parse(JSON.stringify(oldTree))], levels: 999}); // TODO GET IT WORKING WITHOUT THIS HACKY FIX
        $('.newMeta #tree').treeview({data: [JSON.parse(JSON.stringify(newTree))], levels: 999});
    },
    convertMeta: function (meta) {
        if (meta.type.charAt(0) == "[") {
            meta.text = "" + meta.index + ". " + meta.type.substring(6, meta.type.length - 3) + " Array";
        } else {
            meta.text = "" + meta.index + ". " + meta.type.substring(5, meta.type.length - 3);
        }

        meta.nodeId = meta.index;
        delete meta.index;
        delete meta.field;
        delete meta.function;
        delete meta.type;
        return meta;
    },
    convertTree: function (tree) {
        tree.nodes = [];
        for (var i in tree.metadata) {
            tree.nodes.push(web.convertMeta(tree.metadata[i]));
        }
        for (var i in tree.children) {
            tree.nodes.push(web.convertTree(tree.children[i]));
        }
        tree.nodeId = 10;
        delete tree.metadata;
        delete tree.children;
        if (tree.entityName == "") {
            tree.text = "Unknown (" + tree.className + ")"
        } else {
            tree.text = tree.entityName + " (" + tree.className + ")"
        }
        tree.icon = "fa fa-smile-o";
        tree.backColor = "#eff4ff";
        delete tree.entityName;
        delete tree.className;

        if (tree.nodes.length == 0)
            delete tree.nodes;
        return tree;
    }
};

var dataHandler = {
    requestCompare: function (oldV, newV) {
        $.ajax({
            dataType: "json",
            type: "GET",
            url: "./v1/compare",
            data: {"old": oldV, "new": newV},
            success: function (json) {
                moduleManager.execute(json);
            },
            error: function (err) {
                console.log(err);
            }
        });
    }
};

var moduleManager = {
    modules: {},
    on: function (name, func) {
        console.log("register module " + name);
        moduleManager.modules[name] = func;
    },

    execute: function (json) {
        // console.log(JSON.stringify(data));

        Object.keys(json.oldVersion).forEach(function (key, index) {
            console.log(key + " " + moduleManager.modules);
            if (!moduleManager.modules.hasOwnProperty(key))
                console.error("No module found " + key + " [" + JSON.stringify({}) + "]");
            else
                moduleManager.modules[key](json.oldVersion[key], json.newVersion[key]);
        });

        web.isHidden = true;
        $("#beginModal").modal("hide");
    }
};

function registerModules() {
    // info about the jar
    moduleManager.on("JarModule", function (oldV, newV) {
        console.log(oldV);
        console.log(newV);
        web.addInfo(oldV, newV);
    });

    moduleManager.on("BurgerModule", function (oldV, newV) {
        web.setProtocolId(oldV.protocol, newV.protocol);
        Object.keys(oldV.changedPackets).forEach(function (key, index) {
            web.addPacket(oldV.changedPackets[key], newV.changedPackets[key]);
        });
    })

    moduleManager.on("MetadataModule", function (oldV, newV) {
        web.addMetadata(oldV, newV)
    })
}
$(document).ready(function () {
    web.registerListeners();
    registerModules();

    $('#beginModal').modal('show').on('hide.bs.modal', function (e) {
        if (web.isHidden)
            return;
        e.preventDefault();
    });

    if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)) {
        $('.selectpicker').selectpicker('mobile');
    }
});
<commit_msg>Fix packet compare<commit_after>/*
 * Copyright (c) 2016 Mats & Myles
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
if (!String.prototype.format) {
    String.prototype.format = function () {
        var args = arguments;
        return this.replace(/{(\d+)}/g, function (match, number) {
            return typeof args[number] != 'undefined' ? args[number] : match;
        });
    };
}
String.prototype.formatArg = function (args) {
    return this.replace(/{(\d+)}/g, function (match, number) {
        return typeof args[number] != 'undefined' ? args[number] : match;
    });
};

// TODO MAKE THIS WHOLE PART BETTER, I COPIED THIS FROM THE PROTOTYPE.
var web = {
    isHidden: false,
    registerListeners: function () {
        $('#compare').click(function () {
            var compare = $("#compare");
            compare.html(" Compare versions"); // TODO FIND A BETTER WAY FOR THIS
            compare.prop("disabled", true);

            var oldV = $("#old").find('option:selected').val();
            var newV = $("#new").find('option:selected').val();

            dataHandler.requestCompare(oldV, newV);
        })
    },
    addInfo: function (oldVersion, newVersion) {
        var title = "INFORMATION";
        var template = "Minecraft Version: {0}
 \
        Version Type: {1}
 \
        Version release time: {2}
 \
        Protocol id: Unknown";

        this.addHtml
        (title,
            template.format(oldVersion.id, oldVersion.type, oldVersion.releaseTime, "pidOld"),
            title,
            template.format(newVersion.id, newVersion.type, newVersion.releaseTime, "pidNew"));
    },
    addPacket: function (oldVersion, newVersion) {
        var oldTitle, oldData, newTitle, newData;
        if (typeof oldVersion === 'undefined') {
            oldTitle = "NON-EXISTENT";
            oldData = "";
        } else {
            oldTitle = this.getPacketTitle(oldVersion);
            oldData = htmlParser.getInstructions(oldVersion.instructions, 0);
        }
        if (typeof newVersion === 'undefined') {
            newTitle = "REMOVED";
            newData = "";
        } else {
            newTitle = this.getPacketTitle(newVersion);
            newData = htmlParser.getInstructions(newVersion.instructions, 0);
        }

        this.addHtml(
            oldTitle,
            oldData,
            newTitle,
            newData
        );
    },
    getPacketTitle: function (packet) {
        console.log(packet);
        return "" + packet.state + ": 0x" + Number(packet.id).toString(16) + " (" + packet.class + ") - " + packet.direction
    },
    setProtocolId: function (oldId, newId) {
        $("#pidOld").html(oldId);
        $("#pidNew").html(newId);
    },
    addHtml: function (title, footer, newTitle, newFooter) {
        $("#data")
            .append("
"
                + "
"
                + "
"
                + "
" + title + "
"
                + "
" + footer + "
"
                + "
"
                + "
"
                + "
"
                + "
"
                + "
" + newTitle + "
"
                + "
" + newFooter + "
"
                + "
"
                + "
");
    },
    addMetadata: function (oldV, newV) {
        $("#data")
            .append("
"
                + "
"
                + "
"
                + "
Metadata
"
                + "

"
                + "
"
                + "
"
                + "
"
                + "
"
                + "
Metadata
"
                + "

"
                + "
"
                + "
");
        web.generateTree(oldV, newV);
    },

    generateTree: function (oldV, newV) {
        var oldTree = oldV;
        var newTree = newV;
        web.convertTree(oldTree);
        web.convertTree(newTree);
        console.info([newTree]);
        $('.oldMeta #tree').treeview({data: [JSON.parse(JSON.stringify(oldTree))], levels: 999}); // TODO GET IT WORKING WITHOUT THIS HACKY FIX
        $('.newMeta #tree').treeview({data: [JSON.parse(JSON.stringify(newTree))], levels: 999});
    },
    convertMeta: function (meta) {
        if (meta.type.charAt(0) == "[") {
            meta.text = "" + meta.index + ". " + meta.type.substring(6, meta.type.length - 3) + " Array";
        } else {
            meta.text = "" + meta.index + ". " + meta.type.substring(5, meta.type.length - 3);
        }

        meta.nodeId = meta.index;
        delete meta.index;
        delete meta.field;
        delete meta.function;
        delete meta.type;
        return meta;
    },
    convertTree: function (tree) {
        tree.nodes = [];
        for (var i in tree.metadata) {
            tree.nodes.push(web.convertMeta(tree.metadata[i]));
        }
        for (var i in tree.children) {
            tree.nodes.push(web.convertTree(tree.children[i]));
        }
        tree.nodeId = 10;
        delete tree.metadata;
        delete tree.children;
        if (tree.entityName == "") {
            tree.text = "Unknown (" + tree.className + ")"
        } else {
            tree.text = tree.entityName + " (" + tree.className + ")"
        }
        tree.icon = "fa fa-smile-o";
        tree.backColor = "#eff4ff";
        delete tree.entityName;
        delete tree.className;

        if (tree.nodes.length == 0)
            delete tree.nodes;
        return tree;
    }
};

var dataHandler = {
    requestCompare: function (oldV, newV) {
        $.ajax({
            dataType: "json",
            type: "GET",
            url: "./v1/compare",
            data: {"old": oldV, "new": newV},
            success: function (json) {
                moduleManager.execute(json);
            },
            error: function (err) {
                console.log(err);
            }
        });
    }
};

var moduleManager = {
    modules: {},
    on: function (name, func) {
        console.log("register module " + name);
        moduleManager.modules[name] = func;
    },

    execute: function (json) {
        // console.log(JSON.stringify(data));

        Object.keys(json.oldVersion).forEach(function (key, index) {
            console.log(key + " " + moduleManager.modules);
            if (!moduleManager.modules.hasOwnProperty(key))
                console.error("No module found " + key + " [" + JSON.stringify({}) + "]");
            else
                moduleManager.modules[key](json.oldVersion[key], json.newVersion[key]);
        });

        web.isHidden = true;
        $("#beginModal").modal("hide");
    }
};

function registerModules() {
    // info about the jar
    moduleManager.on("JarModule", function (oldV, newV) {
        console.log(oldV);
        console.log(newV);
        web.addInfo(oldV, newV);
    });

    moduleManager.on("BurgerModule", function (oldV, newV) {
        web.setProtocolId(oldV.protocol, newV.protocol);
        Object.keys(oldV.changedPackets).forEach(function (key, index) {
            web.addPacket(oldV.changedPackets[key], newV.changedPackets[key]);
        });
    })

    moduleManager.on("MetadataModule", function (oldV, newV) {
        web.addMetadata(oldV, newV)
    })
}
$(document).ready(function () {
    web.registerListeners();
    registerModules();

    $('#beginModal').modal('show').on('hide.bs.modal', function (e) {
        if (web.isHidden)
            return;
        e.preventDefault();
    });

    if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)) {
        $('.selectpicker').selectpicker('mobile');
    }
});
<|endoftext|>


<commit_before>// @flow

import React from 'react'
import '../sigma/layout.forceAtlas2'

type State = {
	running: boolean,
	timer?: number,
	drawEdges?: ?boolean
};

type Props = {
	worker: boolean,
	barnesHutOptimize?: boolean,
	barnesHutTheta?: number,
	adjustSizes?: boolean,
	iterationsPerRender?: number,
	linLogMode: boolean,
	outboundAttractionDistribution?: boolean,
	edgeWeightInfluence?: number,
	scalingRatio?: number,
	strongGravityMode?: boolean,
	slowDown?: number,
	gravity?: number,
	timeout?: number,
	sigma?: sigma
};

type DefaultProps = {
	worker: boolean,
	linLogMode: boolean
};

/**

ForceAtlas2 component, starts ForceAtlas2 sigma plugin once component is mounted.
It supposes that sigma graph is already in place, therefore component should not be
mounted while graph is unavailable. It can be used within Sigma component if graph is
preloaded, or within loader component, like NeoCypher.

It accepts all the parameters of ForceAtlas2 described on its github page:
@param {boolean} [worker=true]           Use a web worker to run calculations in separate thread
@param {boolean} barnesHutOptimize  Use the algorithm's Barnes-Hut to improve repulsion's scalability
									This is useful for large graph but harmful to small ones.
@param {number} barnesHutTheta
@param {boolean} adjustSizes
@param {number} iterationsPerRender
@param {boolean} [linLogMode=true]
@param {boolean} outboundAttractionDistribution
@param {number} edgeWeightInfluence
@param {number} scalingRatio
@param {boolean} strongGravityMode
@param {number} gravity
@param {number} slowDown
@param {number} timeout   how long algorythm should run. default=graph.nodes().length * 10

[see sigma plugin page for more details](https://github.com/jacomyal/sigma.js/tree/master/plugins/sigma.layout.forceAtlas2)

**/

class ForceAtlas2 extends React.Component {
	state: State;
	props: Props;
	static defaultProps: DefaultProps = {
		worker: true,
		linLogMode: true
	}

	constructor(props: Props) {
		super(props)
		this.state = {running:false}
	}

	componentDidMount() {
		this._refreshGraph()
	}

	componentDidUpdate(prevProps: Props, prevState: State) {
		let s = this.props.sigma
		if(prevState.running && !this.state.running && s) {
				s.stopForceAtlas2()
				s.settings({drawEdges:prevState.drawEdges===false ? false : true})
				s.refresh();
		}
	}

	componentWillUnmount() {
		if(this.props.sigma) this.props.sigma.killForceAtlas2()
		if(this.state.timer) clearTimeout(this.state.timer)
	}

  //TODO: Add composition of child components after timeout
	render() {
        if (!this.state.running) {
            return
{ embedProps(this.props.children, {sigma: this.props.sigma}) }
;
        }
        return null;
    }

	_refreshGraph() {
		let s = this.props.sigma
		if(!sigma || !s) return

		let drawEdges = s.settings("drawEdges")
		if(s.graph.edges().length > 1000)
				s.settings({drawEdges: false})

		s.startForceAtlas2(this._stripOptions(this.props));
		// TODO: convert running status to state
		let timer = setTimeout(() => {
					this.setState({running:false, timer:undefined})
				}, this.props.timeout || s.graph.nodes().length*8 );
		this.setState({running:true, timer, drawEdges})
	}

	//strip force atlas options from component props
  _stripOptions(props: Props): Props {
        return Object.assign({}, props, {
            sigma: undefined

        })
    }

}

export default ForceAtlas2;
<commit_msg>Fixed missing import<commit_after>// @flow

import React from 'react'
import '../sigma/layout.forceAtlas2'
import { embedProps } from './tools'

type State = {
	running: boolean,
	timer?: number,
	drawEdges?: ?boolean
};

type Props = {
	worker: boolean,
	barnesHutOptimize?: boolean,
	barnesHutTheta?: number,
	adjustSizes?: boolean,
	iterationsPerRender?: number,
	linLogMode: boolean,
	outboundAttractionDistribution?: boolean,
	edgeWeightInfluence?: number,
	scalingRatio?: number,
	strongGravityMode?: boolean,
	slowDown?: number,
	gravity?: number,
	timeout?: number,
	sigma?: sigma
};

type DefaultProps = {
	worker: boolean,
	linLogMode: boolean
};

/**

ForceAtlas2 component, starts ForceAtlas2 sigma plugin once component is mounted.
It supposes that sigma graph is already in place, therefore component should not be
mounted while graph is unavailable. It can be used within Sigma component if graph is
preloaded, or within loader component, like NeoCypher.

It accepts all the parameters of ForceAtlas2 described on its github page:
@param {boolean} [worker=true]           Use a web worker to run calculations in separate thread
@param {boolean} barnesHutOptimize  Use the algorithm's Barnes-Hut to improve repulsion's scalability
									This is useful for large graph but harmful to small ones.
@param {number} barnesHutTheta
@param {boolean} adjustSizes
@param {number} iterationsPerRender
@param {boolean} [linLogMode=true]
@param {boolean} outboundAttractionDistribution
@param {number} edgeWeightInfluence
@param {number} scalingRatio
@param {boolean} strongGravityMode
@param {number} gravity
@param {number} slowDown
@param {number} timeout   how long algorythm should run. default=graph.nodes().length * 10

[see sigma plugin page for more details](https://github.com/jacomyal/sigma.js/tree/master/plugins/sigma.layout.forceAtlas2)

**/

class ForceAtlas2 extends React.Component {
	state: State;
	props: Props;
	static defaultProps: DefaultProps = {
		worker: true,
		linLogMode: true
	}

	constructor(props: Props) {
		super(props)
		this.state = {running:false}
	}

	componentDidMount() {
		this._refreshGraph()
	}

	componentDidUpdate(prevProps: Props, prevState: State) {
		let s = this.props.sigma
		if(prevState.running && !this.state.running && s) {
				s.stopForceAtlas2()
				s.settings({drawEdges:prevState.drawEdges===false ? false : true})
				s.refresh();
		}
	}

	componentWillUnmount() {
		if(this.props.sigma) this.props.sigma.killForceAtlas2()
		if(this.state.timer) clearTimeout(this.state.timer)
	}

  //TODO: Add composition of child components after timeout
	render() {
        if (!this.state.running) {
            return
{ embedProps(this.props.children, {sigma: this.props.sigma}) }
;
        }
        return null;
    }

	_refreshGraph() {
		let s = this.props.sigma
		if(!sigma || !s) return

		let drawEdges = s.settings("drawEdges")
		if(s.graph.edges().length > 1000)
				s.settings({drawEdges: false})

		s.startForceAtlas2(this._stripOptions(this.props));
		// TODO: convert running status to state
		let timer = setTimeout(() => {
					this.setState({running:false, timer:undefined})
				}, this.props.timeout || s.graph.nodes().length*8 );
		this.setState({running:true, timer, drawEdges})
	}

	//strip force atlas options from component props
  _stripOptions(props: Props): Props {
        return Object.assign({}, props, {
            sigma: undefined

        })
    }

}

export default ForceAtlas2;
<|endoftext|>