1 // MISC
  2 
  3 /**
  4  * @namespace
  5  */
  6 
  7 var Util = {};
  8 
  9 Util.is_ie = navigator.appVersion.indexOf('MSIE') >= 0;
 10 Util.is_ie6 = navigator.appVersion.indexOf('MSIE 6') >= 0;
 11 Util.addCommas = function(nStr)
 12 {
 13 	nStr += '';
 14 	var x = nStr.split('.');
 15 	var x1 = x[0];
 16 	var x2 = x.length > 1 ? '.' + x[1] : '';
 17 	var rgx = /(\d+)(\d{3})/;
 18 	while (rgx.test(x1)) {
 19 		x1 = x1.replace(rgx, '$1' + ',' + '$2');
 20 	}
 21 	return x1 + x2;
 22 };
 23 
 24 Util.wheel = function(event){
 25     var delta = 0;
 26     if (!event) event = window.event;
 27     if (event.wheelDelta) {
 28         delta = event.wheelDelta/120;
 29         if (window.opera) delta = -delta;
 30     } else if (event.detail) { delta = -event.detail/3;	}
 31     return Math.round(delta); //Safari Round
 32 };
 33 
 34 Util.isRightButton = function(e) {
 35     if (!e) var e = window.event;
 36     if (e.which) return e.which == 3;
 37     else if (e.button) return e.button == 2;
 38 };
 39 
 40 Util.getViewportWidth = function() {
 41   var width = 0;
 42   if( document.documentElement && document.documentElement.clientWidth ) {
 43     width = document.documentElement.clientWidth;
 44   }
 45   else if( document.body && document.body.clientWidth ) {
 46     width = document.body.clientWidth;
 47   }
 48   else if( window.innerWidth ) {
 49     width = window.innerWidth - 18;
 50   }
 51   return width;
 52 };
 53 
 54 Util.getViewportHeight = function() {
 55   var height = 0;
 56   if( document.documentElement && document.documentElement.clientHeight ) {
 57     height = document.documentElement.clientHeight;
 58   }
 59   else if( document.body && document.body.clientHeight ) {
 60     height = document.body.clientHeight;
 61   }
 62   else if( window.innerHeight ) {
 63     height = window.innerHeight - 18;
 64   }
 65   return height;
 66 };
 67 
 68 Util.findNearest = function(numArray, num) {
 69     var minIndex = 0;
 70     var min = Math.abs(num - numArray[0]);
 71     for (var i = 0; i < numArray.length; i++) {
 72         if (Math.abs(num - numArray[i]) < min) {
 73             minIndex = i;
 74             min = Math.abs(num - numArray[i]);
 75         }
 76     }
 77     return minIndex;
 78 };
 79 
 80 /**
 81  * replace variables in a template string with values
 82  * @param template String with variable names in curly brackets
 83  *                 e.g., "http://foo/{bar}?arg={baz}
 84  * @param fillWith object with attribute-value mappings
 85  *                 e.g., {'bar': 'someurl', 'baz': 'valueforbaz'}
 86  * @returns the template string with variables in fillWith replaced
 87  *                 e.g., 'htp://foo/someurl?arg=valueforbaz'
 88  */
 89 Util.fillTemplate = function(template, fillWith) {
 90     return template.replace(/\{([^}]+)\}/g,
 91                             function(match, group) {
 92                                 if (fillWith[group] !== undefined)
 93                                     return fillWith[group];
 94                                 else
 95                                     return "{" + group + "}";
 96                             });
 97 };
 98 
 99 /**
100  * function to load a specified resource only once
101  * @param {Object}   dojoXhrArgs object containing arguments for dojo.xhrGet,
102  *                               like <code>url</code> and <code>handleAs</code>
103  * @param {Object}   stateObj object that stores the state of the load
104  * @param {Function} successCallback function to call on a successful load
105  * @param {Function} errorCallback function to call on an unsuccessful load
106  */
107 Util.maybeLoad = function ( dojoXhrArgs, stateObj, successCallback, errorCallback) {
108     if (stateObj.state) {
109         if ("loaded" == stateObj.state) {
110             successCallback(stateObj.data);
111         } else if ("error" == stateObj.state) {
112             errorCallback();
113         } else if ("loading" == stateObj.state) {
114             stateObj.successCallbacks.push(successCallback);
115             if (errorCallback) stateObj.errorCallbacks.push(errorCallback);
116         }
117     } else {
118         stateObj.state = "loading";
119         stateObj.successCallbacks = [successCallback];
120         stateObj.errorCallbacks = [errorCallback];
121 
122         var args = dojo.clone( dojoXhrArgs );
123         args.load = function(o) {
124             stateObj.state = "loaded";
125             stateObj.data = o;
126             var cbs = stateObj.successCallbacks;
127             for (var c = 0; c < cbs.length; c++) cbs[c](o);
128         };
129         args.error = function(error) {
130             console.error(''+error);
131             stateObj.state = "error";
132             var cbs = stateObj.errorCallbacks;
133             for (var c = 0; c < cbs.length; c++) cbs[c]();
134         };
135 
136         dojo.xhrGet( args );
137     }
138 };
139 
140 /**
141  * updates a with values from b, recursively
142  */
143 Util.deepUpdate = function(a, b) {
144     for (var prop in b) {
145         if ((prop in a)
146             && ("object" == typeof b[prop])
147             && ("object" == typeof a[prop]) ) {
148             Util.deepUpdate(a[prop], b[prop]);
149         } else if( typeof a[prop] == 'undefined' || typeof b[prop] != undefined ){
150             a[prop] = b[prop];
151         }
152     }
153     return a;
154 };
155 
156 // from http://bugs.dojotoolkit.org/ticket/5794
157 Util.resolveUrl = function(baseUrl, relativeUrl) {
158     // summary:
159     // This takes a base url and a relative url and resolves the target url.
160     // For example:
161     // resolveUrl("http://www.domain.com/path1/path2","../path3") ->"http://www.domain.com/path1/path3"
162     //
163     if (relativeUrl.match(/\w+:\/\//))
164 	return relativeUrl;
165     if (relativeUrl.charAt(0)=='/') {
166 	baseUrl = baseUrl.match(/.*\/\/[^\/]*/);
167 	return (baseUrl ? baseUrl[0] : '') + relativeUrl;
168     }
169     //TODO: handle protocol relative urls:  ://www.domain.com
170     baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*$/)[0].length);// clean off the trailing path
171     if (relativeUrl == '.')
172 	return baseUrl;
173     while (relativeUrl.substring(0,3) == '../') {
174 	baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*\/$/)[0].length);
175 	relativeUrl = relativeUrl.substring(3);
176     }
177     return baseUrl + relativeUrl;
178 };
179 
180 Util.parseLocString = function( locstring ) {
181     locstring = dojo.trim( locstring );
182 
183     //                                (chromosome)    (    start      )   (  sep     )     (    end   )
184     var matches = locstring.match(/^(((\S*)\s*:)?\s*(-?[\d,.']+)\s*(\.\.|-|\s+))?\s*(-?[\d,.']+)$/i);
185     //matches potentially contains location components:
186     //matches[3] = chromosome (optional)
187     //matches[4] = start base (optional)
188     //matches[6] = end base (or center base, if it's the only one)
189 
190     if( !matches )
191         return null;
192 
193     // parses a number from a locstring that's a coordinate, and
194     // converts it from 1-based to interbase coordinates
195     var parseCoord = function( coord ) {
196         coord = (coord+'').replace(/\D/g,'');
197         var num = parseInt( coord, 10 );
198         return typeof num == 'number' && !isNaN(num) ? num : null;
199     };
200 
201     return {
202         start: parseCoord( matches[4] )-1,
203         end:   parseCoord( matches[6] ),
204         ref:   matches[3]
205     };
206 };
207 
208 Util.assembleLocString = function( loc_in ) {
209     var s = '',
210         types = { start: 'number', end: 'number', ref: 'string' },
211         location = {}
212        ;
213 
214     // filter the incoming loc_in to only pay attention to slots that we
215     // know how to handle
216     for( var slot in types ) {
217         if( types[slot] == typeof loc_in[slot]
218             && (types[slot] != 'number' || !isNaN(loc_in[slot])) //filter any NaNs
219           ) {
220             location[slot] = loc_in[slot];
221         }
222     }
223 
224     //finally assemble our string
225     if( 'ref' in location ) {
226         s += location.ref;
227         if( location.start || location.end )
228             s += ':';
229     }
230     if( 'start' in location ) {
231         s += (Math.round(location.start)+1).toFixed(0).toLocaleString();
232         if( 'end' in location )
233             s+= '..';
234     }
235     if( 'end' in location )
236         s += Math.round(location.end).toFixed(0).toLocaleString();
237 
238     return s;
239 };
240 
241 // given a possible reference sequence name and an object as { 'foo':
242 // <refseq foo>, ... }, try to match that reference sequence name
243 // against the actual name of one of the reference sequences.  returns
244 // the reference sequence record, or null
245 // if none matched.
246 Util.matchRefSeqName = function( name, refseqs ) {
247     for( var ref in refseqs ) {
248         if( ! refseqs.hasOwnProperty(ref) )
249             continue;
250 
251         var ucname = name.toUpperCase();
252         var ucref  = ref.toUpperCase();
253 
254 	if(    ucname == ucref
255             || "CHR" + ucname == ucref
256             || ucname == "CHR" + ucref
257           ) {
258             return refseqs[ref];
259         }
260     }
261     return null;
262 };
263 
264 /**
265  * Wrap a handler function to be called 1ms later in a window timeout.
266  * This will usually give a better stack trace for figuring out where
267  * errors are happening.
268  */
269 Util.debugHandler = function( context, func ) {
270     var debuggedHandler = function() {
271         var args = arguments;
272         window.setTimeout( function() { func.apply(context,args);}, 1);
273     };
274     return debuggedHandler;
275 };
276 
277 if (!Array.prototype.reduce)
278 {
279   Array.prototype.reduce = function(fun /*, initial*/)
280   {
281     var len = this.length;
282     if (typeof fun != "function")
283       throw new TypeError();
284 
285     // no value to return if no initial value and an empty array
286     if (len == 0 && arguments.length == 1)
287       throw new TypeError();
288 
289     var i = 0;
290     if (arguments.length >= 2)
291     {
292       var rv = arguments[1];
293     }
294     else
295     {
296       do
297       {
298         if (i in this)
299         {
300           rv = this[i++];
301           break;
302         }
303 
304         // if array contains no values, no initial value to return
305         if (++i >= len)
306           throw new TypeError();
307       }
308       while (true);
309     }
310 
311     for (; i < len; i++)
312     {
313       if (i in this)
314         rv = fun.call(null, rv, this[i], i, this);
315     }
316 
317     return rv;
318   };
319 }
320 
321 if (!Array.prototype.map)
322 {
323   Array.prototype.map = function(fun /*, thisp */)
324   {
325     "use strict";
326 
327     if (this === void 0 || this === null)
328       throw new TypeError();
329 
330     var t = Object(this);
331     var len = t.length >>> 0;
332     if (typeof fun !== "function")
333       throw new TypeError();
334 
335     var res = new Array(len);
336     var thisp = arguments[1];
337     for (var i = 0; i < len; i++)
338     {
339       if (i in t)
340         res[i] = fun.call(thisp, t[i], i, t);
341     }
342 
343     return res;
344   };
345 }
346 
347 if (!Array.prototype.indexOf)
348 {
349   Array.prototype.indexOf = function(searchElement /*, fromIndex */)
350   {
351     "use strict";
352 
353     if (this === void 0 || this === null)
354       throw new TypeError();
355 
356     var t = Object(this);
357     var len = t.length >>> 0;
358     if (len === 0)
359       return -1;
360 
361     var n = 0;
362     if (arguments.length > 0)
363     {
364       n = Number(arguments[1]);
365       if (n !== n) // shortcut for verifying if it's NaN
366         n = 0;
367       else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0))
368         n = (n > 0 || -1) * Math.floor(Math.abs(n));
369     }
370 
371     if (n >= len)
372       return -1;
373 
374     var k = n >= 0
375           ? n
376           : Math.max(len - Math.abs(n), 0);
377 
378     for (; k < len; k++)
379     {
380       if (k in t && t[k] === searchElement)
381         return k;
382     }
383     return -1;
384   };
385 }
386 
387 /**
388  * @class
389  */
390 function Finisher(fun) {
391     this.fun = fun;
392     this.count = 0;
393 }
394 
395 Finisher.prototype.inc = function() {
396     this.count++;
397 };
398 
399 Finisher.prototype.dec = function() {
400     this.count--;
401     this.finish();
402 };
403 
404 Finisher.prototype.finish = function() {
405     if (this.count <= 0) this.fun();
406 };
407 
408 
409 /*
410 
411 Copyright (c) 2007-2010 The Evolutionary Software Foundation
412 
413 Created by Mitchell Skinner <mitch_skinner@berkeley.edu>
414 
415 This package and its accompanying libraries are free software; you can
416 redistribute it and/or modify it under the terms of the LGPL (either
417 version 2.1, or at your option, any later version) or the Artistic
418 License 2.0.  Refer to LICENSE for the full license text.
419 
420 */
421