<commit_before>/* globals angular */
angular.module('gc.fastRepeat', []).directive('fastRepeat', ['$compile', '$parse', function ($compile, $parse) {
    'use strict';

    var fastRepeatId = 0,
        showProfilingInfo = false;

    // JSON.stringify replacer function which removes any keys that start with $$.
    // This prevents unnecessary updates when we watch a JSON stringified value.
    function JSONStripper(key, value) {
        if(key.slice && key.slice(0,2) == '$$') { return undefined; }
        return value;
    }

    function getTime() { // For profiling
        if(window.performance && window.performance.now) { return window.performance.now(); }
        else { return (new Date()).getTime(); }
    }

    return {
        restrict: 'A',
        scope: false,
        transclude: 'element',
        priority: 10000,
        compile: function(tElement, tAttrs) {
            return function link(listScope, element, attrs, ctrl, transclude) {
                var repeatParts = attrs.fastRepeat.split(' in ');
                var repeatListName = repeatParts[1], repeatVarName = repeatParts[0];
                var currentRowEls = {};
                var t;

                // Scope created for evaluating the rows -- child of the scope the list was created in.
                // Note this is almost a misnomer since the list doesn't have a scope of its own.
                var rowScope = listScope.$new(false);

                // Transclude the contents of the fast repeat.
                // This function is called for every row. It reuses the rowTpl and scope for each row.
                transclude(rowScope, function(rowTpl, scope) {
                    function render(item) {
                        scope[repeatVarName] = item;
                        scope.$digest();
                        rowTpl.attr('fast-repeat-id', item.$$fastRepeatId);
                        return rowTpl.clone();
                    }

                    var getter = $parse(repeatListName); // getter(scope) should be the value of the list.

                    // Here is the main watch. Testing has shown that watching the stringified list can
                    // save roughly 500ms per digest in certain cases.
                    // JSONStripper is used to remove the $$fastRepeatId that we attach to the objects.
                    listScope.$watch(function(scp){ return JSON.stringify(getter(scp), JSONStripper); }, function(list) {
                        list = getter(listScope);

                        if (showProfilingInfo) {
                            t = getTime();
                        }

                        // Rendering is done in a postDigest so that we are outside of the main digest cycle.
                        // This allows us to digest the individual row scope repeatedly without major hackery.
                        listScope.$$postDigest(function() {

                            // Generate ids if necessary and arrange in a hash map
                            var listByIds = {};
                            angular.forEach(list, function(item) {
                                if(!item.$$fastRepeatId) {
                                    item.$$fastRepeatId = ++fastRepeatId;
                                }
                                listByIds[item.$$fastRepeatId] = item;
                            });

                            // Delete removed rows
                            angular.forEach(currentRowEls, function(row, id) {
                                if(!listByIds[id]) {
                                    row.el.detach();
                                }<commit_msg>Fix undefined exceptions.<commit_after>/* globals angular */
angular.module('gc.fastRepeat', []).directive('fastRepeat', ['$compile', '$parse', function ($compile, $parse) {
    'use strict';

    var fastRepeatId = 0,
        showProfilingInfo = false;

    // JSON.stringify replacer function which removes any keys that start with $$.
    // This prevents unnecessary updates when we watch a JSON stringified value.
    function JSONStripper(key, value) {
        if(key.slice && key.slice(0,2) == '$$') { return undefined; }
        return value;
    }

    function getTime() { // For profiling
        if(window.performance && window.performance.now) { return window.performance.now(); }
        else { return (new Date()).getTime(); }
    }

    return {
        restrict: 'A',
        scope: false,
        transclude: 'element',
        priority: 10000,
        compile: function(tElement, tAttrs) {
            return function link(listScope, element, attrs, ctrl, transclude) {
                var repeatParts = attrs.fastRepeat.split(' in ');
                var repeatListName = repeatParts[1], repeatVarName = repeatParts[0];
                var getter = $parse(repeatListName); // getter(scope) should be the value of the list.
                var currentRowEls = {};
                var t;

                // Scope created for evaluating the rows -- child of the scope the list was created in.
                // Note this is almost a misnomer since the list doesn't have a scope of its own.
                var rowScope = listScope.$new(false);
                rowScope[repeatVarName] = getter(listScope)[0] || {}; // The rowTpl will be digested once -- want to make sure it has valid data for the first wasted digest.  Default to first row or {} if no rows

                // Transclude the contents of the fast repeat.
                // This function is called for every row. It reuses the rowTpl and scope for each row.
                transclude(rowScope, function(rowTpl, scope) {
                    function render(item) {
                        scope[repeatVarName] = item;
                        scope.$digest();
                        rowTpl.attr('fast-repeat-id', item.$$fastRepeatId);
                        return rowTpl.clone();
                    }


                    // Here is the main watch. Testing has shown that watching the stringified list can
                    // save roughly 500ms per digest in certain cases.
                    // JSONStripper is used to remove the $$fastRepeatId that we attach to the objects.
                    listScope.$watch(function(scp){ return JSON.stringify(getter(scp), JSONStripper); }, function(list) {
                        list = getter(listScope);

                        if (showProfilingInfo) {
                            t = getTime();
                        }

                        // Rendering is done in a postDigest so that we are outside of the main digest cycle.
                        // This allows us to digest the individual row scope repeatedly without major hackery.
                        listScope.$$postDigest(function() {

                            // Generate ids if necessary and arrange in a hash map
                            var listByIds = {};
                            angular.forEach(list, function(item) {
                                if(!item.$$fastRepeatId) {
                                    item.$$fastRepeatId = ++fastRepeatId;
                                }
                                listByIds[item.$$fastRepeatId] = item;
                            });

                            // Delete removed rows
                            angular.forEach(currentRowEls, function(row, id) {
                                if(!listByIds[id]) {
                                    row.el.detach();
                                }
{'fraction_non_alphanumeric': 0.09041495426222988, 'fraction_numerical': 0.0035794776614079277, 'mean_word_length': 1.4249437479909997, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '610107', 'n_tokens_mistral': 1857, 'n_tokens_neox': 1721, 'n_words': 579}