extensions/net.sf.basedb.varsearch/trunk/resources/varsearch.js

Code
Comments
Other
Rev Date Author Line
6157 02 Mar 21 nicklas 1 var VarSearch = function()
6157 02 Mar 21 nicklas 2 {
6157 02 Mar 21 nicklas 3   var varsearch = {};
6157 02 Mar 21 nicklas 4   var HOME = null;
6157 02 Mar 21 nicklas 5
7307 28 Aug 23 nicklas 6   varsearch.VERSION = '1.10-dev';
6160 04 Mar 21 nicklas 7
6160 04 Mar 21 nicklas 8   varsearch.checkIsAdmin = function(message, callback, servletDir)
6160 04 Mar 21 nicklas 9   {
6160 04 Mar 21 nicklas 10     var url = (servletDir || '../') + 'Session.servlet?ID='+App.getSessionId();
6160 04 Mar 21 nicklas 11     url += '&cmd=CheckIsAdmin';
6160 04 Mar 21 nicklas 12     if (message) url += '&message='+encodeURIComponent(message);
6160 04 Mar 21 nicklas 13     if (!callback) callback = function() {};
6160 04 Mar 21 nicklas 14     Wizard.asyncJsonRequest(url, callback);
6160 04 Mar 21 nicklas 15   }
6160 04 Mar 21 nicklas 16
6157 02 Mar 21 nicklas 17   varsearch.initElements = function(element, autoInit)
6157 02 Mar 21 nicklas 18   {
6170 17 Mar 21 nicklas 19     if (autoInit == 'varsearch-options') 
6170 17 Mar 21 nicklas 20     {
6170 17 Mar 21 nicklas 21       Events.addEventHandler(element, 'click', varsearch.showOptions);
6170 17 Mar 21 nicklas 22     }
6170 17 Mar 21 nicklas 23     else if (autoInit == 'varsearch-hit')
6170 17 Mar 21 nicklas 24     {
6170 17 Mar 21 nicklas 25       Events.addEventHandler(element, 'click', varsearch.showHitDetails);
6170 17 Mar 21 nicklas 26     }
6522 20 Dec 21 nicklas 27     else if (autoInit == 'varsearch-rba-hits')
6522 20 Dec 21 nicklas 28     {
6522 20 Dec 21 nicklas 29       Events.addEventHandler(element, 'click', varsearch.showRbaHits);
6522 20 Dec 21 nicklas 30     }
6189 29 Mar 21 nicklas 31     else if (autoInit == 'varsearch-hide-col')
6189 29 Mar 21 nicklas 32     {
6190 29 Mar 21 nicklas 33       var draggable = element.parentNode;
6189 29 Mar 21 nicklas 34       Events.addEventHandler(element, 'click', varsearch.hideColumn);
6190 29 Mar 21 nicklas 35       Events.addEventHandler(draggable, 'dragstart', varsearch.beginColumnDrag);
6190 29 Mar 21 nicklas 36       Events.addEventHandler(draggable, 'dragover', varsearch.checkColumnDropTarget);
6190 29 Mar 21 nicklas 37       Events.addEventHandler(draggable, 'dragleave', varsearch.leaveColumnDropTarget);
6190 29 Mar 21 nicklas 38       Events.addEventHandler(draggable, 'drop', varsearch.columnDropped);
6190 29 Mar 21 nicklas 39       Events.addEventHandler(draggable, 'dragend', varsearch.endColumnDrag);
6189 29 Mar 21 nicklas 40     }
6157 02 Mar 21 nicklas 41   }
6157 02 Mar 21 nicklas 42
6157 02 Mar 21 nicklas 43   varsearch.showOptions = function(event)
6157 02 Mar 21 nicklas 44   {
6157 02 Mar 21 nicklas 45     var url = varsearch.getHome()+'options.jsp?ID='+App.getSessionId();
6157 02 Mar 21 nicklas 46     url += '&indexId='+Data.get(event.currentTarget, 'index-id');
6157 02 Mar 21 nicklas 47     Dialogs.openPopup(url, 'VarSearchOptions', 600, 400);
6157 02 Mar 21 nicklas 48   }
6189 29 Mar 21 nicklas 49   
6190 29 Mar 21 nicklas 50   varsearch.setVisibleColumns = function(indexId, columns)
6190 29 Mar 21 nicklas 51   {
6190 29 Mar 21 nicklas 52     var url = varsearch.getHome()+'Session.servlet?ID='+App.getSessionId();
6190 29 Mar 21 nicklas 53     url += '&cmd=SetVisibleColumns';
6190 29 Mar 21 nicklas 54     url += '&indexId='+encodeURIComponent(indexId);
6190 29 Mar 21 nicklas 55     url += '&visibleColumns='+encodeURIComponent(columns);
6190 29 Mar 21 nicklas 56     Wizard.asyncJsonRequest(url, varsearch.reloadPage, 'POST');
6190 29 Mar 21 nicklas 57   }
6190 29 Mar 21 nicklas 58   
6189 29 Mar 21 nicklas 59   varsearch.hideColumn = function(event)
6189 29 Mar 21 nicklas 60   {
6190 29 Mar 21 nicklas 61     var element = event.currentTarget.parentNode;
6190 29 Mar 21 nicklas 62     var colToHide = Data.get(element, 'col');
6190 29 Mar 21 nicklas 63     var indexId = Data.get(element, 'index-id');
6189 29 Mar 21 nicklas 64     // Find all other columns for the same index
6278 04 Jun 21 nicklas 65     var visible = varsearch.getVisibleColumns(indexId, colToHide);
6190 29 Mar 21 nicklas 66     varsearch.setVisibleColumns(indexId, visible.join(','));
6190 29 Mar 21 nicklas 67   }
6190 29 Mar 21 nicklas 68
6190 29 Mar 21 nicklas 69   /**
6190 29 Mar 21 nicklas 70     Get all visible column for the given index.
6190 29 Mar 21 nicklas 71   */
6190 29 Mar 21 nicklas 72   varsearch.getVisibleColumns = function(indexId, except)
6190 29 Mar 21 nicklas 73   {
6189 29 Mar 21 nicklas 74     var visible = [];
6189 29 Mar 21 nicklas 75     var cols = document.getElementsByClassName('hide-col');
6189 29 Mar 21 nicklas 76     for (var i = 0; i < cols.length; i++)
6189 29 Mar 21 nicklas 77     {
6190 29 Mar 21 nicklas 78       var col = cols[i].parentNode;
6190 29 Mar 21 nicklas 79       if (Data.get(col, 'index-id') == indexId)
6189 29 Mar 21 nicklas 80       {
6190 29 Mar 21 nicklas 81         if (except && Data.get(col, 'col') == except) continue;
6190 29 Mar 21 nicklas 82         visible[visible.length] = Data.get(col, 'col');
6189 29 Mar 21 nicklas 83       }
6189 29 Mar 21 nicklas 84     }
6190 29 Mar 21 nicklas 85     return visible;
6190 29 Mar 21 nicklas 86   }
6190 29 Mar 21 nicklas 87
6190 29 Mar 21 nicklas 88   /**
6190 29 Mar 21 nicklas 89     Start dragging a table column. Store table-id, column-id 
6190 29 Mar 21 nicklas 90     and column-index on the table element.
6190 29 Mar 21 nicklas 91   */
6190 29 Mar 21 nicklas 92   varsearch.beginColumnDrag = function(event)
6190 29 Mar 21 nicklas 93   {
6190 29 Mar 21 nicklas 94     var dragSrc = {};
6190 29 Mar 21 nicklas 95     dragSrc.indexId = Data.get(event.currentTarget, 'index-id');
6190 29 Mar 21 nicklas 96     dragSrc.col = Data.get(event.currentTarget, 'col');
6190 29 Mar 21 nicklas 97     dragSrc.colNo = Data.int(event.currentTarget, 'col-no');
6189 29 Mar 21 nicklas 98     
6190 29 Mar 21 nicklas 99     // Store the drag data on the <div> grid element
6190 29 Mar 21 nicklas 100     Doc.element('varsearch-header.'+dragSrc.indexId).dragSrc = dragSrc;
6190 29 Mar 21 nicklas 101     event.dataTransfer.effectAllowed = 'move';
6190 29 Mar 21 nicklas 102     // In FF, we also need to call setData() or the drag event will not start
6190 29 Mar 21 nicklas 103     // but this results in an exception in IE!!!
6190 29 Mar 21 nicklas 104     try
6190 29 Mar 21 nicklas 105     {
6190 29 Mar 21 nicklas 106       event.dataTransfer.setData('application/json', JSON.stringify(dragSrc));
6190 29 Mar 21 nicklas 107     }
6190 29 Mar 21 nicklas 108     catch (e)
6190 29 Mar 21 nicklas 109     {}
6189 29 Mar 21 nicklas 110   }
6157 02 Mar 21 nicklas 111
6190 29 Mar 21 nicklas 112   /**
6190 29 Mar 21 nicklas 113     Checks if dropping is allowed at this target.
6190 29 Mar 21 nicklas 114     We only allow dragging within the same table,
6190 29 Mar 21 nicklas 115     and the target column must be different from the 
6190 29 Mar 21 nicklas 116     source column.
6190 29 Mar 21 nicklas 117   */
6190 29 Mar 21 nicklas 118   varsearch.checkColumnDropTarget = function(event)
6190 29 Mar 21 nicklas 119   {
6190 29 Mar 21 nicklas 120     var indexId = Data.get(event.currentTarget, 'index-id');
6190 29 Mar 21 nicklas 121     var dragSrc = Doc.element('varsearch-header.'+indexId).dragSrc;
6190 29 Mar 21 nicklas 122     
6190 29 Mar 21 nicklas 123     var dropLocation = varsearch.checkTheDropTarget(dragSrc, event.currentTarget);
6190 29 Mar 21 nicklas 124     if (dropLocation)
6190 29 Mar 21 nicklas 125     {
6190 29 Mar 21 nicklas 126       // Enable drop target!
6190 29 Mar 21 nicklas 127       event.preventDefault();
6190 29 Mar 21 nicklas 128       event.dataTransfer.dropEffect = 'move';
6190 29 Mar 21 nicklas 129       // Left or right depending on the order or columns
6190 29 Mar 21 nicklas 130       Doc.addClass(event.currentTarget, dropLocation < 0 ? 'table-drop-left' : 'table-drop-right');
6190 29 Mar 21 nicklas 131     }
6190 29 Mar 21 nicklas 132   }
6190 29 Mar 21 nicklas 133   
6190 29 Mar 21 nicklas 134   /**
6190 29 Mar 21 nicklas 135     Remove drop indicator classes when leaving.
6190 29 Mar 21 nicklas 136   */
6190 29 Mar 21 nicklas 137   varsearch.leaveColumnDropTarget = function(event)
6190 29 Mar 21 nicklas 138   {
6190 29 Mar 21 nicklas 139     Doc.removeClass(event.currentTarget, 'table-drop-right');
6190 29 Mar 21 nicklas 140     Doc.removeClass(event.currentTarget, 'table-drop-left');
6190 29 Mar 21 nicklas 141   }
6190 29 Mar 21 nicklas 142
6190 29 Mar 21 nicklas 143   /**
6190 29 Mar 21 nicklas 144     Another column was dropped here!
6190 29 Mar 21 nicklas 145   */
6190 29 Mar 21 nicklas 146   varsearch.columnDropped = function(event)
6190 29 Mar 21 nicklas 147   {
6190 29 Mar 21 nicklas 148     App.debug('columnDropped');
6190 29 Mar 21 nicklas 149     Doc.removeClass(event.currentTarget, 'table-drop-right');
6190 29 Mar 21 nicklas 150     Doc.removeClass(event.currentTarget, 'table-drop-left');
6190 29 Mar 21 nicklas 151     
6190 29 Mar 21 nicklas 152     // Check that we can drop here
6190 29 Mar 21 nicklas 153     var indexId = Data.get(event.currentTarget, 'index-id');
6190 29 Mar 21 nicklas 154     var dragSrc = Doc.element('varsearch-header.'+indexId).dragSrc;
6190 29 Mar 21 nicklas 155     if (!varsearch.checkTheDropTarget(dragSrc, event.currentTarget)) return;
6190 29 Mar 21 nicklas 156     
6190 29 Mar 21 nicklas 157     // Get the existing columns as an array
6190 29 Mar 21 nicklas 158     var colArray = varsearch.getVisibleColumns(indexId);
6190 29 Mar 21 nicklas 159     
6190 29 Mar 21 nicklas 160     var indexSrc = colArray.indexOf(dragSrc.col);
6190 29 Mar 21 nicklas 161     var indexDest = colArray.indexOf(Data.get(event.currentTarget, 'col'));
6190 29 Mar 21 nicklas 162     // Remove the dragged column from the array
6190 29 Mar 21 nicklas 163     if (indexSrc >= 0) colArray.splice(indexSrc, 1);
6190 29 Mar 21 nicklas 164     // Insert the dragged column before or after the column it was dropped on
6190 29 Mar 21 nicklas 165     if (indexDest >= 0) colArray.splice(indexDest, 0, dragSrc.col);
6190 29 Mar 21 nicklas 166     
6190 29 Mar 21 nicklas 167     // Update and submit the table
6190 29 Mar 21 nicklas 168     varsearch.setVisibleColumns(indexId, colArray.join(','));
6190 29 Mar 21 nicklas 169   }
6190 29 Mar 21 nicklas 170
6190 29 Mar 21 nicklas 171
6190 29 Mar 21 nicklas 172   /**
6190 29 Mar 21 nicklas 173     Checks if dropping is allowed at this target.
6190 29 Mar 21 nicklas 174     We only allow dragging within the same table,
6190 29 Mar 21 nicklas 175     and the target column must be different from the 
6190 29 Mar 21 nicklas 176     source column.
6190 29 Mar 21 nicklas 177     returns: 0 = no drop, -1 drop to left, +1 drop to right
6190 29 Mar 21 nicklas 178   */
6190 29 Mar 21 nicklas 179   varsearch.checkTheDropTarget = function(dragSrc, dragTarget)
6190 29 Mar 21 nicklas 180   {
6190 29 Mar 21 nicklas 181     if (!dragSrc) return false;
6190 29 Mar 21 nicklas 182     var indexId = Data.get(dragTarget, 'index-id');
6190 29 Mar 21 nicklas 183     var colNo = Data.int(dragTarget, 'col-no');
6190 29 Mar 21 nicklas 184     return indexId == dragSrc.indexId ? colNo - dragSrc.colNo : 0;
6190 29 Mar 21 nicklas 185   }
6190 29 Mar 21 nicklas 186
6190 29 Mar 21 nicklas 187
6170 17 Mar 21 nicklas 188   varsearch.showHitDetails = function(event)
6170 17 Mar 21 nicklas 189   {
6170 17 Mar 21 nicklas 190     var url = varsearch.getHome()+'hit-details.jsp?ID='+App.getSessionId();
6170 17 Mar 21 nicklas 191     url += '&rbaId='+Data.get(event.currentTarget, 'rba');
6170 17 Mar 21 nicklas 192     url += '&fileId='+Data.get(event.currentTarget, 'file');
6170 17 Mar 21 nicklas 193     url += '&lineNo='+Data.get(event.currentTarget, 'line');
6551 26 Jan 22 nicklas 194     url += '&snpId='+Data.get(event.currentTarget, 'snp');
6173 18 Mar 21 nicklas 195     url += '&idx='+encodeURIComponent(Data.get(event.currentTarget, 'idx'));
6170 17 Mar 21 nicklas 196     Dialogs.openPopup(url, 'VarSearchHit', 600, 400);
6170 17 Mar 21 nicklas 197   }
6170 17 Mar 21 nicklas 198
6522 20 Dec 21 nicklas 199   varsearch.showRbaHits = function(event)
6522 20 Dec 21 nicklas 200   {
6522 20 Dec 21 nicklas 201     var url = varsearch.getHome()+'rba-hits.jsp?ID='+App.getSessionId();
6522 20 Dec 21 nicklas 202     url += '&rbaId='+Data.get(event.currentTarget, 'rba');
6526 20 Dec 21 nicklas 203     url += '&fileId='+Data.get(event.currentTarget, 'file');
6522 20 Dec 21 nicklas 204     url += '&idx='+encodeURIComponent(Data.get(event.currentTarget, 'idx'));
6522 20 Dec 21 nicklas 205     Dialogs.openPopup(url, 'VarSearchRbaHits', 900, 600);
6522 20 Dec 21 nicklas 206   }
6522 20 Dec 21 nicklas 207
6189 29 Mar 21 nicklas 208   varsearch.reloadPage = function()
6189 29 Mar 21 nicklas 209   {
6189 29 Mar 21 nicklas 210     window.location.reload();
6189 29 Mar 21 nicklas 211   }
6189 29 Mar 21 nicklas 212
6157 02 Mar 21 nicklas 213   // Find the <script> tag for 'varsearch.js' and extract the path to it
6157 02 Mar 21 nicklas 214   varsearch.getHome = function()
6157 02 Mar 21 nicklas 215   {
6157 02 Mar 21 nicklas 216     if (HOME != null) return HOME;
6157 02 Mar 21 nicklas 217     
6157 02 Mar 21 nicklas 218     var scripts = document.getElementsByTagName('script');
6157 02 Mar 21 nicklas 219     for (var i = scripts.length - 1; i >= 0; i--)
6157 02 Mar 21 nicklas 220     {
6157 02 Mar 21 nicklas 221       var src = scripts[i].src;
6157 02 Mar 21 nicklas 222       var end = src.indexOf('varsearch.js');
6157 02 Mar 21 nicklas 223       if (end > 0)
6157 02 Mar 21 nicklas 224       {
6157 02 Mar 21 nicklas 225         var start = src.indexOf(App.getRoot());
6157 02 Mar 21 nicklas 226         HOME = src.substring(start, end);
6157 02 Mar 21 nicklas 227       }
6157 02 Mar 21 nicklas 228     }
6157 02 Mar 21 nicklas 229     // Fallback that should never happen!
6157 02 Mar 21 nicklas 230     if (HOME == null) HOME = App.getRoot() + 'extensions/varsearch.jar/';
6157 02 Mar 21 nicklas 231     return HOME;
6157 02 Mar 21 nicklas 232   }
6163 05 Mar 21 nicklas 233   
6163 05 Mar 21 nicklas 234   /**
6163 05 Mar 21 nicklas 235     Add thousand separators to the count
6163 05 Mar 21 nicklas 236   */
6163 05 Mar 21 nicklas 237   varsearch.niceCount = function(count)
6163 05 Mar 21 nicklas 238   {
6544 19 Jan 22 nicklas 239     if (!count && count != 0) return '-';
6253 25 May 21 nicklas 240     return count.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1&#8239;'); // &#8239; == NARROW NO-BREAK SPACE
6163 05 Mar 21 nicklas 241   }
6157 02 Mar 21 nicklas 242
6157 02 Mar 21 nicklas 243   return varsearch;
6157 02 Mar 21 nicklas 244 }();
6157 02 Mar 21 nicklas 245
6157 02 Mar 21 nicklas 246 Doc.addElementInitializer(VarSearch.initElements);
6157 02 Mar 21 nicklas 247
6160 04 Mar 21 nicklas 248 var Wizard = function()
6160 04 Mar 21 nicklas 249 {
6160 04 Mar 21 nicklas 250   var wizard = {};
6160 04 Mar 21 nicklas 251   var internal = {};
6533 21 Dec 21 nicklas 252   var debug = 0;
6160 04 Mar 21 nicklas 253   
6160 04 Mar 21 nicklas 254   var numLiveAsyncRequests = 0;
6160 04 Mar 21 nicklas 255   var hasFatalError = false;
6160 04 Mar 21 nicklas 256
6160 04 Mar 21 nicklas 257
6160 04 Mar 21 nicklas 258   /**
6160 04 Mar 21 nicklas 259     Submit an ansynchronous request for JSON data. 'GET' method
6160 04 Mar 21 nicklas 260     is used if 'method' parameter is not set.
6160 04 Mar 21 nicklas 261     The 'callback' should be a method accepting a single
6160 04 Mar 21 nicklas 262     JSON parameter object. If the 'status' of the response
6160 04 Mar 21 nicklas 263     is not 'ok' this method will call setFatalError and
6160 04 Mar 21 nicklas 264     without calling the callback method.
6160 04 Mar 21 nicklas 265     
6160 04 Mar 21 nicklas 266     The 'errorCallback' is an optional function parameter that should
6160 04 Mar 21 nicklas 267     take two parameters. The first is an Error object and the second is
6160 04 Mar 21 nicklas 268     the JSON response. The seconds parameter only has a value if the error 
6160 04 Mar 21 nicklas 269     happens after parsing the response and is null otherwise.
6160 04 Mar 21 nicklas 270   */
6160 04 Mar 21 nicklas 271   wizard.asyncJsonRequest = function(url, callback, method, postdata, errorCallback)
6160 04 Mar 21 nicklas 272   {
6160 04 Mar 21 nicklas 273     if (debug)
6160 04 Mar 21 nicklas 274     {
6160 04 Mar 21 nicklas 275       App.debug((method || 'GET') + ': ' + url);
6160 04 Mar 21 nicklas 276       if (postdata) App.debug(postdata);
6160 04 Mar 21 nicklas 277     }
6160 04 Mar 21 nicklas 278     var request = Ajax.getXmlHttpRequest();
6160 04 Mar 21 nicklas 279     request.jsonCallback = callback;
6160 04 Mar 21 nicklas 280     request.errorCallback = errorCallback;
6160 04 Mar 21 nicklas 281     request.open(method || 'GET', url, true);
6160 04 Mar 21 nicklas 282     // Firefox only adds request header with needed "boundary" info, if request header is not set explicitly
6160 04 Mar 21 nicklas 283     if (postdata && !(postdata instanceof FormData)) 
6160 04 Mar 21 nicklas 284     {
6160 04 Mar 21 nicklas 285       request.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
6160 04 Mar 21 nicklas 286     }
6160 04 Mar 21 nicklas 287     Ajax.setReadyStateHandler(request, internal.asyncCallbackWrapper, internal.asyncCallbackWrapper);
6160 04 Mar 21 nicklas 288     numLiveAsyncRequests++;
6160 04 Mar 21 nicklas 289     request.send(postdata || null);
6160 04 Mar 21 nicklas 290   }
6160 04 Mar 21 nicklas 291   
6160 04 Mar 21 nicklas 292   /**
6160 04 Mar 21 nicklas 293     Callback wrapper for ansynchronous requests.
6160 04 Mar 21 nicklas 294   */
6160 04 Mar 21 nicklas 295   internal.asyncCallbackWrapper = function(request)
6160 04 Mar 21 nicklas 296   {
6160 04 Mar 21 nicklas 297     numLiveAsyncRequests--;
6160 04 Mar 21 nicklas 298     if (debug) App.debug(request.responseText);
6160 04 Mar 21 nicklas 299     if (!hasFatalError) Wizard.hideWizardStatus();
6160 04 Mar 21 nicklas 300
6160 04 Mar 21 nicklas 301     var response = null;
6160 04 Mar 21 nicklas 302     var messages = null;
6160 04 Mar 21 nicklas 303     try
6160 04 Mar 21 nicklas 304     {
6160 04 Mar 21 nicklas 305       if (request.status != 200)
6160 04 Mar 21 nicklas 306       {
6160 04 Mar 21 nicklas 307         throw new Error(request.responseText);
6160 04 Mar 21 nicklas 308       }
6160 04 Mar 21 nicklas 309       else
6160 04 Mar 21 nicklas 310       {
6160 04 Mar 21 nicklas 311         if (request.getResponseHeader('Content-Type').indexOf('json') != -1)
6160 04 Mar 21 nicklas 312         {
6160 04 Mar 21 nicklas 313           response = JSON.parse(request.responseText);
6160 04 Mar 21 nicklas 314           messages = response.messages;
6160 04 Mar 21 nicklas 315           if (response.status != 'ok')
6160 04 Mar 21 nicklas 316           {
6160 04 Mar 21 nicklas 317             throw new Error(response.message || response.stacktrace);
6160 04 Mar 21 nicklas 318           }
6160 04 Mar 21 nicklas 319         }
6160 04 Mar 21 nicklas 320         else
6160 04 Mar 21 nicklas 321         {
6160 04 Mar 21 nicklas 322           response = request.responseText;
6160 04 Mar 21 nicklas 323         }
6160 04 Mar 21 nicklas 324         if (request.jsonCallback && request.jsonCallback.call)
6160 04 Mar 21 nicklas 325         {
6160 04 Mar 21 nicklas 326           request.jsonCallback.call(null, response);
6160 04 Mar 21 nicklas 327         }
6160 04 Mar 21 nicklas 328       }
6160 04 Mar 21 nicklas 329     }
6160 04 Mar 21 nicklas 330     catch (err)
6160 04 Mar 21 nicklas 331     {
6160 04 Mar 21 nicklas 332       if (request.errorCallback && request.errorCallback.call)
6160 04 Mar 21 nicklas 333       {
6160 04 Mar 21 nicklas 334         request.errorCallback.call(null, err, response);
6160 04 Mar 21 nicklas 335       }
6160 04 Mar 21 nicklas 336       else
6160 04 Mar 21 nicklas 337       {
6160 04 Mar 21 nicklas 338         Wizard.setFatalError(err, messages);
6160 04 Mar 21 nicklas 339       }
6160 04 Mar 21 nicklas 340     }
6160 04 Mar 21 nicklas 341   }
6160 04 Mar 21 nicklas 342   
6160 04 Mar 21 nicklas 343   
6160 04 Mar 21 nicklas 344   /**
6160 04 Mar 21 nicklas 345     A fatal error has occurred and the wizard will not be able to continue.
6160 04 Mar 21 nicklas 346     This method will display the error message, disable all form input fields
6160 04 Mar 21 nicklas 347     and buttons (except the 'Restart' button).
6160 04 Mar 21 nicklas 348   */
6160 04 Mar 21 nicklas 349   wizard.setFatalError = function(err, messages)
6160 04 Mar 21 nicklas 350   {
6160 04 Mar 21 nicklas 351     hasFatalError = true;
6160 04 Mar 21 nicklas 352     var msg = err;
6160 04 Mar 21 nicklas 353     if (err.toString)
6160 04 Mar 21 nicklas 354     {
6160 04 Mar 21 nicklas 355       App.debug(err.toString());
6160 04 Mar 21 nicklas 356       msg = Strings.encodeTags(err.toString());
6160 04 Mar 21 nicklas 357       if (err.stack && !messages)
6160 04 Mar 21 nicklas 358       {
6160 04 Mar 21 nicklas 359         App.debug(err.stack);
6160 04 Mar 21 nicklas 360         msg += '<br><pre>'+Strings.encodeTags(err.stack)+'</pre>';
6160 04 Mar 21 nicklas 361       }
6160 04 Mar 21 nicklas 362     }
6160 04 Mar 21 nicklas 363     else
6160 04 Mar 21 nicklas 364     {
6160 04 Mar 21 nicklas 365       App.debug(msg);
6160 04 Mar 21 nicklas 366     }
6160 04 Mar 21 nicklas 367     
6160 04 Mar 21 nicklas 368     if (messages) 
6160 04 Mar 21 nicklas 369     {
6160 04 Mar 21 nicklas 370       msg = '<div class="messagecontainer error">'+msg+'</div>'+internal.generateMessageList(messages).html;
6160 04 Mar 21 nicklas 371       wizard.setWizardStatus('success', msg);
6160 04 Mar 21 nicklas 372     }
6160 04 Mar 21 nicklas 373     else
6160 04 Mar 21 nicklas 374     {
6160 04 Mar 21 nicklas 375       wizard.setWizardStatus('messagecontainer error', msg || 'Unexpected failure!');
6160 04 Mar 21 nicklas 376     }
6160 04 Mar 21 nicklas 377     
6160 04 Mar 21 nicklas 378     Doc.addClass('wizard', 'disabled');
6160 04 Mar 21 nicklas 379     internal.disableAllFormElements('wizard', true);
6160 04 Mar 21 nicklas 380     Doc.show('gorestart');
6160 04 Mar 21 nicklas 381   }
6160 04 Mar 21 nicklas 382   
6160 04 Mar 21 nicklas 383   /**
6160 04 Mar 21 nicklas 384     Display a global wizard-status message. This may for example be
6160 04 Mar 21 nicklas 385     an error message, a work-in-progress indicator, etc.
6160 04 Mar 21 nicklas 386   */
6160 04 Mar 21 nicklas 387   wizard.setWizardStatus = function(className, msg)
6160 04 Mar 21 nicklas 388   {
6160 04 Mar 21 nicklas 389     var status = Doc.element('wizard-status');
6160 04 Mar 21 nicklas 390     status.className = className;
6160 04 Mar 21 nicklas 391     status.innerHTML = msg;
6160 04 Mar 21 nicklas 392     Doc.show(status);
6160 04 Mar 21 nicklas 393   }
6160 04 Mar 21 nicklas 394   
6160 04 Mar 21 nicklas 395   
6160 04 Mar 21 nicklas 396   /**
6160 04 Mar 21 nicklas 397     Hide the current global wizard-status message.
6160 04 Mar 21 nicklas 398   */
6160 04 Mar 21 nicklas 399   wizard.hideWizardStatus = function()
6160 04 Mar 21 nicklas 400   {
6160 04 Mar 21 nicklas 401     Doc.hide('wizard-status');
6160 04 Mar 21 nicklas 402     Doc.hide('wizard-progress');
6160 04 Mar 21 nicklas 403   }
6160 04 Mar 21 nicklas 404   
6161 04 Mar 21 nicklas 405     /**
6161 04 Mar 21 nicklas 406     Display a 'loading' global wizard-status message.
6161 04 Mar 21 nicklas 407     The 'progress' parameter is optional. If given,
6161 04 Mar 21 nicklas 408     it should be the name of a progress reporter.
6161 04 Mar 21 nicklas 409     This activates the display of a progress bar which
6161 04 Mar 21 nicklas 410     Relax will update at regular intervals in the background.
6161 04 Mar 21 nicklas 411     See SessionServlet (cmd=GetProgress) for more information.
6161 04 Mar 21 nicklas 412   */
6161 04 Mar 21 nicklas 413   wizard.showLoadingAnimation = function(msg, progress)
6161 04 Mar 21 nicklas 414   {
6161 04 Mar 21 nicklas 415     wizard.setWizardStatus('loading', msg  || 'Working, please wait...');
6161 04 Mar 21 nicklas 416     if (progress)
6161 04 Mar 21 nicklas 417     {
6161 04 Mar 21 nicklas 418       var progressBar = Doc.element('wizard-progress');
6161 04 Mar 21 nicklas 419       progressBar.classList.add('progress-bar');
6161 04 Mar 21 nicklas 420
6161 04 Mar 21 nicklas 421       // Get/create child elements
6161 04 Mar 21 nicklas 422       var done = Doc.element('wizard-progress-done');
6161 04 Mar 21 nicklas 423       if (!done)
6161 04 Mar 21 nicklas 424       {
6161 04 Mar 21 nicklas 425         done = document.createElement('span');
6161 04 Mar 21 nicklas 426         done.id = 'wizard-progress-done';
6161 04 Mar 21 nicklas 427         done.className = 'progress-bar-done bg-filled-100 topborder bottomborder leftborder';
6161 04 Mar 21 nicklas 428         progressBar.appendChild(done);
6161 04 Mar 21 nicklas 429       }
6161 04 Mar 21 nicklas 430       var remain = Doc.element('wizard-progress-remain');
6161 04 Mar 21 nicklas 431       if (!remain)
6161 04 Mar 21 nicklas 432       {
6161 04 Mar 21 nicklas 433         remain = document.createElement('span');
6161 04 Mar 21 nicklas 434         remain.id = 'wizard-progress-remain';
6161 04 Mar 21 nicklas 435         remain.className = 'progress-bar-remain topborder bottomborder rightborder';
6161 04 Mar 21 nicklas 436         progressBar.appendChild(remain);
6161 04 Mar 21 nicklas 437       }
6161 04 Mar 21 nicklas 438         
6161 04 Mar 21 nicklas 439       progressBar.setAttribute('value', '0%');
6161 04 Mar 21 nicklas 440       done.style.width = '0%';
6161 04 Mar 21 nicklas 441       remain.style.width = '100%';
6161 04 Mar 21 nicklas 442       Doc.show(progressBar);
6161 04 Mar 21 nicklas 443       
6161 04 Mar 21 nicklas 444       // Initial request for updating the progress bar
6161 04 Mar 21 nicklas 445       progressTimer = setTimeout(internal.requestProgressUpdate, 1500, progress);
6161 04 Mar 21 nicklas 446     }
6161 04 Mar 21 nicklas 447   }
6161 04 Mar 21 nicklas 448
6161 04 Mar 21 nicklas 449   /**
6161 04 Mar 21 nicklas 450     Make a request for the progress information. The 'progress'
6161 04 Mar 21 nicklas 451     parameter is the name of a progress bar.
6161 04 Mar 21 nicklas 452   */
6161 04 Mar 21 nicklas 453   internal.requestProgressUpdate = function(progress)
6161 04 Mar 21 nicklas 454   {
6161 04 Mar 21 nicklas 455     if (hasFatalError) return;
6161 04 Mar 21 nicklas 456     var url = '../Session.servlet?ID='+App.getSessionId();
6161 04 Mar 21 nicklas 457     url += '&cmd=GetProgress';
6161 04 Mar 21 nicklas 458     url += '&name='+encodeURIComponent(progress);
6161 04 Mar 21 nicklas 459     
6161 04 Mar 21 nicklas 460     var request = Ajax.getXmlHttpRequest();
6161 04 Mar 21 nicklas 461     request.open("GET", url, true);
6161 04 Mar 21 nicklas 462     Ajax.setReadyStateHandler(request, internal.onProgressReport, internal.onProgressReport);
6161 04 Mar 21 nicklas 463     request.send(null);
6161 04 Mar 21 nicklas 464   }
6161 04 Mar 21 nicklas 465   
6161 04 Mar 21 nicklas 466   /**
6161 04 Mar 21 nicklas 467     Callback when receiving a progress update.
6161 04 Mar 21 nicklas 468   */
6161 04 Mar 21 nicklas 469   internal.onProgressReport = function(request)
6161 04 Mar 21 nicklas 470   {
6161 04 Mar 21 nicklas 471     if (hasFatalError) return;
6161 04 Mar 21 nicklas 472     if (debug) App.debug(Strings.encodeTags(request.responseText));
6161 04 Mar 21 nicklas 473     var response;
6161 04 Mar 21 nicklas 474     var error = false;
6161 04 Mar 21 nicklas 475     try
6161 04 Mar 21 nicklas 476     {
6161 04 Mar 21 nicklas 477       response = JSON.parse(request.responseText);
6161 04 Mar 21 nicklas 478       if (response.status != 'ok')
6161 04 Mar 21 nicklas 479       {
6161 04 Mar 21 nicklas 480         error = response.message || response.stacktrace || 'Unexpected error';
6161 04 Mar 21 nicklas 481       }
6161 04 Mar 21 nicklas 482     }
6161 04 Mar 21 nicklas 483     catch (ex)
6161 04 Mar 21 nicklas 484     {
6161 04 Mar 21 nicklas 485       error = ex;
6161 04 Mar 21 nicklas 486     }
6161 04 Mar 21 nicklas 487     
6161 04 Mar 21 nicklas 488     if (error)
6161 04 Mar 21 nicklas 489     {
6161 04 Mar 21 nicklas 490       return;
6161 04 Mar 21 nicklas 491     }
6161 04 Mar 21 nicklas 492   
6161 04 Mar 21 nicklas 493     var progress = response.progress;
6161 04 Mar 21 nicklas 494     var progressBar = Doc.element('wizard-progress');
6161 04 Mar 21 nicklas 495   
6161 04 Mar 21 nicklas 496     if (!progress) 
6161 04 Mar 21 nicklas 497     {
6161 04 Mar 21 nicklas 498       // Give up if no progress information
6161 04 Mar 21 nicklas 499       Doc.hide(progressBar);
6161 04 Mar 21 nicklas 500       return;
6161 04 Mar 21 nicklas 501     }
6161 04 Mar 21 nicklas 502     
6161 04 Mar 21 nicklas 503     var done = Doc.element('wizard-progress-done');
6161 04 Mar 21 nicklas 504     var remain = Doc.element('wizard-progress-remain');
6161 04 Mar 21 nicklas 505   
6161 04 Mar 21 nicklas 506     progressBar.setAttribute('value', progress.percent+'%');
6161 04 Mar 21 nicklas 507     done.style.width = progress.percent+'%';
6161 04 Mar 21 nicklas 508     remain.style.width = (100-progress.percent)+'%';
6161 04 Mar 21 nicklas 509     
6161 04 Mar 21 nicklas 510     if (progress.message)
6161 04 Mar 21 nicklas 511     {
6161 04 Mar 21 nicklas 512       Doc.element('wizard-status').innerHTML = Strings.encodeTags(progress.message);
6161 04 Mar 21 nicklas 513     }
6161 04 Mar 21 nicklas 514     
6161 04 Mar 21 nicklas 515     if (progress.percent < 100)
6161 04 Mar 21 nicklas 516     {
6161 04 Mar 21 nicklas 517       // Continue to request updates until we 100% has been reached
6161 04 Mar 21 nicklas 518       progressTimer = setTimeout(internal.requestProgressUpdate, 1500, progress.name);
6161 04 Mar 21 nicklas 519     }
6161 04 Mar 21 nicklas 520     else
6161 04 Mar 21 nicklas 521     {
6161 04 Mar 21 nicklas 522       progressTimer = null;
6161 04 Mar 21 nicklas 523       Doc.hide(progressBar);
6161 04 Mar 21 nicklas 524     }
6161 04 Mar 21 nicklas 525   }
6161 04 Mar 21 nicklas 526
6160 04 Mar 21 nicklas 527   // Disable all form elements inside the given container
6160 04 Mar 21 nicklas 528   internal.disableAllFormElements = function(container, hideButtons)
6160 04 Mar 21 nicklas 529   {
6160 04 Mar 21 nicklas 530     container = Doc.element(container);
6160 04 Mar 21 nicklas 531     if (!container) return;
6160 04 Mar 21 nicklas 532     var input = container.getElementsByTagName('input');
6160 04 Mar 21 nicklas 533     for (var i = 0; i < input.length; i++)
6160 04 Mar 21 nicklas 534     {
6160 04 Mar 21 nicklas 535       input[i].disabled = true;
6160 04 Mar 21 nicklas 536     }
6160 04 Mar 21 nicklas 537     var select = container.getElementsByTagName('select');
6160 04 Mar 21 nicklas 538     for (var i = 0; i < select.length; i++)
6160 04 Mar 21 nicklas 539     {
6160 04 Mar 21 nicklas 540       select[i].disabled = true;
6160 04 Mar 21 nicklas 541     }
6160 04 Mar 21 nicklas 542     var text = container.getElementsByTagName('textarea');
6160 04 Mar 21 nicklas 543     for (var i = 0; i < text.length; i++)
6160 04 Mar 21 nicklas 544     {
6160 04 Mar 21 nicklas 545       text[i].disabled = true;
6160 04 Mar 21 nicklas 546     }
6160 04 Mar 21 nicklas 547     if (hideButtons)
6160 04 Mar 21 nicklas 548     {
6160 04 Mar 21 nicklas 549       internal.hideButtons(container);
6160 04 Mar 21 nicklas 550     }
6160 04 Mar 21 nicklas 551   }
6160 04 Mar 21 nicklas 552   
6160 04 Mar 21 nicklas 553   // Hide all 'basicbuttons':s in the given container
6160 04 Mar 21 nicklas 554   internal.hideButtons = function(container)
6160 04 Mar 21 nicklas 555   {
6160 04 Mar 21 nicklas 556     container = Doc.element(container);
6160 04 Mar 21 nicklas 557     var buttons = container.getElementsByClassName('basicbutton');
6160 04 Mar 21 nicklas 558     for (var i = 0; i < buttons.length; i++)
6160 04 Mar 21 nicklas 559     {
6160 04 Mar 21 nicklas 560       Doc.hide(buttons[i]);
6160 04 Mar 21 nicklas 561     }
6160 04 Mar 21 nicklas 562   }
6160 04 Mar 21 nicklas 563   
6160 04 Mar 21 nicklas 564   
6160 04 Mar 21 nicklas 565   /**
6160 04 Mar 21 nicklas 566     Initialize the wizard:
6160 04 Mar 21 nicklas 567     * Display version number
6160 04 Mar 21 nicklas 568   */
6160 04 Mar 21 nicklas 569   internal.initWizard = function()
6160 04 Mar 21 nicklas 570   {
6160 04 Mar 21 nicklas 571     var path = document.getElementsByClassName('path');
6160 04 Mar 21 nicklas 572     if (path && path.length)
6160 04 Mar 21 nicklas 573     {
6160 04 Mar 21 nicklas 574       var varsearchVersion = document.createElement('span');
6160 04 Mar 21 nicklas 575       varsearchVersion.id = 'varsearch-version';
6160 04 Mar 21 nicklas 576       varsearchVersion.innerHTML = '[Variant Search '+VarSearch.VERSION+']';
6160 04 Mar 21 nicklas 577       path[0].appendChild(varsearchVersion);
6160 04 Mar 21 nicklas 578     }
6160 04 Mar 21 nicklas 579   }
6160 04 Mar 21 nicklas 580   
6160 04 Mar 21 nicklas 581   Doc.addFinalizer(internal.initWizard);
6160 04 Mar 21 nicklas 582   return wizard;
6160 04 Mar 21 nicklas 583 }();
6160 04 Mar 21 nicklas 584
6161 04 Mar 21 nicklas 585 /**
6161 04 Mar 21 nicklas 586   Holds one or more filter criterias. Use the 'add*' methods
6161 04 Mar 21 nicklas 587   to add criterias to the filter, and the 'check(item)' method
6161 04 Mar 21 nicklas 588   to evaluate all criterias against a single item.
6161 04 Mar 21 nicklas 589 */
6161 04 Mar 21 nicklas 590 var Filter = function()
6161 04 Mar 21 nicklas 591 {
6161 04 Mar 21 nicklas 592   this.criterias = [];
6161 04 Mar 21 nicklas 593 }
6160 04 Mar 21 nicklas 594
6161 04 Mar 21 nicklas 595 /**
6161 04 Mar 21 nicklas 596   Adds a text criteria comparing the given item property to
6161 04 Mar 21 nicklas 597   a static value (f2).
6161 04 Mar 21 nicklas 598 */
6161 04 Mar 21 nicklas 599 Filter.prototype.addTextCriteria = function(itemProp, f2)
6161 04 Mar 21 nicklas 600 {
6161 04 Mar 21 nicklas 601   if (f2 != '') 
6161 04 Mar 21 nicklas 602   {
6161 04 Mar 21 nicklas 603     var cmp;
6161 04 Mar 21 nicklas 604     if (f2 == '=')
6161 04 Mar 21 nicklas 605     {
6161 04 Mar 21 nicklas 606       cmp = Comparator.eq;
6161 04 Mar 21 nicklas 607       f2 = null;
6161 04 Mar 21 nicklas 608     }
6161 04 Mar 21 nicklas 609     else if (f2 == '<>')
6161 04 Mar 21 nicklas 610     {
6161 04 Mar 21 nicklas 611       cmp = Comparator.neq;
6161 04 Mar 21 nicklas 612       f2 = null;
6161 04 Mar 21 nicklas 613     }
6161 04 Mar 21 nicklas 614     else
6161 04 Mar 21 nicklas 615     {
6161 04 Mar 21 nicklas 616       cmp = Comparator.eq;
6161 04 Mar 21 nicklas 617     }
6161 04 Mar 21 nicklas 618     this.criterias[this.criterias.length] = function(item) { return cmp(itemProp(item), f2) };
6161 04 Mar 21 nicklas 619   }
6161 04 Mar 21 nicklas 620 }
6161 04 Mar 21 nicklas 621
6161 04 Mar 21 nicklas 622 /**
6161 04 Mar 21 nicklas 623   Adds a text criteria checking if the given item property to
6161 04 Mar 21 nicklas 624   contains a static value (f2).
6161 04 Mar 21 nicklas 625 */
6161 04 Mar 21 nicklas 626 Filter.prototype.addContainsCriteria = function(itemProp, f2)
6161 04 Mar 21 nicklas 627 {
6161 04 Mar 21 nicklas 628   if (f2 != '') 
6161 04 Mar 21 nicklas 629   {
6161 04 Mar 21 nicklas 630     var cmp;
6161 04 Mar 21 nicklas 631     if (f2 == '=')
6161 04 Mar 21 nicklas 632     {
6161 04 Mar 21 nicklas 633       cmp = Comparator.eq;
6161 04 Mar 21 nicklas 634       f2 = null;
6161 04 Mar 21 nicklas 635     }
6161 04 Mar 21 nicklas 636     else if (f2 == '<>')
6161 04 Mar 21 nicklas 637     {
6161 04 Mar 21 nicklas 638       cmp = Comparator.neq;
6161 04 Mar 21 nicklas 639       f2 = null;
6161 04 Mar 21 nicklas 640     }
6161 04 Mar 21 nicklas 641     else
6161 04 Mar 21 nicklas 642     {
6161 04 Mar 21 nicklas 643       cmp = Comparator.contains;
6161 04 Mar 21 nicklas 644     }
6161 04 Mar 21 nicklas 645     this.criterias[this.criterias.length] = function(item) { return cmp(itemProp(item), f2) };
6161 04 Mar 21 nicklas 646   }
6161 04 Mar 21 nicklas 647 }
6161 04 Mar 21 nicklas 648
6161 04 Mar 21 nicklas 649 /**
6161 04 Mar 21 nicklas 650   Evaluate all criterias. Return true if all pass, false otherwise.
6161 04 Mar 21 nicklas 651 */
6161 04 Mar 21 nicklas 652 Filter.prototype.check = function(item)
6161 04 Mar 21 nicklas 653 {
6161 04 Mar 21 nicklas 654   for (var i = 0; i < this.criterias.length; i++)
6161 04 Mar 21 nicklas 655   {
6161 04 Mar 21 nicklas 656     if (!this.criterias[i](item)) return false;
6161 04 Mar 21 nicklas 657   }
6161 04 Mar 21 nicklas 658   return true;
6161 04 Mar 21 nicklas 659 }
6161 04 Mar 21 nicklas 660
6161 04 Mar 21 nicklas 661
6161 04 Mar 21 nicklas 662 var Comparator = function()
6161 04 Mar 21 nicklas 663 {
6161 04 Mar 21 nicklas 664   var cmp = {};
6161 04 Mar 21 nicklas 665   
6161 04 Mar 21 nicklas 666   /**
6161 04 Mar 21 nicklas 667     Return a comparator based on the symbol
6161 04 Mar 21 nicklas 668     which can be either '>' or '<' to return
6161 04 Mar 21 nicklas 669     Comparator.gt or Comparator.lt. Otherwise
6161 04 Mar 21 nicklas 670     the given default or Comparator.eq is returned.
6161 04 Mar 21 nicklas 671   */
6161 04 Mar 21 nicklas 672   cmp.get = function(symbol, defaultCmp)
6161 04 Mar 21 nicklas 673   {
6161 04 Mar 21 nicklas 674     var c;
6161 04 Mar 21 nicklas 675     if (symbol == '<')
6161 04 Mar 21 nicklas 676     {
6161 04 Mar 21 nicklas 677       c = cmp.lt;
6161 04 Mar 21 nicklas 678     }
6161 04 Mar 21 nicklas 679     else if (symbol == '>')
6161 04 Mar 21 nicklas 680     {
6161 04 Mar 21 nicklas 681       c = cmp.gt;
6161 04 Mar 21 nicklas 682     }
6161 04 Mar 21 nicklas 683     else
6161 04 Mar 21 nicklas 684     {
6161 04 Mar 21 nicklas 685       c = defaultCmp || cmp.eq;
6161 04 Mar 21 nicklas 686     }
6161 04 Mar 21 nicklas 687     return c;
6161 04 Mar 21 nicklas 688   }
6161 04 Mar 21 nicklas 689   
6161 04 Mar 21 nicklas 690   // Compare if two values are equal
6161 04 Mar 21 nicklas 691   cmp.eq = function(v1, v2)
6161 04 Mar 21 nicklas 692   {
6161 04 Mar 21 nicklas 693     return v1 == v2;
6161 04 Mar 21 nicklas 694   }
6161 04 Mar 21 nicklas 695   
6161 04 Mar 21 nicklas 696   // Compare if two values are different
6161 04 Mar 21 nicklas 697   cmp.neq = function(v1, v2)
6161 04 Mar 21 nicklas 698   {
6161 04 Mar 21 nicklas 699     return v1 != v2;
6161 04 Mar 21 nicklas 700   }
6161 04 Mar 21 nicklas 701   
6161 04 Mar 21 nicklas 702   // Check if v2 is contained in v1
6161 04 Mar 21 nicklas 703   cmp.contains = function(v1, v2)
6161 04 Mar 21 nicklas 704   {
6161 04 Mar 21 nicklas 705     return v1 != null && v2 != null && v1.indexOf(v2) >= 0;
6161 04 Mar 21 nicklas 706   }
6161 04 Mar 21 nicklas 707   
6161 04 Mar 21 nicklas 708   // Compare if two values are appoximately equals (difference is not more than 0.05)
6161 04 Mar 21 nicklas 709   cmp.approx = function(v1, v2)
6161 04 Mar 21 nicklas 710   {
6161 04 Mar 21 nicklas 711     return Math.abs(v2 - v1) < 0.05;
6161 04 Mar 21 nicklas 712   }
6161 04 Mar 21 nicklas 713   
6161 04 Mar 21 nicklas 714   // Compare if v1 starts with the same string as v2
6161 04 Mar 21 nicklas 715   cmp.startsWith = function(v1, v2)
6161 04 Mar 21 nicklas 716   {
6161 04 Mar 21 nicklas 717     return v1 != null && v1.indexOf(v2) == 0;
6161 04 Mar 21 nicklas 718   }
6161 04 Mar 21 nicklas 719   
6161 04 Mar 21 nicklas 720   // Compare if v1 is greater than or equal to v2
6161 04 Mar 21 nicklas 721   cmp.gt = function(v1, v2)
6161 04 Mar 21 nicklas 722   {
6161 04 Mar 21 nicklas 723     return v1 != null && v2 != null && v1 >= v2;
6161 04 Mar 21 nicklas 724   }
6161 04 Mar 21 nicklas 725   
6161 04 Mar 21 nicklas 726   // Compare if v1 is less than or equal to v2
6161 04 Mar 21 nicklas 727   cmp.lt = function(v1, v2)
6161 04 Mar 21 nicklas 728   {
6161 04 Mar 21 nicklas 729     return v1 != null && v2 != null && v1 <= v2;
6161 04 Mar 21 nicklas 730   }
6161 04 Mar 21 nicklas 731   
6161 04 Mar 21 nicklas 732   return cmp;
6161 04 Mar 21 nicklas 733 }();
6161 04 Mar 21 nicklas 734
6161 04 Mar 21 nicklas 735