<NME> LRUCache.js
<BEF> define([
           'dojo/_base/declare',
           'dojo/_base/array',
           'JBrowse/Util',
           'JBrowse/Digest/Crc32'
       ],
       function( declare, array, Util, digest ) {

return declare( null,

/**
 * @lends JBrowse.Store.LRUCache
 */
{

    /**
     * An LRU cache.
     *
     * @param args.fillCallback
     * @param args.maxSize
     * @param args.sizeFunction
     * @param args.keyFunction
     * @param args.name
        this.fill = args.fillCallback;
        this.maxSize = args.maxSize || 1000000;

        this._size = args.sizeFunction || this._size;
        this._keyString = args.keyFunction || this._keyString;


        this.name = args.name || 'LRUcache';

        this._size = args.sizeFunction || this._size;
        this._keyString = args.keyFunction || this._keyString;

        this.itemCount = 0;
        this.size = 0;

        this._cacheByKey = {};

        // each end of a doubly-linked list, sorted in usage order
    },

    _log: function() {
        // if( typeof arguments[0] == 'string' )
        //     while( arguments[0].length < 15 )
        //         arguments[0] += ' ';

    get: function( inKey, callback ) {
        var keyString = this._keyString( inKey );
        var record = this._cacheByKey[ keyString ];

        if( !record ) {
            this._log( 'miss', keyString );

        if( !record ) {
            // call our fill callback if necessary

            this._log( 'cache miss', key );

            this._attemptFill( inKey, key, callback );

            return;
        }

        this._log( 'cache hit', key, record.value );

        // take it out of the linked list
        if( record.prev )
        var results = [];
        var cache = this._cacheByKey;
        for( var k in cache ) {
            if( keyRegex.test( k ) && cache.hasOwnProperty(k) )
                results.push( cache[k] );
        }
        return results;
    },

    forEach: function( func, context ) {
        if( ! context ) context = this;
        var i = 0;
        for( var record = this._cacheNewest; record; record = record.next ) {
            func.call( context, record, i++ );
        }
    },
    some: function( func, context ) {
        if( ! context ) context = this;
        var i = 0;
        for( var record = this._cacheNewest; record; record = record.next ) {
            if( func.call( context, record, i++ ) )
                    delete this._inProgressFills[ key ];

                    if( value ) {
                        this._log( 'cache fill', key );
                        this.set( inKey, value );
                    }
                    array.forEach( fillRecord.callbacks, function( cb ) {
    },

    touchRecord: function( record ) {
        if( ! record )
            }
        }
        else {
            this._log( "cache can't fill", key );
            callback( undefined );
        }
    },
        this._llRemove( record );

        // add it back into the list as newest
        this._llPush( record );
    },

    // take a record out of the LRU linked list
    _llRemove: function( record ) {
        if( record.prev )
            record.prev.next = record.next;
            key: key,
            size: this._size( value )
        };
        this._log( 'cache set', key, record, this.size );

        // evict items if necessary
        this._prune( record.size );
            this._cacheOldest = record.next;

        record.prev = null;
        record.next = null;
    },

    _llPush: function( record ) {
        if( this._cacheNewest ) {
            this._cacheNewest.next = record;
            record.prev = this._cacheNewest;
        }
        this._cacheNewest = record;
        if( ! this._cacheOldest )
            this._cacheOldest = record;
    },

    _attemptFill: function( inKey, keyString, callback ) {
        if( this.fill ) {

            var fillRecord = this._inProgressFills[ keyString ] =
                this._inProgressFills[ keyString ] || { callbacks: [], running: false };

            fillRecord.callbacks.push( callback );

            if( ! fillRecord.running ) {
                fillRecord.running = true;
                this.fill( inKey, dojo.hitch( this, function( keyString, inKey, fillRecord, value, error, hints ) {
                    delete this._inProgressFills[ keyString ];
                    fillRecord.running = false;

                    if( value && ! ( hints && hints.nocache ) ) {
                        this._log( 'fill', keyString );
                        this.set( inKey, value );
                    }
                    array.forEach( fillRecord.callbacks, function( cb ) {
                                       try {
                                           cb.call( this, value, error );
                                       } catch(x) {
                                           console.error(''+x, x.stack, x);
                                       }
                                   }, this );
                }, keyString, inKey, fillRecord ));
            }
        }
        else {
            try {
                callback( undefined );
            } catch(x) {
                console.error(x);
            }
        }
    },

    set: function( inKey, value ) {
        var keyString = this._keyString( inKey );
        if( this._cacheByKey[keyString] ) {
            return;
        }
        while( this.size + (newItemSize||0) > this.maxSize ) {
            var oldest = this._cacheOldest;
            if( oldest ) {
                this._log( 'cache evict', oldest );

                // update the oldest and newest pointers
                if( ! oldest.next ) // if this was also the newest
            console.error(e)
            size = 1
        }
        var record = {
            value: value,
            key: inKey,
            keyString: keyString,
            size: size
        };

        if( record.size > this.maxSize ) {
            this._warn( 'Cache cannot fit', keyString, '('+Util.addCommas(record.size) + ' > ' + Util.addCommas(this.maxSize)+')' );
            return;
        }

        this._log( 'set', keyString, record, this.size );

        // evict items if necessary
                this.itemCount--;
                this.size -= oldest.size;
            } else {
                // should not be reached
                console.error("cache error!");
                return;
            }
        }
        // update our total size and item count
        this.size += record.size;
        this.itemCount++;

        return;
    },

    _keyString: function( inKey ) {
        var type = typeof inKey;
        if( type == 'object' && typeof inKey.toUniqueString == 'function' ) {
            return inKey.toUniqueString();
        }
        else {
            return digest.objectFingerprint( inKey );
        }
    },

    _size: function( value ) {
        var type = typeof value;
        var sum = 0;
        if( type == 'object' && type !== null ) {
            var sizeType = typeof value.size;
            if( sizeType == 'number' ) {
                return sizeType;
            }
            else if( sizeType == 'function' ) {
                return value.size();
            }
            else if( value.byteLength ) {
                return value.byteLength;
            } else {
                for( var k in value ) {
                    if( value.hasOwnProperty( k ) ) {
                        sum += this._size( value[k] );
                    }
                }
            }
            return sum;
        } else if( type == 'string' ) {
            return value.length;
        } else {
            return 1;
        }
    },

    _prune: function( newItemSize ) {
        while( this.size + (newItemSize||0) > this.maxSize ) {
            var oldest = this._cacheOldest;
            if( oldest ) {
                this._log( 'evict', oldest );

                // // update the oldest and newest pointers
                // if( ! oldest.next ) // if this was also the newest
                //     this._cacheNewest = oldest.prev; // probably undef
                // this._cacheOldest = oldest.next; // maybe undef

                // take it out of the linked list
                this._llRemove( oldest );

                // delete it from the byKey structure
                delete this._cacheByKey[ oldest.keyString ];

                // remove its linked-list links in case that makes it
                // easier for the GC
                delete oldest.next;
                delete oldest.prev;

                // update our size and item counts
                this.itemCount--;
                this.size -= oldest.size;
            } else {
                // should usually not be reached
                this._error( "eviction error", this.size, newItemSize, this );
                return;
            }
        }
    },

    _log: function() {
        if( this.verbose )
            console.log.apply( console, arguments );
    },
    _warn: function() {
        console.warn.apply( console, arguments );
    },
    _error: function() {
        console.error.apply( console, arguments );
    }

});
});

<MSG> refinements to LRUCache logging, can now give caches names

<DFF> @@ -24,6 +24,8 @@ return declare( null,
         this.fill = args.fillCallback;
         this.maxSize = args.maxSize || 1000000;
 
+        this.name = args.name || 'cache';
+
         this._size = args.sizeFunction || this._size;
         this._keyString = args.keyFunction || this._keyString;
 
@@ -42,6 +44,7 @@ return declare( null,
     },
 
     _log: function() {
+        // arguments[0] = this.name+' '+arguments[0];
         // if( typeof arguments[0] == 'string' )
         //     while( arguments[0].length < 15 )
         //         arguments[0] += ' ';
@@ -56,14 +59,14 @@ return declare( null,
         if( !record ) {
             // call our fill callback if necessary
 
-            this._log( 'cache miss', key );
+            this._log( 'miss', key );
 
             this._attemptFill( inKey, key, callback );
 
             return;
         }
 
-        this._log( 'cache hit', key, record.value );
+        this._log( 'hit', key, record.value );
 
         // take it out of the linked list
         if( record.prev )
@@ -91,7 +94,7 @@ return declare( null,
                     delete this._inProgressFills[ key ];
 
                     if( value ) {
-                        this._log( 'cache fill', key );
+                        this._log( 'fill', key );
                         this.set( inKey, value );
                     }
                     array.forEach( fillRecord.callbacks, function( cb ) {
@@ -102,7 +105,7 @@ return declare( null,
             }
         }
         else {
-            this._log( "cache can't fill", key );
+            this._log( "can't fill", key );
             callback( undefined );
         }
     },
@@ -119,7 +122,7 @@ return declare( null,
             key: key,
             size: this._size( value )
         };
-        this._log( 'cache set', key, record, this.size );
+        this._log( 'set', key, record, this.size );
 
         // evict items if necessary
         this._prune( record.size );
@@ -184,7 +187,7 @@ return declare( null,
         while( this.size + (newItemSize||0) > this.maxSize ) {
             var oldest = this._cacheOldest;
             if( oldest ) {
-                this._log( 'cache evict', oldest );
+                this._log( 'evict', oldest );
 
                 // update the oldest and newest pointers
                 if( ! oldest.next ) // if this was also the newest
@@ -209,8 +212,8 @@ return declare( null,
                 this.itemCount--;
                 this.size -= oldest.size;
             } else {
-                // should not be reached
-                console.error("cache error!");
+                // should usually not be reached
+                console.error( this.name + " eviction error", this.size, newItemSize );
                 return;
             }
         }

{'commit': 'diff --git a/src/JBrowse/Model/XHRBlob.js b/src/JBrowse/Model/XHRBlob.js\nindex d42996a59..8ebac50ec 100644\n--- a/src/JBrowse/Model/XHRBlob.js\n+++ b/src/JBrowse/Model/XHRBlob.js\n@@ -36,6 +36,7 @@ var XHRBlob = declare( FileBlob,\n \n         this.cache = opts.cache\n             || new LRUCache({\n+                                name: \'XHRBlob cache\',\n                                 fillCallback: dojo.hitch(this, \'_fetch\')\n                             });\n         this.opts.cache = this.cache;\ndiff --git a/src/JBrowse/Store/LRUCache.js b/src/JBrowse/Store/LRUCache.js\nindex 48e468a15..cd54b8982 100644\n--- a/src/JBrowse/Store/LRUCache.js\n+++ b/src/JBrowse/Store/LRUCache.js\n@@ -24,6 +24,8 @@ return declare( null,\n         this.fill = args.fillCallback;\n         this.maxSize = args.maxSize || 1000000;\n \n+        this.name = args.name || \'cache\';\n+\n         this._size = args.sizeFunction || this._size;\n         this._keyString = args.keyFunction || this._keyString;\n \n@@ -42,6 +44,7 @@ return declare( null,\n     },\n \n     _log: function() {\n+        // arguments[0] = this.name+\' \'+arguments[0];\n         // if( typeof arguments[0] == \'string\' )\n         //     while( arguments[0].length < 15 )\n         //         arguments[0] += \' \';\n@@ -56,14 +59,14 @@ return declare( null,\n         if( !record ) {\n             // call our fill callback if necessary\n \n-            this._log( \'cache miss\', key );\n+            this._log( \'miss\', key );\n \n             this._attemptFill( inKey, key, callback );\n \n             return;\n         }\n \n-        this._log( \'cache hit\', key, record.value );\n+        this._log( \'hit\', key, record.value );\n \n         // take it out of the linked list\n         if( record.prev )\n@@ -91,7 +94,7 @@ return declare( null,\n                     delete this._inProgressFills[ key ];\n \n                     if( value ) {\n-                        this._log( \'cache fill\', key );\n+                        this._log( \'fill\', key );\n                         this.set( inKey, value );\n                     }\n                     array.forEach( fillRecord.callbacks, function( cb ) {\n@@ -102,7 +105,7 @@ return declare( null,\n             }\n         }\n         else {\n-            this._log( "cache can\'t fill", key );\n+            this._log( "can\'t fill", key );\n             callback( undefined );\n         }\n     },\n@@ -119,7 +122,7 @@ return declare( null,\n             key: key,\n             size: this._size( value )\n         };\n-        this._log( \'cache set\', key, record, this.size );\n+        this._log( \'set\', key, record, this.size );\n \n         // evict items if necessary\n         this._prune( record.size );\n@@ -184,7 +187,7 @@ return declare( null,\n         while( this.size + (newItemSize||0) > this.maxSize ) {\n             var oldest = this._cacheOldest;\n             if( oldest ) {\n-                this._log( \'cache evict\', oldest );\n+                this._log( \'evict\', oldest );\n \n                 // update the oldest and newest pointers\n                 if( ! oldest.next ) // if this was also the newest\n@@ -209,8 +212,8 @@ return declare( null,\n                 this.itemCount--;\n                 this.size -= oldest.size;\n             } else {\n-                // should not be reached\n-                console.error("cache error!");\n+                // should usually not be reached\n+                console.error( this.name + " eviction error", this.size, newItemSize );\n                 return;\n             }\n         }\n', 'commit_subject': 'refinements to LRUCache logging, can now give caches names', 'repo_name': 'GMOD/jbrowse', 'committer': {'name': 'Robert Buels', 'email': '08f6e0b425f11eb922c9a362cbdf67a59bd5bd95@gmail.com', 'time_sec': '1349974008', 'tz_offset': '-240', 'date': {'seconds': '1349974008'}}, 'commit_tree': '056075661a9dbbbc08aa95d02e503139b10ed27f', 'commit_message': 'refinements to LRUCache logging, can now give caches names\n', 'language_name': 'HTML', 'license': 'lgpl-2.1', 'hunks': '@@ -24,6 +24,8 @@ return declare( null,\n         this.fill = args.fillCallback;\n         this.maxSize = args.maxSize || 1000000;\n \n+        this.name = args.name || \'cache\';\n+\n         this._size = args.sizeFunction || this._size;\n         this._keyString = args.keyFunction || this._keyString;\n \n@@ -42,6 +44,7 @@ return declare( null,\n     },\n \n     _log: function() {\n+        // arguments[0] = this.name+\' \'+arguments[0];\n         // if( typeof arguments[0] == \'string\' )\n         //     while( arguments[0].length < 15 )\n         //         arguments[0] += \' \';\n@@ -56,14 +59,14 @@ return declare( null,\n         if( !record ) {\n             // call our fill callback if necessary\n \n-            this._log( \'cache miss\', key );\n+            this._log( \'miss\', key );\n \n             this._attemptFill( inKey, key, callback );\n \n             return;\n         }\n \n-        this._log( \'cache hit\', key, record.value );\n+        this._log( \'hit\', key, record.value );\n \n         // take it out of the linked list\n         if( record.prev )\n@@ -91,7 +94,7 @@ return declare( null,\n                     delete this._inProgressFills[ key ];\n \n                     if( value ) {\n-                        this._log( \'cache fill\', key );\n+                        this._log( \'fill\', key );\n                         this.set( inKey, value );\n                     }\n                     array.forEach( fillRecord.callbacks, function( cb ) {\n@@ -102,7 +105,7 @@ return declare( null,\n             }\n         }\n         else {\n-            this._log( "cache can\'t fill", key );\n+            this._log( "can\'t fill", key );\n             callback( undefined );\n         }\n     },\n@@ -119,7 +122,7 @@ return declare( null,\n             key: key,\n             size: this._size( value )\n         };\n-        this._log( \'cache set\', key, record, this.size );\n+        this._log( \'set\', key, record, this.size );\n \n         // evict items if necessary\n         this._prune( record.size );\n@@ -184,7 +187,7 @@ return declare( null,\n         while( this.size + (newItemSize||0) > this.maxSize ) {\n             var oldest = this._cacheOldest;\n             if( oldest ) {\n-                this._log( \'cache evict\', oldest );\n+                this._log( \'evict\', oldest );\n \n                 // update the oldest and newest pointers\n                 if( ! oldest.next ) // if this was also the newest\n@@ -209,8 +212,8 @@ return declare( null,\n                 this.itemCount--;\n                 this.size -= oldest.size;\n             } else {\n-                // should not be reached\n-                console.error("cache error!");\n+                // should usually not be reached\n+                console.error( this.name + " eviction error", this.size, newItemSize );\n                 return;\n             }\n         }\n', 'addition_count': 11, 'deletion_count': 8, 'src_file': 'a/src/JBrowse/Store/LRUCache.js', 'tgt_file': 'b/src/JBrowse/Store/LRUCache.js', 'file_extension': '.js', 'before_file': 'define([\n           \'dojo/_base/declare\',\n           \'dojo/_base/array\',\n           \'JBrowse/Util\',\n           \'JBrowse/Digest/Crc32\'\n       ],\n       function( declare, array, Util, digest ) {\n\nreturn declare( null,\n\n/**\n * @lends JBrowse.Store.LRUCache\n */\n{\n\n    /**\n     * An LRU cache.\n     *\n     * @param args.fillCallback\n     * @param args.maxSize\n     * @param args.sizeFunction\n     * @param args.keyFunction\n     * @param args.name\n        this.fill = args.fillCallback;\n        this.maxSize = args.maxSize || 1000000;\n\n        this._size = args.sizeFunction || this._size;\n        this._keyString = args.keyFunction || this._keyString;\n\n\n        this.name = args.name || \'LRUcache\';\n\n        this._size = args.sizeFunction || this._size;\n        this._keyString = args.keyFunction || this._keyString;\n\n        this.itemCount = 0;\n        this.size = 0;\n\n        this._cacheByKey = {};\n\n        // each end of a doubly-linked list, sorted in usage order\n    },\n\n    _log: function() {\n        // if( typeof arguments[0] == \'string\' )\n        //     while( arguments[0].length < 15 )\n        //         arguments[0] += \' \';\n\n    get: function( inKey, callback ) {\n        var keyString = this._keyString( inKey );\n        var record = this._cacheByKey[ keyString ];\n\n        if( !record ) {\n            this._log( \'miss\', keyString );\n\n        if( !record ) {\n            // call our fill callback if necessary\n\n            this._log( \'cache miss\', key );\n\n            this._attemptFill( inKey, key, callback );\n\n            return;\n        }\n\n        this._log( \'cache hit\', key, record.value );\n\n        // take it out of the linked list\n        if( record.prev )\n        var results = [];\n        var cache = this._cacheByKey;\n        for( var k in cache ) {\n            if( keyRegex.test( k ) && cache.hasOwnProperty(k) )\n                results.push( cache[k] );\n        }\n        return results;\n    },\n\n    forEach: function( func, context ) {\n        if( ! context ) context = this;\n        var i = 0;\n        for( var record = this._cacheNewest; record; record = record.next ) {\n            func.call( context, record, i++ );\n        }\n    },\n    some: function( func, context ) {\n        if( ! context ) context = this;\n        var i = 0;\n        for( var record = this._cacheNewest; record; record = record.next ) {\n            if( func.call( context, record, i++ ) )\n                    delete this._inProgressFills[ key ];\n\n                    if( value ) {\n                        this._log( \'cache fill\', key );\n                        this.set( inKey, value );\n                    }\n                    array.forEach( fillRecord.callbacks, function( cb ) {\n    },\n\n    touchRecord: function( record ) {\n        if( ! record )\n            }\n        }\n        else {\n            this._log( "cache can\'t fill", key );\n            callback( undefined );\n        }\n    },\n        this._llRemove( record );\n\n        // add it back into the list as newest\n        this._llPush( record );\n    },\n\n    // take a record out of the LRU linked list\n    _llRemove: function( record ) {\n        if( record.prev )\n            record.prev.next = record.next;\n            key: key,\n            size: this._size( value )\n        };\n        this._log( \'cache set\', key, record, this.size );\n\n        // evict items if necessary\n        this._prune( record.size );\n            this._cacheOldest = record.next;\n\n        record.prev = null;\n        record.next = null;\n    },\n\n    _llPush: function( record ) {\n        if( this._cacheNewest ) {\n            this._cacheNewest.next = record;\n            record.prev = this._cacheNewest;\n        }\n        this._cacheNewest = record;\n        if( ! this._cacheOldest )\n            this._cacheOldest = record;\n    },\n\n    _attemptFill: function( inKey, keyString, callback ) {\n        if( this.fill ) {\n\n            var fillRecord = this._inProgressFills[ keyString ] =\n                this._inProgressFills[ keyString ] || { callbacks: [], running: false };\n\n            fillRecord.callbacks.push( callback );\n\n            if( ! fillRecord.running ) {\n                fillRecord.running = true;\n                this.fill( inKey, dojo.hitch( this, function( keyString, inKey, fillRecord, value, error, hints ) {\n                    delete this._inProgressFills[ keyString ];\n                    fillRecord.running = false;\n\n                    if( value && ! ( hints && hints.nocache ) ) {\n                        this._log( \'fill\', keyString );\n                        this.set( inKey, value );\n                    }\n                    array.forEach( fillRecord.callbacks, function( cb ) {\n                                       try {\n                                           cb.call( this, value, error );\n                                       } catch(x) {\n                                           console.error(\'\'+x, x.stack, x);\n                                       }\n                                   }, this );\n                }, keyString, inKey, fillRecord ));\n            }\n        }\n        else {\n            try {\n                callback( undefined );\n            } catch(x) {\n                console.error(x);\n            }\n        }\n    },\n\n    set: function( inKey, value ) {\n        var keyString = this._keyString( inKey );\n        if( this._cacheByKey[keyString] ) {\n            return;\n        }\n        while( this.size + (newItemSize||0) > this.maxSize ) {\n            var oldest = this._cacheOldest;\n            if( oldest ) {\n                this._log( \'cache evict\', oldest );\n\n                // update the oldest and newest pointers\n                if( ! oldest.next ) // if this was also the newest\n            console.error(e)\n            size = 1\n        }\n        var record = {\n            value: value,\n            key: inKey,\n            keyString: keyString,\n            size: size\n        };\n\n        if( record.size > this.maxSize ) {\n            this._warn( \'Cache cannot fit\', keyString, \'(\'+Util.addCommas(record.size) + \' > \' + Util.addCommas(this.maxSize)+\')\' );\n            return;\n        }\n\n        this._log( \'set\', keyString, record, this.size );\n\n        // evict items if necessary\n                this.itemCount--;\n                this.size -= oldest.size;\n            } else {\n                // should not be reached\n                console.error("cache error!");\n                return;\n            }\n        }\n        // update our total size and item count\n        this.size += record.size;\n        this.itemCount++;\n\n        return;\n    },\n\n    _keyString: function( inKey ) {\n        var type = typeof inKey;\n        if( type == \'object\' && typeof inKey.toUniqueString == \'function\' ) {\n            return inKey.toUniqueString();\n        }\n        else {\n            return digest.objectFingerprint( inKey );\n        }\n    },\n\n    _size: function( value ) {\n        var type = typeof value;\n        var sum = 0;\n        if( type == \'object\' && type !== null ) {\n            var sizeType = typeof value.size;\n            if( sizeType == \'number\' ) {\n                return sizeType;\n            }\n            else if( sizeType == \'function\' ) {\n                return value.size();\n            }\n            else if( value.byteLength ) {\n                return value.byteLength;\n            } else {\n                for( var k in value ) {\n                    if( value.hasOwnProperty( k ) ) {\n                        sum += this._size( value[k] );\n                    }\n                }\n            }\n            return sum;\n        } else if( type == \'string\' ) {\n            return value.length;\n        } else {\n            return 1;\n        }\n    },\n\n    _prune: function( newItemSize ) {\n        while( this.size + (newItemSize||0) > this.maxSize ) {\n            var oldest = this._cacheOldest;\n            if( oldest ) {\n                this._log( \'evict\', oldest );\n\n                // // update the oldest and newest pointers\n                // if( ! oldest.next ) // if this was also the newest\n                //     this._cacheNewest = oldest.prev; // probably undef\n                // this._cacheOldest = oldest.next; // maybe undef\n\n                // take it out of the linked list\n                this._llRemove( oldest );\n\n                // delete it from the byKey structure\n                delete this._cacheByKey[ oldest.keyString ];\n\n                // remove its linked-list links in case that makes it\n                // easier for the GC\n                delete oldest.next;\n                delete oldest.prev;\n\n                // update our size and item counts\n                this.itemCount--;\n                this.size -= oldest.size;\n            } else {\n                // should usually not be reached\n                this._error( "eviction error", this.size, newItemSize, this );\n                return;\n            }\n        }\n    },\n\n    _log: function() {\n        if( this.verbose )\n            console.log.apply( console, arguments );\n    },\n    _warn: function() {\n        console.warn.apply( console, arguments );\n    },\n    _error: function() {\n        console.error.apply( console, arguments );\n    }\n\n});\n});\n', 'message': 'refinements to LRUCache logging, can now give caches names\n', 'author_name': 'Robert Buels', 'lang': 'js'}