<commit_before>'use strict';

if (angular && typeof(angular.module) === 'function') {
    angular.module('sync.storage', [])
        .factory('SyncStorageService', ['$timeout', function($timeout) {
            SyncStorage.angularTimeouts.push({
                timeout: $timeout,
                promise: null,
                trigger: function() {
                    if (this.promise) {
                        this.timeout.cancel(this.promise);
                        this.promise = null;
                    }

                    this.promise = this.timeout(function() {}, 25);
                }
            });

            return {};
        }]);
}

function Observable() {
    if (typeof(Object.defineProperty) === 'function') {
        Object.defineProperty(this, '_key', { value: null, writable: true });
    }
    else {
        this._key = null;
    }

    this.init = function(key, observer) {
        this._key = key;
        var object = this;
        setTimeout(function() {
            delete object.init;
            Object.observe(object, observer);
        }, 0);

        return this;
    };

    return this;
}

(function() {
    var CACHE_PREFIX = 'SyncStorage.Cache.';

    function transmogrify(object, newClass) {
        newClass.call(object);
        object.__proto__ = newClass.prototype;
        return object;
    }

    Observable.mutate = function(target, key, observer) {
        transmogrify(target, Observable);
        target.init(key, observer);
        return target;
    };

    function List() {
        if (typeof(Object.defineProperty) === 'function') {
            Object.defineProperty(this, 'count', {
                get: function() {
                    return Object.keys(this).length;
                }
            });
        }
    }

    var loadScript = function(url, callback) {
        var script = document.createElement('script');
        script.src = url;
        script.onload = callback;
        document.getElementsByTagName('head')[0].appendChild(script);
    };

    var instances = {};

    var loadFromLocalStorage = function(key, defaultValue) {
        if (typeof(localStorage[key]) === 'undefined')
            return defaultValue;

        return JSON.parse(localStorage[key]);
    };

    var instance = function(url, options) {
        var self = this;

        self.url = url;

        // TODO: need to add code to save to local storage after update
        // TODO: deep observation
        var objects = {};

        var scheduleCacheUpdate = function(key) {
            var type = key.split(':')[0];

            var pending = scheduleCacheUpdate.pending;
            if (!pending)
                pending = scheduleCacheUpdate.pending = {};

            if (pending[type]) {
                clearTimeout(pending[type]);
                delete pending[type];
            }

            pending[type] = setTimeout(function() {
                delete pending[type];

                var string;

                if (objects[type] instanceof List)
                    string = new SyncStorage.SyncStorageCsv().stringify(objects[type]);
                else
                    string = JSON.stringify(objects[type]);

                if (string !== null && string.length > 0 && string !== 'null')
                    localStorage[CACHE_PREFIX + type] = string;
                else
                    delete localStorage[CACHE_PREFIX + type];
            }, 1000);<commit_msg>lots of bug fixes and cleanup<commit_after>'use strict';

if (angular && typeof(angular.module) === 'function') {
    angular.module('sync.storage', [])
        .factory('SyncStorageService', ['$timeout', function($timeout) {
            SyncStorage.angularTimeouts.push({
                timeout: $timeout,
                promise: null,
                trigger: function() {
                    if (this.promise) {
                        this.timeout.cancel(this.promise);
                        this.promise = null;
                    }

                    this.promise = this.timeout(function() {}, 25);
                }
            });

            return {};
        }]);
}

function Observable() {
    if (typeof(Object.defineProperty) === 'function') {
        Object.defineProperty(this, '_key', { value: null, writable: true });
    }
    else {
        this._key = null;
    }

    this.init = function(key, observer) {
        this._key = key;
        var object = this;
        setTimeout(function() {
            delete object.init;
            Object.observe(object, observer);
        }, 0);

        return this;
    };

    return this;
}

(function() {
    var CACHE_PREFIX = 'SyncStorage.Cache.';

    function transmogrify(object, newClass) {
        newClass.call(object);
        object.__proto__ = newClass.prototype;
        return object;
    }

    Observable.mutate = function(target, key, observer) {
        transmogrify(target, Observable);
        target.init(key, observer);
        return target;
    };

    function List() {
        if (typeof(Object.defineProperty) === 'function') {
            Object.defineProperty(this, 'count', {
                get: function() {
                    return Object.keys(this).length;
                }
            });
        }
    }

    var loadScript = function(url, callback) {
        var script = document.createElement('script');
        script.src = url;
        script.onload = callback;
        document.getElementsByTagName('head')[0].appendChild(script);
    };

    var instances = {};

    var loadFromLocalStorage = function(key, defaultValue) {
        if (typeof(localStorage[key]) === 'undefined')
            return defaultValue;

        return JSON.parse(localStorage[key]);
    };

    var instance = function(url, options) {
        var self = this;

        self.url = url;

        // TODO: deep observation
        var objects = {};

        var scheduleCacheUpdate = function(key) {
            var type = key.split(':')[0];

            var pending = scheduleCacheUpdate.pending;
            if (!pending)
                pending = scheduleCacheUpdate.pending = {};

            if (pending[type]) {
                clearTimeout(pending[type]);
                delete pending[type];
            }

            pending[type] = setTimeout(function() {
                delete pending[type];

                var string;

                if (objects[type] instanceof List)
                    string = new SyncStorage.SyncStorageCsv().stringify(objects[type]);
                else
                    string = JSON.stringify(objects[type]);

                if (string !== null && string.length > 0 && string !== 'null')
                    localStorage[CACHE_PREFIX + type] = string;
                else
                    delete localStorage[CACHE_PREFIX + type];
            }, 1000);
{'fraction_non_alphanumeric': 0.12112799532437171, 'fraction_numerical': 0.0029222676797194622, 'mean_word_length': 1.6500193573364306, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4968472', 'n_tokens_mistral': 1798, 'n_tokens_neox': 1692, 'n_words': 374}