From 087beda4bcc026f4295d090c6a17c6e7ca00d572 Mon Sep 17 00:00:00 2001 From: Mark W <24956497+ndg63276@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:50:30 +0100 Subject: [PATCH 01/14] Merge pre-release/2024-R4.3 into master (#842) * LIMS-1368: Fix incorrectly summed dose for DC groups when filtered (#803) * LIMS-1099: Limit what can be done with red proteins (#827) * LIMS-14: Show a direct link to the autoprocessing log (#805) * LIMS-1422: Reprocessing dialog edits the end value unpredictably (#819) * LIMS-1424: Show visit for each container if set (#828) * LIMS-1425: Show samples with no processing as grey (#820) --- api/src/Page/DC.php | 7 ++- api/src/Page/Download.php | 45 ++++++++++++++++++- api/src/Page/Sample.php | 17 ++++--- api/src/Page/Shipment.php | 39 +++++++++++++++- client/src/js/models/shipment.js | 5 +++ client/src/js/modules/dc/views/distl.js | 4 +- client/src/js/modules/dc/views/reprocess2.js | 20 ++++++--- client/src/js/modules/shipment/views/plate.js | 2 +- .../src/js/modules/shipment/views/shipment.js | 2 +- .../js/modules/shipment/views/shipmentadd.js | 19 +++++++- .../mx/shipment/views/container-mixin.js | 2 +- .../mx/shipment/views/mx-container-add.vue | 23 +++------- .../mx/shipment/views/mx-container-view.vue | 39 +++++++++------- client/src/js/templates/dc/dc_autoproc.html | 1 + .../js/templates/shipment/containerli.html | 7 ++- .../js/templates/shipment/shipmentadd.html | 14 +++--- client/src/js/utils/editable.js | 9 +++- 17 files changed, 188 insertions(+), 67 deletions(-) diff --git a/api/src/Page/DC.php b/api/src/Page/DC.php index 33f259721..bc43ba535 100644 --- a/api/src/Page/DC.php +++ b/api/src/Page/DC.php @@ -440,7 +440,7 @@ function _data_collections($single = null) dc.c2lens, dc.objaperture, dc.magnification, - dc.totalexposeddose as totaldose, + dose.total as totaldose, CAST(dc.totalabsorbeddose AS DECIMAL(5, 2)) as totalabsdose, d.detectorpixelsizehorizontal, d.detectorpixelsizevertical, @@ -574,7 +574,7 @@ function _data_collections($single = null) max(dc.c2lens) as c2lens, max(dc.objaperture) as objaperture, max(dc.magnification) as magnification, - sum(dc.totalabsorbeddose) as totaldose, + dose.total as totaldose, CAST(dc.totalabsorbeddose AS DECIMAL(5, 2)) as totalabsdose, max(d.detectormanufacturer) as detectormanufacturer, max(d.detectormodel) as detectormodel, @@ -660,6 +660,9 @@ function _data_collections($single = null) SELECT $extcg $fields FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid + INNER JOIN ( + select datacollectiongroupid, sum(totalabsorbeddose) as total from datacollection group by datacollectiongroupid) dose + ON dc.datacollectiongroupid = dose.datacollectiongroupid INNER JOIN blsession ses ON ses.sessionid = dcg.sessionid LEFT OUTER JOIN experimenttype et on dcg.experimenttypeid = et.experimenttypeid $sample_joins[0] diff --git a/api/src/Page/Download.php b/api/src/Page/Download.php index debeaeb45..a1ed29bca 100644 --- a/api/src/Page/Download.php +++ b/api/src/Page/Download.php @@ -56,6 +56,7 @@ class Download extends Page array('/ap/attachments(/:AUTOPROCPROGRAMATTACHMENTID)(/dl/:download)', 'get', '_get_autoproc_attachments'), array('/ap/archive/:AUTOPROCPROGRAMID', 'get', '_get_autoproc_archive'), + array('/ap/log/:AUTOPROCPROGRAMID', 'get', '_get_autoproc_log'), ); @@ -220,7 +221,7 @@ function __get_autoproc_attachments() INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid WHERE s.proposalid=:1 $where - ORDER BY importancerank" + ORDER BY importancerank, filename" ), $args); return $rows; @@ -413,6 +414,48 @@ function _get_attachment() } } + # ------------------------------------------------------------------------ + # Get the most important log of an autoprocessing job + function _get_autoproc_log() + { + + if (!$this->has_arg('prop')) { + $this->_error('No proposal specified'); + } + $args = array($this->proposalid, $this->arg('AUTOPROCPROGRAMID')); + + $rows = $this->db->union(array( + "SELECT app.autoprocprogramid, appa.filename, appa.filepath + FROM autoprocintegration api + INNER JOIN autoprocprogram app ON api.autoprocprogramid = app.autoprocprogramid + INNER JOIN autoprocprogramattachment appa ON appa.autoprocprogramid = app.autoprocprogramid + INNER JOIN datacollection dc ON dc.datacollectionid = api.datacollectionid + INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid + INNER JOIN blsession s ON s.sessionid = dcg.sessionid + WHERE s.proposalid=:1 + AND app.autoprocprogramid=:2 + AND appa.importancerank=1 + AND appa.filetype='Log'", + "SELECT app.autoprocprogramid, appa.filename, appa.filepath + FROM autoprocprogram app + INNER JOIN processingjob pj on pj.processingjobid = app.processingjobid + INNER JOIN autoprocprogramattachment appa ON appa.autoprocprogramid = app.autoprocprogramid + INNER JOIN datacollection dc ON dc.datacollectionid = pj.datacollectionid + INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid + INNER JOIN blsession s ON s.sessionid = dcg.sessionid + WHERE s.proposalid=:1 + AND app.autoprocprogramid=:2 + AND appa.importancerank=1 + AND appa.filetype='Log'" + ), $args); + + if (!sizeof($rows)) { + return $this->_error('No log file found'); + } + $this->_get_file($rows[0]['AUTOPROCPROGRAMID'], $rows[0]); + + } + # ------------------------------------------------------------------------ # Get an archive of an autoproc diff --git a/api/src/Page/Sample.php b/api/src/Page/Sample.php index eb8850184..d1a051493 100644 --- a/api/src/Page/Sample.php +++ b/api/src/Page/Sample.php @@ -1819,12 +1819,17 @@ function _distinct_proteins() $where .= ' AND pr.externalid IS NOT NULL'; } - $has_safety_level = $this->has_arg('SAFETYLEVEL'); - if ($has_safety_level && $this->arg('SAFETYLEVEL') === 'ALL') { - $where .= ' AND pr.safetyLevel IS NOT NULL'; - } else if ($has_safety_level && $this->arg('SAFETYLEVEL') !== 'ALL') { - $where .= ' AND pr.safetyLevel=:' . (sizeof($args) + 1); - array_push($args, $this->arg('SAFETYLEVEL')); + if ($this->has_arg('SAFETYLEVEL')) { + if ($this->arg('SAFETYLEVEL') === 'ALL') { + $where .= ' AND pr.safetyLevel IS NOT NULL'; + } else if (strtolower($this->arg('SAFETYLEVEL')) === 'yellow') { + $where .= " AND pr.safetyLevel in ('green','yellow')"; + } else if (strtolower($this->arg('SAFETYLEVEL')) === 'red') { + $where .= " AND pr.safetyLevel in ('green','yellow','red')"; + } else { + $where .= ' AND pr.safetyLevel=:' . (sizeof($args) + 1); + array_push($args, $this->arg('SAFETYLEVEL')); + } } if ($this->has_arg('term')) { diff --git a/api/src/Page/Shipment.php b/api/src/Page/Shipment.php index 6b3f16f4a..fdb44b725 100644 --- a/api/src/Page/Shipment.php +++ b/api/src/Page/Shipment.php @@ -1626,7 +1626,7 @@ function _get_dewars() $order = $cols[$this->arg('sort_by')] . ' ' . $dir; } - $dewars = $this->db->paginate("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number) as firstexperiment, r.labcontactid, se.beamlineoperator as localcontact, se.beamlinename, TO_CHAR(se.startdate, 'HH24:MI DD-MM-YYYY') as firstexperimentst, d.firstexperimentid, s.shippingid, s.shippingname, d.facilitycode, count(c.containerid) as ccount, (case when se.visit_number > 0 then (CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number)) else '' end) as exp, d.code, d.barcode, d.storagelocation, d.dewarstatus, d.dewarid, d.trackingnumbertosynchrotron, d.trackingnumberfromsynchrotron, d.externalShippingIdFromSynchrotron, s.deliveryagent_agentname, d.weight, d.deliveryagent_barcode, GROUP_CONCAT(c.code SEPARATOR ', ') as containers, s.sendinglabcontactid, s.returnlabcontactid, pe.givenname, pe.familyname + $dewars = $this->db->paginate("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number) as firstexperiment, r.labcontactid, se.beamlineoperator as localcontact, se.beamlinename, TO_CHAR(se.startdate, 'HH24:MI DD-MM-YYYY') as firstexperimentst, d.firstexperimentid, s.shippingid, s.shippingname, d.facilitycode, count(c.containerid) as ccount, (case when se.visit_number > 0 then (CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number)) else '' end) as exp, d.code, d.barcode, d.storagelocation, d.dewarstatus, d.dewarid, d.trackingnumbertosynchrotron, d.trackingnumberfromsynchrotron, d.externalShippingIdFromSynchrotron, s.deliveryagent_agentname, d.weight, d.deliveryagent_barcode, GROUP_CONCAT(c.code SEPARATOR ', ') as containers, s.sendinglabcontactid, s.returnlabcontactid, pe.givenname, pe.familyname, s.safetylevel as shippingsafetylevel FROM dewar d LEFT OUTER JOIN container c ON c.dewarid = d.dewarid INNER JOIN shipping s ON d.shippingid = s.shippingid @@ -1700,6 +1700,39 @@ function _add_dewar() $this->_output(array('DEWARID' => $id)); } + # Check new safety level is ok + function _check_safety_level() + { + $ship = $this->db->pq("SELECT cq.containerqueueid, p.safetylevel FROM dewar d + LEFT OUTER JOIN container c on c.dewarid = d.dewarid + LEFT OUTER JOIN containerqueue cq on cq.containerid = c.containerid + LEFT OUTER JOIN blsample b ON b.containerid = c.containerid + LEFT OUTER JOIN crystal cr ON cr.crystalid = b.crystalid + LEFT OUTER JOIN protein p ON p.proteinid = cr.proteinid + WHERE d.shippingid = :1", array($this->arg('sid'))); + if (strtolower($this->arg('SAFETYLEVEL')) == 'green') { + foreach ($ship as $s) { + if (strtolower($s['SAFETYLEVEL']) == 'yellow') + $this->_error('Cannot set safety level to green as one or more samples are yellow.'); + if (strtolower($s['SAFETYLEVEL']) == 'red') + $this->_error('Cannot set safety level to green as one or more samples are red.'); + } + } else if (strtolower($this->arg('SAFETYLEVEL')) == 'yellow') { + foreach ($ship as $s) { + if ($s['CONTAINERQUEUEID'] != null) + $this->_error('Cannot set safety level to yellow as one or more containers are queued.'); + if (strtolower($s['SAFETYLEVEL']) == 'red') + $this->_error('Cannot set safety level to yellow as one or more samples are red.'); + } + } else { + foreach ($ship as $s) { + if ($s['CONTAINERQUEUEID'] != null) + $this->_error('Cannot set safety level to red as one or more containers are queued.'); + } + } + } + + # Update shipment function _update_shipment() { @@ -1721,6 +1754,9 @@ function _update_shipment() if (in_array($f, array('DELIVERYAGENT_DELIVERYDATE', 'DELIVERYAGENT_SHIPPINGDATE'))) { $fl = "TO_DATE(:1, 'DD-MM-YYYY')"; } + if ($f == 'SAFETYLEVEL') { + $this->_check_safety_level(); + } $this->db->pq("UPDATE shipping SET $f=$fl WHERE shippingid=:2", array($this->arg($f), $this->arg('sid'))); @@ -2120,6 +2156,7 @@ function _get_all_containers() } // $this->db->set_debug(True); $rows = $this->db->paginate("SELECT round(TIMESTAMPDIFF('HOUR', min(ci.bltimestamp), CURRENT_TIMESTAMP)/24,1) as age, case when count(ci2.containerinspectionid) > 1 then 0 else 1 end as allow_adhoc, c.scheduleid, c.screenid, sc.name as screen, c.imagerid, i.temperature as temperature, i.name as imager, TO_CHAR(max(ci.bltimestamp), 'HH24:MI DD-MM-YYYY') as lastinspection, round(TIMESTAMPDIFF('HOUR', max(ci.bltimestamp), CURRENT_TIMESTAMP)/24,1) as lastinspectiondays, count(distinct ci.containerinspectionid) as inspections, CONCAT(p.proposalcode, p.proposalnumber) as prop, c.bltimestamp, c.samplechangerlocation, c.beamlinelocation, d.dewarstatus, c.containertype, c.capacity, c.containerstatus, c.containerid, c.code as name, d.code as dewar, sh.shippingname as shipment, d.dewarid, sh.shippingid, count(distinct s.blsampleid) as samples, cq.containerqueueid, TO_CHAR(cq.createdtimestamp, 'DD-MM-YYYY HH24:MI') as queuedtimestamp, CONCAT(p.proposalcode, p.proposalnumber, '-', ses.visit_number) as visit, ses.beamlinename, c.requestedreturn, c.requestedimagerid, c.comments, c.experimenttype, c.storagetemperature, c.barcode, reg.barcode as registry, reg.containerregistryid, + sh.safetylevel as shippingsafetylevel, (SELECT sch.name FROM schedule sch WHERE sch.scheduleid = c.scheduleid) as schedule, (SELECT i2.name FROM imager i2 WHERE i2.imagerid = c.requestedimagerid) as requestedimager, (SELECT count(distinct ss.blsubsampleid) FROM blsubsample ss RIGHT OUTER JOIN blsample s2 ON s2.blsampleid = ss.blsampleid WHERE s2.containerid = c.containerid AND ss.source='manual') as subsamples, diff --git a/client/src/js/models/shipment.js b/client/src/js/models/shipment.js index e1849b9bf..eccef8c27 100644 --- a/client/src/js/models/shipment.js +++ b/client/src/js/models/shipment.js @@ -18,6 +18,11 @@ define(['backbone'], function (Backbone) { pattern: 'fcode', }, + FIRSTEXPERIMENTID: { + required: false, + pattern: 'number', + }, + REMOTEORMAILIN: { required: false, }, diff --git a/client/src/js/modules/dc/views/distl.js b/client/src/js/modules/dc/views/distl.js index e21dd9bd4..76496c70f 100644 --- a/client/src/js/modules/dc/views/distl.js +++ b/client/src/js/modules/dc/views/distl.js @@ -27,7 +27,7 @@ define(['marionette', 'modules/dc/models/distl', 'utils', }, plotSelected: function(e, ranges) { - this.trigger('plot:select', Math.floor(ranges.xaxis.from), Math.ceil(ranges.xaxis.to)) + this.trigger('plot:select', Math.round(ranges.xaxis.from), Math.round(ranges.xaxis.to)) }, plotUnselected: function(e) { @@ -112,4 +112,4 @@ define(['marionette', 'modules/dc/models/distl', 'utils', } }) -}) \ No newline at end of file +}) diff --git a/client/src/js/modules/dc/views/reprocess2.js b/client/src/js/modules/dc/views/reprocess2.js index 53ebb4d54..6dee03148 100644 --- a/client/src/js/modules/dc/views/reprocess2.js +++ b/client/src/js/modules/dc/views/reprocess2.js @@ -110,13 +110,19 @@ define(['backbone', 'marionette', 'views/dialog', var si = parseInt(this.model.get('SI')) var ni = parseInt(this.model.get('NUMIMG')) - if (this.ui.st.val() > (si+ni-1)) this.ui.st.val(si+ni-1) - if (this.ui.st.val() < si) this.ui.st.val(si) + if (this.ui.st.val()) { + if (this.ui.st.val() > (si+ni-1)) this.ui.st.val(si+ni-1) + if (this.ui.st.val() < si) this.ui.st.val(si) + } - if (this.ui.en.val() > (si+ni-1)) this.ui.en.val(si+ni-1) - if (this.ui.en.val() < si) this.ui.en.val(si) + if (this.ui.en.val()) { + if (this.ui.en.val() > (si+ni-1)) this.ui.en.val(si+ni-1) + if (this.ui.en.val() < si) this.ui.en.val(si) + } - this.plotview.setSelection(parseInt(this.ui.st.val()), parseInt(this.ui.en.val())) + if (this.ui.st.val() && this.ui.en.val()) { + this.plotview.setSelection(parseInt(this.ui.st.val()), parseInt(this.ui.en.val())) + } }, initialize: function(options) { @@ -377,7 +383,7 @@ define(['backbone', 'marionette', 'views/dialog', }, this) $.when.apply($, reqs).done(function() { - app.alert({ message: jobs+' reprocessing job(s) successfully submitted'}) + app.message({ message: jobs+' reprocessing job(s) successfully submitted'}) _.each(rps, function(rp) { self._enqueue({ PROCESSINGJOBID: rp.get('PROCESSINGJOBID') }) }) @@ -463,7 +469,7 @@ define(['backbone', 'marionette', 'views/dialog', reqs.push(reprocessingsweeps.save()) $.when.apply($, reqs).done(function() { - app.alert({ message: '1 reprocessing job successfully submitted'}) + app.message({ message: '1 reprocessing job successfully submitted'}) self._enqueue({ PROCESSINGJOBID: reprocessing.get('PROCESSINGJOBID') }) }) }, diff --git a/client/src/js/modules/shipment/views/plate.js b/client/src/js/modules/shipment/views/plate.js index 62d6b0a90..f0bb321f5 100644 --- a/client/src/js/modules/shipment/views/plate.js +++ b/client/src/js/modules/shipment/views/plate.js @@ -250,7 +250,7 @@ define(['marionette', 'backbone', 'utils', 'backbone-validation'], function(Mari this.ctx.fillStyle = sample.get(this.rankOption.value) ? utils.rainbow(val/4) - : (sample.get(this.rankOption.check) > 0 ? 'yellow' : '#dfdfdf') + : '#dfdfdf' this.ctx.fill() } else { diff --git a/client/src/js/modules/shipment/views/shipment.js b/client/src/js/modules/shipment/views/shipment.js index 6932f8645..bbde464f3 100644 --- a/client/src/js/modules/shipment/views/shipment.js +++ b/client/src/js/modules/shipment/views/shipment.js @@ -200,7 +200,7 @@ define(['marionette', var edit = new Editable({ model: this.model, el: this.$el }) edit.create('SHIPPINGNAME', 'textlong') - edit.create('SAFETYLEVEL', 'select', { data: {'Green': 'Green', 'Yellow':'Yellow', 'Red': 'Red'} }) + edit.create('SAFETYLEVEL', 'select', { data: {'Green': 'Green', 'Yellow':'Yellow', 'Red': 'Red'}, alert: true, revert: true }) edit.create('COMMENTS', 'textarea') edit.create('DELIVERYAGENT_AGENTNAME', 'text') edit.create('DELIVERYAGENT_AGENTCODE', 'text') diff --git a/client/src/js/modules/shipment/views/shipmentadd.js b/client/src/js/modules/shipment/views/shipmentadd.js index a5fea270b..602e33487 100644 --- a/client/src/js/modules/shipment/views/shipmentadd.js +++ b/client/src/js/modules/shipment/views/shipmentadd.js @@ -63,6 +63,7 @@ define(['marionette', 'views/form', 'click a.add_lc': 'addLC', 'click @ui.noexp': 'updateFirstExp', 'click @ui.dynamic': 'updateDynamicSchedule', + 'change select[name^=SAFETYLEVEL]': 'changeSafetyLevel', }, ui: { @@ -73,6 +74,8 @@ define(['marionette', 'views/form', noexp: 'input[name=noexp]', dynamic: 'input[name=DYNAMIC]', // A checkbox to indicate dynamic/remote mail-in scheduling comments: 'textarea[name=COMMENTS]', // We need this so we can prefill comments to aid users + safetylevel: 'select[name^=SAFETYLEVEL]', + udcresponsive: '.udcresponsive', }, addLC: function(e) { @@ -97,12 +100,24 @@ define(['marionette', 'views/form', app.alert({ message: 'Something went wrong registering this shipment, please try again'}) }, + changeSafetyLevel: function() { + if (this.ui.safetylevel.val() === 'Green') { + this.ui.udcresponsive.show() + } else { + this.ui.noexp.prop('checked', false) + this.updateFirstExp() + this.ui.dynamic.prop('checked', false) + this.updateDynamicSchedule() + this.ui.udcresponsive.hide() + } + }, + updateFirstExp: function() { if (this.ui.noexp.is(':checked')) { this.ui.first.html('') this.ui.dynamic.prop('checked', false) } else { - this.ui.first.html(this.visits.opts()) + this.ui.first.html(''+this.visits.opts()) } }, @@ -121,7 +136,7 @@ define(['marionette', 'views/form', } } else { this.model.validation.REMOTEORMAILIN.required = false - this.ui.first.html(this.visits.opts()) + this.ui.first.html(''+this.visits.opts()) this.$el.find(".remoteform").hide() if (industrial_visit) { this.$el.find(".remoteormailin").hide() diff --git a/client/src/js/modules/types/mx/shipment/views/container-mixin.js b/client/src/js/modules/types/mx/shipment/views/container-mixin.js index f622ac683..e29c60b4d 100644 --- a/client/src/js/modules/types/mx/shipment/views/container-mixin.js +++ b/client/src/js/modules/types/mx/shipment/views/container-mixin.js @@ -128,7 +128,7 @@ export default { proteinsCollection.queryParams.external = 1 } - proteinsCollection.queryParams.SAFETYLEVEL = 'ALL' + proteinsCollection.queryParams.SAFETYLEVEL = this.shippingSafetyLevel const result = await this.$store.dispatch('getCollection', proteinsCollection) this.proteins = result.toJSON() diff --git a/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue b/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue index f08642bef..9f2e59ee1 100644 --- a/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue +++ b/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue @@ -90,9 +90,13 @@
+ + Cannot queue containers in {{ shippingSafetyLevel }} shipments +
@@ -461,6 +465,7 @@ export default { // The dewar that this container will belong to dewar: null, + shippingSafetyLevel: null, processingPipeline: '', processingPipelines: [], @@ -542,23 +547,6 @@ export default { } } }, - AUTOMATED: { - immediate: true, - handler: function(newVal) { - const proteinsCollection = new DistinctProteins() - // If now on, add safety level to query - // Automated collections limited to GREEN Low risk samples - if (newVal) { - proteinsCollection.queryParams.SAFETYLEVEL = 'GREEN'; - } else { - proteinsCollection.queryParams.SAFETYLEVEL = 'ALL'; - } - this.$store.dispatch('getCollection', proteinsCollection).then( (result) => { - this.proteins = result.toJSON() - }) - app.trigger('samples:automated', newVal) - } - }, CONTAINERREGISTRYID: { immediate: true, handler: function(newVal) { @@ -593,6 +581,7 @@ export default { created: function() { this.containerType = INITIAL_CONTAINER_TYPE this.dewar = this.options.dewar.toJSON() + this.shippingSafetyLevel = this.dewar.SHIPPINGSAFETYLEVEL this.DEWARID = this.dewar.DEWARID this.resetSamples(this.containerType.CAPACITY) diff --git a/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue b/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue index 20c5b46ac..9de2394dc 100644 --- a/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue +++ b/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue @@ -106,24 +106,25 @@ @click="onUnQueueContainer" > Unqueue + + Cannot queue containers in {{ shippingSafetyLevel }} shipments + + + There was an error submitting the container to the queue. Please fix any errors in the samples table. + Try again + Cancel + - - There was an error submitting the container to the queue. Please fix any errors in the samples table. - Try again - Cancel - - - Queue this container for Auto Collect - + Queue this container for Auto Collect @@ -324,6 +325,7 @@ export default { dewarsCollection: null, selectedDewarId: null, selectedShipmentId: null, + shippingSafetyLevel: null, editingSampleLocation: null } }, @@ -354,6 +356,7 @@ export default { loadContainerData() { this.container = Object.assign({}, this.containerModel.toJSON()) this.containerId = this.containerModel.get('CONTAINERID') + this.shippingSafetyLevel = this.containerModel.get('SHIPPINGSAFETYLEVEL') this.containerQueueId = this.containerModel.get('CONTAINERQUEUEID') if (this.containerQueueId) this.QUEUEFORUDC = true }, @@ -477,6 +480,7 @@ export default { }) this.$nextTick(() => { this.loadContainerData() + this.getProteins() // TODO: Toggle Auto in the samples table }) } catch (error) { @@ -497,6 +501,7 @@ export default { this.$emit('update-container-state', { CONTAINERQUEUEID: null }) this.$nextTick(() => { this.loadContainerData() + this.getProteins() // TODO: Toggle Auto in the samples table }) } catch (error) { diff --git a/client/src/js/templates/dc/dc_autoproc.html b/client/src/js/templates/dc/dc_autoproc.html index cde85c1f9..94c69e5d9 100644 --- a/client/src/js/templates/dc/dc_autoproc.html +++ b/client/src/js/templates/dc/dc_autoproc.html @@ -22,6 +22,7 @@ <% } %> + Processing Log Plots Archive Logs & Files diff --git a/client/src/js/templates/shipment/containerli.html b/client/src/js/templates/shipment/containerli.html index b3f73d39a..c93be9f2b 100644 --- a/client/src/js/templates/shipment/containerli.html +++ b/client/src/js/templates/shipment/containerli.html @@ -1,10 +1,13 @@ <%- NAME %> (<%-SAMPLES%> samples) <% if (REQUESTEDIMAGERID) { %> - [Req: <%-REQUESTEDIMAGER%>] + [Req: <%-REQUESTEDIMAGER%>] <% } %> <% if (IMAGERID) { %> - [Img: <%-IMAGER%>] + [Img: <%-IMAGER%>] + <% } %> + <% if (VISIT) { %> + (<%-VISIT%>) <% } %> Print Container Report diff --git a/client/src/js/templates/shipment/shipmentadd.html b/client/src/js/templates/shipment/shipmentadd.html index 3866fe012..927b68a39 100644 --- a/client/src/js/templates/shipment/shipmentadd.html +++ b/client/src/js/templates/shipment/shipmentadd.html @@ -52,12 +52,14 @@

Add New Shipment

- - + + + +
diff --git a/client/src/js/utils/editable.js b/client/src/js/utils/editable.js index 3515c210d..622779b54 100644 --- a/client/src/js/utils/editable.js +++ b/client/src/js/utils/editable.js @@ -158,10 +158,13 @@ define(['marionette', */ create: function(attr, type, options, refetch) { var submit = function(value, settings) { + prevValue = this.model.get(attr) this.model.set(attr, value) console.log('valid', this.model.isValid(true), attr, 'changed', this.model.changedAttributes()) var self = this + toReturn = refetch ? '' : _.escape(value) this.model.save(this.model.changedAttributes(), { patch: true, validate: false, + async: !(options && options.revert), success: function() { if (refetch) self.model.fetch() }, @@ -177,10 +180,14 @@ define(['marionette', if (json.message) app.alert({ message: json.message }) else app.alert({ message: 'Something went wrong' }) } + if(options && options.revert) { + self.model.set(attr, prevValue) + toReturn = prevValue + } } }) - return refetch ? '' : _.escape(value) + return toReturn } var onsubmit = function(settings, td) { From f7780e95af0d90d9ffe4dab587adf37cb02476e5 Mon Sep 17 00:00:00 2001 From: Mark W <24956497+ndg63276@users.noreply.github.com> Date: Tue, 22 Oct 2024 09:23:16 +0100 Subject: [PATCH 02/14] Merge pre-release/2024-R4.4 into master (#849) * LIMS-1003: Fix searching of container inspections (#825) Co-authored-by: Mark Williams * LIMS-380: Fix position of autoPROC images (#831) * LIMS-380: Fix position of autoPROC images * LIMS-380: Just use iframe size of 98% * Set width/height to percentage value (#838) --------- Co-authored-by: Mark Williams Co-authored-by: Guilherme Francisco * LIMS-1430: Fix bugs on prepare for data collection page (#829) * LIMS-1430: Allow queuing even if no visible samples * LIMS-1430: Show number of samples to queue * LIMS-1430: Watch all subsamples for changes * LIMS-1430: Remove TODO list * LIMS-1430: Display number is of data collections, not samples * LIMS-1430: Dont allow queueing if hidden data collections are invalid * Update client/src/js/modules/imaging/views/queuecontainer.js Co-authored-by: Guilherme Francisco --------- Co-authored-by: Mark Williams Co-authored-by: Guilherme Francisco * LIMS-1390: Fix searching of data collection groups (#833) Co-authored-by: Mark Williams * LIMS-1286: Show ERA status on calendar and visits list (#834) Co-authored-by: Mark Williams * LIMS-1354: Migrate from ActiveMQ to RabbitMQ (#826) * LIMS-1354: Migrate from ActiveMQ to RabbitMQ * Unwrap try...catch * Add routing_key and vhost * Add polyfill for BCMath as php-bcmath extension is not installed but required by php-amqplib for AMQP --------- Co-authored-by: Mark Williams Co-authored-by: Guilherme Francisco Co-authored-by: James Hall <41731916+JPHall-DLS@users.noreply.github.com> --- api/composer.json | 5 +- api/src/Page.php | 16 ++-- api/src/Page/DC.php | 13 +-- api/src/Page/Imaging.php | 11 ++- api/src/Page/Process.php | 4 +- api/src/Page/Proposal.php | 1 + api/src/Queue.php | 46 +++++------ .../views/components/calendar-day-events.vue | 9 +- .../modules/imaging/views/queuecontainer.js | 82 +++++++++++++++---- .../js/modules/visits/views/visit_list.vue | 9 +- .../js/templates/imaging/queuecontainer.html | 10 +-- client/src/js/views/log.js | 10 +-- 12 files changed, 134 insertions(+), 82 deletions(-) diff --git a/api/composer.json b/api/composer.json index 8101fed18..8ec82129d 100644 --- a/api/composer.json +++ b/api/composer.json @@ -21,12 +21,13 @@ "mpdf/mpdf": "8.1.2", "ralouphie/getallheaders": "2.0.5", "slim/slim": "2.6.2", - "stomp-php/stomp-php": "3.0.6", + "php-amqplib/php-amqplib": "^2.0", "symfony/http-foundation": "^5.4", "symfony/filesystem": "^5.4", "mpdf/qrcode": "^1.2", "mtcmedia/dhl-api": "dev-master#9b4b6315", - "maennchen/zipstream-php": "2.1.0" + "maennchen/zipstream-php": "2.1.0", + "phpseclib/bcmath_compat": "^2.0" }, "autoload": { "psr-4": { diff --git a/api/src/Page.php b/api/src/Page.php index c9f293507..f098868f9 100644 --- a/api/src/Page.php +++ b/api/src/Page.php @@ -1109,14 +1109,16 @@ function _submit_zocalo_recipe($recipe, $parameters, $error_code = 500) } - function _send_zocalo_message($zocalo_queue, $zocalo_message, $error_code = 500) + function _send_zocalo_message($rabbitmq_zocalo_vhost, $zocalo_message, $error_code = 500) { global - $zocalo_server, - $zocalo_username, - $zocalo_password; + $rabbitmq_zocalo_host, + $rabbitmq_zocalo_port, + $rabbitmq_zocalo_username, + $rabbitmq_zocalo_password, + $rabbitmq_zocalo_routing_key; - if (empty($zocalo_server) || empty($zocalo_queue)) + if (empty($rabbitmq_zocalo_host) || empty($rabbitmq_zocalo_vhost)) { $message = 'Zocalo server or queue not specified.'; error_log($message); @@ -1129,8 +1131,8 @@ function _send_zocalo_message($zocalo_queue, $zocalo_message, $error_code = 500) try { error_log("Sending message" . var_export($zocalo_message, true)); - $queue = new Queue($zocalo_server, $zocalo_username, $zocalo_password); - $queue->send($zocalo_queue, $zocalo_message, true, $this->user->loginId); + $queue = new Queue($rabbitmq_zocalo_host, $rabbitmq_zocalo_port, $rabbitmq_zocalo_username, $rabbitmq_zocalo_password, $rabbitmq_zocalo_vhost); + $queue->send($zocalo_message, $rabbitmq_zocalo_routing_key); } catch (Exception $e) { diff --git a/api/src/Page/DC.php b/api/src/Page/DC.php index bc43ba535..c18d0dacd 100644 --- a/api/src/Page/DC.php +++ b/api/src/Page/DC.php @@ -368,13 +368,14 @@ function _data_collections($single = null) $s = str_replace('_', '$_', $this->arg('s')); $st = sizeof($args) + 1; - $where .= " AND (lower(dc.filetemplate) LIKE lower(CONCAT(CONCAT('%',:$st),'%')) ESCAPE '$' OR lower(dc.imagedirectory) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "), '%')) ESCAPE '$' OR lower(smp.name) LIKE lower(CONCAT(CONCAT('%', :" . ($st + 2) . "), '%')) ESCAPE '$')"; - $where2 .= " AND (lower(es.comments) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 3) . "), '%')) ESCAPE '$' OR lower(es.element) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 4) . "), '%')) ESCAPE '$' OR lower(smp.name) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 5) . "), '%')) ESCAPE '$')"; + $where .= " AND (dc.filetemplate LIKE CONCAT('%',:$st,'%') ESCAPE '$' OR dc.imagedirectory LIKE CONCAT('%',:" . ($st + 1) . ",'%') ESCAPE '$' OR smp.name LIKE CONCAT('%', :" . ($st + 2) . ",'%') ESCAPE '$')"; + $where2 .= " AND (es.comments LIKE CONCAT('%',:" . ($st + 3) . ",'%') ESCAPE '$' OR es.element LIKE CONCAT('%',:" . ($st + 4) . ",'%') ESCAPE '$' OR smp.name LIKE CONCAT('%',:" . ($st + 5) . ",'%') ESCAPE '$')"; $where3 .= ' AND r.robotactionid < 0'; - $where4 .= " AND (lower(xrf.filename) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 6) . "), '%')) ESCAPE '$' OR lower(smp.name) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 7) . "), '%')) ESCAPE '$')"; + $where4 .= " AND (xrf.filename LIKE CONCAT('%',:" . ($st + 6) . ",'%') ESCAPE '$' OR smp.name LIKE CONCAT('%',:" . ($st + 7) . ",'%') ESCAPE '$')"; for ($i = 0; $i < 8; $i++) array_push($args, $s); + } # Set Count field @@ -511,9 +512,9 @@ function _data_collections($single = null) // $this->db->set_debug(True); // will want to support these too at some point - $where2 = ' AND es.energyscanid < 0'; - $where3 = ' AND r.robotactionid < 0'; - $where4 = ' AND xrf.xfefluorescencespectrumid < 0'; + $where2 .= ' AND es.energyscanid < 0'; + $where3 .= ' AND r.robotactionid < 0'; + $where4 .= ' AND xrf.xfefluorescencespectrumid < 0'; if ($this->has_arg('dcg')) { $where .= ' AND dc.datacollectiongroupid=:' . (sizeof($args) + 1); diff --git a/api/src/Page/Imaging.php b/api/src/Page/Imaging.php index 43c480859..fa19544ad 100644 --- a/api/src/Page/Imaging.php +++ b/api/src/Page/Imaging.php @@ -376,6 +376,11 @@ function _get_inspections() $where .= " AND i.state='Completed'"; } + if ($this->has_arg('s')) { + $where .= " AND (CONCAT(p.proposalcode, p.proposalnumber) LIKE CONCAT('%', :" . (sizeof($args) + 1) . ", '%') OR c.code LIKE CONCAT('%', :" . (sizeof($args) + 2) . ", '%'))"; + array_push($args, $this->arg('s'), $this->arg('s')); + } + $tot = $this->db->pq("SELECT count(i.containerinspectionid) as tot FROM containerinspection i INNER JOIN container c ON c.containerid = i.containerid INNER JOIN dewar d ON d.dewarid = c.dewarid @@ -422,12 +427,6 @@ function _get_inspections() $order = $cols[$this->arg('sort_by')] . ' ' . $dir; } - if ($this->has_arg('s')) { - $where .= " AND (LOWER(CONCAT(p.proposalcode, p.proposalnumber)) LIKE LOWER(CONCAT(CONCAT('%', :" . (sizeof($args) + 1) . "), '%')) OR LOWER(c.code) LIKE LOWER(CONCAT(CONCAT('%', :" . (sizeof($args) + 2) . "), '%')))"; - array_push($args, $this->arg('s')); - array_push($args, $this->arg('s')); - } - if ($this->has_arg('ty')) { if ($this->arg('ty') == 'COMPLETED') $where .= " AND i.state = 'Completed' AND i.manual=0"; diff --git a/api/src/Page/Process.php b/api/src/Page/Process.php index 70fadb66d..d26ccd61b 100644 --- a/api/src/Page/Process.php +++ b/api/src/Page/Process.php @@ -355,7 +355,7 @@ function _add_reprocessing_sweep($args) { function _enqueue() { - global $zocalo_mx_reprocess_queue; + global $rabbitmq_zocalo_vhost; if (!$this->has_arg('PROCESSINGJOBID')) $this->_error('No processing job specified'); @@ -379,7 +379,7 @@ function _enqueue() 'ispyb_process' => intval($this->arg('PROCESSINGJOBID')), ) ); - $this->_send_zocalo_message($zocalo_mx_reprocess_queue, $message); + $this->_send_zocalo_message($rabbitmq_zocalo_vhost, $message); $this->_output(new \stdClass); } diff --git a/api/src/Page/Proposal.php b/api/src/Page/Proposal.php index ab719e2e7..f8207ed2f 100644 --- a/api/src/Page/Proposal.php +++ b/api/src/Page/Proposal.php @@ -456,6 +456,7 @@ function _get_visits($visit = null, $output = true) s.beamlineoperator AS lc, s.comments, s.scheduled, + s.riskrating, st.typename AS sessiontype, DATE_FORMAT(s.startdate, '%d-%m-%Y %H:%i') AS startdate, DATE_FORMAT(s.enddate, '%d-%m-%Y %H:%i') AS enddate, diff --git a/api/src/Queue.php b/api/src/Queue.php index 44b334099..4440d45a7 100644 --- a/api/src/Queue.php +++ b/api/src/Queue.php @@ -2,42 +2,34 @@ namespace SynchWeb; -use Stomp\Exception\StompException; -use Stomp\Stomp; +use PhpAmqpLib\Connection\AMQPStreamConnection; +use PhpAmqpLib\Message\AMQPMessage; class Queue { - private $server, $username, $password; + private $host, $port, $username, $password, $vhost; - function __construct($server, $username, $password) + function __construct($host, $port, $username, $password, $vhost) { - $this->server = $server; + $this->host = $host; + $this->port = $port; $this->username = $username; $this->password = $password; + $this->vhost = $vhost; } - function send($queue, array $message, $persistent = false, $login = null) + function send(array $message, $routing_key) { - try { - $connection = new Stomp($this->server); - - $connection->connect($this->username, $this->password); - - $connection->send( - $queue, - json_encode($message, JSON_UNESCAPED_SLASHES), - array( - 'persistent' => ($persistent === true), - 'synchweb.host' => gethostname(), - 'synchweb.user' => $login, - ) - ); - - $connection->disconnect(); - } catch (StompException $e) { - /** @noinspection PhpUnhandledExceptionInspection */ - - throw $e; - } + $connection = new AMQPStreamConnection($this->host, $this->port, $this->username, $this->password, $this->vhost); + $channel = $connection->channel(); + + $msg = new AMQPMessage( + json_encode($message, JSON_UNESCAPED_SLASHES) + ); + + $channel->basic_publish($msg, null, $routing_key); + + $channel->close(); + $connection->close(); } } diff --git a/client/src/js/modules/calendar/views/components/calendar-day-events.vue b/client/src/js/modules/calendar/views/components/calendar-day-events.vue index 7c2b70a20..319978312 100644 --- a/client/src/js/modules/calendar/views/components/calendar-day-events.vue +++ b/client/src/js/modules/calendar/views/components/calendar-day-events.vue @@ -21,7 +21,12 @@ class="tw-no-underline tw-text-content-page-color" > {{ session['VISIT'] }} - ({{ session['LEN'] }}) + + 🟢 + 🟡 + 🔴 + + ({{ session['LEN'] }})

- {{ session['BEAMLINEOPERATOR'] }} @@ -105,4 +110,4 @@ export default { } } - \ No newline at end of file + diff --git a/client/src/js/modules/imaging/views/queuecontainer.js b/client/src/js/modules/imaging/views/queuecontainer.js index 2ec12d35d..9de3e613f 100644 --- a/client/src/js/modules/imaging/views/queuecontainer.js +++ b/client/src/js/modules/imaging/views/queuecontainer.js @@ -99,7 +99,7 @@ define(['marionette', var self = this p.save({}, { success: function() { - app.alert({ message: 'Preset successfully saved' }) + app.message({ message: 'Preset successfully saved' }) self.column.get('plans').add(p) }, error: function() { @@ -128,8 +128,7 @@ define(['marionette', render: function() { this.$el.empty() - if (!this.model.get('CONTAINERQUEUEID')) - this.$el.html('\ + this.$el.html('\ \ ') @@ -539,6 +538,9 @@ define(['marionette', xtal: '.xtalpreview', nodata: 'input[name=nodata]', notcompleted: 'input[name=notcompleted]', + queuebutton: '.queuebutton', + unqueuebutton: '.unqueuebutton', + queuelength: '.queuelength', }, @@ -634,7 +636,7 @@ define(['marionette', }, success: function(resp) { _.each(resp, function (r) { - let ss = self.qsubsamples.fullCollection.findWhere({ BLSUBSAMPLEID: r.BLSUBSAMPLEID }) + let ss = self.typeselector.shadowCollection.findWhere({ BLSUBSAMPLEID: r.BLSUBSAMPLEID }) ss.set({ READYFORQUEUE: '0' }) }) }, @@ -650,12 +652,13 @@ define(['marionette', e.preventDefault() utils.confirm({ title: 'Unqueue Container?', - content: 'Are you sure you want to remove this container from the queue? You will loose your current place', + content: 'Are you sure you want to remove this container from the queue? You will lose your current place', callback: this.doUnqueueContainer.bind(this) }) }, doUnqueueContainer: function() { + var self = this Backbone.ajax({ url: app.apiurl+'/shipment/containers/queue', data: { @@ -663,7 +666,10 @@ define(['marionette', UNQUEUE: 1, }, success: function() { - app.alert({ message: 'Container Successfully Unqueued' }) + app.message({ message: 'Container Successfully Unqueued' }) + self.ui.unqueuebutton.hide() + self.ui.queuebutton.show() + self.model.set('CONTAINERQUEUEID', null) }, error: function() { app.alert({ message: 'Something went wrong unqueuing this container' }) @@ -710,12 +716,12 @@ define(['marionette', queueContainer: function(e) { e.preventDefault() - if (!this.qsubsamples.fullCollection.length) { + if (!this.typeselector.shadowCollection.length) { app.alert({ message: 'Please add some samples before queuing this container' }) return } - // need to validate all models here again incase they havnt been rendered + // need to validate all models here again in case they haven't been rendered this.qsubsamples.fullCollection.each(function(qs) { if (qs.get('_valid') !== undefined) return @@ -724,7 +730,7 @@ define(['marionette', console.log({ experimentCell: val }) }, this) - var invalid = this.qsubsamples.fullCollection.where({ '_valid': false }) + var invalid = this.typeselector.shadowCollection.where({ '_valid': false }) console.log('queue', invalid, invalid.length > 0) if (invalid.length > 0) { app.alert({ message: 'There are '+invalid.length+' sub samples with invalid experimental plans, please either correct or remove these from the queue' }) @@ -732,13 +738,17 @@ define(['marionette', if (inv) inv.set({ isSelected: true }) } else { + var self = this Backbone.ajax({ url: app.apiurl+'/shipment/containers/queue', data: { CONTAINERID: this.model.get('CONTAINERID') }, - success: function() { - app.alert({ message: 'Container Successfully Queued' }) + success: function(json) { + app.message({ message: 'Container Successfully Queued' }) + self.ui.unqueuebutton.show() + self.ui.queuebutton.hide() + self.model.set('CONTAINERQUEUEID', json.CONTAINERQUEUEID) }, error: function() { app.alert({ message: 'Something went wrong queuing this container' }) @@ -768,21 +778,25 @@ define(['marionette', initialize: function() { this._lastSample = null + this._subsamples_ready = [] this.platetypes = new PlateTypes() this.type = this.platetypes.findWhere({ name: this.model.get('CONTAINERTYPE') }) + this.unfilteredSubsamples = null this.subsamples = new SubSamples() this.subsamples.queryParams.cid = this.model.get('CONTAINERID') this.subsamples.state.pageSize = 10 - this.listenTo(this.subsamples, 'change:isSelected', this.selectSubSample, this) - this.listenTo(this.subsamples, 'sync add remove change:READYFORQUEUE', this.refreshQSubSamples, this) - this.subsamples.fetch() + + this._subsamples_ready.push(this.subsamples.fetch()) this.inspections = new ContainerInspections() this.inspections.queryParams.cid = this.model.get('CONTAINERID') this.inspections.setSorting('BLTIMESTAMP', 1) - this.inspections.fetch().done(this.getInspectionImages.bind(this)) + + this._subsamples_ready.push(this.inspections.fetch()) + + $.when.apply($, this._subsamples_ready).done(this.onSubsamplesReady.bind(this)) this.inspectionimages = new InspectionImages() @@ -832,6 +846,31 @@ define(['marionette', this.ui.preset.html(this.plans.opts()) }, + onSubsamplesReady: function() { + this.unfilteredSubsamples = this.subsamples.fullCollection.clone() + this.getInspectionImages() + this.refreshQSubSamples() + this.listenTo(this.subsamples, 'change:isSelected', this.selectSubSample, this) + this.listenTo(this.unfilteredSubsamples, 'sync add remove change:READYFORQUEUE', this.refreshQSubSamples, this) + this.listenTo(this.unfilteredSubsamples, 'change', this.updateQueueLength) + this.listenTo(this.model, 'change:CONTAINERQUEUEID', this.onContainerQueueIdChange) + }, + + updateQueueLength: function() { + var n = this.typeselector.shadowCollection.length + this.ui.queuelength.html(`(${n} data collection${n===1 ? '' : 's'})`) + }, + + onContainerQueueIdChange: function() { + if (!this.model.get('CONTAINERQUEUEID')) { + var models = this.unfilteredSubsamples.filter(function(m) { return m.get('CONTAINERQUEUEID') }) + _.each(models, function(model) { + model.set('READYFORQUEUE', '1') + }, this) + } + this.doOnRender() + }, + getInspectionImages: function() { this.inspectionimages.queryParams.iid = this.inspections.at(0).get('CONTAINERINSPECTIONID') this.inspectionimages.fetch().done(this.selectSample.bind(this)) @@ -843,8 +882,8 @@ define(['marionette', refreshQSubSamples: function() { if (this.model.get('CONTAINERQUEUEID')) { - this.qsubsamples.fullCollection.reset(this.subsamples.fullCollection.where({ CONTAINERQUEUEID: this.model.get('CONTAINERQUEUEID') })) - } else this.qsubsamples.fullCollection.reset(this.subsamples.fullCollection.where({ READYFORQUEUE: '1' })) + this.qsubsamples.fullCollection.reset(this.unfilteredSubsamples.where({ CONTAINERQUEUEID: this.model.get('CONTAINERQUEUEID') })) + } else this.qsubsamples.fullCollection.reset(this.unfilteredSubsamples.where({ READYFORQUEUE: '1' })) }, selectSubSample: function() { @@ -859,6 +898,7 @@ define(['marionette', onRender: function() { + this.ui.unqueuebutton.hide() this.subsamples.queryParams.nodata = this.getNoData.bind(this) this.subsamples.queryParams.notcompleted = this.getNotCompleted.bind(this) this._ready.done(this.doOnRender.bind(this)) @@ -904,7 +944,6 @@ define(['marionette', { name: '_valid', label: 'Valid', cell: table.TemplateCell, editable: false, test: '_valid', template: '' }, { name: '', cell: table.StatusCell, editable: false }, { label: '', cell: SnapshotCell, editable: false, inspectionimages: this.inspectionimages }, - { label: '', cell: ActionsCell, editable: false, plans: this.plans }, ] if (app.mobile()) { @@ -928,8 +967,15 @@ define(['marionette', if (this.model.get('CONTAINERQUEUEID')) { this.ui.rpreset.hide() + this.ui.queuebutton.hide() + this.ui.unqueuebutton.show() queuedSubSamples.push({ label: '', cell: table.StatusCell, editable: false }) queuedSubSamples.push({ label: '', cell: table.TemplateCell, editable: false, template: '' }) + } else { + this.ui.rpreset.show() + this.ui.queuebutton.show() + this.ui.unqueuebutton.hide() + queuedSubSamples.push({ label: '', cell: ActionsCell, editable: false, plans: this.plans }) } this.table2 = new TableView({ diff --git a/client/src/js/modules/visits/views/visit_list.vue b/client/src/js/modules/visits/views/visit_list.vue index 5b1abbf84..c3feef51a 100644 --- a/client/src/js/modules/visits/views/visit_list.vue +++ b/client/src/js/modules/visits/views/visit_list.vue @@ -57,7 +57,10 @@ - + 🟢 + 🟡 + 🔴 +

@@ -134,6 +137,10 @@ export default { key: "BEAMLINENAME", title: 'Beamline' }, + { + key: "ERA", + title: 'ERA' + }, { key: "DEWARS", title: 'Dewar(s)' diff --git a/client/src/js/templates/imaging/queuecontainer.html b/client/src/js/templates/imaging/queuecontainer.html index cff3c820b..4c84d99c9 100644 --- a/client/src/js/templates/imaging/queuecontainer.html +++ b/client/src/js/templates/imaging/queuecontainer.html @@ -2,8 +2,7 @@

Prepare Container for Data Collection

This pages allows you to queue samples in a container for data collection

-<% if (CONTAINERQUEUEID) { %> -

+

<% if (['disposed', 'in_storage', null].indexOf(CONTAINERSTATUS) > -1) { %> This container is queued for data collection, you can not modify it without unqueuing it Unqueue @@ -11,7 +10,6 @@

Prepare Container for Data Collection

This container is en route for data collection, it cannot be unqueued. <% } %>

-<% } %>
@@ -65,10 +63,12 @@

Queued Samples

-<% if (!CONTAINERQUEUEID) { %> +

Queue Plate

Queuing the container will make the queued items immutable, please make sure you have added all sub samples, and that they have valid experimental plans

+
-<% } %> +
+ diff --git a/client/src/js/views/log.js b/client/src/js/views/log.js index 88db1ab96..54d25ae47 100644 --- a/client/src/js/views/log.js +++ b/client/src/js/views/log.js @@ -7,8 +7,7 @@ define(['marionette', 'views/dialog', 'utils'], function(Marionette, DialogView, initialize: function(options) { this.url = options.url this.load() - this.iframe = $( - ``) + this.iframe = $(``) }, // Override existing dialogOptions of Dialog View @@ -117,11 +116,10 @@ define(['marionette', 'views/dialog', 'utils'], function(Marionette, DialogView, onRender: function() { this.$el.append(this.iframe) - this.$el.find('iframe').css('width', $(window).width()*(app.mobile() ? 0.8 : 0.5)) - this.$el.find('iframe').css('height', $(window).height()*(app.mobile() ? 0.8 : 0.5)) - + this.$el.find('iframe').css('width', "99%") + this.$el.find('iframe').css('height', "98%") } }) -}) \ No newline at end of file +}) From c5f958c10b51f226bc105015d62cc1d5fb496090 Mon Sep 17 00:00:00 2001 From: Mark W <24956497+ndg63276@users.noreply.github.com> Date: Tue, 12 Nov 2024 09:59:17 +0000 Subject: [PATCH 03/14] Merge pre-release/2024-R5.1 into master (#859) * LIMS-139: link multiplex results to sample or sample group (#816) * LIMS-1454: Show non-automatic downstream processing (#837) * LIMS-1462: Show characterizations on Screenings tab (#844) * LIMS-1467: Allow lab contacts to have spaces in their names (#843) * LIMS-1489: Update message when trying to queue containers in uploaded shipments (#847) --- api/src/Page/Contact.php | 4 ++-- api/src/Page/DC.php | 8 ++++---- api/src/Page/Processing.php | 15 ++++++++++++--- client/src/js/models/labcontact.js | 4 ++-- client/src/js/modules/shipment/models/dispatch.js | 4 ++-- client/src/js/modules/shipment/views/dispatch.js | 2 ++ .../types/mx/shipment/views/mx-container-add.vue | 5 ++++- .../types/mx/shipment/views/mx-container-view.vue | 3 +++ 8 files changed, 31 insertions(+), 14 deletions(-) diff --git a/api/src/Page/Contact.php b/api/src/Page/Contact.php index 2136b5f00..3b1159550 100644 --- a/api/src/Page/Contact.php +++ b/api/src/Page/Contact.php @@ -9,8 +9,8 @@ class Contact extends Page { public static $arg_list = array('CARDNAME' => '([\w\s\-])+', - 'FAMILYNAME' => '([\w\-])+', - 'GIVENNAME' => '([\w\-])+', + 'FAMILYNAME' => '([\w\s\-])+', + 'GIVENNAME' => '([\w\s\-])+', 'PHONENUMBER' => '.*', 'EMAILADDRESS' => '.*', 'LABNAME' => '([\w\s\-])+', diff --git a/api/src/Page/DC.php b/api/src/Page/DC.php index c18d0dacd..34e59f5f2 100644 --- a/api/src/Page/DC.php +++ b/api/src/Page/DC.php @@ -121,11 +121,11 @@ function _data_collections($single = null) $where = ''; if ($this->arg('t') == 'sc') - $where = ' AND (dc.overlap != 0 OR ifnull(et.name, dcg.experimenttype) = "Screening")'; + $where = ' AND (dc.overlap != 0 OR ifnull(et.name, dcg.experimenttype) in ("Screening", "Characterization"))'; else if ($this->arg('t') == 'gr') $where = ' AND dc.axisrange = 0'; else if ($this->arg('t') == 'fc') - $where = ' AND dc.overlap = 0 AND dc.axisrange > 0 AND dc.numberOfImages > 1 AND ifnull(et.name, dcg.experimenttype) != "Screening"'; + $where = ' AND dc.overlap = 0 AND dc.axisrange > 0 AND dc.numberOfImages > 1 AND ifnull(et.name, dcg.experimenttype) not in ("Screening", "Characterization")'; } else if ($this->arg('t') == 'edge') { $where2 = ''; } else if ($this->arg('t') == 'mca') { @@ -139,11 +139,11 @@ function _data_collections($single = null) $where2 = " AND es.comments LIKE '%_FLAG_%'"; $where4 = " AND xrf.comments LIKE '%_FLAG_%'"; } else if ($this->arg('t') == 'ap') { - $where = ' AND app.processingstatus = 1'; + $where = " AND ifnull(et.name, dcg.experimenttype) not in ('Screening', 'Characterization') AND app.processingstatus = 1"; $extj[0] .= "INNER JOIN autoprocintegration ap ON dc.datacollectionid = ap.datacollectionid INNER JOIN autoprocprogram app ON app.autoprocprogramid = ap.autoprocprogramid"; } else if ($this->arg('t') == 'ph') { - $where = " AND app.processingstatus = 1 AND app.processingprograms in ('big_ep', 'fast_ep')"; + $where = " AND ifnull(et.name, dcg.experimenttype) not in ('Screening', 'Characterization') AND app.processingstatus = 1 AND app.processingprograms in ('big_ep', 'fast_ep')"; $extj[0] .= "INNER JOIN processingjob pj ON dc.datacollectionid = pj.datacollectionid INNER JOIN autoprocprogram app ON app.processingjobid = pj.processingjobid"; } else if ($this->arg('t') == 'err') { diff --git a/api/src/Page/Processing.php b/api/src/Page/Processing.php index 20546718c..098d77b26 100644 --- a/api/src/Page/Processing.php +++ b/api/src/Page/Processing.php @@ -141,7 +141,7 @@ function _autoproc_status($where, $ids) { INNER JOIN processingjob pj ON pj.datacollectionid = dc.datacollectionid INNER JOIN autoprocprogram app ON pj.processingjobid = app.processingjobid LEFT OUTER JOIN autoprocintegration api ON api.autoprocprogramid = app.autoprocprogramid - WHERE $where AND api.autoprocintegrationid IS NULL AND pj.automatic = 1 + WHERE $where AND api.autoprocintegrationid IS NULL AND app.processingprograms NOT IN ('$filter')", ), $ids @@ -697,11 +697,17 @@ private function _autoprocessing_query_builder($where, $group, $order = '') { ap.refinedcell_alpha as cell_al, ap.refinedcell_beta as cell_be, ap.refinedcell_gamma as cell_ga, - (SELECT COUNT(api1.autoprocintegrationid) FROM autoprocintegration api1 WHERE api1.autoprocprogramid = app.autoprocprogramid) as imagesweepcount, + (SELECT COUNT(api1.autoprocintegrationid) FROM autoprocintegration api1 WHERE api1.autoprocprogramid = app.autoprocprogramid) as imagesweepcount, app.processingstatus, app.processingmessage, count(distinct pjis.datacollectionid) as dccount, max(pjis.processingjobid) as processingjobid, + (SELECT IFNULL(blsg.name, bls.name) FROM processingjobparameter pjp + LEFT OUTER JOIN blsample bls ON pjp.parametervalue = bls.blsampleid + LEFT OUTER JOIN blsamplegroup blsg ON pjp.parametervalue = blsg.blsamplegroupid + WHERE pjp.processingjobid = pj.processingjobid + AND pjp.parameterkey in ('sample_id', 'sample_group_id') + ) as groupname, pj.automatic"; $from = "FROM autoprocintegration api"; @@ -751,7 +757,7 @@ private function _format_auto_processing_result($table_rows, $messages_result) { 'cell_ga', ); $resolution_data = array('rlow', 'rhigh'); - $returned_keys = array('PROCESSINGJOBID', 'IMAGESWEEPCOUNT', 'DCCOUNT', 'TYPE', 'PROCESSINGSTATUS', 'PROCESSINGMESSAGE'); + $returned_keys = array('PROCESSINGJOBID', 'IMAGESWEEPCOUNT', 'DCCOUNT', 'TYPE', 'PROCESSINGSTATUS', 'PROCESSINGMESSAGE', 'GROUPNAME'); foreach($table_rows as &$row) { if (!array_key_exists($row['AUTOPROCPROGRAMID'], $formatted_result)) { @@ -772,6 +778,9 @@ private function _format_auto_processing_result($table_rows, $messages_result) { $prefix = preg_match('/multi/', $value) ? '' : 'multi-'; $value = $row['DCCOUNT'] . 'x ' . $prefix . $value; } + if ($row['GROUPNAME']) { + $value .= ' ('.$row['GROUPNAME'].')'; + } } diff --git a/client/src/js/models/labcontact.js b/client/src/js/models/labcontact.js index b2eff310d..9a56f9923 100644 --- a/client/src/js/models/labcontact.js +++ b/client/src/js/models/labcontact.js @@ -14,11 +14,11 @@ define(['backbone'], function(Backbone) { }, FAMILYNAME: { required: true, - pattern: 'wwdash', + pattern: 'wwsdash', }, GIVENNAME: { required: true, - pattern: 'wwdash', + pattern: 'wwsdash', }, PHONENUMBER: { required: true, diff --git a/client/src/js/modules/shipment/models/dispatch.js b/client/src/js/modules/shipment/models/dispatch.js index 195045bf3..bfbc37c60 100644 --- a/client/src/js/modules/shipment/models/dispatch.js +++ b/client/src/js/modules/shipment/models/dispatch.js @@ -24,12 +24,12 @@ define(['backbone'], function(Backbone) { GIVENNAME: { required: function () {return this.dispatchDetailsRequired}, - pattern: 'wwdash', + pattern: 'wwsdash', }, FAMILYNAME: { required: function () {return this.dispatchDetailsRequired}, - pattern: 'wwdash', + pattern: 'wwsdash', }, PHONENUMBER: { diff --git a/client/src/js/modules/shipment/views/dispatch.js b/client/src/js/modules/shipment/views/dispatch.js index 113415195..416375168 100644 --- a/client/src/js/modules/shipment/views/dispatch.js +++ b/client/src/js/modules/shipment/views/dispatch.js @@ -276,6 +276,7 @@ define(['marionette', 'views/form', this.dispatchCountry = this.ui.country.val() this.ui.courierSection.show(); this.ui.dispatchDetails.show(); + this.model.visitRequired = true this.model.dispatchDetailsRequired = true this.ui.submit.show(); if ( @@ -334,6 +335,7 @@ define(['marionette', 'views/form', ){ this.model.visitRequired = false this.ui.dispatchDetails.hide() + this.model.dispatchDetailsRequired = false this.ui.submit.text("Proceed") this.ui.shippingadvice.html("On clicking 'Proceed' you will be redirected to the new Diamond shipping service to book the shipment. Please ensure all stages of the form are completed.

") } diff --git a/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue b/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue index 9f2e59ee1..865384a38 100644 --- a/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue +++ b/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue @@ -94,6 +94,9 @@ v-model="QUEUEFORUDC" name="Queue For UDC" /> + + Cannot queue container until shipment safety level is set + Cannot queue containers in {{ shippingSafetyLevel }} shipments @@ -663,7 +666,7 @@ export default { await this.$store.dispatch('samples/save', containerId) this.$store.commit('notifications/addNotification', { - message: `New Container created, click here to view it`, + message: `New Container created, click here to view it. Remember to add sequences and PDBs if needed.`, level: 'info', persist: true }) diff --git a/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue b/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue index 9de2394dc..fcd97ec9a 100644 --- a/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue +++ b/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue @@ -106,6 +106,9 @@ @click="onUnQueueContainer" > Unqueue
+ + Cannot queue container until shipment safety level is set + Cannot queue containers in {{ shippingSafetyLevel }} shipments From 6ba748fcc5f973fc0e8be71c3b25b91e74a819a8 Mon Sep 17 00:00:00 2001 From: Mark W <24956497+ndg63276@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:05:01 +0000 Subject: [PATCH 04/14] LIMS-1498: Change name of processed data archive (#855) Co-authored-by: Mark Williams --- api/src/Page/Download.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/Page/Download.php b/api/src/Page/Download.php index a1ed29bca..4a6c925f3 100644 --- a/api/src/Page/Download.php +++ b/api/src/Page/Download.php @@ -468,14 +468,14 @@ function _get_autoproc_archive() $aps = $this->db->union( array( - "SELECT app.autoprocprogramid, app.processingprograms, app.processingstatus + "SELECT app.autoprocprogramid, app.processingprograms, app.processingstatus, dc.imageprefix, dc.datacollectionnumber FROM autoprocintegration api INNER JOIN autoprocprogram app ON api.autoprocprogramid = app.autoprocprogramid INNER JOIN datacollection dc ON dc.datacollectionid = api.datacollectionid INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid WHERE s.proposalid=:1 AND app.autoprocprogramid=:2", - "SELECT app.autoprocprogramid, app.processingprograms, app.processingstatus + "SELECT app.autoprocprogramid, app.processingprograms, app.processingstatus, dc.imageprefix, dc.datacollectionnumber FROM autoprocprogram app INNER JOIN processingjob pj on pj.processingjobid = app.processingjobid INNER JOIN datacollection dc ON dc.datacollectionid = pj.datacollectionid @@ -497,7 +497,7 @@ function _get_autoproc_archive() } $clean_program = preg_replace('/[^A-Za-z0-9\-]/', '', $ap['PROCESSINGPROGRAMS']); - $zipName = $this->arg('AUTOPROCPROGRAMID') . '_' . $clean_program; + $zipName = $this->arg('AUTOPROCPROGRAMID') . '_' . $ap['IMAGEPREFIX'] . '_' . $ap['DATACOLLECTIONNUMBER'] . '_' . $clean_program; $this->_streamZipFile($files, $zipName); } From 93177457f947a9da9127a475b9a7e1116cba34e7 Mon Sep 17 00:00:00 2001 From: Mark W <24956497+ndg63276@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:05:09 +0000 Subject: [PATCH 05/14] LIMS-1469: Fix download button on old summary page (#840) Co-authored-by: Mark Williams --- api/src/Page/Download.php | 4 +--- client/src/js/modules/dc/views/summary.js | 2 +- client/src/js/modules/summary/views/summary.vue | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/api/src/Page/Download.php b/api/src/Page/Download.php index 4a6c925f3..6333e6b0e 100644 --- a/api/src/Page/Download.php +++ b/api/src/Page/Download.php @@ -92,9 +92,7 @@ function _download_visit() $this->_error('There doesnt seem to be a data archive available for this visit'); } - # ------------------------------------------------------------------------ - # Download mtz/log file for Fast DP / XIA2 - # TODO: Delete me + # This method either returns a list of plots from MX auto processing tools (n_obs, n_uniq, completeness etc.) # Or returns a specific plot based on auto processing attachment id (aid). # Individual plotly format Graphs can be returned via an aid, but will not be included in the list of plots (as their format is different) diff --git a/client/src/js/modules/dc/views/summary.js b/client/src/js/modules/dc/views/summary.js index bf7116830..670ad84b4 100644 --- a/client/src/js/modules/dc/views/summary.js +++ b/client/src/js/modules/dc/views/summary.js @@ -125,7 +125,7 @@ define(['marionette', { label: 'Resolution', cell: APCell, template: '<%-SHELLS.overall.RLOW%> - <%-SHELLS.overall.RHIGH%>
<%-SHELLS.innerShell.RLOW%> - <%-SHELLS.innerShell.RHIGH%>
<%-SHELLS.outerShell.RLOW%> - <%-SHELLS.outerShell.RHIGH%>', editable: false }, { label: 'Rmeas', cell: APCell, template: '<%-SHELLS.overall.RMEAS%>
<%-SHELLS.innerShell.RMEAS%>
<%-SHELLS.outerShell.RMEAS%>', editable: false }, { label: 'Completeness', cell: APCell, template: '<%-SHELLS.overall.COMPLETENESS%>
<%-SHELLS.innerShell.COMPLETENESS%>
<%-SHELLS.outerShell.COMPLETENESS%>', editable: false }, - { label: '', cell: APCell, template: ' Download MTZ file', editable: false }, + { label: '', cell: APCell, template: ' Download autoprocessing archive', editable: false }, ] diff --git a/client/src/js/modules/summary/views/summary.vue b/client/src/js/modules/summary/views/summary.vue index e57f83fed..9eb3ebcae 100644 --- a/client/src/js/modules/summary/views/summary.vue +++ b/client/src/js/modules/summary/views/summary.vue @@ -397,8 +397,8 @@

- +

--> From 44528722c8c3cc34e35eb23d757b7d55b6225116 Mon Sep 17 00:00:00 2001 From: Mark W <24956497+ndg63276@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:05:17 +0000 Subject: [PATCH 06/14] LIMS-1463: Always display Mesh3D data collections as grid scans (#845) * LIMS-1463: Always display Mesh3d data collections as grid scans * LIMS-1463: Combine if clauses --------- Co-authored-by: Mark Williams --- api/src/Page/DC.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api/src/Page/DC.php b/api/src/Page/DC.php index 34e59f5f2..eaf03183d 100644 --- a/api/src/Page/DC.php +++ b/api/src/Page/DC.php @@ -993,9 +993,10 @@ function _data_collections($single = null) $dc['DCT'] = 'Data Collection'; } - if ($dc['DCT'] == 'Mesh') + if ($dc['DCT'] == 'Mesh' || $dc['DCT'] == 'Mesh3D' || + ($dc['DCT'] != 'Serial Fixed' && $dc['DCT'] != 'Serial Jet' && $dc['AXISRANGE'] == 0 && $dc['NI'] > 1) + ) { $dc['DCT'] = 'Grid Scan'; - if ($dc['DCT'] != 'Serial Fixed' && $dc['DCT'] != 'Serial Jet' && $dc['AXISRANGE'] == 0 && $dc['NI'] > 1) { $dc['TYPE'] = 'grid'; } //$this->profile('dc'); From d19fb86d727e8ace12937d41b4c0db8869b97fd0 Mon Sep 17 00:00:00 2001 From: Mark W <24956497+ndg63276@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:05:24 +0000 Subject: [PATCH 07/14] LIMS-1515: Add energy value to data collections (#853) * LIMS-1515: Add energy value to data collections * LIMS-1515: Use a class constant --------- Co-authored-by: Mark Williams --- api/src/Page/DC.php | 33 +++++++++++++++++++++------- client/src/js/templates/dc/dc.html | 2 +- client/src/js/templates/dc/edge.html | 10 ++++----- client/src/js/templates/dc/grid.html | 2 +- client/src/js/templates/dc/mca.html | 4 ++-- 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/api/src/Page/DC.php b/api/src/Page/DC.php index eaf03183d..165e53c6d 100644 --- a/api/src/Page/DC.php +++ b/api/src/Page/DC.php @@ -62,6 +62,8 @@ class DC extends Page array('/dat/:id', 'get', '_plot'), ); + const EVTOA = 12398.4198; + # ------------------------------------------------------------------------ # Data Collection AJAX Requests # This is pretty crazy, it will return unioned data collections, energy @@ -422,9 +424,12 @@ function _data_collections($single = null) dc.transmission, dc.axisrange, dc.wavelength, + ".self::EVTOA."/dc.wavelength as energy, dc.comments, 1 as epk, 1 as ein, + 1 as wpk, + 1 as win, dc.xtalsnapshotfullpath1 as x1, dc.xtalsnapshotfullpath2 as x2, dc.xtalsnapshotfullpath3 as x3, @@ -556,9 +561,12 @@ function _data_collections($single = null) min(dc.transmission) as transmission, min(dc.axisrange) as axisrange, min(dc.wavelength) as wavelength, + ".self::EVTOA."/min(dc.wavelength) as energy, min(dc.comments) as comments, 1 as epk, 1 as ein, + 1 as wpk, + 1 as win, min(dc.xtalsnapshotfullpath1) as x1, min(dc.xtalsnapshotfullpath2) as x2, min(dc.xtalsnapshotfullpath3) as x3, @@ -705,17 +713,20 @@ function _data_collections($single = null) es.energyscanid, 1, es.element, - es.peakfprime, + es.peakfprime as resolution, es.exposuretime, es.axisposition, - es.peakfdoubleprime, + es.peakfdoubleprime as numimg, es.starttime as st, es.transmissionfactor, - es.inflectionfprime, - es.inflectionfdoubleprime, + es.inflectionfprime as axisrange, + es.inflectionfdoubleprime as wavelength, + 1 as energy, es.comments, es.peakenergy, es.inflectionenergy, + ".self::EVTOA."/es.peakenergy as wpk, + ".self::EVTOA."/es.inflectionenergy as win, 'A', 'A', 'A', @@ -805,10 +816,13 @@ function _data_collections($single = null) TO_CHAR(xrf.starttime, 'DD-MM-YYYY HH24:MI:SS') as st, xrf.beamtransmission, 1, - xrf.energy, + ".self::EVTOA."/xrf.energy as wavelength, + xrf.energy as energy, xrf.comments, 1, 1, + 1 as wpk, + 1 as win, 'A', 'A', 'A', @@ -899,9 +913,12 @@ function _data_collections($single = null) 1, 1, 1, + 1 as energy, 'A', 1, 1, + 1 as wpk, + 1 as win, r.xtalsnapshotbefore, r.xtalsnapshotafter, 'A', @@ -974,7 +991,7 @@ function _data_collections($single = null) // Data collections if ($dc['TYPE'] == 'data') { - $nf = array(1 => array('AXISSTART', 'CHISTART', 'PHI', 'OVERLAP'), 2 => array('RESOLUTION', 'TRANSMISSION', 'AXISRANGE', 'TOTALDOSE'), 4 => array('WAVELENGTH', 'EXPOSURETIME')); + $nf = array(0 => array('ENERGY'), 1 => array('AXISSTART', 'CHISTART', 'PHI', 'OVERLAP'), 2 => array('RESOLUTION', 'TRANSMISSION', 'AXISRANGE', 'TOTALDOSE'), 4 => array('WAVELENGTH', 'EXPOSURETIME')); $dc['DIRFULL'] = $dc['DIR']; $dc['DIR'] = preg_replace('/.*\/' . $this->arg('prop') . '-' . $dc['VN'] . '\//', '', $dc['DIR']); @@ -1011,12 +1028,12 @@ function _data_collections($single = null) $dc['FILETEMPLATE'] = preg_replace('/.*\/' . $this->arg('prop') . '-' . $dc['VN'] . '\//', '', $dc['FILETEMPLATE']); - $nf = array(2 => array('EXPOSURETIME', 'AXISSTART', 'RESOLUTION', 'TRANSMISSION')); + $nf = array(1 => array('EPK', 'EIN'), 2 => array('AXISRANGE', 'WAVELENGTH', 'EXPOSURETIME', 'AXISSTART', 'RESOLUTION', 'TRANSMISSION', 'NUMIMG'), 5 => array('WPK', 'WIN')); $this->profile('edge'); // MCA Scans } else if ($dc['TYPE'] == 'mca') { - $nf = array(2 => array('EXPOSURETIME', 'WAVELENGTH', 'TRANSMISSION')); + $nf = array(0 => array('ENERGY'), 2 => array('EXPOSURETIME', 'TRANSMISSION'), 4 => array('WAVELENGTH')); $dc['DIRFULL'] = $dc['DIR']; $dc['DIR'] = preg_replace('/.*\/\d\d\d\d\/\w\w\d+-\d+\//', '', $dc['DIR']); diff --git a/client/src/js/templates/dc/dc.html b/client/src/js/templates/dc/dc.html index 4237f4515..b23bb657a 100644 --- a/client/src/js/templates/dc/dc.html +++ b/client/src/js/templates/dc/dc.html @@ -24,7 +24,7 @@

<% if (SI != 1 ) { %>
  • First Image: <%-SI%>
  • <% } %> <% if ((KAPPA && KAPPA !=0) || (PHI && PHI != 0) || (CHISTART && CHISTART !=0)) { %>
  • <% if (KAPPA != null) { %>κ: <%-KAPPA%>°<% } %> <% if (PHI != null) { %>φ: <%-PHI%>°<% } %> <% if (CHISTART != null) { %>χ: <%-CHISTART%>°<% } %>
  • <% } %>
  • Resolution: <%-RESOLUTION%>Å
  • -
  • Wavelength: <%-WAVELENGTH%>Å
  • +
  • Wavelength: <%-WAVELENGTH%>Å (<%-ENERGY%>eV)
  • Exposure: <%-EXPOSURETIME%>s
  • <%if (DCC > 1 && TOTALDOSE) { %>
  • Total Dose: <%-TOTALDOSE%>MGy
  • diff --git a/client/src/js/templates/dc/edge.html b/client/src/js/templates/dc/edge.html index 1349e0ba5..4e2697a12 100644 --- a/client/src/js/templates/dc/edge.html +++ b/client/src/js/templates/dc/edge.html @@ -9,11 +9,11 @@

      -
    • Scan File: <%-FILETEMPLATE%>
    • -
    • E(Peak): <%-EPK.toFixed(1) %>eV (<% print ((12398.4193/EPK).toFixed(4)) %>Å)
    • -
    • f’’: <%-NUMIMG%> / f’: <%-RESOLUTION%>e
    • -
    • E(Inf): <%-EIN.toFixed(1) %>eV (<% print((12398.4193/EIN).toFixed(4)) %>Å)
    • -
    • f’’: <%-WAVELENGTH%> / f’: <%-AXISRANGE%>e
    • +
    • Scan File: <%-FILETEMPLATE%>
    • +
    • E(Peak): <%-EPK%>eV (<%-WPK%>Å)
    • +
    • f’’: <%-NUMIMG%>e- / f’: <%-RESOLUTION%>e-
    • +
    • E(Inf): <%-EIN%>eV (<%-WIN%>Å)
    • +
    • f’’: <%-WAVELENGTH%>e- / f’: <%-AXISRANGE%>e-
    • Exposure: <%-EXPOSURETIME%>s
    • Transmission: <%-TRANSMISSION%>%
    • Beamsize: <%-BSX%>x<%-BSY%>μm
    • diff --git a/client/src/js/templates/dc/grid.html b/client/src/js/templates/dc/grid.html index 4232c7392..996258409 100644 --- a/client/src/js/templates/dc/grid.html +++ b/client/src/js/templates/dc/grid.html @@ -8,7 +8,7 @@

    • &<%-ROTATIONAXIS%>; Start: <%-AXISSTART%>°
    • <% if ((KAPPA && KAPPA !=0) || (PHI && PHI != 0) || (CHISTART && CHISTART !=0)) { %>
    • <% if (KAPPA != null) { %>κ: <%-KAPPA%>°<% } %> <% if (PHI != null) { %>φ: <%-PHI%>°<% } %> <% if (CHISTART != null) { %>χ: <%-CHISTART%>°<% } %>
    • <% } %>
    • Resolution: <%-RESOLUTION%>Å
    • -
    • Wavelength: <%-WAVELENGTH%>Å
    • +
    • Wavelength: <%-WAVELENGTH%>Å (<%-ENERGY%>eV)
    • Exposure: <%-EXPOSURETIME%>s
    • Transmission: <%-TRANSMISSION%>%
    • Beamsize: <%-BSX%>x<%-BSY%>μm
    • diff --git a/client/src/js/templates/dc/mca.html b/client/src/js/templates/dc/mca.html index 69da71439..9bea88fde 100644 --- a/client/src/js/templates/dc/mca.html +++ b/client/src/js/templates/dc/mca.html @@ -10,11 +10,11 @@

        -
      • Energy: <%-WAVELENGTH%>eV
      • +
      • Energy: <%-ENERGY%>eV (<%-WAVELENGTH%>Å)
      • Exposure: <%-EXPOSURETIME%>s
      • Transmission: <%-TRANSMISSION%>%
      • Beamsize: <%-BSX%>x<%-BSY%>μm
      • Axis Position: <%-AXISSTART%>°
      • Comment: <%-COMMENTS%>
      - \ No newline at end of file + From 37dd4130249aa6ebc185682d7f3412eb97651c72 Mon Sep 17 00:00:00 2001 From: Mark W <24956497+ndg63276@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:05:32 +0000 Subject: [PATCH 08/14] LIMS-261: Allow download of PDB files (#857) * LIMS-261: Allow download of PDB files * LIMS-261: Use RCSB for links * LIMS-261: Use EBI instead of RCSB --------- Co-authored-by: Mark Williams --- api/src/Page/Sample.php | 16 ++++++++++++++ client/src/js/modules/samples/views/pdbs.js | 23 ++++++++++++++++----- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/api/src/Page/Sample.php b/api/src/Page/Sample.php index d1a051493..4d2093fff 100644 --- a/api/src/Page/Sample.php +++ b/api/src/Page/Sample.php @@ -195,6 +195,7 @@ class Sample extends Page array('/pdbs(/pid/:pid)', 'get', '_get_pdbs'), array('/pdbs', 'post', '_add_pdb'), array('/pdbs(/:pdbid)', 'delete', '_remove_pdb'), + array('/pdbs/download/:pdbid', 'get', '_download_pdb'), array('/concentrationtypes', 'get', '_concentration_types'), array('/componenttypes', 'get', '_component_types'), @@ -2062,6 +2063,21 @@ function _get_pdbs() $this->_output($rows); } + # ------------------------------------------------------------------------ + # Download a pdb file + function _download_pdb() + { + if (!$this->has_arg('pdbid')) + $this->_error('No PDB id specified'); + + $pdb = $this->db->pq("SELECT name, contents FROM pdb WHERE pdbid = :1", array($this->arg('pdbid'))); + $pdb = $pdb[0]; + + header('Content-Type:text/plain'); + header('Content-Disposition:attachment;filename='.$pdb['NAME']); + print $pdb['CONTENTS']; + } + # ------------------------------------------------------------------------ # Add a new pdb function _add_pdb() diff --git a/client/src/js/modules/samples/views/pdbs.js b/client/src/js/modules/samples/views/pdbs.js index 28708ce93..bbbd52b22 100644 --- a/client/src/js/modules/samples/views/pdbs.js +++ b/client/src/js/modules/samples/views/pdbs.js @@ -1,15 +1,28 @@ -define(['marionette'], function(Marionette) { +define(['marionette', 'utils'], function(Marionette, utils) { var UserItem = Marionette.ItemView.extend({ - template: _.template('<%-NAME%> <% if (CODE) { %>[CODE]<% } else { %>[File]<% } %> Delete'), + template: _.template(''), tagName: 'li', attributes: { 'data-testid': 'protein-pdb-list-item' }, events: { - 'click a.delete': 'deleteUser', + 'click a.delete': 'deletePDB', + 'click a.download': utils.signHandler, }, - deleteUser: function(e) { + render: function() { + UserItem.__super__.render.call(this) + const linkButton = ' EBI' + const deleteButton = ' Delete' + const downloadButton = ' Download' + if (this.model.get('CODE')) { + this.$el.append(this.model.get('NAME')+' [Code] '+linkButton+' '+deleteButton+'') + } else { + this.$el.append(this.model.get('NAME')+' [File] '+downloadButton+' '+deleteButton+'') + } + }, + + deletePDB: function(e) { this.model.destroy() }, }) @@ -30,4 +43,4 @@ define(['marionette'], function(Marionette) { }) -}) \ No newline at end of file +}) From bb2f836e9d420251bb77d6a3bdf54dbc14627324 Mon Sep 17 00:00:00 2001 From: Mark W <24956497+ndg63276@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:06:08 +0000 Subject: [PATCH 09/14] LIMS-1529: Fix fast ep model viewer (#858) Co-authored-by: Mark Williams --- api/scripts/mtz2map.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/api/scripts/mtz2map.sh b/api/scripts/mtz2map.sh index 00542447d..1d25a72a4 100755 --- a/api/scripts/mtz2map.sh +++ b/api/scripts/mtz2map.sh @@ -14,6 +14,14 @@ else fi fi +#export CCP4_MASTER=/dls_sw/apps/ccp4/ +export CCP4_MASTER=$5 +export CINCL=$CCP4_MASTER/include +export CLIBD=$CCP4_MASTER/lib/data + +export CCP4_SCR=/tmp +export root=$CCP4_MASTER/bin + if [ $3 == 'dimple' -o $3 == 'mrbump' ]; then if [ -f $4 ]; then @@ -28,14 +36,6 @@ else fi fi -#export CCP4_MASTER=/dls_sw/apps/ccp4/ -export CCP4_MASTER=$5 -export CINCL=$CCP4_MASTER/include -export CLIBD=$CCP4_MASTER/lib/data - -export CCP4_SCR=/tmp -export root=$CCP4_MASTER/bin - if [ $3 == 'dimple' ]; then if $root/mtzinfo $mtz | grep -q PH2FOFCWT; then From 443a35468e35a04073bd7c1b0791adbf36cbd606 Mon Sep 17 00:00:00 2001 From: Mark W <24956497+ndg63276@users.noreply.github.com> Date: Tue, 3 Dec 2024 09:34:35 +0000 Subject: [PATCH 10/14] LIMS-1514: Allow sorting of visit list (#854) * LIMS-1514: Allow sorting of visit list * LIMS-1514: Remove editing artifacts * LIMS-1514: Allow searching by beamline name --------- Co-authored-by: Mark Williams --- api/src/Page/Proposal.php | 33 ++++----------- .../js/modules/visits/views/visit_list.vue | 42 +++++++++++++++---- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/api/src/Page/Proposal.php b/api/src/Page/Proposal.php index f8207ed2f..fcc019322 100644 --- a/api/src/Page/Proposal.php +++ b/api/src/Page/Proposal.php @@ -393,7 +393,8 @@ function _get_visits($visit = null, $output = true) } if ($this->has_arg('s')) { - $where .= " AND s.visit_number LIKE :" . (sizeof($args) + 1); + $where .= " AND (s.visit_number LIKE :" . (sizeof($args) + 1) . " OR s.beamlinename LIKE :" . (sizeof($args) + 2) . ")"; + array_push($args, $this->arg('s')); array_push($args, $this->arg('s')); } @@ -435,7 +436,9 @@ function _get_visits($visit = null, $output = true) $order = 's.startdate DESC'; if ($this->has_arg('sort_by')) { - $cols = array('ST' => 's.startdate', 'EN' => 's.enddate', 'VIS' => 's.visit_number', 'BL' => 's.beamlinename', 'LC' => 's.beamlineoperator', 'COMMENT' => 's.comments'); + $cols = array('ST' => 's.startdate', 'EN' => 's.enddate', 'VIS' => 's.visit_number', 'BL' => 's.beamlinename', + 'LC' => 's.beamlineoperator', 'COMMENT' => 's.comments', 'ERA' => 's.riskrating', + 'SESSIONTYPE' => 'sessiontype', 'DCCOUNT' => 'dccount'); $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; if (array_key_exists($this->arg('sort_by'), $cols)) $order = $cols[$this->arg('sort_by')] . ' ' . $dir; @@ -469,6 +472,7 @@ function _get_visits($visit = null, $output = true) s.beamcalendarid, CONCAT(p.proposalcode, p.proposalnumber) AS proposal, COUNT(shp.personid) AS persons, + COUNT(distinct dc.datacollectionid) AS dccount, s.proposalid FROM BLSession s INNER JOIN proposal p ON p.proposalid = s.proposalid @@ -476,35 +480,14 @@ function _get_visits($visit = null, $output = true) LEFT OUTER JOIN session_has_person shp ON shp.sessionid = s.sessionid LEFT OUTER JOIN beamlinesetup bls on bls.beamlinesetupid = s.beamlinesetupid LEFT OUTER JOIN beamcalendar bc ON bc.beamcalendarid = s.beamcalendarid + LEFT OUTER JOIN datacollectiongroup dcg ON dcg.sessionid = s.sessionid + LEFT OUTER JOIN datacollection dc ON dcg.datacollectiongroupid = dc.datacollectiongroupid $where GROUP BY s.sessionid ORDER BY $order", $args); - $ids = array(); - $wcs = array(); - foreach ($rows as $r) { - array_push($ids, $r['SESSIONID']); - array_push($wcs, 'dcg.sessionid=:' . sizeof($ids)); - } - - $dcs = array(); - if (sizeof($ids)) { - $where = implode(' OR ', $wcs); - $tdcs = $this->db->pq("SELECT count(dc.datacollectionid) as c, dcg.sessionid - FROM datacollection dc - INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid - WHERE $where GROUP BY dcg.sessionid", $ids); - foreach ($tdcs as $t) - $dcs[$t['SESSIONID']] = $t['C']; - } - foreach ($rows as &$r) { - $dc = array_key_exists($r['SESSIONID'], $dcs) ? $dcs[$r['SESSIONID']] : 0; - $r['COMMENT'] = $r['COMMENTS']; - $r['DCCOUNT'] = $dc; - $bl_type = $this->_get_type_from_beamline($r['BL']); - $r['TYPE'] = $bl_type ? $bl_type : 'gen'; } diff --git a/client/src/js/modules/visits/views/visit_list.vue b/client/src/js/modules/visits/views/visit_list.vue index c3feef51a..26c019ee9 100644 --- a/client/src/js/modules/visits/views/visit_list.vue +++ b/client/src/js/modules/visits/views/visit_list.vue @@ -16,15 +16,16 @@
      @@ -120,21 +121,25 @@ export default { visits: [], dewars: [], searchVisit : '', + orderBy: '', + order: 1, headers: [ { key: "STARTDATE", - title: 'Start' + title: 'Start', + order: 'ST' }, { key: "ENDDATE", - title: 'End' + title: 'End', + order: 'EN' }, { key: "VIS", title: 'Number' }, { - key: "BEAMLINENAME", + key: "BL", title: 'Beamline' }, { @@ -147,7 +152,8 @@ export default { }, { key: "UNIQUELCS", - title: 'Local Contact' + title: 'Local Contact', + order: 'LC' }, { key: "COMMENTS", @@ -178,6 +184,7 @@ export default { window.location.href = '/proposals/'; } this.fetchData() + this.fetchData = _.debounce(this.fetchData, 500) }, methods: { async fetchData() { @@ -188,8 +195,18 @@ export default { page: this.currentPage, per_page: this.pageSize, prop: this.proposal, - s: this.searchVisit, + order: 'order', + directions: { + '-1': 'asc', + '1': 'desc', + }, + }; + this.visitCollection.state = { + sortKey: 'sort_by', + order: this.order, }; + if (this.searchVisit) this.visitCollection.queryParams.s = this.searchVisit; + if (this.orderBy) this.visitCollection.queryParams.sort_by = this.orderBy; const results = await this.$store.dispatch('getCollection', this.visitCollection); this.visits = results.toJSON().map((e) => { @@ -259,6 +276,15 @@ export default { window.location.href = 'dc/visit/' + this.proposal + '-' + visit.VIS; } }, + headerClick(event) { + if (this.orderBy === event.target.id) { + this.order = -this.order; + } else { + this.order = 1; + } + this.orderBy = event.target.id; + this.fetchData(); + }, handleFocusOut(visit) { // if click outside of active input box then input returns to text if(visit.clicked) { From 53b2d3411e32799ad0d12068d85f5830bdaca503 Mon Sep 17 00:00:00 2001 From: Mark W <24956497+ndg63276@users.noreply.github.com> Date: Tue, 3 Dec 2024 09:34:43 +0000 Subject: [PATCH 11/14] LIMS-753: Allow LN2 topups as part of dewar history (#856) Co-authored-by: Mark Williams --- api/src/Page/Shipment.php | 178 +++++++++++++++++++------------------- 1 file changed, 88 insertions(+), 90 deletions(-) diff --git a/api/src/Page/Shipment.php b/api/src/Page/Shipment.php index fdb44b725..b646338c7 100644 --- a/api/src/Page/Shipment.php +++ b/api/src/Page/Shipment.php @@ -484,10 +484,10 @@ function _add_history() if (!$this->has_arg('BARCODE')) $this->_error('No barcode specified'); - if (!$this->has_arg('LOCATION')) - $this->_error('No location specified'); + if (!$this->has_arg('LOCATION') && !$this->has_arg('STATUS')) + $this->_error('No location or status specified'); - $dew = $this->db->pq("SELECT CONCAT(pe.givenname, ' ', pe.familyname) as lcout, pe.emailaddress as lcoutemail, CONCAT(CONCAT(pe2.givenname, ' '), pe2.familyname) as lcret, pe2.emailaddress as lcretemail, CONCAT(p.proposalcode, p.proposalnumber, '-', e.visit_number) as firstexp, TO_CHAR(e.startdate, 'DD-MM-YYYY HH24:MI') as firstexpst, e.beamlinename, e.beamlineoperator, d.dewarid, d.trackingnumberfromsynchrotron, s.shippingid, s.shippingname, p.proposalcode, CONCAT(p.proposalcode, p.proposalnumber) as prop, d.barcode, d.facilitycode, d.firstexperimentid, d.dewarstatus + $dew = $this->db->pq("SELECT CONCAT(pe.givenname, ' ', pe.familyname) as lcout, pe.emailaddress as lcoutemail, CONCAT(CONCAT(pe2.givenname, ' '), pe2.familyname) as lcret, pe2.emailaddress as lcretemail, CONCAT(p.proposalcode, p.proposalnumber, '-', e.visit_number) as firstexp, TO_CHAR(e.startdate, 'DD-MM-YYYY HH24:MI') as firstexpst, e.beamlinename, e.beamlineoperator, d.dewarid, d.trackingnumberfromsynchrotron, s.shippingid, s.shippingname, p.proposalcode, CONCAT(p.proposalcode, p.proposalnumber) as prop, d.barcode, d.facilitycode, d.firstexperimentid, d.dewarstatus, d.storagelocation FROM dewar d INNER JOIN shipping s ON s.shippingid = d.shippingid LEFT OUTER JOIN labcontact c ON s.sendinglabcontactid = c.labcontactid @@ -505,15 +505,9 @@ function _add_history() $track = $this->has_arg('TRACKINGNUMBERFROMSYNCHROTRON') ? $this->arg('TRACKINGNUMBERFROMSYNCHROTRON') : $dew['TRACKINGNUMBERFROMSYNCHROTRON']; - // What was the last history entry for this dewar? - // If it's come from a beamline, register flag so we can e-mail further down... - $last_history_results = $this->db->pq("SELECT storageLocation FROM dewartransporthistory WHERE dewarId = :1 ORDER BY DewarTransportHistoryId DESC LIMIT 1", array($dew['DEWARID'])); - - if (sizeof($last_history_results)) { - $last_history = $last_history_results[0]; - // We only add data to dewar history in lower case from this method. - // If that ever changes, update this to become case insensitive search - $last_location = $last_history['STORAGELOCATION']; + if ($this->has_arg('LOCATION')) { + // If it's come from a beamline, register flag so we can e-mail further down... + $last_location = $dew['STORAGELOCATION']; if (!isset($dewar_complete_email_locations) || !is_array($dewar_complete_email_locations)) { $bls = $this->_get_beamlines_from_type('all'); $send_return_email = in_array($last_location, $bls); @@ -521,99 +515,103 @@ function _add_history() $email_location = $dewar_complete_email_locations[$last_location]; $send_return_email = preg_match($email_location, strtolower($this->arg('LOCATION'))); } + // If dewar status is dispatch-requested - don't change it. + // This way the dewar can be moved between storage locations and keep the record that a user requested the dispatch + // Default status is 'at-facility' + $dewarstatus = strtolower($dew['DEWARSTATUS']) == 'dispatch-requested' ? 'dispatch-requested' : 'at facility'; + $dewarlocation = $this->arg('LOCATION'); + } else { - // No history - could be a new dewar, so not necessarily an error... - if ($this->debug) - error_log("No previous dewar transport history for DewarId " . $dew['DEWARID']); + // just a status given + $dewarstatus = $this->arg('STATUS'); + $dewarlocation = $dew['STORAGELOCATION']; } - // If dewar status is dispatch-requested - don't change it. - // This way the dewar can be moved between storage locations and keep the record that a user requested the dispatch - // Default status is 'at-facility' - $dewarstatus = strtolower($dew['DEWARSTATUS']) == 'dispatch-requested' ? 'dispatch-requested' : 'at facility'; - $this->db->pq("INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate) VALUES (s_dewartransporthistory.nextval,:1,lower(:2),lower(:3),CURRENT_TIMESTAMP) RETURNING dewartransporthistoryid INTO :id", array($dew['DEWARID'], $dewarstatus, $this->arg('LOCATION'))); + $this->db->pq("INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate) VALUES (s_dewartransporthistory.nextval,:1,lower(:2),lower(:3),CURRENT_TIMESTAMP) RETURNING dewartransporthistoryid INTO :id", array($dew['DEWARID'], $dewarstatus, $dewarlocation)); $dhid = $this->db->id(); - $this->db->pq("UPDATE dewar set dewarstatus=lower(:4), storagelocation=lower(:2), trackingnumberfromsynchrotron=:3 WHERE dewarid=:1", array($dew['DEWARID'], $this->arg('LOCATION'), $track, $dewarstatus)); - $this->db->pq("UPDATE shipping set shippingstatus=lower(:2) WHERE shippingid=:1", array($dew['SHIPPINGID'], $dewarstatus)); - - $containers = $this->db->pq("SELECT containerid - FROM container - WHERE dewarid=:1", array($dew['DEWARID'])); - foreach ($containers as $c) { - $this->db->pq("INSERT INTO containerhistory (containerid,status) VALUES (:1,:2)", array($c['CONTAINERID'], 'at facility')); - } - - // Email - // EHCs, local contact(s), labcontact, dh, pa - $dew['NOW'] = strftime('%d-%m-%Y %H:%M'); - $dew['INCONTACTS'] = $in_contacts; - $dew['TRACKINGNUMBERFROMSYNCHROTRON'] = $track; - - if (strtolower($this->arg('LOCATION')) == 'stores-in' && $dew['LCOUTEMAIL']) { - $lcs = $this->db->pq("SELECT p.login - FROM person p - INNER JOIN session_has_person shp ON shp.personid = p.personid - WHERE shp.sessionid=:1 AND (shp.role = 'Local Contact' OR shp.role = 'Local Contact 2')", array($dew['FIRSTEXPERIMENTID'])); - $emails = array($dew['LCOUTEMAIL'], $arrival_email); - foreach ($lcs as $lc) { - array_push($emails, $this->_get_email($lc['LOGIN'])); - } + if ($this->has_arg('LOCATION')) { + $this->db->pq("UPDATE dewar set dewarstatus=lower(:4), storagelocation=lower(:2), trackingnumberfromsynchrotron=:3 WHERE dewarid=:1", array($dew['DEWARID'], $this->arg('LOCATION'), $track, $dewarstatus)); + $this->db->pq("UPDATE shipping set shippingstatus=lower(:2) WHERE shippingid=:1", array($dew['SHIPPINGID'], $dewarstatus)); - $email = new Email($dew['PROPOSALCODE'] == 'in' ? 'dewar-stores-in-in' : 'dewar-stores-in', '*** Dewar Received for ' . $dew['PROP'] . ' at ' . $dew['NOW'] . ' ***'); - $email->data = $dew; - $email->send(implode(', ', $emails)); - } + $containers = $this->db->pq("SELECT containerid + FROM container + WHERE dewarid=:1", array($dew['DEWARID'])); + foreach ($containers as $c) { + $this->db->pq("INSERT INTO containerhistory (containerid,status) VALUES (:1,:2)", array($c['CONTAINERID'], 'at facility')); + } - if (strtolower($this->arg('LOCATION')) == 'stores-out' && $dew['LCRETEMAIL']) { - $email = new Email('dewar-stores-out', '*** Dewar ready to leave Synchrotron ***'); - $email->data = $dew; - $email->send($dew['LCRETEMAIL']); - } + // Email + // EHCs, local contact(s), labcontact, dh, pa + $dew['NOW'] = strftime('%d-%m-%Y %H:%M'); + $dew['INCONTACTS'] = $in_contacts; + $dew['TRACKINGNUMBERFROMSYNCHROTRON'] = $track; + + if (strtolower($this->arg('LOCATION')) == 'stores-in' && $dew['LCOUTEMAIL']) { + $lcs = $this->db->pq("SELECT p.login + FROM person p + INNER JOIN session_has_person shp ON shp.personid = p.personid + WHERE shp.sessionid=:1 AND (shp.role = 'Local Contact' OR shp.role = 'Local Contact 2')", array($dew['FIRSTEXPERIMENTID'])); + $emails = array($dew['LCOUTEMAIL'], $arrival_email); + foreach ($lcs as $lc) { + array_push($emails, $this->_get_email($lc['LOGIN'])); + } - if (strpos(strtolower($this->arg('LOCATION')), '-rack') !== false && $dew['LCRETEMAIL']) { - $dew['LOCATION'] = $this->arg('LOCATION'); + $email = new Email($dew['PROPOSALCODE'] == 'in' ? 'dewar-stores-in-in' : 'dewar-stores-in', '*** Dewar Received for ' . $dew['PROP'] . ' at ' . $dew['NOW'] . ' ***'); + $email->data = $dew; + $email->send(implode(', ', $emails)); + } - $email = new Email('dewar-rack', '*** Dewar now outside Beamline ***'); - $email->data = $dew; - $email->send($dew['LCRETEMAIL']); - } + if (strtolower($this->arg('LOCATION')) == 'stores-out' && $dew['LCRETEMAIL']) { + $email = new Email('dewar-stores-out', '*** Dewar ready to leave Synchrotron ***'); + $email->data = $dew; + $email->send($dew['LCRETEMAIL']); + } - if ($dew['LCRETEMAIL'] && $send_return_email) { - // Any data collections for this dewar's containers? - // Note this counts data collection ids for containers and uses the DataCollection.SESSIONID to determine the session/visit - // Should work for UDC (where container.sessionid is set) as well as any normal scheduled session (where container.sessionid is not set) - $rows = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', ses.visit_number) as visit, dc.sessionid, count(dc.datacollectionid) as dccount - FROM Dewar d - INNER JOIN Container c on c.dewarid = d.dewarid - INNER JOIN BLSample bls ON bls.containerid = c.containerid - INNER JOIN DataCollection dc ON dc.blsampleid = bls.blsampleid - INNER JOIN BLSession ses ON dc.sessionid = ses.sessionid - INNER JOIN Proposal p ON p.proposalid = ses.proposalid - WHERE d.dewarid = :1 - GROUP BY dc.sessionid", array($dew['DEWARID'])); + if (strpos(strtolower($this->arg('LOCATION')), '-rack') !== false && $dew['LCRETEMAIL']) { + $dew['LOCATION'] = $this->arg('LOCATION'); - if (sizeof($rows)) - $dew['DC'] = $rows; + $email = new Email('dewar-rack', '*** Dewar now outside Beamline ***'); + $email->data = $dew; + $email->send($dew['LCRETEMAIL']); + } - $cc = array($dewar_complete_email ? $dewar_complete_email : null); + if ($dew['LCRETEMAIL'] && $send_return_email) { + // Any data collections for this dewar's containers? + // Note this counts data collection ids for containers and uses the DataCollection.SESSIONID to determine the session/visit + // Should work for UDC (where container.sessionid is set) as well as any normal scheduled session (where container.sessionid is not set) + $rows = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', ses.visit_number) as visit, dc.sessionid, count(dc.datacollectionid) as dccount + FROM Dewar d + INNER JOIN Container c on c.dewarid = d.dewarid + INNER JOIN BLSample bls ON bls.containerid = c.containerid + INNER JOIN DataCollection dc ON dc.blsampleid = bls.blsampleid + INNER JOIN BLSession ses ON dc.sessionid = ses.sessionid + INNER JOIN Proposal p ON p.proposalid = ses.proposalid + WHERE d.dewarid = :1 + GROUP BY dc.sessionid", array($dew['DEWARID'])); + + if (sizeof($rows)) + $dew['DC'] = $rows; + + $cc = array($dewar_complete_email ? $dewar_complete_email : null); + + $owners = $this->db->pq("SELECT p.emailaddress + FROM Container c + INNER JOIN Person p ON c.ownerId = p.personId + WHERE c.dewarId = :1 + GROUP BY p.emailaddress", array($dew['DEWARID'])); + + foreach ($owners as $owner) { + if ($owner['EMAILADDRESS'] != '') array_push($cc, $owner['EMAILADDRESS']); + } - $owners = $this->db->pq("SELECT p.emailaddress - FROM Container c - INNER JOIN Person p ON c.ownerId = p.personId - WHERE c.dewarId = :1 - GROUP BY p.emailaddress", array($dew['DEWARID'])); + // Log the event if debugging + if ($this->debug) error_log("Dewar " . $dew['DEWARID'] . " back from beamline..."); - foreach ($owners as $owner) { - if ($owner['EMAILADDRESS'] != '') array_push($cc, $owner['EMAILADDRESS']); + $email = new Email('storage-rack', '*** Visit finished, dewar awaiting instructions ***'); + $email->data = $dew; + $email->send($dew['LCRETEMAIL'], implode(', ', $cc)); } - - // Log the event if debugging - if ($this->debug) error_log("Dewar " . $dew['DEWARID'] . " back from beamline..."); - - $email = new Email('storage-rack', '*** Visit finished, dewar awaiting instructions ***'); - $email->data = $dew; - $email->send($dew['LCRETEMAIL'], implode(', ', $cc)); } $this->_output(array('DEWARHISTORYID' => $dhid)); From 6b40dbb65d2afd557a427f5194dea1d0d7fbb02f Mon Sep 17 00:00:00 2001 From: Mark W <24956497+ndg63276@users.noreply.github.com> Date: Tue, 3 Dec 2024 09:34:48 +0000 Subject: [PATCH 12/14] LIMS-1530: Add To Queue Fails when Not Completed filter applied (#864) * [LIMS-1552] Remove old jQuery version (#861) * Remove old jQuery version * Remove desktop jQuery * LIMS-1530: Add To Queue Fails when Not Completed filter applied --------- Co-authored-by: Guilherme Francisco Co-authored-by: Mark Williams --- .../modules/imaging/views/queuecontainer.js | 2 +- .../src/js/vendor/jquery/jquery-1.9.1.min.js | 5 -- client/src/js/views/log.js | 46 +------------------ .../js/vendor/jquery/jquery-1.9.1.min.js | 5 -- client/webpack.config.js | 5 -- 5 files changed, 3 insertions(+), 60 deletions(-) delete mode 100644 client/src/js/vendor/jquery/jquery-1.9.1.min.js delete mode 100644 client/touchscreen/js/vendor/jquery/jquery-1.9.1.min.js diff --git a/client/src/js/modules/imaging/views/queuecontainer.js b/client/src/js/modules/imaging/views/queuecontainer.js index 9de3e613f..fceae44f9 100644 --- a/client/src/js/modules/imaging/views/queuecontainer.js +++ b/client/src/js/modules/imaging/views/queuecontainer.js @@ -773,7 +773,7 @@ define(['marionette', }, refreshSubSamples: function() { - this.subsamples.fetch() + this.subsamples.fetch().done(this.onSubsamplesReady.bind(this)) }, initialize: function() { diff --git a/client/src/js/vendor/jquery/jquery-1.9.1.min.js b/client/src/js/vendor/jquery/jquery-1.9.1.min.js deleted file mode 100644 index 006e95310..000000000 --- a/client/src/js/vendor/jquery/jquery-1.9.1.min.js +++ /dev/null @@ -1,5 +0,0 @@ -/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license -//@ sourceMappingURL=jquery.min.map -*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
      - {{value.title}} + class="tw-w-1/8 tw-bg-table-header-background tw-text-table-header-color tw-py-2 tw-text-center"> + + {{value.title}}
      a",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="
      t
      ",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
      ",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj; -return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="
      ",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
      ","
      "],area:[1,"",""],param:[1,"",""],thead:[1,"","
      "],tr:[2,"","
      "],col:[2,"","
      "],td:[3,"","
      "],_default:b.support.htmlSerialize?[0,"",""]:[1,"X
      ","
      "]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l) -}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("