1 dojo.declare( 'JBrowse.Model.AutocompleteStore', null,
  2 /**
  3  * @lends JBrowse.Model.AutocompleteStore
  4  */
  5 {
  6     /**
  7      * @constructs
  8      */
  9     constructor: function( /**Object*/ args ) {
 10         if( ! args.namesTrie )
 11             throw "must provide a namesTrie argument";
 12 
 13         this.namesTrie = args.namesTrie;
 14 
 15         this.resultLimit = args.resultLimit || 15;
 16         this.tooManyMatchesMessage = args.tooManyMatchesMessage || '(too many matches to display)';
 17 
 18         // generate stopPrefixes
 19         var stopPrefixes = this.stopPrefixes = {};
 20         // make our stopPrefixes an object as { prefix: true, ... }
 21         // with all possible prefixes of our stop prefixes
 22         if( args.stopPrefixes ) {
 23             var prefixesInput = typeof args.stopPrefixes == 'string'
 24                 ? [ args.stopPrefixes ] : args.stopPrefixes;
 25 
 26             dojo.forEach( prefixesInput, function(prefix) {
 27                 while( prefix.length ) {
 28                     stopPrefixes[prefix] = true;
 29                     prefix = prefix.substr( 0, prefix.length - 1 );
 30                 }
 31             });
 32         }
 33 
 34         // make a self-modifying method for extracting the that
 35         // detects whether the name store is formatted with tools
 36         // pre-1.4 or post-1.4.  for pre-1.4 formats, will just
 37         // complete with the lower-case version of the name.  for
 38         // post-1.4, use the original-case version that's stored in
 39         // the name record.
 40         this.nodeText = function(node) {
 41             if( typeof node[1][0][0] == 'number' ) {
 42                 // pre-1.4, for backcompat
 43                 this.nodeText = function(node) { return node[0]; };
 44             } else {
 45                 // post-1.4
 46                 this.nodeText = function(node) { return node[1][0][0]; };
 47             }
 48             return this.nodeText( node );
 49         };
 50     },
 51 
 52     // dojo.data.api.Read support
 53 
 54     fetch: function( /**Object*/ request ) {
 55         var start = request.start || 0;
 56         var matchLimit = Math.min( this.resultLimit, Math.max(0, request.count) );
 57         var matchesRemaining = matchLimit;
 58 	var scope = request.scope || dojo.global;
 59         var aborted = false;
 60 
 61         // wrap our abort function to set a flag
 62         request.abort = function() {
 63             var oldabort = request.abort || function() {};
 64             return function() {
 65                 aborted = true;
 66                 oldabort.call( scope, request );
 67             };
 68         }.call(this);
 69 
 70         if( ! request.store )
 71             request.store = this;
 72 
 73         var gotTree = false; // stays false if tree isn't found
 74         var matches = [];
 75         var prefix = (request.query.name || '').replace(/\*$/,'');
 76 
 77         if( ! this.stopPrefixes[ prefix ] ) {
 78             this.namesTrie.mappingsFromPrefix(
 79                 prefix,
 80                 dojo.hitch( this, function(tree) {
 81                     gotTree = true;
 82                     // use dojo.some so that we can break out of the loop when we hit the limit
 83                     dojo.some( tree, function(node) {
 84                                    if( matchesRemaining-- ) {
 85                                            matches.push({ name: this.nodeText(node) });
 86                                    }
 87                                    return matchesRemaining < 0;
 88                                },this);
 89                 }));
 90         }
 91 
 92         // if we found more than the match limit
 93         if( matchesRemaining < 0 )
 94             matches.push({ name: this.tooManyMatchesMessage, hitLimit: true });
 95 
 96         if( request.onBegin )
 97             request.onBegin.call( scope, matches.length, request );
 98         if( request.sort )
 99             matches.sort(dojo.data.util.sorter.createSortFunction(request.sort, this));
100         if( request.onItem ) {
101             dojo.forEach( matches, function( item ) {
102                 if( !aborted )
103                     request.onItem.call( scope, item, request );
104             });
105         }
106 	if(request.onComplete && !aborted){
107 	    request.onComplete.call( scope, matches, request );
108 	}
109     },
110 
111     getValue: function( i, attr, defaultValue ) {
112         var v = i[attr];
113         return typeof v == 'undefined' ? defaultValue : v;
114     },
115     getValues: function( i, attr ) {
116         var a = [ i[attr] ];
117         return typeof a[0] == 'undefined' ? [] : a;
118     },
119 
120     getAttributes: function(item)  {
121         return dojof.keys( item );
122     },
123 
124     hasAttribute: function(item,attr) {
125         return item.hasOwnProperty(attr);
126     },
127 
128     containsValue: function(item, attribute, value) {
129         return item[attribute] == value;
130     },
131 
132     isItem: function(item) {
133         return typeof item == 'object' && typeof item.label == 'string';
134     },
135 
136     isItemLoaded: function() {
137         return true;
138     },
139 
140     loadItem: function( args ) {
141     },
142 
143     close: function() {},
144 
145     getLabel: function(i) {
146         return this.getValue(i,'name',undefined);
147     },
148     getLabelAttributes: function(i) {
149         return ['name'];
150     }
151 });
152