extensions/net.sf.basedb.reggie/trunk/resources/libprep/pools.js

Code
Comments
Other
Rev Date Author Line
1807 23 Jan 13 nicklas 1
1807 23 Jan 13 nicklas 2 var POOL_NAMES = 
1807 23 Jan 13 nicklas 3   ['i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'ix', 'x', 'xi', 'xii' ];
1807 23 Jan 13 nicklas 4
3809 22 Mar 16 nicklas 5 var BC_1_8 =   ['AD001', 'AD002', 'AD003', 'AD004', 'AD005', 'AD006', 'AD007', 'AD008'];
3809 22 Mar 16 nicklas 6 var BC_8_1 =   ['AD008', 'AD007', 'AD006', 'AD005', 'AD004', 'AD003', 'AD002', 'AD001'];
3809 22 Mar 16 nicklas 7 var BC_9_16 =  ['AD009', 'AD010', 'AD011', 'AD012', 'AD013', 'AD014', 'AD015', 'AD016'];
3809 22 Mar 16 nicklas 8 var BC_16_9 =  ['AD016', 'AD015', 'AD014', 'AD013', 'AD012', 'AD011', 'AD010', 'AD009'];
3809 22 Mar 16 nicklas 9 var BC_18_27 = ['AD018', 'AD019', 'AD020', 'AD021', 'AD022', 'AD023', 'AD025', 'AD027'];
3809 22 Mar 16 nicklas 10 var BC_27_18 = ['AD027', 'AD025', 'AD023', 'AD022', 'AD021', 'AD020', 'AD019', 'AD018'];
1814 30 Jan 13 nicklas 11
1809 25 Jan 13 nicklas 12 // Index Sets = IS
1814 30 Jan 13 nicklas 13 var IS_G1 = {color: 'bg-green',  barcodes: BC_1_8};
1814 30 Jan 13 nicklas 14 var IS_G2 = {color: 'bg-green',  barcodes: BC_9_16};
1814 30 Jan 13 nicklas 15 var IS_G3 = {color: 'bg-green',  barcodes: BC_18_27};
1809 25 Jan 13 nicklas 16
2218 10 Feb 14 nicklas 17 var IS_G4 = {color: 'bg-green-dk',  barcodes: BC_8_1};
2218 10 Feb 14 nicklas 18 var IS_G5 = {color: 'bg-green-dk',  barcodes: BC_16_9};
2218 10 Feb 14 nicklas 19 var IS_G6 = {color: 'bg-green-dk',  barcodes: BC_27_18};
2218 10 Feb 14 nicklas 20
1814 30 Jan 13 nicklas 21 var IS_B1 = {color: 'bg-blue',   barcodes: BC_1_8};
1814 30 Jan 13 nicklas 22 var IS_B2 = {color: 'bg-blue',   barcodes: BC_9_16};
1814 30 Jan 13 nicklas 23 var IS_B3 = {color: 'bg-blue',   barcodes: BC_18_27};
1809 25 Jan 13 nicklas 24
2218 10 Feb 14 nicklas 25 var IS_B4 = {color: 'bg-blue-dk',   barcodes: BC_8_1};
2218 10 Feb 14 nicklas 26 var IS_B5 = {color: 'bg-blue-dk',   barcodes: BC_16_9};
2218 10 Feb 14 nicklas 27 var IS_B6 = {color: 'bg-blue-dk',   barcodes: BC_27_18};
2218 10 Feb 14 nicklas 28
1814 30 Jan 13 nicklas 29 var IS_Y1 = {color: 'bg-yellow', barcodes: BC_1_8};
1814 30 Jan 13 nicklas 30 var IS_Y2 = {color: 'bg-yellow', barcodes: BC_9_16};
1814 30 Jan 13 nicklas 31 var IS_Y3 = {color: 'bg-yellow', barcodes: BC_18_27};
1814 30 Jan 13 nicklas 32
2218 10 Feb 14 nicklas 33 var IS_Y4 = {color: 'bg-yellow-dk', barcodes: BC_8_1};
2218 10 Feb 14 nicklas 34 var IS_Y5 = {color: 'bg-yellow-dk', barcodes: BC_16_9};
2218 10 Feb 14 nicklas 35 var IS_Y6 = {color: 'bg-yellow-dk', barcodes: BC_27_18};
1814 30 Jan 13 nicklas 36
1807 23 Jan 13 nicklas 37 var POOL_SCHEMA =
1807 23 Jan 13 nicklas 38 [
1807 23 Jan 13 nicklas 39   {
4426 27 Mar 17 nicklas 40     id: '1by24offset',
4426 27 Mar 17 nicklas 41     name: '1 pool × 24 samples (offset)',
4426 27 Mar 17 nicklas 42     type: 'manual',
4426 27 Mar 17 nicklas 43     numPools: 1,
4426 27 Mar 17 nicklas 44     numWellsPerPool: 24,
4426 27 Mar 17 nicklas 45     columnClasses: ['pool-left', 'pool-middle', 'pool-right'],
4426 27 Mar 17 nicklas 46     barcodeVariants:
4426 27 Mar 17 nicklas 47       [
4426 27 Mar 17 nicklas 48          {name: 'A', indexSets: [null, null, IS_G1, IS_B2, IS_Y3, null, null, null, null, null, null, null]}
4426 27 Mar 17 nicklas 49       ],
4426 27 Mar 17 nicklas 50     
4426 27 Mar 17 nicklas 51     getPoolNumForColumn: function(colNum)
4426 27 Mar 17 nicklas 52     {
4426 27 Mar 17 nicklas 53       var poolNum = -1;
4426 27 Mar 17 nicklas 54       if (colNum >= 2 && colNum <= 4)
4426 27 Mar 17 nicklas 55       {
4426 27 Mar 17 nicklas 56         poolNum = 0;
4426 27 Mar 17 nicklas 57       }
4426 27 Mar 17 nicklas 58       return poolNum;
4426 27 Mar 17 nicklas 59     },
4426 27 Mar 17 nicklas 60        
4426 27 Mar 17 nicklas 61     getClassNameForWell: function(well)
4426 27 Mar 17 nicklas 62     {
4426 27 Mar 17 nicklas 63       var c = well.column;
4426 27 Mar 17 nicklas 64       var cls = (c >= 2 && c <= 4) ? 'primary ' : 'secondary ';
4426 27 Mar 17 nicklas 65       cls += (c >= 1 && c <= 5) ? this.columnClasses[(c+1) % 3] : '';
4426 27 Mar 17 nicklas 66       return cls;
4426 27 Mar 17 nicklas 67     },
4426 27 Mar 17 nicklas 68       
4426 27 Mar 17 nicklas 69     getWellsInPool: function(plate, poolNum)
4426 27 Mar 17 nicklas 70     {
4426 27 Mar 17 nicklas 71       var result = [];
4426 27 Mar 17 nicklas 72       var index = poolNum*this.numWellsPerPool+2*plate.rows;
4426 27 Mar 17 nicklas 73       while (result.length < this.numWellsPerPool && index < plate.wells.length)
4426 27 Mar 17 nicklas 74       {
4426 27 Mar 17 nicklas 75         result[result.length] = plate.wells[index];
4426 27 Mar 17 nicklas 76         index++;
4426 27 Mar 17 nicklas 77       }
4426 27 Mar 17 nicklas 78       return result;
4426 27 Mar 17 nicklas 79     }
4426 27 Mar 17 nicklas 80   },
4426 27 Mar 17 nicklas 81   {
4426 27 Mar 17 nicklas 82     id: '2by24offset',
4426 27 Mar 17 nicklas 83     name: '2 pool × 24 samples (offset)',
4426 27 Mar 17 nicklas 84     type: 'manual',
4426 27 Mar 17 nicklas 85     numPools: 2,
4426 27 Mar 17 nicklas 86     numWellsPerPool: 24,
4426 27 Mar 17 nicklas 87     columnClasses: ['pool-left', 'pool-middle', 'pool-right'],
4426 27 Mar 17 nicklas 88     barcodeVariants:
4426 27 Mar 17 nicklas 89       [
4426 27 Mar 17 nicklas 90          {name: 'A-A', indexSets: [null, null, IS_G1, IS_B2, IS_Y3, IS_G1, IS_B2, IS_Y3, null, null, null, null]}
4426 27 Mar 17 nicklas 91       ],
4426 27 Mar 17 nicklas 92     
4426 27 Mar 17 nicklas 93     getPoolNumForColumn: function(colNum)
4426 27 Mar 17 nicklas 94     {
4426 27 Mar 17 nicklas 95       var poolNum = -1;
4426 27 Mar 17 nicklas 96       if (colNum >= 2 && colNum <= 7)
4426 27 Mar 17 nicklas 97       {
4426 27 Mar 17 nicklas 98         poolNum = Math.floor((colNum - 2) / 3);
4426 27 Mar 17 nicklas 99       }
4426 27 Mar 17 nicklas 100       return poolNum;
4426 27 Mar 17 nicklas 101     },
4426 27 Mar 17 nicklas 102        
4426 27 Mar 17 nicklas 103     getClassNameForWell: function(well)
4426 27 Mar 17 nicklas 104     {
4426 27 Mar 17 nicklas 105       var c = well.column;
4426 27 Mar 17 nicklas 106       var cls = (c >= 2 && c <= 7) ? 'primary ' : 'secondary ';
4426 27 Mar 17 nicklas 107       cls += (c >= 1 && c <= 8) ? this.columnClasses[(c+1) % 3] : '';
4426 27 Mar 17 nicklas 108       return cls;
4426 27 Mar 17 nicklas 109     },
4426 27 Mar 17 nicklas 110       
4426 27 Mar 17 nicklas 111     getWellsInPool: function(plate, poolNum)
4426 27 Mar 17 nicklas 112     {
4426 27 Mar 17 nicklas 113       var result = [];
4426 27 Mar 17 nicklas 114       var index = poolNum*this.numWellsPerPool+2*plate.rows;
4426 27 Mar 17 nicklas 115       while (result.length < this.numWellsPerPool && index < plate.wells.length)
4426 27 Mar 17 nicklas 116       {
4426 27 Mar 17 nicklas 117         result[result.length] = plate.wells[index];
4426 27 Mar 17 nicklas 118         index++;
4426 27 Mar 17 nicklas 119       }
4426 27 Mar 17 nicklas 120       return result;
4426 27 Mar 17 nicklas 121     }
4426 27 Mar 17 nicklas 122   },
4426 27 Mar 17 nicklas 123   {
1807 23 Jan 13 nicklas 124     id: '4by24',
1807 23 Jan 13 nicklas 125     name: '4 pools × 24 samples',
3289 30 Apr 15 nicklas 126     type: 'manual',
1807 23 Jan 13 nicklas 127      numPools: 4,
1807 23 Jan 13 nicklas 128      numWellsPerPool: 24,
1807 23 Jan 13 nicklas 129      columnClasses: ['pool-left', 'pool-middle', 'pool-right'],
1809 25 Jan 13 nicklas 130     barcodeVariants:
1809 25 Jan 13 nicklas 131       [
2218 10 Feb 14 nicklas 132          {
2218 10 Feb 14 nicklas 133            name: 'A-B-C-D', 
2218 10 Feb 14 nicklas 134            indexSets: [IS_G1, IS_B2, IS_Y3, IS_G1, IS_B2, IS_Y6, IS_G4, IS_B5, IS_Y3, IS_G4, IS_B5, IS_Y6]
2218 10 Feb 14 nicklas 135          },
2218 10 Feb 14 nicklas 136          {
2218 10 Feb 14 nicklas 137            name: 'A-A-A-A', 
2218 10 Feb 14 nicklas 138            indexSets: [IS_G1, IS_B2, IS_Y3, IS_G1, IS_B2, IS_Y3, IS_G1, IS_B2, IS_Y3, IS_G1, IS_B2, IS_Y3]
2218 10 Feb 14 nicklas 139          },
2218 10 Feb 14 nicklas 140          {
2218 10 Feb 14 nicklas 141            /* Same as A-A-A-A, kept for backwards compatibility */
2218 10 Feb 14 nicklas 142            name: 'FULL', 
2218 10 Feb 14 nicklas 143            disabled: 1,
2218 10 Feb 14 nicklas 144            indexSets: [IS_G1, IS_B2, IS_Y3, IS_G1, IS_B2, IS_Y3, IS_G1, IS_B2, IS_Y3, IS_G1, IS_B2, IS_Y3]
2218 10 Feb 14 nicklas 145          }
1809 25 Jan 13 nicklas 146       ],
1807 23 Jan 13 nicklas 147
1807 23 Jan 13 nicklas 148     getPoolNumForColumn: function(colNum)
1807 23 Jan 13 nicklas 149     {
1807 23 Jan 13 nicklas 150       var poolNum = Math.floor(colNum / 3);
1807 23 Jan 13 nicklas 151       return poolNum;
1807 23 Jan 13 nicklas 152     },
1807 23 Jan 13 nicklas 153
1807 23 Jan 13 nicklas 154      getClassNameForWell: function(well)
1807 23 Jan 13 nicklas 155     {
1807 23 Jan 13 nicklas 156       var c = well.column;
1807 23 Jan 13 nicklas 157       var cls = 'primary ';
1807 23 Jan 13 nicklas 158       cls += this.columnClasses[c % 3];
1807 23 Jan 13 nicklas 159       return cls;
1807 23 Jan 13 nicklas 160     },
1807 23 Jan 13 nicklas 161     
1807 23 Jan 13 nicklas 162     getWellsInPool: function(plate, poolNum)
1807 23 Jan 13 nicklas 163     {
1807 23 Jan 13 nicklas 164       var result = [];
1807 23 Jan 13 nicklas 165       var index = poolNum * this.numWellsPerPool;
1807 23 Jan 13 nicklas 166       while (result.length < this.numWellsPerPool && index < plate.wells.length)
1807 23 Jan 13 nicklas 167       {
1807 23 Jan 13 nicklas 168         result[result.length] = plate.wells[index];
1807 23 Jan 13 nicklas 169         index++;
1807 23 Jan 13 nicklas 170       }
1807 23 Jan 13 nicklas 171       return result;
1807 23 Jan 13 nicklas 172     }
1807 23 Jan 13 nicklas 173   
1807 23 Jan 13 nicklas 174   },
1807 23 Jan 13 nicklas 175   {
3235 10 Apr 15 nicklas 176     id: '1by16',
3235 10 Apr 15 nicklas 177     name: '1 pool × 16 samples',
3289 30 Apr 15 nicklas 178     type: 'manual',
3235 10 Apr 15 nicklas 179     numPools: 1,
3235 10 Apr 15 nicklas 180     numWellsPerPool: 16,
3235 10 Apr 15 nicklas 181     columnClasses: ['pool-right', 'pool-left'],
3235 10 Apr 15 nicklas 182     barcodeVariants:
3235 10 Apr 15 nicklas 183       [
3235 10 Apr 15 nicklas 184          {name: 'A', indexSets: [null, null, null, null, null, IS_G1, IS_G2, null, null, null, null, null]},
3235 10 Apr 15 nicklas 185          {name: 'B', indexSets: [null, null, null, null, null, IS_B2, IS_B3, null, null, null, null, null]},
3235 10 Apr 15 nicklas 186          {name: 'C', indexSets: [null, null, null, null, null, IS_Y3, IS_Y1, null, null, null, null, null]}
3235 10 Apr 15 nicklas 187       ],
3235 10 Apr 15 nicklas 188     
3235 10 Apr 15 nicklas 189     getPoolNumForColumn: function(colNum)
3235 10 Apr 15 nicklas 190     {
3235 10 Apr 15 nicklas 191       var poolNum = -1;
3235 10 Apr 15 nicklas 192       if (colNum == 5 || colNum == 6)
3235 10 Apr 15 nicklas 193       {
3235 10 Apr 15 nicklas 194         poolNum = 0;
3235 10 Apr 15 nicklas 195       }
3235 10 Apr 15 nicklas 196       return poolNum;
3235 10 Apr 15 nicklas 197     },
3235 10 Apr 15 nicklas 198        
3235 10 Apr 15 nicklas 199     getClassNameForWell: function(well)
3235 10 Apr 15 nicklas 200     {
3235 10 Apr 15 nicklas 201       var c = well.column;
3235 10 Apr 15 nicklas 202       var cls = (c == 5 || c == 6) ? 'primary ' : 'secondary ';
3235 10 Apr 15 nicklas 203       cls += (c >= 4 && c <= 7) ? this.columnClasses[c % 2] : '';
3235 10 Apr 15 nicklas 204       return cls;
3235 10 Apr 15 nicklas 205     },
3235 10 Apr 15 nicklas 206       
3235 10 Apr 15 nicklas 207     getWellsInPool: function(plate, poolNum)
3235 10 Apr 15 nicklas 208     {
3235 10 Apr 15 nicklas 209       var result = [];
3235 10 Apr 15 nicklas 210       var index = poolNum*this.numWellsPerPool+5*plate.rows;
3235 10 Apr 15 nicklas 211       while (result.length < this.numWellsPerPool && index < plate.wells.length)
3235 10 Apr 15 nicklas 212       {
3235 10 Apr 15 nicklas 213         result[result.length] = plate.wells[index];
3235 10 Apr 15 nicklas 214         index++;
3235 10 Apr 15 nicklas 215       }
3235 10 Apr 15 nicklas 216       return result;
3235 10 Apr 15 nicklas 217     }
3235 10 Apr 15 nicklas 218   },
3235 10 Apr 15 nicklas 219   {
3510 24 Sep 15 nicklas 220     id: '1by16offset',
3510 24 Sep 15 nicklas 221     name: '1 pool × 16 samples (offset)',
3510 24 Sep 15 nicklas 222     type: 'manual',
3510 24 Sep 15 nicklas 223     numPools: 1,
3510 24 Sep 15 nicklas 224     numWellsPerPool: 16,
3510 24 Sep 15 nicklas 225     columnClasses: ['pool-left', 'pool-right'],
3510 24 Sep 15 nicklas 226     barcodeVariants:
3510 24 Sep 15 nicklas 227       [
3510 24 Sep 15 nicklas 228          {name: 'A', indexSets: [null, null, IS_G1, IS_G2, null, null, null, null, null, null, null, null]},
3510 24 Sep 15 nicklas 229          {name: 'B', indexSets: [null, null, IS_B2, IS_B3, null, null, null, null, null, null, null, null]},
3510 24 Sep 15 nicklas 230          {name: 'C', indexSets: [null, null, IS_Y3, IS_Y1, null, null, null, null, null, null, null, null]}
3510 24 Sep 15 nicklas 231       ],
3510 24 Sep 15 nicklas 232     
3510 24 Sep 15 nicklas 233     getPoolNumForColumn: function(colNum)
3510 24 Sep 15 nicklas 234     {
3510 24 Sep 15 nicklas 235       var poolNum = -1;
3510 24 Sep 15 nicklas 236       if (colNum == 2 || colNum == 3)
3510 24 Sep 15 nicklas 237       {
3510 24 Sep 15 nicklas 238         poolNum = 0;
3510 24 Sep 15 nicklas 239       }
3510 24 Sep 15 nicklas 240       return poolNum;
3510 24 Sep 15 nicklas 241     },
3510 24 Sep 15 nicklas 242        
3510 24 Sep 15 nicklas 243     getClassNameForWell: function(well)
3510 24 Sep 15 nicklas 244     {
3510 24 Sep 15 nicklas 245       var c = well.column;
3510 24 Sep 15 nicklas 246       var cls = (c == 2 || c == 3) ? 'primary ' : 'secondary ';
3510 24 Sep 15 nicklas 247       cls += (c >= 1 && c <= 4) ? this.columnClasses[c % 2] : '';
3510 24 Sep 15 nicklas 248       return cls;
3510 24 Sep 15 nicklas 249     },
3510 24 Sep 15 nicklas 250       
3510 24 Sep 15 nicklas 251     getWellsInPool: function(plate, poolNum)
3510 24 Sep 15 nicklas 252     {
3510 24 Sep 15 nicklas 253       var result = [];
3510 24 Sep 15 nicklas 254       var index = poolNum*this.numWellsPerPool+2*plate.rows;
3510 24 Sep 15 nicklas 255       while (result.length < this.numWellsPerPool && index < plate.wells.length)
3510 24 Sep 15 nicklas 256       {
3510 24 Sep 15 nicklas 257         result[result.length] = plate.wells[index];
3510 24 Sep 15 nicklas 258         index++;
3510 24 Sep 15 nicklas 259       }
3510 24 Sep 15 nicklas 260       return result;
3510 24 Sep 15 nicklas 261     }
3510 24 Sep 15 nicklas 262   },
3510 24 Sep 15 nicklas 263   {
3235 10 Apr 15 nicklas 264     id: '2by16',
3235 10 Apr 15 nicklas 265     name: '2 pools × 16 samples',
3289 30 Apr 15 nicklas 266     type: 'manual',
3235 10 Apr 15 nicklas 267      numPools: 2,
1807 23 Jan 13 nicklas 268      numWellsPerPool: 16,
1807 23 Jan 13 nicklas 269      columnClasses: ['pool-left', 'pool-right'],
1809 25 Jan 13 nicklas 270     barcodeVariants:
1809 25 Jan 13 nicklas 271       [
3235 10 Apr 15 nicklas 272          {name: 'A-B', indexSets: [null, null, null, null, IS_G1, IS_G2, IS_B3, IS_B1, null, null, null, null]},
3235 10 Apr 15 nicklas 273          {name: 'B-C', indexSets: [null, null, null, null, IS_B3, IS_B1, IS_Y2, IS_Y3, null, null, null, null]},
3235 10 Apr 15 nicklas 274          {name: 'C-A', indexSets: [null, null, null, null, IS_Y2, IS_Y3, IS_G1, IS_G2, null, null, null, null]}
1809 25 Jan 13 nicklas 275       ],
1807 23 Jan 13 nicklas 276
1807 23 Jan 13 nicklas 277     getPoolNumForColumn: function(colNum)
1807 23 Jan 13 nicklas 278     {
1807 23 Jan 13 nicklas 279       var poolNum = -1;
3235 10 Apr 15 nicklas 280       if (colNum >= 4 && colNum <= 7)
1807 23 Jan 13 nicklas 281       {
3235 10 Apr 15 nicklas 282         poolNum = Math.floor((colNum - 4) / 2);
1807 23 Jan 13 nicklas 283       }
1807 23 Jan 13 nicklas 284       return poolNum;
1807 23 Jan 13 nicklas 285     },
1807 23 Jan 13 nicklas 286      
1807 23 Jan 13 nicklas 287      getClassNameForWell: function(well)
1807 23 Jan 13 nicklas 288     {
1807 23 Jan 13 nicklas 289       var c = well.column;
3235 10 Apr 15 nicklas 290       var cls = (c >= 4 && c <= 7) ? 'primary ' : 'secondary ';
3235 10 Apr 15 nicklas 291       if (c >= 3 && c <= 8) cls += this.columnClasses[c % 2];
1807 23 Jan 13 nicklas 292       return cls;
1807 23 Jan 13 nicklas 293     },
1807 23 Jan 13 nicklas 294     
1807 23 Jan 13 nicklas 295     getWellsInPool: function(plate, poolNum)
1807 23 Jan 13 nicklas 296      {
1807 23 Jan 13 nicklas 297        var result = [];
3235 10 Apr 15 nicklas 298        var index = poolNum*this.numWellsPerPool + 4 * plate.rows;
1807 23 Jan 13 nicklas 299       while (result.length < this.numWellsPerPool && index < plate.wells.length)
1807 23 Jan 13 nicklas 300       {
1807 23 Jan 13 nicklas 301         result[result.length] = plate.wells[index];
1807 23 Jan 13 nicklas 302         index++;
1807 23 Jan 13 nicklas 303       }
1807 23 Jan 13 nicklas 304        return result;
1807 23 Jan 13 nicklas 305      }
1807 23 Jan 13 nicklas 306    },
1807 23 Jan 13 nicklas 307   {
4555 29 Aug 17 nicklas 308     id: '2by16offset',
4555 29 Aug 17 nicklas 309     name: '2 pool × 16 samples (offset)',
4555 29 Aug 17 nicklas 310     type: 'manual',
4555 29 Aug 17 nicklas 311     numPools: 2,
4555 29 Aug 17 nicklas 312     numWellsPerPool: 16,
4555 29 Aug 17 nicklas 313     columnClasses: ['pool-left', 'pool-right'],
4555 29 Aug 17 nicklas 314     barcodeVariants:
4555 29 Aug 17 nicklas 315       [
4555 29 Aug 17 nicklas 316          {name: 'A-B', indexSets: [null, null, IS_G1, IS_G2, IS_B3, IS_B1, null, null, null, null, null, null]},
4555 29 Aug 17 nicklas 317          {name: 'B-C', indexSets: [null, null, IS_B3, IS_B1, IS_Y2, IS_Y3, null, null, null, null, null, null]},
4555 29 Aug 17 nicklas 318          {name: 'C-A', indexSets: [null, null, IS_Y2, IS_Y3, IS_G1, IS_G2, null, null, null, null, null, null]}
4555 29 Aug 17 nicklas 319       ],
4555 29 Aug 17 nicklas 320     
4555 29 Aug 17 nicklas 321     getPoolNumForColumn: function(colNum)
4555 29 Aug 17 nicklas 322     {
4555 29 Aug 17 nicklas 323       var poolNum = -1;
4555 29 Aug 17 nicklas 324       if (colNum >= 2 && colNum <= 5)
4555 29 Aug 17 nicklas 325       {
4555 29 Aug 17 nicklas 326         poolNum = Math.floor((colNum - 2) / 2);
4555 29 Aug 17 nicklas 327       }
4555 29 Aug 17 nicklas 328       return poolNum;
4555 29 Aug 17 nicklas 329     },
4555 29 Aug 17 nicklas 330        
4555 29 Aug 17 nicklas 331      getClassNameForWell: function(well)
4555 29 Aug 17 nicklas 332     {
4555 29 Aug 17 nicklas 333       var c = well.column;
4555 29 Aug 17 nicklas 334       var cls = (c >= 2 && c <= 5) ? 'primary ' : 'secondary ';
4555 29 Aug 17 nicklas 335       if (c >= 1 && c <= 6) cls += this.columnClasses[c % 2];
4555 29 Aug 17 nicklas 336       return cls;
4555 29 Aug 17 nicklas 337     },
4555 29 Aug 17 nicklas 338       
4555 29 Aug 17 nicklas 339     getWellsInPool: function(plate, poolNum)
4555 29 Aug 17 nicklas 340      {
4555 29 Aug 17 nicklas 341        var result = [];
4555 29 Aug 17 nicklas 342        var index = poolNum*this.numWellsPerPool + 2*plate.rows;
4555 29 Aug 17 nicklas 343       while (result.length < this.numWellsPerPool && index < plate.wells.length)
4555 29 Aug 17 nicklas 344       {
4555 29 Aug 17 nicklas 345         result[result.length] = plate.wells[index];
4555 29 Aug 17 nicklas 346         index++;
4555 29 Aug 17 nicklas 347       }
4555 29 Aug 17 nicklas 348        return result;
4555 29 Aug 17 nicklas 349      }
4555 29 Aug 17 nicklas 350   },
4555 29 Aug 17 nicklas 351   {
3235 10 Apr 15 nicklas 352     id: '3by16',
3235 10 Apr 15 nicklas 353     name: '3 pools × 16 samples',
3289 30 Apr 15 nicklas 354     type: 'manual',
3235 10 Apr 15 nicklas 355     numPools: 3,
1807 23 Jan 13 nicklas 356     numWellsPerPool: 16,
1807 23 Jan 13 nicklas 357     columnClasses: ['pool-right', 'pool-left'],
1809 25 Jan 13 nicklas 358     barcodeVariants:
1809 25 Jan 13 nicklas 359       [
3235 10 Apr 15 nicklas 360          {name: 'A-B-C', indexSets: [null, null, null, IS_G1, IS_G2, IS_B3, IS_B1, IS_Y2, IS_Y3, null, null, null, null]},
3235 10 Apr 15 nicklas 361          {name: 'B-C-A', indexSets: [null, null, null, IS_B3, IS_B1, IS_Y2, IS_Y3, IS_G1, IS_G2, null, null, null, null]},
3235 10 Apr 15 nicklas 362          {name: 'C-A-B', indexSets: [null, null, null, IS_Y2, IS_Y3, IS_G1, IS_G2, IS_B3, IS_B2, null, null, null, null]}
1809 25 Jan 13 nicklas 363       ],
3235 10 Apr 15 nicklas 364     
1807 23 Jan 13 nicklas 365     getPoolNumForColumn: function(colNum)
1807 23 Jan 13 nicklas 366     {
1807 23 Jan 13 nicklas 367       var poolNum = -1;
3235 10 Apr 15 nicklas 368       if (colNum >= 3 && colNum <= 8)
1807 23 Jan 13 nicklas 369       {
3235 10 Apr 15 nicklas 370         poolNum = Math.floor((colNum - 3) / 2);
1807 23 Jan 13 nicklas 371       }
1807 23 Jan 13 nicklas 372       return poolNum;
1807 23 Jan 13 nicklas 373     },
3235 10 Apr 15 nicklas 374        
1807 23 Jan 13 nicklas 375     getClassNameForWell: function(well)
1807 23 Jan 13 nicklas 376     {
1807 23 Jan 13 nicklas 377       var c = well.column;
3235 10 Apr 15 nicklas 378       var cls = (c >= 3 && c <= 8) ? 'primary ' : 'secondary ';
3235 10 Apr 15 nicklas 379       if (c >= 2 && c <= 9) cls += this.columnClasses[c % 2];
1807 23 Jan 13 nicklas 380       return cls;
1807 23 Jan 13 nicklas 381     },
3235 10 Apr 15 nicklas 382       
3235 10 Apr 15 nicklas 383     getWellsInPool: function(plate, poolNum)
3235 10 Apr 15 nicklas 384     {
3235 10 Apr 15 nicklas 385       var result = [];
3235 10 Apr 15 nicklas 386        var index = poolNum*this.numWellsPerPool + 3 * plate.rows;
3235 10 Apr 15 nicklas 387       while (result.length < this.numWellsPerPool && index < plate.wells.length)
3235 10 Apr 15 nicklas 388       {
3235 10 Apr 15 nicklas 389         result[result.length] = plate.wells[index];
3235 10 Apr 15 nicklas 390         index++;
3235 10 Apr 15 nicklas 391       }
3235 10 Apr 15 nicklas 392       return result;
3235 10 Apr 15 nicklas 393     }
3235 10 Apr 15 nicklas 394   },
3235 10 Apr 15 nicklas 395   {
3235 10 Apr 15 nicklas 396     id: '4by16',
3235 10 Apr 15 nicklas 397     name: '4 pools × 16 samples',
3289 30 Apr 15 nicklas 398     type: 'manual',
3235 10 Apr 15 nicklas 399      numPools: 4,
3235 10 Apr 15 nicklas 400      numWellsPerPool: 16,
3235 10 Apr 15 nicklas 401      columnClasses: ['pool-left', 'pool-right'],
3235 10 Apr 15 nicklas 402     barcodeVariants:
3235 10 Apr 15 nicklas 403       [
3235 10 Apr 15 nicklas 404          {name: 'A-B-C-A', indexSets: [null, null, IS_G1, IS_G2, IS_B3, IS_B1, IS_Y2, IS_Y3, IS_G1, IS_G2, null, null]},
3235 10 Apr 15 nicklas 405          {name: 'B-C-A-B', indexSets: [null, null, IS_B3, IS_B1, IS_Y2, IS_Y3, IS_G1, IS_G2, IS_B3, IS_B1, null, null]},
3235 10 Apr 15 nicklas 406          {name: 'C-A-B-C', indexSets: [null, null, IS_Y2, IS_Y3, IS_G1, IS_G2, IS_B3, IS_B1, IS_Y2, IS_Y3, null, null]}
3235 10 Apr 15 nicklas 407       ],
3235 10 Apr 15 nicklas 408
3235 10 Apr 15 nicklas 409     getPoolNumForColumn: function(colNum)
3235 10 Apr 15 nicklas 410     {
3235 10 Apr 15 nicklas 411       var poolNum = -1;
3235 10 Apr 15 nicklas 412       if (colNum >= 2 && colNum <= 9)
3235 10 Apr 15 nicklas 413       {
3235 10 Apr 15 nicklas 414         poolNum = Math.floor((colNum - 2) / 2);
3235 10 Apr 15 nicklas 415       }
3235 10 Apr 15 nicklas 416       return poolNum;
3235 10 Apr 15 nicklas 417     },
3235 10 Apr 15 nicklas 418      
3235 10 Apr 15 nicklas 419      getClassNameForWell: function(well)
3235 10 Apr 15 nicklas 420     {
3235 10 Apr 15 nicklas 421       var c = well.column;
3235 10 Apr 15 nicklas 422       var cls = (c >= 2 && c <= 9) ? 'primary ' : 'secondary ';
3235 10 Apr 15 nicklas 423       if (c >= 1 && c <= 10) cls += this.columnClasses[c % 2];
3235 10 Apr 15 nicklas 424       return cls;
3235 10 Apr 15 nicklas 425     },
1807 23 Jan 13 nicklas 426     
1807 23 Jan 13 nicklas 427     getWellsInPool: function(plate, poolNum)
1807 23 Jan 13 nicklas 428      {
1807 23 Jan 13 nicklas 429        var result = [];
3235 10 Apr 15 nicklas 430        var index = poolNum*this.numWellsPerPool + 2 * plate.rows;
1807 23 Jan 13 nicklas 431       while (result.length < this.numWellsPerPool && index < plate.wells.length)
1807 23 Jan 13 nicklas 432       {
1807 23 Jan 13 nicklas 433         result[result.length] = plate.wells[index];
1807 23 Jan 13 nicklas 434         index++;
1807 23 Jan 13 nicklas 435       }
1807 23 Jan 13 nicklas 436        return result;
1807 23 Jan 13 nicklas 437      }
3235 10 Apr 15 nicklas 438    },
2838 20 Oct 14 nicklas 439   {
3235 10 Apr 15 nicklas 440     id: '5by16',
3235 10 Apr 15 nicklas 441     name: '5 pools × 16 samples',
3289 30 Apr 15 nicklas 442     type: 'manual',
3235 10 Apr 15 nicklas 443     numPools: 5,
2838 20 Oct 14 nicklas 444     numWellsPerPool: 16,
2838 20 Oct 14 nicklas 445     columnClasses: ['pool-right', 'pool-left'],
2838 20 Oct 14 nicklas 446     barcodeVariants:
2838 20 Oct 14 nicklas 447       [
3235 10 Apr 15 nicklas 448          {name: 'A-B-C-A-B', indexSets: [null, IS_G1, IS_G2, IS_B3, IS_B1, IS_Y2, IS_Y3, IS_G1, IS_G2, IS_B3, IS_B1, null]},
3235 10 Apr 15 nicklas 449          {name: 'B-C-A-B-C', indexSets: [null, IS_B3, IS_B1, IS_Y2, IS_Y3, IS_G1, IS_G2, IS_B3, IS_B1, IS_Y2, IS_Y3, null]},
3235 10 Apr 15 nicklas 450          {name: 'C-A-B-C-A', indexSets: [null, IS_Y2, IS_Y3, IS_G1, IS_G2, IS_B3, IS_B1, IS_Y2, IS_Y3, IS_G1, IS_G2, null]}
2838 20 Oct 14 nicklas 451       ],
3235 10 Apr 15 nicklas 452   
2838 20 Oct 14 nicklas 453     getPoolNumForColumn: function(colNum)
2838 20 Oct 14 nicklas 454     {
2838 20 Oct 14 nicklas 455       var poolNum = -1;
3235 10 Apr 15 nicklas 456       if (colNum >= 1 && colNum <= 10)
2838 20 Oct 14 nicklas 457       {
3235 10 Apr 15 nicklas 458         poolNum = Math.floor((colNum-1) / 2);
2838 20 Oct 14 nicklas 459       }
2838 20 Oct 14 nicklas 460       return poolNum;
2838 20 Oct 14 nicklas 461     },
3235 10 Apr 15 nicklas 462      
2838 20 Oct 14 nicklas 463     getClassNameForWell: function(well)
2838 20 Oct 14 nicklas 464     {
2838 20 Oct 14 nicklas 465       var c = well.column;
3235 10 Apr 15 nicklas 466       var cls = (c >= 1 && c <= 10) ? 'primary ' : 'secondary ';
3235 10 Apr 15 nicklas 467       cls += this.columnClasses[c % 2];
2838 20 Oct 14 nicklas 468       return cls;
2838 20 Oct 14 nicklas 469     },
3235 10 Apr 15 nicklas 470     
2838 20 Oct 14 nicklas 471     getWellsInPool: function(plate, poolNum)
3235 10 Apr 15 nicklas 472      {
3235 10 Apr 15 nicklas 473        var result = [];
3235 10 Apr 15 nicklas 474        var index = poolNum*this.numWellsPerPool+plate.rows;
2838 20 Oct 14 nicklas 475       while (result.length < this.numWellsPerPool && index < plate.wells.length)
2838 20 Oct 14 nicklas 476       {
2838 20 Oct 14 nicklas 477         result[result.length] = plate.wells[index];
2838 20 Oct 14 nicklas 478         index++;
2838 20 Oct 14 nicklas 479       }
3235 10 Apr 15 nicklas 480        return result;
3235 10 Apr 15 nicklas 481      }
5884 27 Mar 20 nicklas 482   },
5884 27 Mar 20 nicklas 483   {
5884 27 Mar 20 nicklas 484     id: 'external32',
5884 27 Mar 20 nicklas 485     name: 'External × 32 samples (offset)',
5884 27 Mar 20 nicklas 486     type: 'external',
5884 27 Mar 20 nicklas 487     numPools: 1,
5884 27 Mar 20 nicklas 488     numWellsPerPool: 32,
5884 27 Mar 20 nicklas 489     columnClasses: ['pool-left', 'pool-middle', 'pool-middle', 'pool-right'],
5884 27 Mar 20 nicklas 490     barcodeVariants: [],
5884 27 Mar 20 nicklas 491     
5884 27 Mar 20 nicklas 492     getPoolNumForColumn: function(colNum)
5884 27 Mar 20 nicklas 493     {
5884 27 Mar 20 nicklas 494       var poolNum = -1;
5884 27 Mar 20 nicklas 495       if (colNum >= 2 && colNum <= 5)
5884 27 Mar 20 nicklas 496       {
5884 27 Mar 20 nicklas 497         poolNum = 0;
5884 27 Mar 20 nicklas 498       }
5884 27 Mar 20 nicklas 499       return poolNum;
5884 27 Mar 20 nicklas 500     },
5884 27 Mar 20 nicklas 501        
5884 27 Mar 20 nicklas 502     getClassNameForWell: function(well)
5884 27 Mar 20 nicklas 503     {
5884 27 Mar 20 nicklas 504       var c = well.column;
5884 27 Mar 20 nicklas 505       var cls = (c >= 2 && c <= 5) ? 'primary ' : 'secondary ';
5884 27 Mar 20 nicklas 506       cls += (c >= 1 && c <= 6) ? this.columnClasses[(c+2) % 4] : '';
5884 27 Mar 20 nicklas 507       return cls;
5884 27 Mar 20 nicklas 508     },
5884 27 Mar 20 nicklas 509     
5884 27 Mar 20 nicklas 510     getWellsInPool: function(plate, poolNum)
5884 27 Mar 20 nicklas 511     {
5884 27 Mar 20 nicklas 512       var result = [];
5884 27 Mar 20 nicklas 513       var index = poolNum*this.numWellsPerPool+2*plate.rows;
5884 27 Mar 20 nicklas 514       while (result.length < this.numWellsPerPool && index < plate.wells.length)
5884 27 Mar 20 nicklas 515       {
5884 27 Mar 20 nicklas 516         result[result.length] = plate.wells[index];
5884 27 Mar 20 nicklas 517         index++;
5884 27 Mar 20 nicklas 518       }
5884 27 Mar 20 nicklas 519       return result;
5884 27 Mar 20 nicklas 520     }
5884 27 Mar 20 nicklas 521   },
5884 27 Mar 20 nicklas 522   {
5884 27 Mar 20 nicklas 523     id: 'external64',
5884 27 Mar 20 nicklas 524     name: 'External × 64 samples',
5884 27 Mar 20 nicklas 525     type: 'external',
5884 27 Mar 20 nicklas 526     numPools: 1,
5884 27 Mar 20 nicklas 527     numWellsPerPool: 64,
5884 27 Mar 20 nicklas 528     columnClasses: ['pool-left', 'pool-middle', 'pool-middle', 'pool-middle', 'pool-middle', 'pool-middle', 'pool-middle', 'pool-right'],
5884 27 Mar 20 nicklas 529     barcodeVariants: [],
5884 27 Mar 20 nicklas 530     
5884 27 Mar 20 nicklas 531     getPoolNumForColumn: function(colNum)
5884 27 Mar 20 nicklas 532     {
5884 27 Mar 20 nicklas 533       var poolNum = -1;
5884 27 Mar 20 nicklas 534       if (colNum >= 2 && colNum <= 9)
5884 27 Mar 20 nicklas 535       {
5884 27 Mar 20 nicklas 536         poolNum = 0;
5884 27 Mar 20 nicklas 537       }
5884 27 Mar 20 nicklas 538       return poolNum;
5884 27 Mar 20 nicklas 539     },
5884 27 Mar 20 nicklas 540        
5884 27 Mar 20 nicklas 541     getClassNameForWell: function(well)
5884 27 Mar 20 nicklas 542     {
5884 27 Mar 20 nicklas 543       var c = well.column;
5884 27 Mar 20 nicklas 544       var cls = (c >= 2 && c <= 9) ? 'primary ' : 'secondary ';
5884 27 Mar 20 nicklas 545       cls += (c >= 1 && c <= 10) ? this.columnClasses[(c+6) % 8] : '';
5884 27 Mar 20 nicklas 546       return cls;
5884 27 Mar 20 nicklas 547     },
5884 27 Mar 20 nicklas 548     
5884 27 Mar 20 nicklas 549     getWellsInPool: function(plate, poolNum)
5884 27 Mar 20 nicklas 550     {
5884 27 Mar 20 nicklas 551       var result = [];
5884 27 Mar 20 nicklas 552       var index = poolNum*this.numWellsPerPool+2*plate.rows;
5884 27 Mar 20 nicklas 553       while (result.length < this.numWellsPerPool && index < plate.wells.length)
5884 27 Mar 20 nicklas 554       {
5884 27 Mar 20 nicklas 555         result[result.length] = plate.wells[index];
5884 27 Mar 20 nicklas 556         index++;
5884 27 Mar 20 nicklas 557       }
5884 27 Mar 20 nicklas 558       return result;
5884 27 Mar 20 nicklas 559     }
1807 23 Jan 13 nicklas 560   }
1807 23 Jan 13 nicklas 561 ];
1807 23 Jan 13 nicklas 562
1809 25 Jan 13 nicklas 563 /**
1809 25 Jan 13 nicklas 564   Methods for handling pooling layout schemas.
1809 25 Jan 13 nicklas 565 */
1807 23 Jan 13 nicklas 566 var PoolSchema = function()
1807 23 Jan 13 nicklas 567 {
1807 23 Jan 13 nicklas 568   var ps = {};
1807 23 Jan 13 nicklas 569   
3289 30 Apr 15 nicklas 570   ps.initList = function(list, selectedId, type)
1807 23 Jan 13 nicklas 571   {
3289 30 Apr 15 nicklas 572     var selectedSchema = null;
1807 23 Jan 13 nicklas 573     for (var i = 0; i < POOL_SCHEMA.length; i++)
1807 23 Jan 13 nicklas 574     {
1807 23 Jan 13 nicklas 575       var schema = POOL_SCHEMA[i];
3289 30 Apr 15 nicklas 576       if (!type || schema.type == type)
3289 30 Apr 15 nicklas 577       {
3289 30 Apr 15 nicklas 578         var option = new Option(schema.name, schema.id);
3289 30 Apr 15 nicklas 579         list[list.length] = option;
3289 30 Apr 15 nicklas 580         if (selectedId == schema.id || selectedSchema == null) 
3289 30 Apr 15 nicklas 581         {
3289 30 Apr 15 nicklas 582           selectedSchema = schema;
3289 30 Apr 15 nicklas 583           list.selectedIndex = list.length-1;
3289 30 Apr 15 nicklas 584         }
3289 30 Apr 15 nicklas 585       }
1807 23 Jan 13 nicklas 586     }
3289 30 Apr 15 nicklas 587     return selectedSchema;
1807 23 Jan 13 nicklas 588   }
1807 23 Jan 13 nicklas 589   
1809 25 Jan 13 nicklas 590   ps.initVariantList = function(list, schema, selectedId)
1809 25 Jan 13 nicklas 591   {
1809 25 Jan 13 nicklas 592     if (!selectedId) selectedId = schema.barcodeVariants[0].name;
1809 25 Jan 13 nicklas 593     var selectedIndex = 0;
1809 25 Jan 13 nicklas 594     list.length = 0;
1809 25 Jan 13 nicklas 595     for (var i = 0; i < schema.barcodeVariants.length; i++)
1809 25 Jan 13 nicklas 596     {
1809 25 Jan 13 nicklas 597       var variant = schema.barcodeVariants[i];
2218 10 Feb 14 nicklas 598       if (!variant.disabled)
2218 10 Feb 14 nicklas 599       {
2218 10 Feb 14 nicklas 600         var option = new Option(variant.name, variant.name);
2218 10 Feb 14 nicklas 601         list[list.length] = option;
2218 10 Feb 14 nicklas 602         if (selectedId == variant.name) selectedIndex = i;
2218 10 Feb 14 nicklas 603       }
1809 25 Jan 13 nicklas 604     }
1809 25 Jan 13 nicklas 605     list.selectedIndex = selectedIndex;
1809 25 Jan 13 nicklas 606     return schema.barcodeVariants[selectedIndex];
1809 25 Jan 13 nicklas 607   }
1807 23 Jan 13 nicklas 608   
1807 23 Jan 13 nicklas 609   ps.getById = function(schemaId)
1807 23 Jan 13 nicklas 610   {
1807 23 Jan 13 nicklas 611     for (var i = 0; i < POOL_SCHEMA.length; i++)
1807 23 Jan 13 nicklas 612     {
1807 23 Jan 13 nicklas 613       if (POOL_SCHEMA[i].id == schemaId)
1807 23 Jan 13 nicklas 614       {
1807 23 Jan 13 nicklas 615         return POOL_SCHEMA[i];
1807 23 Jan 13 nicklas 616       }
1807 23 Jan 13 nicklas 617     }
1807 23 Jan 13 nicklas 618     return null;
1807 23 Jan 13 nicklas 619   }
1807 23 Jan 13 nicklas 620   
1814 30 Jan 13 nicklas 621   ps.getBarcodeVariantByName = function(schema, barcodeVariant)
1814 30 Jan 13 nicklas 622   {
1814 30 Jan 13 nicklas 623     for (var i = 0; i < schema.barcodeVariants.length; i++)
1814 30 Jan 13 nicklas 624     {
1814 30 Jan 13 nicklas 625       if (barcodeVariant == schema.barcodeVariants[i].name) 
1814 30 Jan 13 nicklas 626       {
1814 30 Jan 13 nicklas 627         return schema.barcodeVariants[i];
1814 30 Jan 13 nicklas 628       }
1814 30 Jan 13 nicklas 629     }
1814 30 Jan 13 nicklas 630     return schema.barcodeVariants[0];
1814 30 Jan 13 nicklas 631   }
1814 30 Jan 13 nicklas 632   
3297 30 Apr 15 nicklas 633   ps.buildPoolTableRow = function(schema, columns, interactive, prefix, suffix)
1807 23 Jan 13 nicklas 634   {
3297 30 Apr 15 nicklas 635     if (!prefix) prefix = '';
3297 30 Apr 15 nicklas 636     if (!suffix) suffix = '';
1807 23 Jan 13 nicklas 637     var html = '<th></th>';
1807 23 Jan 13 nicklas 638     var colNum = 0;
1807 23 Jan 13 nicklas 639     var poolClass = interactive ? 'pool link' : 'pool';
1807 23 Jan 13 nicklas 640     var afterPool = false;
1807 23 Jan 13 nicklas 641     while (colNum < columns)
1807 23 Jan 13 nicklas 642     {
1807 23 Jan 13 nicklas 643       var poolNum = schema ? schema.getPoolNumForColumn(colNum) : -1;
1807 23 Jan 13 nicklas 644       var colspan = 1;
1807 23 Jan 13 nicklas 645       if (poolNum == -1)
1807 23 Jan 13 nicklas 646       {
2218 10 Feb 14 nicklas 647         while (colNum+colspan < columns)
2218 10 Feb 14 nicklas 648         {
2218 10 Feb 14 nicklas 649           if (schema.getPoolNumForColumn(colNum+colspan) != -1) break;
2218 10 Feb 14 nicklas 650           colspan++;
2218 10 Feb 14 nicklas 651         }
2218 10 Feb 14 nicklas 652         var className = afterPool ? 'nopool-after-pool' : 'nopool-before-pool';
2218 10 Feb 14 nicklas 653         html += '<th class="'+className+'" colspan="'+colspan+'">&nbsp;</th>';
1807 23 Jan 13 nicklas 654       }
1807 23 Jan 13 nicklas 655       else
1807 23 Jan 13 nicklas 656       {
1807 23 Jan 13 nicklas 657         while (colNum+colspan < columns)
1807 23 Jan 13 nicklas 658         {
1807 23 Jan 13 nicklas 659           if (schema.getPoolNumForColumn(colNum+colspan) != poolNum) break;
1807 23 Jan 13 nicklas 660           colspan++;
1807 23 Jan 13 nicklas 661         }
1807 23 Jan 13 nicklas 662         html += '<th id="pool.' + poolNum + '" colspan="'+colspan+'"';
1807 23 Jan 13 nicklas 663         html += ' class="'+poolClass+'"';
1809 25 Jan 13 nicklas 664         html += ' data-pool-num="' + poolNum + '"';
1807 23 Jan 13 nicklas 665         html += '>'+POOL_NAMES[poolNum]+'</th>';
1807 23 Jan 13 nicklas 666         afterPool = true;
1807 23 Jan 13 nicklas 667       }
1807 23 Jan 13 nicklas 668       colNum += colspan;
1807 23 Jan 13 nicklas 669     }
1809 25 Jan 13 nicklas 670     
1809 25 Jan 13 nicklas 671     var row = document.getElementById('pool-row');
3297 30 Apr 15 nicklas 672     row.innerHTML = prefix+html+suffix;
1807 23 Jan 13 nicklas 673   }
1807 23 Jan 13 nicklas 674   
1807 23 Jan 13 nicklas 675   return ps;
1807 23 Jan 13 nicklas 676 }();
1809 25 Jan 13 nicklas 677
1809 25 Jan 13 nicklas 678
2027 30 Sep 13 nicklas 679 var PoolMix = function()
2027 30 Sep 13 nicklas 680 {
2027 30 Sep 13 nicklas 681   
2027 30 Sep 13 nicklas 682   var LIMIT_FOR_EXTRA_LARGE_MIX = 1.0;
2027 30 Sep 13 nicklas 683   var AVAILABLE_VOLUME = 10.0;
2027 30 Sep 13 nicklas 684   var AVAILABLE_VOLUME_AFTER_SPEEDVAC = 5.0;
2027 30 Sep 13 nicklas 685   
2027 30 Sep 13 nicklas 686   var pm = {};
2027 30 Sep 13 nicklas 687   
2027 30 Sep 13 nicklas 688   /**
2027 30 Sep 13 nicklas 689     Round the value to the nearest value with 
2027 30 Sep 13 nicklas 690     the given number of decimals (default=1).
2027 30 Sep 13 nicklas 691   */
2027 30 Sep 13 nicklas 692   pm.round = function(value, numDecimals)
2027 30 Sep 13 nicklas 693   {
2027 30 Sep 13 nicklas 694     if (!numDecimals) numDecimals = 1;
2027 30 Sep 13 nicklas 695     var pow = Math.pow(10, numDecimals);
2027 30 Sep 13 nicklas 696     return Math.round(value * pow) / pow;
2027 30 Sep 13 nicklas 697   }
2027 30 Sep 13 nicklas 698   
2027 30 Sep 13 nicklas 699   /**
2027 30 Sep 13 nicklas 700     Round the value down to the nearest value with 
2027 30 Sep 13 nicklas 701     the given number of decimals (default=1).
2027 30 Sep 13 nicklas 702   */
2027 30 Sep 13 nicklas 703   pm.floor = function(value, numDecimals)
2027 30 Sep 13 nicklas 704   {
2027 30 Sep 13 nicklas 705     if (!numDecimals) numDecimals = 1;
2027 30 Sep 13 nicklas 706     var pow = Math.pow(10, numDecimals);
2027 30 Sep 13 nicklas 707     return Math.floor(value * pow) / pow;
2027 30 Sep 13 nicklas 708   }
2027 30 Sep 13 nicklas 709   
2027 30 Sep 13 nicklas 710   /**
2027 30 Sep 13 nicklas 711     Round the value up to the nearest value with 
2027 30 Sep 13 nicklas 712     the given number of decimals (default=1).
2027 30 Sep 13 nicklas 713   */
2027 30 Sep 13 nicklas 714   pm.ceil = function(value, numDecimals)
2027 30 Sep 13 nicklas 715   {
2027 30 Sep 13 nicklas 716     if (!numDecimals) numDecimals = 1;
2027 30 Sep 13 nicklas 717     var pow = Math.pow(10, numDecimals);
2027 30 Sep 13 nicklas 718     return Math.ceil(value * pow) / pow;
2027 30 Sep 13 nicklas 719   }
2027 30 Sep 13 nicklas 720   
2027 30 Sep 13 nicklas 721   /**
2140 18 Nov 13 nicklas 722     Checks if v1 is less than v2 within the given accuracy
2140 18 Nov 13 nicklas 723     Eg. v1+accuracy <= v2
2140 18 Nov 13 nicklas 724    */
2140 18 Nov 13 nicklas 725   pm.lessThan = function(v1, v2, accuracy)
2140 18 Nov 13 nicklas 726   {
2140 18 Nov 13 nicklas 727     return v1 + accuracy < v2;
2140 18 Nov 13 nicklas 728   }
2140 18 Nov 13 nicklas 729   
2140 18 Nov 13 nicklas 730   /**
2140 18 Nov 13 nicklas 731     Calculate the volume of a lib to use for a pool given the
2027 30 Sep 13 nicklas 732     target molarity for the pool and average volume for each lib
2140 18 Nov 13 nicklas 733     in the pool. This method calculates two different volumes:
2027 30 Sep 13 nicklas 734     
2140 18 Nov 13 nicklas 735     lib.volume = The theoretical volume needed to reach the same
2140 18 Nov 13 nicklas 736     amount of DNA as the target molarity and volume suggests
2027 30 Sep 13 nicklas 737     
2140 18 Nov 13 nicklas 738     lib.actualVolume: The actual volume that can be used due to
2140 18 Nov 13 nicklas 739     remaining quantity, lib molarity and mixing strategy
2140 18 Nov 13 nicklas 740     
2027 30 Sep 13 nicklas 741     The volume is rounded to one decimal in µl.
2140 18 Nov 13 nicklas 742     
2027 30 Sep 13 nicklas 743   */
2041 04 Oct 13 nicklas 744   pm.calculateLibVolume = function(lib, targetMolarity, targetVolume, mixingStrategy)
2027 30 Sep 13 nicklas 745   {
5436 17 May 19 nicklas 746     if (!lib.molarity) return;
3314 07 May 15 nicklas 747     var volume = targetVolume * targetMolarity / lib.molarity;
2027 30 Sep 13 nicklas 748     var mixFactor = 1;
2140 18 Nov 13 nicklas 749     
2140 18 Nov 13 nicklas 750     if (pm.lessThan(volume, LIMIT_FOR_EXTRA_LARGE_MIX, 0.05))
2027 30 Sep 13 nicklas 751     {
2186 10 Jan 14 nicklas 752       // Mix 'dil' so that we take about 2 times the minimal limit
2186 10 Jan 14 nicklas 753       mixFactor = Math.max(2 * LIMIT_FOR_EXTRA_LARGE_MIX / volume);
2027 30 Sep 13 nicklas 754     }
3314 07 May 15 nicklas 755
3314 07 May 15 nicklas 756     // Re-calculate the volume to get the rounding correct (eg. to avoid doubling rounding errors)
3314 07 May 15 nicklas 757     volume = pm.round(mixFactor * volume) / mixFactor;
2027 30 Sep 13 nicklas 758     
2140 18 Nov 13 nicklas 759     // This is the theoretical volume that should be used...
2027 30 Sep 13 nicklas 760     lib.volume = volume;
2140 18 Nov 13 nicklas 761     lib.eb = null;
2027 30 Sep 13 nicklas 762     lib.mixFactor = mixFactor;
2140 18 Nov 13 nicklas 763       
2140 18 Nov 13 nicklas 764     // However, reality has limitations due to remaining volume, mixing strategy, etc.
2140 18 Nov 13 nicklas 765     // Check what volume that can actually be used
2186 10 Jan 14 nicklas 766     if (mixingStrategy == 'fixed')
2140 18 Nov 13 nicklas 767     {
2140 18 Nov 13 nicklas 768       // Do not use more than target volume or remaining volume
2140 18 Nov 13 nicklas 769       volume = Math.min(volume, targetVolume, lib.remainingVolume);
2140 18 Nov 13 nicklas 770     }
2140 18 Nov 13 nicklas 771     else
2140 18 Nov 13 nicklas 772     {
2140 18 Nov 13 nicklas 773       volume = Math.min(volume, lib.remainingVolume);
2140 18 Nov 13 nicklas 774     }
2140 18 Nov 13 nicklas 775     lib.actualVolume = volume;      
2027 30 Sep 13 nicklas 776   }
2027 30 Sep 13 nicklas 777   
2027 30 Sep 13 nicklas 778   /**
2140 18 Nov 13 nicklas 779     Same as above method except that we already know the mixFactor and actualVolume of
2140 18 Nov 13 nicklas 780     the lib since that has already been saved to the database.
2027 30 Sep 13 nicklas 781   */
2140 18 Nov 13 nicklas 782   pm.calculateLibVolumeForProtocol = function(lib, targetMolarity, targetVolume, mixingStrategy)
2027 30 Sep 13 nicklas 783   {
2140 18 Nov 13 nicklas 784     if (lib.mixFactor > 1)
2027 30 Sep 13 nicklas 785     {
2186 10 Jan 14 nicklas 786       lib.volume = pm.round(lib.mixFactor * targetVolume * targetMolarity / lib.molarity) / lib.mixFactor;
2027 30 Sep 13 nicklas 787     }
2041 04 Oct 13 nicklas 788     else
2027 30 Sep 13 nicklas 789     {
2140 18 Nov 13 nicklas 790       lib.volume = pm.round(targetVolume * targetMolarity / lib.molarity);
2140 18 Nov 13 nicklas 791     }
2140 18 Nov 13 nicklas 792   }
2041 04 Oct 13 nicklas 793
2140 18 Nov 13 nicklas 794   
2140 18 Nov 13 nicklas 795   /**
2140 18 Nov 13 nicklas 796     Calculate the volume of EB to mix with a lib.
2140 18 Nov 13 nicklas 797     
2140 18 Nov 13 nicklas 798     The volume is rounded to one decimal in µl.
2140 18 Nov 13 nicklas 799   */
2140 18 Nov 13 nicklas 800   pm.calculateEbVolume = function(lib, targetMolarity, targetVolume, mixingStrategy)
2140 18 Nov 13 nicklas 801   {
2140 18 Nov 13 nicklas 802     var eb = 0;
2186 10 Jan 14 nicklas 803     if (mixingStrategy == 'fixed')
2140 18 Nov 13 nicklas 804     {
2140 18 Nov 13 nicklas 805       eb = targetVolume - lib.volume;
2140 18 Nov 13 nicklas 806     }
2186 10 Jan 14 nicklas 807     else if (lib.mixFactor > 1)
2186 10 Jan 14 nicklas 808     {
2186 10 Jan 14 nicklas 809       eb = (lib.targetVolume ? lib.targetVolume : targetVolume) - lib.volume;
2186 10 Jan 14 nicklas 810     }
2140 18 Nov 13 nicklas 811     
2140 18 Nov 13 nicklas 812     // This is the theoretical volume that should be used...
2140 18 Nov 13 nicklas 813     lib.eb = eb;
2186 10 Jan 14 nicklas 814     lib.mixingStrategy = mixingStrategy;
2140 18 Nov 13 nicklas 815     
2140 18 Nov 13 nicklas 816     // However, reality has limitations due to remaining volume, mixing strategy, etc.
2140 18 Nov 13 nicklas 817     // Check what volume that can actually be used
2140 18 Nov 13 nicklas 818     if (mixingStrategy == 'fixed' || lib.mixFactor > 1)
2140 18 Nov 13 nicklas 819     {
2140 18 Nov 13 nicklas 820       if (pm.lessThan(lib.molarity, targetMolarity, 0.01))
2041 04 Oct 13 nicklas 821       {
2140 18 Nov 13 nicklas 822         // Do not dilute if molarity is lower than target molarity
2140 18 Nov 13 nicklas 823         eb = 0;
2041 04 Oct 13 nicklas 824       }
2140 18 Nov 13 nicklas 825       else if (pm.lessThan(lib.actualVolume, lib.volume, 0.05))
2041 04 Oct 13 nicklas 826       {
2140 18 Nov 13 nicklas 827         // Reduce EB by the same factor as we have to reduce the volume
2140 18 Nov 13 nicklas 828         // so the final molarity is still 
2140 18 Nov 13 nicklas 829         eb = pm.round(eb * lib.actualVolume / lib.volume);
2041 04 Oct 13 nicklas 830       }
2140 18 Nov 13 nicklas 831     }    
2140 18 Nov 13 nicklas 832     lib.actualEb = eb;
2027 30 Sep 13 nicklas 833   }
2140 18 Nov 13 nicklas 834
2027 30 Sep 13 nicklas 835   
2140 18 Nov 13 nicklas 836   
2140 18 Nov 13 nicklas 837   /**
2140 18 Nov 13 nicklas 838     Summarize information for the pool. This will calculate the following and
2140 18 Nov 13 nicklas 839     return as an object:
2140 18 Nov 13 nicklas 840     
2140 18 Nov 13 nicklas 841     libVolume: Total volume of libs in the pool (µl)
2140 18 Nov 13 nicklas 842     libAmount: Total amount of libs in the pool (nano-mol)
2140 18 Nov 13 nicklas 843     ebVolumeFromLibs: Total EB volume from mixed directly with libs
2140 18 Nov 13 nicklas 844     ebVolumeExtra: Extra EB to add to the pool (dynamic mixing)
2140 18 Nov 13 nicklas 845     molarity: The final pool molarity after mixing everything
2140 18 Nov 13 nicklas 846   */
2140 18 Nov 13 nicklas 847   pm.calculateFinalPoolInfo = function(libs, targetMolarity, targetVolume, mixingStrategy)
2027 30 Sep 13 nicklas 848   {
2027 30 Sep 13 nicklas 849     var libVolume = 0;
2027 30 Sep 13 nicklas 850     var libAmount = 0;
2140 18 Nov 13 nicklas 851     var ebVolumeFromLibs = 0;
2140 18 Nov 13 nicklas 852     
2027 30 Sep 13 nicklas 853     for (var libNo = 0; libNo < libs.length; libNo++)
2027 30 Sep 13 nicklas 854     {
2027 30 Sep 13 nicklas 855       var lib = libs[libNo];
2140 18 Nov 13 nicklas 856       
2140 18 Nov 13 nicklas 857       var volume = lib.actualVolume;
2140 18 Nov 13 nicklas 858       var eb = lib.actualEb;
2140 18 Nov 13 nicklas 859
2140 18 Nov 13 nicklas 860       libVolume += volume;
2140 18 Nov 13 nicklas 861       ebVolumeFromLibs += eb;
2140 18 Nov 13 nicklas 862       libAmount += volume * lib.molarity;
2027 30 Sep 13 nicklas 863     }
2027 30 Sep 13 nicklas 864     
2140 18 Nov 13 nicklas 865     var info = {};
2140 18 Nov 13 nicklas 866     info.libVolume = libVolume;
2140 18 Nov 13 nicklas 867     info.libAmount = libAmount;
2140 18 Nov 13 nicklas 868     info.ebVolumeFromLibs = ebVolumeFromLibs;
2140 18 Nov 13 nicklas 869     
2140 18 Nov 13 nicklas 870     // Calculate extra EB and total pool volume
2140 18 Nov 13 nicklas 871     if (mixingStrategy == 'dynamic')
2040 03 Oct 13 nicklas 872     {
2140 18 Nov 13 nicklas 873       // Use the target molarity to calculate the total volume
2140 18 Nov 13 nicklas 874       var targetVolume = pm.round(libAmount / targetMolarity);
2140 18 Nov 13 nicklas 875       info.ebVolumeExtra = pm.round(targetVolume - info.libVolume - info.ebVolumeFromLibs);
2140 18 Nov 13 nicklas 876       if (info.ebVolumeExtra < 0)
2140 18 Nov 13 nicklas 877       {
2140 18 Nov 13 nicklas 878         info.totalVolume = info.libVolume + info.ebVolumeFromLibs;
2140 18 Nov 13 nicklas 879       }
2140 18 Nov 13 nicklas 880       else
2140 18 Nov 13 nicklas 881       {
2140 18 Nov 13 nicklas 882         info.totalVolume = targetVolume;
2140 18 Nov 13 nicklas 883       }
2040 03 Oct 13 nicklas 884     }
2040 03 Oct 13 nicklas 885     else
2040 03 Oct 13 nicklas 886     {
2140 18 Nov 13 nicklas 887       // No extra EB
2140 18 Nov 13 nicklas 888       info.ebVolumeExtra = 0;
2140 18 Nov 13 nicklas 889       info.totalVolume = info.libVolume + info.ebVolumeFromLibs;
2040 03 Oct 13 nicklas 890     }
2140 18 Nov 13 nicklas 891     // Calcualate the final molarity (which may be different that target due to rounding)
2140 18 Nov 13 nicklas 892     info.molarity = info.libAmount / info.totalVolume;
2140 18 Nov 13 nicklas 893
2027 30 Sep 13 nicklas 894     return info;
2027 30 Sep 13 nicklas 895   }
2027 30 Sep 13 nicklas 896   
2027 30 Sep 13 nicklas 897   return pm;
2027 30 Sep 13 nicklas 898 }();