From ab3254ac75922f76a87f266130668c10477b5722 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 21 Aug 2024 16:26:59 +0100 Subject: [PATCH 01/26] build: remove some legacy rules --- .eslintrc.js | 5 ++--- app/frontend/javascript/.eslintrc.js | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 1a9cc08d7..4b10d00a2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -23,8 +23,7 @@ module.exports = { argsIgnorePattern: '^_', }, ], - // The API sends snake case stuff, and this lets us pass things straight - // through. Not a great compromise though. - 'vue/prop-name-casing': ['off'], + // Legacy in from the old days. We should remove these: + 'vue/prop-name-casing': ['warn'], }, } diff --git a/app/frontend/javascript/.eslintrc.js b/app/frontend/javascript/.eslintrc.js index d8c10d9e8..abb91924d 100644 --- a/app/frontend/javascript/.eslintrc.js +++ b/app/frontend/javascript/.eslintrc.js @@ -24,7 +24,7 @@ module.exports = { vars: 'all', args: 'after-used', ignoreRestSiblings: false, - argsIgnorePattern: '^_|undefined', // `undefined` is legacy and should be removed + argsIgnorePattern: '^_', }, ], // We need a proper logging solution (see https://github.com/sanger/limber/issues/836), @@ -32,7 +32,6 @@ module.exports = { 'no-console': ['error', { allow: ['warn', 'error', 'log'] }], // Legacy in from the old days. We should remove these: 'vue/prop-name-casing': ['warn'], - 'no-shadow-restricted-names': ['warn'], // specifically for `undefined` }, overrides: [ { From edaa6a1771a4a6128c1fc60477b375e55cfff884 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 21 Aug 2024 16:55:33 +0100 Subject: [PATCH 02/26] build: remove commonjs from linting --- .eslintrc.js | 1 - app/frontend/javascript/.eslintrc.js | 1 - 2 files changed, 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 4b10d00a2..afbb3ed3e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,6 @@ module.exports = { env: { browser: true, - commonjs: true, es6: true, jasmine: true, node: true, diff --git a/app/frontend/javascript/.eslintrc.js b/app/frontend/javascript/.eslintrc.js index abb91924d..e8b0d49f0 100644 --- a/app/frontend/javascript/.eslintrc.js +++ b/app/frontend/javascript/.eslintrc.js @@ -2,7 +2,6 @@ module.exports = { env: { 'vitest/env': true, browser: true, - commonjs: true, es6: true, node: true, }, From 44dbb60843ea3fa84cf8c4c79287eeab3141facb Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 21 Aug 2024 16:38:58 +0100 Subject: [PATCH 03/26] build: add no-var rule --- .eslintrc.js | 1 + app/frontend/javascript/.eslintrc.js | 1 + 2 files changed, 2 insertions(+) diff --git a/.eslintrc.js b/.eslintrc.js index afbb3ed3e..c2c876121 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -22,6 +22,7 @@ module.exports = { argsIgnorePattern: '^_', }, ], + 'no-var': 'error', // Legacy in from the old days. We should remove these: 'vue/prop-name-casing': ['warn'], }, diff --git a/app/frontend/javascript/.eslintrc.js b/app/frontend/javascript/.eslintrc.js index e8b0d49f0..224fad5ba 100644 --- a/app/frontend/javascript/.eslintrc.js +++ b/app/frontend/javascript/.eslintrc.js @@ -26,6 +26,7 @@ module.exports = { argsIgnorePattern: '^_', }, ], + 'no-var': 'error', // We need a proper logging solution (see https://github.com/sanger/limber/issues/836), // but until then: 'no-console': ['error', { allow: ['warn', 'error', 'log'] }], From 6ed334b1ceef06ed7bb9451a9c137194b6e5cce1 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 21 Aug 2024 16:39:27 +0100 Subject: [PATCH 04/26] style: replace vars with const and let yarn lint --fix --- app/frontend/javascript/bed_verification.js | 20 ++++---- .../components/CustomTaggedPlate.vue | 8 ++-- .../custom-tagged-plate/tagClashFunctions.js | 8 ++-- .../custom-tagged-plate/tagLayoutFunctions.js | 2 +- .../file-list/components/FileList.vue | 4 +- app/frontend/javascript/file-list/index.js | 2 +- app/frontend/javascript/legacy_scripts_a.js | 18 ++++---- .../javascript/lib/array_fill_polyfill.js | 16 +++---- .../lib/disable_enter_key_submit.js | 4 +- .../javascript/lib/status_collector.js | 8 ++-- app/frontend/javascript/lib/tag_collector.js | 10 ++-- app/frontend/javascript/lib/validator.js | 2 +- .../components/TubeArraySummary.spec.js | 10 ++-- .../components/TubeArraySummary.vue | 6 +-- .../multi-stamp/components/MultiStamp.vue | 2 +- .../MultiStampLibrarySplitter.spec.js | 12 ++--- .../javascript/multi_plate_pooling.js | 46 +++++++++---------- app/frontend/javascript/multi_tube_pooling.js | 12 ++--- .../pipeline-graph/graphFunctions.js | 2 +- .../pooled_tubes_from_whole_plates.js | 28 +++++------ app/frontend/javascript/session_scripts.js | 10 ++-- app/frontend/javascript/shared/buildArray.js | 2 +- .../shared/components/LabwareScan.vue | 2 +- .../javascript/state_change_reasons.js | 4 +- app/frontend/javascript/state_machine.js | 4 +- app/frontend/javascript/tag_by_tag_plate.js | 22 ++++----- .../components/TransferVolumes.spec.js | 26 +++++------ 27 files changed, 145 insertions(+), 145 deletions(-) diff --git a/app/frontend/javascript/bed_verification.js b/app/frontend/javascript/bed_verification.js index 298d99014..fd70e8080 100644 --- a/app/frontend/javascript/bed_verification.js +++ b/app/frontend/javascript/bed_verification.js @@ -11,7 +11,7 @@ import jQuery from 'jquery' //= require lib/ajax_support - var closeIcon = function () { + let closeIcon = function () { return $(document.createElement('a')) .attr('class', 'close') .attr('aria-label', 'close') @@ -21,8 +21,8 @@ import jQuery from 'jquery' SCAPE.robot_beds = {} SCAPE.robot_barcode = '' - var newScanned = function (bed, labware) { - var new_li + let newScanned = function (bed, labware) { + let new_li // $('#whole\\['+bed+'\\]').detach(); new_li = $(document.createElement('li')) .attr('data-bed', bed) @@ -58,14 +58,14 @@ import jQuery from 'jquery' $('#bed_list').append(new_li) } - var newRobotScanned = function (robot_barcode) { + let newRobotScanned = function (robot_barcode) { $('#robot').text('Robot: ' + robot_barcode) $('#robot_barcode').val(robot_barcode) SCAPE.robot_barcode = robot_barcode } var removeEntry = function () { - var lw_index, bed_list + let lw_index, bed_list bed_list = SCAPE.robot_beds[$(this).attr('data-bed')] lw_index = bed_list.indexOf($(this).attr('data-labware')) bed_list.splice(lw_index, 1) @@ -76,7 +76,7 @@ import jQuery from 'jquery' $('#bed_list') } - var checkResponse = function (response) { + let checkResponse = function (response) { if ($('#bed_list').children().length === 0) { // We don't have any content $('#loadingModal').fadeOut(100) @@ -92,7 +92,7 @@ import jQuery from 'jquery' } var flagBeds = function (beds, message) { - var bad_beds = [] + let bad_beds = [] $.each(beds, function (bed_id) { // here we check the validity of each bed in the hash returned from the ruby robot // valid_relationships method and if the bed is valid clear the error flags (in case the @@ -119,7 +119,7 @@ import jQuery from 'jquery' $('#bed_list li[data-bed="' + bed_id + '"]').removeClass('bad_bed list-group-item-danger') } - var wait = function () { + let wait = function () { $('#loadingModal').fadeIn(100) } @@ -135,7 +135,7 @@ import jQuery from 'jquery' } $('#plate_scan').on('change', function () { - var plate_barcode, bed_barcode, robot_barcode + let plate_barcode, bed_barcode, robot_barcode plate_barcode = this.value bed_barcode = $('#bed_scan').val() robot_barcode = $('#robot_scan').val() @@ -147,7 +147,7 @@ import jQuery from 'jquery' }) $('#robot_scan').on('change', function () { - var robot_barcode + let robot_barcode robot_barcode = this.value newRobotScanned(robot_barcode) }) diff --git a/app/frontend/javascript/custom-tagged-plate/components/CustomTaggedPlate.vue b/app/frontend/javascript/custom-tagged-plate/components/CustomTaggedPlate.vue index 9786c2988..07220aff2 100644 --- a/app/frontend/javascript/custom-tagged-plate/components/CustomTaggedPlate.vue +++ b/app/frontend/javascript/custom-tagged-plate/components/CustomTaggedPlate.vue @@ -226,7 +226,7 @@ export default { tagMapIds = this.tagLayout[position].slice(0) - for (var i = 0; i < tagMapIds.length; i++) { + for (let i = 0; i < tagMapIds.length; i++) { if (tagMapIds[i] === -1) { cw[position]['validity'] = { valid: false, @@ -440,19 +440,19 @@ export default { if (this.numberOfTag1GroupTags > 0) { if (this.numberOfTag2GroupTags > 0) { const numUseableTags = Math.min(this.numberOfTag1GroupTags, this.numberOfTag2GroupTags) - for (var iBoth = 0; iBoth < numUseableTags; iBoth++) { + for (let iBoth = 0; iBoth < numUseableTags; iBoth++) { const tg1 = this.tag1GroupTags[iBoth] const tg2 = this.tag2GroupTags[iBoth] tagOligoStrings[tg1.index] = tg1.oligo + ':' + tg2.oligo } } else { - for (var i1 = 0; i1 < this.tag1GroupTags.length; i1++) { + for (let i1 = 0; i1 < this.tag1GroupTags.length; i1++) { const tg = this.tag1GroupTags[i1] tagOligoStrings[tg.index] = tg.oligo } } } else if (this.numberOfTag2GroupTags > 0) { - for (var i2 = 0; i2 < this.tag2GroupTags.length; i2++) { + for (let i2 = 0; i2 < this.tag2GroupTags.length; i2++) { const tg = this.tag2GroupTags[i2] tagOligoStrings[tg.index] = tg.oligo } diff --git a/app/frontend/javascript/custom-tagged-plate/tagClashFunctions.js b/app/frontend/javascript/custom-tagged-plate/tagClashFunctions.js index 238f43216..c4aa5a516 100644 --- a/app/frontend/javascript/custom-tagged-plate/tagClashFunctions.js +++ b/app/frontend/javascript/custom-tagged-plate/tagClashFunctions.js @@ -70,7 +70,7 @@ function extractParentUsedOligos(parentPlate) { parentUsedOligos[submDetails.id] = {} // add the submission used tags - for (var i = 0; i < submDetails.usedTags.length; i++) { + for (let i = 0; i < submDetails.usedTags.length; i++) { const oligoStr = submDetails.usedTags[i].join(':') parentUsedOligos[submDetails.id][oligoStr] = ['submission'] } @@ -107,7 +107,7 @@ function extractChildUsedOligos(parentUsedOligos, parentWellSubmDetails, tagLayo const tagMapIds = tagLayout[position] let oligos = [] - for (var i = 0; i < tagMapIds.length; i++) { + for (let i = 0; i < tagMapIds.length; i++) { let tagMapId = tagMapIds[i] // check for tag substitution @@ -173,7 +173,7 @@ function extractSubmDetailsFromRequestsAsSource(well) { let submDetails = { id: null, usedTags: [] } const requestsLen = well.requests_as_source.length - for (var i = 0; i < requestsLen; i++) { + for (let i = 0; i < requestsLen; i++) { if (well.requests_as_source[i] && well.requests_as_source[i].submission) { submDetails.id = well.requests_as_source[i].submission.id if (well.requests_as_source[i].submission.used_tags) { @@ -192,7 +192,7 @@ function extractSubmDetailsFromAliquots(well) { let submDetails = { id: null, usedTags: [] } const requestsLen = well.aliquots.length - for (var i = 0; i < requestsLen; i++) { + for (let i = 0; i < requestsLen; i++) { if (well.aliquots[i] && well.aliquots[i].request && well.aliquots[i].request.submission) { submDetails.id = well.aliquots[i].request.submission.id if (well.aliquots[i].request.submission.used_tags) { diff --git a/app/frontend/javascript/custom-tagged-plate/tagLayoutFunctions.js b/app/frontend/javascript/custom-tagged-plate/tagLayoutFunctions.js index f3447e868..580e8ec0a 100644 --- a/app/frontend/javascript/custom-tagged-plate/tagLayoutFunctions.js +++ b/app/frontend/javascript/custom-tagged-plate/tagLayoutFunctions.js @@ -31,7 +31,7 @@ function processTagsPerWell(acc, well, relIndex, absIndex, tagsPerWell, walker) let relIndexAdj = relIndex * tagsPerWell let absIndexAdj = absIndex * tagsPerWell - for (var i = 0; i < tagsPerWell; i++) { + for (let i = 0; i < tagsPerWell; i++) { acc[well.position].push(walker(well, relIndexAdj + i, absIndexAdj + i)) } return acc diff --git a/app/frontend/javascript/file-list/components/FileList.vue b/app/frontend/javascript/file-list/components/FileList.vue index f5e9c8ac4..85534c06b 100644 --- a/app/frontend/javascript/file-list/components/FileList.vue +++ b/app/frontend/javascript/file-list/components/FileList.vue @@ -25,9 +25,9 @@ export default { }, methods: { fetchData: function () { - var self = this + let self = this self.loading = true - var xhr = new XMLHttpRequest() + let xhr = new XMLHttpRequest() xhr.open('GET', self.base_url) xhr.onload = function () { self.qc_files = JSON.parse(xhr.responseText)['qc_files'] diff --git a/app/frontend/javascript/file-list/index.js b/app/frontend/javascript/file-list/index.js index c524e437c..166bac593 100644 --- a/app/frontend/javascript/file-list/index.js +++ b/app/frontend/javascript/file-list/index.js @@ -23,7 +23,7 @@ document.addEventListener('DOMContentLoaded', () => { if (document.getElementById('files-list')) { /* The files-list element isn't on all pages. So only initialize our * Vue app if we actually find it */ - var app = new Vue({ + let app = new Vue({ el: '#files-list', render: (h) => h(FileList), }) diff --git a/app/frontend/javascript/legacy_scripts_a.js b/app/frontend/javascript/legacy_scripts_a.js index ad1096261..49be624c2 100644 --- a/app/frontend/javascript/legacy_scripts_a.js +++ b/app/frontend/javascript/legacy_scripts_a.js @@ -3,7 +3,7 @@ import { ENTER_KEYCODE, TAB_KEYCODE } from '@/javascript/lib/keycodes.js' ;(function ($, _exports, undefined) { 'use strict' - var PlateViewModel = function (plateElement) { + let PlateViewModel = function (plateElement) { this['pools-view'] = { activate: function () { $('#pools-information li').fadeIn('fast') @@ -28,22 +28,22 @@ import { ENTER_KEYCODE, TAB_KEYCODE } from '@/javascript/lib/keycodes.js' } } - var limberPlateView = function (defaultTab) { - var plateElement = $(this) + let limberPlateView = function (defaultTab) { + let plateElement = $(this) - var control = $('#plate-view-control') + let control = $('#plate-view-control') - var viewModel = new PlateViewModel(plateElement) + let viewModel = new PlateViewModel(plateElement) control.find('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { - var viewName = e.target.dataset.plateView + let viewName = e.target.dataset.plateView if (viewModel[viewName]) { viewModel[viewName].activate() } }) control.find('a[data-toggle="tab"]').on('hide.bs.tab', function (e) { - var viewName = e.target.dataset.plateView + let viewName = e.target.dataset.plateView if (viewModel[viewName]) { viewModel[viewName].deactivate() } @@ -52,7 +52,7 @@ import { ENTER_KEYCODE, TAB_KEYCODE } from '@/javascript/lib/keycodes.js' control.find('a[href="' + defaultTab + '"]').tab('show') plateElement.on('click', '.aliquot', function (event) { - var pool = $(event.currentTarget).data('pool') + let pool = $(event.currentTarget).data('pool') control.find('a[data-plate-view="pools-view"]').tab('show') @@ -90,7 +90,7 @@ import { ENTER_KEYCODE, TAB_KEYCODE } from '@/javascript/lib/keycodes.js' //= require lib/keycodes // Trap the carriage return sent by barcode scanner $(document).on('keydown', '.plate-barcode', function (event) { - var code = event.charCode || event.keyCode + let code = event.charCode || event.keyCode // Check for carrage return (key code ENTER_KEYCODE) if (code === ENTER_KEYCODE || code === TAB_KEYCODE) { if ($(event.currentTarget).val().length > 0) { diff --git a/app/frontend/javascript/lib/array_fill_polyfill.js b/app/frontend/javascript/lib/array_fill_polyfill.js index 043779381..da2ede8e2 100644 --- a/app/frontend/javascript/lib/array_fill_polyfill.js +++ b/app/frontend/javascript/lib/array_fill_polyfill.js @@ -8,24 +8,24 @@ if (!Array.prototype.fill) { throw new TypeError('this is null or not defined') } - var O = Object(this) + let O = Object(this) // Steps 3-5. - var len = O.length >>> 0 + let len = O.length >>> 0 // Steps 6-7. - var start = arguments[1] - var relativeStart = start >> 0 + let start = arguments[1] + let relativeStart = start >> 0 // Step 8. - var k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len) + let k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len) // Steps 9-10. - var end = arguments[2] - var relativeEnd = end === undefined ? len : end >> 0 + let end = arguments[2] + let relativeEnd = end === undefined ? len : end >> 0 // Step 11. - var final = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len) + let final = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len) // Step 12. while (k < final) { diff --git a/app/frontend/javascript/lib/disable_enter_key_submit.js b/app/frontend/javascript/lib/disable_enter_key_submit.js index 9c48c8f98..1f0a3007a 100644 --- a/app/frontend/javascript/lib/disable_enter_key_submit.js +++ b/app/frontend/javascript/lib/disable_enter_key_submit.js @@ -7,7 +7,7 @@ export function disableEnterKeySubmit(elementSelectorToIdentifyPage, formWrapper // Build up a list of all fields we may want to tab through. // Only include fields nested under the specified element. - var fields = $(formWrapperElement).find('input[type=text],input[type=submit]') + let fields = $(formWrapperElement).find('input[type=text],input[type=submit]') // Then to each of the text fields, capture the enter event, // and stop it from auto-submitting the form. @@ -17,7 +17,7 @@ export function disableEnterKeySubmit(elementSelectorToIdentifyPage, formWrapper fields.each(function (index, field) { if ($(field).is('input[type=text]')) { $(field).on('keypress', function (e) { - var code = e.key + let code = e.key if (code === 'Enter') { e.preventDefault() // Stop the form submitting $(fields[index + 1]).focus() // Emulate a tab diff --git a/app/frontend/javascript/lib/status_collector.js b/app/frontend/javascript/lib/status_collector.js index 75886c954..9df381e41 100644 --- a/app/frontend/javascript/lib/status_collector.js +++ b/app/frontend/javascript/lib/status_collector.js @@ -1,7 +1,7 @@ // A status collector can have monitors registered. It will trigger // its onSuccess event when all monitors are true, and its onRevert // event if any are false. -var statusCollector = function (onSuccess, onRevert) { +let statusCollector = function (onSuccess, onRevert) { // Fires when all guards are true this.onSuccess = onSuccess // Fires if a guard is invalidated @@ -11,7 +11,7 @@ var statusCollector = function (onSuccess, onRevert) { // Monitors are registered to a collector. When the change state they // trigger the collector to check the state of all its monitors. -var monitor = function (state, collector) { +let monitor = function (state, collector) { this.valid = state || false this.collector = collector } @@ -29,12 +29,12 @@ monitor.prototype = { statusCollector.prototype = { register: function (status) { - var new_monitor = new monitor(status, this) + let new_monitor = new monitor(status, this) this.monitors.push(new_monitor) return new_monitor }, collate: function () { - for (var i = 0; i < this.monitors.length; i += 1) { + for (let i = 0; i < this.monitors.length; i += 1) { if (!this.monitors[i].valid) { return this.onRevert() } diff --git a/app/frontend/javascript/lib/tag_collector.js b/app/frontend/javascript/lib/tag_collector.js index 4b5ad4f14..4a20da07c 100644 --- a/app/frontend/javascript/lib/tag_collector.js +++ b/app/frontend/javascript/lib/tag_collector.js @@ -1,7 +1,7 @@ // A status collector can have monitors registered. It will trigger // its onSuccess event when all monitors are true, and its onRevert // event if any are false. -var tagStatusCollector = function (dualRequired, onSuccess, onRevert) { +let tagStatusCollector = function (dualRequired, onSuccess, onRevert) { // Fires when all guards are true this.onSuccess = onSuccess // Fires if a guard is invalidated @@ -13,7 +13,7 @@ var tagStatusCollector = function (dualRequired, onSuccess, onRevert) { // Monitors are registered to a collector. When the change state they // trigger the collector to check the state of all its monitors. -var tagMonitor = function (state, collector, monitored) { +let tagMonitor = function (state, collector, monitored) { this.valid = state || false this.collector = collector this.monitored = monitored @@ -35,13 +35,13 @@ tagMonitor.prototype = { tagStatusCollector.prototype = { register: function (status, monitored) { - var new_monitor = new tagMonitor(status, this, monitored) + let new_monitor = new tagMonitor(status, this, monitored) this.monitors.push(new_monitor) return new_monitor }, collate: function () { - var dual_count = 0 - for (var i = 0; i < this.monitors.length; i += 1) { + let dual_count = 0 + for (let i = 0; i < this.monitors.length; i += 1) { if (!this.monitors[i].valid) { return this.onRevert() } diff --git a/app/frontend/javascript/lib/validator.js b/app/frontend/javascript/lib/validator.js index e72328b80..505f87538 100644 --- a/app/frontend/javascript/lib/validator.js +++ b/app/frontend/javascript/lib/validator.js @@ -1,6 +1,6 @@ // accepts a `validation` function which returns true (valid) or false (invalid) // and a `message` string for when it's invalid -var validator = function (validation, message) { +let validator = function (validation, message) { this.validation = validation this.message = message } diff --git a/app/frontend/javascript/multi-stamp-tubes/components/TubeArraySummary.spec.js b/app/frontend/javascript/multi-stamp-tubes/components/TubeArraySummary.spec.js index 5e8b536c2..d4544e16f 100644 --- a/app/frontend/javascript/multi-stamp-tubes/components/TubeArraySummary.spec.js +++ b/app/frontend/javascript/multi-stamp-tubes/components/TubeArraySummary.spec.js @@ -6,15 +6,15 @@ import TubeArraySummary from './TubeArraySummary.vue' import localVue from '@/javascript/test_support/base_vue.js' describe('TubeArraySummary', () => { - var emptyTubes = [] + let emptyTubes = [] for (let i = 0; i < 96; i++) { emptyTubes.push({ index: i, labware: null, state: 'empty' }) } - var fullSetOfTubes = [] + let fullSetOfTubes = [] for (let i = 0; i < 96; i++) { - var machine_barcode = (1000000000000 + i).toString() - var human_barcode = 'NT' + (1000 + i).toString() + 'G' + let machine_barcode = (1000000000000 + i).toString() + let human_barcode = 'NT' + (1000 + i).toString() + 'G' fullSetOfTubes.push({ index: i, labware: { labware_barcode: { human_barcode: human_barcode, machine_barcode: machine_barcode } }, @@ -22,7 +22,7 @@ describe('TubeArraySummary', () => { }) } - var mixtureOfTubesWithDuplicates = [] + let mixtureOfTubesWithDuplicates = [] for (let i = 0; i < 96; i++) { switch (true) { case i < 6: diff --git a/app/frontend/javascript/multi-stamp-tubes/components/TubeArraySummary.vue b/app/frontend/javascript/multi-stamp-tubes/components/TubeArraySummary.vue index 3e7fd3e63..90c5b7fc1 100644 --- a/app/frontend/javascript/multi-stamp-tubes/components/TubeArraySummary.vue +++ b/app/frontend/javascript/multi-stamp-tubes/components/TubeArraySummary.vue @@ -61,7 +61,7 @@ export default { // Create a dictionary of tube barcodes and their numbers of replicates for use // in the summary table tubesDict() { - var summary_dict = {} + let summary_dict = {} const machine_barcodes = this.tubes.reduce((acc, tube) => { if (tube.labware && tube.labware.state == 'passed') { acc.push(tube.labware.labware_barcode.machine_barcode) @@ -70,8 +70,8 @@ export default { }, []) this.tubes.forEach(function (tube) { - var tube_machine_barcode = 'Empty' - var tube_human_barcode = 'Empty' + let tube_machine_barcode = 'Empty' + let tube_human_barcode = 'Empty' // extract the labware barcodes where the labware has been scanned if (tube.labware != null) { diff --git a/app/frontend/javascript/multi-stamp/components/MultiStamp.vue b/app/frontend/javascript/multi-stamp/components/MultiStamp.vue index 7222d6afd..6f3823d1d 100644 --- a/app/frontend/javascript/multi-stamp/components/MultiStamp.vue +++ b/app/frontend/javascript/multi-stamp/components/MultiStamp.vue @@ -231,7 +231,7 @@ export default { transfersError() { const errorMessages = [] if (this.duplicatedTransfers.length > 0) { - var sourceBarcodes = new Set() + let sourceBarcodes = new Set() this.duplicatedTransfers.forEach((transfer) => { sourceBarcodes.add(transfer.plateObj.plate.labware_barcode.human_barcode) }) diff --git a/app/frontend/javascript/multi-stamp/components/MultiStampLibrarySplitter.spec.js b/app/frontend/javascript/multi-stamp/components/MultiStampLibrarySplitter.spec.js index daf593ca8..c89b6e25f 100644 --- a/app/frontend/javascript/multi-stamp/components/MultiStampLibrarySplitter.spec.js +++ b/app/frontend/javascript/multi-stamp/components/MultiStampLibrarySplitter.spec.js @@ -272,10 +272,10 @@ describe('MultiStampLibrarySplitter', () => { }, } - var payloads = [plate1Payload, plate2Payload] + let payloads = [plate1Payload, plate2Payload] - var numCalls = 0 - var listChecks = [] + let numCalls = 0 + let listChecks = [] mock.onPost().reply((config) => { listChecks.push(config) numCalls = numCalls + 1 @@ -289,9 +289,9 @@ describe('MultiStampLibrarySplitter', () => { expect(numCalls).toEqual(2) - for (var i = 0; i < listChecks.length; i++) { - var config = listChecks[i] - var payload = payloads[i] + for (let i = 0; i < listChecks.length; i++) { + let config = listChecks[i] + let payload = payloads[i] expect(config.url).toEqual('example/example') expect(config.data).toEqual(JSON.stringify(payload)) } diff --git a/app/frontend/javascript/multi_plate_pooling.js b/app/frontend/javascript/multi_plate_pooling.js index 15204455b..f262d6bbe 100644 --- a/app/frontend/javascript/multi_plate_pooling.js +++ b/app/frontend/javascript/multi_plate_pooling.js @@ -6,9 +6,9 @@ import jQuery from 'jquery' exports.SCAPE = {} } - var SCAPE = exports.SCAPE + let SCAPE = exports.SCAPE - var WELLS_IN_COLUMN_MAJOR_ORDER = [ + let WELLS_IN_COLUMN_MAJOR_ORDER = [ 'A1', 'B1', 'C1', @@ -107,7 +107,7 @@ import jQuery from 'jquery' 'H12', ] - var SOURCE_STATES = ['passed', 'qc_complete'] + let SOURCE_STATES = ['passed', 'qc_complete'] $(function (_event) { // If we are not on the multi-plate pooling page, don't set anything up. @@ -210,10 +210,10 @@ import jQuery from 'jquery' // Counts the total number of pools on the plate SCAPE.totalPools = function () { - var poolCount = 0 - for (var plateIndex = 0; plateIndex < SCAPE.plates.length; plateIndex += 1) { + let poolCount = 0 + for (let plateIndex = 0; plateIndex < SCAPE.plates.length; plateIndex += 1) { if (SCAPE.plates[plateIndex] !== undefined) { - var preCapPools = SCAPE.plates[plateIndex].preCapPools + let preCapPools = SCAPE.plates[plateIndex].preCapPools poolCount += walkPreCapPools(preCapPools, function () {}) } } @@ -225,7 +225,7 @@ import jQuery from 'jquery' } SCAPE.newAliquot = function (poolNumber, aliquotText) { - var poolNumberInt = parseInt(poolNumber, 10) + let poolNumberInt = parseInt(poolNumber, 10) return $(document.createElement('div')) .addClass('aliquot colour-' + (poolNumberInt + 1)) @@ -234,25 +234,25 @@ import jQuery from 'jquery' } var walkPreCapPools = function (preCapPools, block) { - var poolNumber = -1 - for (var capPool of preCapPools) { + let poolNumber = -1 + for (let capPool of preCapPools) { poolNumber++ block(capPool.wells, poolNumber) } return poolNumber + 1 } - var renderPoolingSummary = function (plates) { - var capPoolOffset = 0 + let renderPoolingSummary = function (plates) { + let capPoolOffset = 0 for (var plate of plates) { if (plate === undefined) { // Nothing } else { - var preCapPools = plate.preCapPools + let preCapPools = plate.preCapPools capPoolOffset += walkPreCapPools(preCapPools, function (wells, poolNumber) { - var destinationWell = WELLS_IN_COLUMN_MAJOR_ORDER[capPoolOffset + poolNumber] - var listElement = $('
  • ') + let destinationWell = WELLS_IN_COLUMN_MAJOR_ORDER[capPoolOffset + poolNumber] + let listElement = $('
  • ') .text(destinationWell) .append('
    ' + wells.length + '
    ') .append('
    ' + plate.barcode + ': ' + wells.join(', ') + '
    ') @@ -265,8 +265,8 @@ import jQuery from 'jquery' SCAPE.renderDestinationPools = function () { $('.destination-plate .well').empty() - var capPoolOffset = 0 - for (var plate of SCAPE.plates) { + let capPoolOffset = 0 + for (let plate of SCAPE.plates) { if (plate !== undefined) { var well capPoolOffset += walkPreCapPools(plate.preCapPools, function (wells, poolNumber) { @@ -278,13 +278,13 @@ import jQuery from 'jquery' } SCAPE.renderSourceWells = function () { - var capPoolOffset = 0 + let capPoolOffset = 0 for (var plateIndex = 0; plateIndex < SCAPE.plates.length; plateIndex += 1) { if (SCAPE.plates[plateIndex] === undefined) { $('.plate-id-' + plateIndex).hide() } else { - var preCapPools = SCAPE.plates[plateIndex].preCapPools - var barcode = SCAPE.plates[plateIndex].barcode + let preCapPools = SCAPE.plates[plateIndex].preCapPools + let barcode = SCAPE.plates[plateIndex].barcode $('.plate-id-' + plateIndex).show() $('.plate-id-' + plateIndex + ' .well').empty() $('.plate-id-' + plateIndex + ' caption').text(barcode) @@ -292,9 +292,9 @@ import jQuery from 'jquery' var newInputs = $(document.createElement('div')).attr('id', 'well-transfers-' + plateIndex) capPoolOffset += walkPreCapPools(preCapPools, function (wells, poolNumber) { - var newInput, well + let newInput, well - for (var wellName of wells) { + for (let wellName of wells) { well = $('.plate-id-' + plateIndex + ' .' + wellName) well.append( SCAPE.newAliquot(capPoolOffset + poolNumber, WELLS_IN_COLUMN_MAJOR_ORDER[capPoolOffset + poolNumber]) @@ -313,7 +313,7 @@ import jQuery from 'jquery' } } - var plateSummaryHandler = function () { + let plateSummaryHandler = function () { SCAPE.renderSourceWells() SCAPE.renderDestinationPools() @@ -329,7 +329,7 @@ import jQuery from 'jquery' this.pos = 0 this.slide = function () { - var scrollTo + let scrollTo this.pos = (this.pos + 1) % $(this).children().length scrollTo = $(this).children()[this.pos].offsetTop - 5 $(this).delay(1000).animate({ scrollTop: scrollTo }, 500, this.slide) diff --git a/app/frontend/javascript/multi_tube_pooling.js b/app/frontend/javascript/multi_tube_pooling.js index c0485a2b0..79ee78582 100644 --- a/app/frontend/javascript/multi_tube_pooling.js +++ b/app/frontend/javascript/multi_tube_pooling.js @@ -9,7 +9,7 @@ import { ENTER_KEYCODE, TAB_KEYCODE } from '@/javascript/lib/keycodes.js' return } - var newScanned, + let newScanned, tubeCollector, siblingTube, barcodeRegister = {} @@ -30,7 +30,7 @@ import { ENTER_KEYCODE, TAB_KEYCODE } from '@/javascript/lib/keycodes.js' this.setMessage('Scanned in and ready to go!') }, setState: function (state) { - for (var i = 0; i < this.states.length; i += 1) { + for (let i = 0; i < this.states.length; i += 1) { if (state === this.states[i]) { this.listElement.addClass(this.states[i] + '-tube') } else { @@ -46,7 +46,7 @@ import { ENTER_KEYCODE, TAB_KEYCODE } from '@/javascript/lib/keycodes.js' newScanned = function (tube_barcode, collector) { // Return immediately if the box is empty - var stripped + let stripped stripped = tube_barcode.replace(/\s*/g, '') if (stripped === '') { return @@ -66,7 +66,7 @@ import { ENTER_KEYCODE, TAB_KEYCODE } from '@/javascript/lib/keycodes.js' $('#scanned_tube_list').append(this.newElement()) }, newElement: function () { - var scanned = this + let scanned = this this.listElement = $(document.createElement('li')) .attr('id', 'listElement[' + this.tubeBarcode + ']') .attr('class', 'wait-tube') @@ -119,7 +119,7 @@ import { ENTER_KEYCODE, TAB_KEYCODE } from '@/javascript/lib/keycodes.js' this.listElement.find('.tube_validation_report').text(message) }, setState: function (state) { - for (var i = 0; i < this.states.length; i += 1) { + for (let i = 0; i < this.states.length; i += 1) { if (state === this.states[i]) { this.listElement.addClass(this.states[i] + '-tube') } else { @@ -157,7 +157,7 @@ import { ENTER_KEYCODE, TAB_KEYCODE } from '@/javascript/lib/keycodes.js' $('#tube_submit').prop('disabled', true) $('#tube_scan').on('keydown', function (e) { - var code = e.charCode || e.keyCode + let code = e.charCode || e.keyCode if (code === ENTER_KEYCODE || code === TAB_KEYCODE) { e.preventDefault() new newScanned(this.value, tubeCollector) diff --git a/app/frontend/javascript/pipeline-graph/graphFunctions.js b/app/frontend/javascript/pipeline-graph/graphFunctions.js index 1ad87a4e6..a85554e7d 100644 --- a/app/frontend/javascript/pipeline-graph/graphFunctions.js +++ b/app/frontend/javascript/pipeline-graph/graphFunctions.js @@ -86,7 +86,7 @@ const getElementPipeline = function (element) { } const pipelineColourEdge = function (edge) { - var pipeline = getElementPipeline(edge) + let pipeline = getElementPipeline(edge) return colourMapping.getPipelineColour(pipeline) } diff --git a/app/frontend/javascript/pooled_tubes_from_whole_plates.js b/app/frontend/javascript/pooled_tubes_from_whole_plates.js index d5b5451a1..f8b5bc07a 100644 --- a/app/frontend/javascript/pooled_tubes_from_whole_plates.js +++ b/app/frontend/javascript/pooled_tubes_from_whole_plates.js @@ -2,7 +2,7 @@ import jQuery from 'jquery' ;(function ($, _exports, undefined) { 'use strict' - var SOURCE_STATES = ['passed', 'qc_complete'] + let SOURCE_STATES = ['passed', 'qc_complete'] $(function (_event) { if ($('#pooled-tubes-from-whole-plates').length === 0) { @@ -11,7 +11,7 @@ import jQuery from 'jquery' //= require lib/array_fill_polyfill - var Pooler = function (labware_number, button) { + let Pooler = function (labware_number, button) { this.tags = Array(labware_number).fill([]) this.labware_details = Array(labware_number).fill({}) this.clashing_tags = null @@ -81,11 +81,11 @@ import jQuery from 'jquery' this.button.attr('disabled', null) }, noDupes: function () { - var set = new Set() - var poolerObj = this + let set = new Set() + let poolerObj = this return this.tags.every(function (tag_set) { return tag_set.every(function (tag) { - var tagsAsString = String(tag) + let tagsAsString = String(tag) if (set.has(tagsAsString)) { poolerObj.clashing_tags = tagsAsString return false @@ -98,7 +98,7 @@ import jQuery from 'jquery' }, } - var pooler = new Pooler($('.labware-box').length, $('#create-labware')) + let pooler = new Pooler($('.labware-box').length, $('#create-labware')) $('.labware-box').on('change', function () { // When we scan in a labware @@ -139,20 +139,20 @@ import jQuery from 'jquery' return $(this).closest('.labware-container') }, checkLabware: function (data, status, scanned_barcode) { - var response = data[this.dataset.labwareType] + let response = data[this.dataset.labwareType] if (SOURCE_STATES.indexOf(response.state) === -1) { this.badLabware() SCAPE.message('Scanned ' + this.dataset.labwareType + 's are unsuitable', 'invalid') } else { - var position = $(this).data('position') + let position = $(this).data('position') if (pooler.record(response, position, scanned_barcode)) { this.goodLabware() } else { - var clashing_labware_barcodes = this.findClashingLabwares() + let clashing_labware_barcodes = this.findClashingLabwares() this.badLabware() - var msg = + let msg = 'The scanned ' + this.dataset.labwareType + ' contains tags that would clash with those in other ' + @@ -165,10 +165,10 @@ import jQuery from 'jquery' } }, findClashingLabwares: function () { - var clashing_labwares = [] + let clashing_labwares = [] Object.keys(pooler.labware_details).forEach(function (key) { - var current_labware_details = pooler.labware_details[key] + let current_labware_details = pooler.labware_details[key] // skip empty labware locations if (current_labware_details.tags == undefined) { return @@ -176,8 +176,8 @@ import jQuery from 'jquery' // check for clashing tags if (current_labware_details.tags.includes(pooler.clashing_tags)) { - var human_barcode = String(current_labware_details.human_barcode) - var machine_barcode = String(current_labware_details.machine_barcode) + let human_barcode = String(current_labware_details.human_barcode) + let machine_barcode = String(current_labware_details.machine_barcode) clashing_labwares.push('' + human_barcode + ' (' + machine_barcode + ')') } }) diff --git a/app/frontend/javascript/session_scripts.js b/app/frontend/javascript/session_scripts.js index 3ca95bbc0..4f3ca8d82 100644 --- a/app/frontend/javascript/session_scripts.js +++ b/app/frontend/javascript/session_scripts.js @@ -3,7 +3,7 @@ import jQuery from 'jquery' ;(function ($, window, undefined) { 'use strict' - var loggedIn, userName, wasLoggedIn, logIn, logOut, updateUserName, warning, alert, success + let loggedIn, userName, wasLoggedIn, logIn, logOut, updateUserName, warning, alert, success loggedIn = function () { return document.cookie.match(/; user_name=([^;]+)/) !== null @@ -18,7 +18,7 @@ import jQuery from 'jquery' } logIn = function () { - var user_name = userName() + let user_name = userName() if (wasLoggedIn) { if (user_name !== wasLoggedIn) { updateUserName(user_name) @@ -35,7 +35,7 @@ import jQuery from 'jquery' } logOut = function () { - var user_name = 'Guest' + let user_name = 'Guest' if (wasLoggedIn) { updateUserName(user_name) $('body').addClass('logged_out').removeClass('logged_in') @@ -56,8 +56,8 @@ import jQuery from 'jquery' } alert = function (message, category, title_text) { - var newDiv = document.createElement('div') - var title = document.createElement('strong') + let newDiv = document.createElement('div') + let title = document.createElement('strong') title.appendChild(document.createTextNode(title_text + ' ')) newDiv.appendChild(title) newDiv.appendChild(document.createTextNode(message)) diff --git a/app/frontend/javascript/shared/buildArray.js b/app/frontend/javascript/shared/buildArray.js index e8ea4c44e..7c67d3239 100644 --- a/app/frontend/javascript/shared/buildArray.js +++ b/app/frontend/javascript/shared/buildArray.js @@ -1,6 +1,6 @@ // Behaves similarly to Array.from(length, function(_, iteration)) const buildArray = function (length, constructor) { - var array = new Array(length) + let array = new Array(length) for (let i = 0; i < length; i++) { array[i] = constructor(i) } diff --git a/app/frontend/javascript/shared/components/LabwareScan.vue b/app/frontend/javascript/shared/components/LabwareScan.vue index bfcc725e3..c98eee908 100644 --- a/app/frontend/javascript/shared/components/LabwareScan.vue +++ b/app/frontend/javascript/shared/components/LabwareScan.vue @@ -202,7 +202,7 @@ export default { }, watch: { state() { - var message + let message if (this.labwareType == 'tube') { message = { labware: this.labware, state: this.state } } else { diff --git a/app/frontend/javascript/state_change_reasons.js b/app/frontend/javascript/state_change_reasons.js index bd13d512a..5801916f2 100644 --- a/app/frontend/javascript/state_change_reasons.js +++ b/app/frontend/javascript/state_change_reasons.js @@ -2,14 +2,14 @@ import jQuery from 'jquery' ;(function ($, _exports, undefined) { 'use strict' - var updateDisplay = function (new_state, speed) { + let updateDisplay = function (new_state, speed) { $('.reason') .not('#' + new_state + '_reasons') .slideUp(speed) $('#' + new_state + '_reasons').slideDown(speed) } - var displayReason = function () { + let displayReason = function () { updateDisplay(this.value, 'fast') } diff --git a/app/frontend/javascript/state_machine.js b/app/frontend/javascript/state_machine.js index 6db2fa8a9..28c752d86 100644 --- a/app/frontend/javascript/state_machine.js +++ b/app/frontend/javascript/state_machine.js @@ -4,7 +4,7 @@ import jQuery from 'jquery' //= require lib/ajax_support - var Events = { + let Events = { on: function () { if (!this.o) this.o = $({}) @@ -18,7 +18,7 @@ import jQuery from 'jquery' }, } - var StateMachine = function () {} + let StateMachine = function () {} StateMachine.fn = StateMachine.prototype diff --git a/app/frontend/javascript/tag_by_tag_plate.js b/app/frontend/javascript/tag_by_tag_plate.js index 146691415..baafa878d 100644 --- a/app/frontend/javascript/tag_by_tag_plate.js +++ b/app/frontend/javascript/tag_by_tag_plate.js @@ -10,20 +10,20 @@ import validator from '@/javascript/lib/validator.js' if ($('#tag-creation-page').length === 0) { return } - var qcableLookup + let qcableLookup //= require lib/ajax_support // Set up some null objects - var unknownTemplate = { unknown: true, dual_index: false } - var unknownQcable = { template_uuid: 'not-loaded' } + let unknownTemplate = { unknown: true, dual_index: false } + let unknownQcable = { template_uuid: 'not-loaded' } qcableLookup = function (barcodeBox, collector) { if (barcodeBox.length === 0) { return false } - var qc_lookup = this + let qc_lookup = this this.inputBox = barcodeBox // the `data` attribute is set when declaring the element in tagged_plate.html.erb @@ -63,7 +63,7 @@ import validator from '@/javascript/lib/validator.js' }).then(this.success(), this.error()) }, success: function () { - var qc_lookup = this + let qc_lookup = this return function (response) { if (response.error) { qc_lookup.message(response.error, 'danger') @@ -76,7 +76,7 @@ import validator from '@/javascript/lib/validator.js' } }, error: function () { - var qc_lookup = this + let qc_lookup = this return function () { qc_lookup.message( 'The barcode could not be found. There may be network issues, or problems with Sequencescape.', @@ -141,8 +141,8 @@ import validator from '@/javascript/lib/validator.js' validPlate: function () { // run through the `validators`, and collect any errors this.errors = '' - for (var i = 0; i < this.validators.length; i += 1) { - var response = this.validators[i].validate(this) + for (let i = 0; i < this.validators.length; i += 1) { + let response = this.validators[i].validate(this) if (!response.valid) { this.errors += ' ' + response.message } @@ -166,7 +166,7 @@ import validator from '@/javascript/lib/validator.js' errors: '', } - var qcCollector = new tagStatusCollector( + let qcCollector = new tagStatusCollector( SCAPE.dualRequired, function () { $('#submit-summary').text('Marks the tag sources as used, and convert the tag plate.') @@ -187,7 +187,7 @@ import validator from '@/javascript/lib/validator.js' $.extend(SCAPE, { fetch_tags: function () { - var selected_layout = $('#plate_tag_plate_template_uuid').val() + let selected_layout = $('#plate_tag_plate_template_uuid').val() if (SCAPE.tag_plates_list[selected_layout] === undefined) { return $([]) } else { @@ -195,7 +195,7 @@ import validator from '@/javascript/lib/validator.js' } }, update_layout: function () { - var tags = this.fetch_tags() + let tags = this.fetch_tags() tags.each(function (_index) { $('#tagging-plate #aliquot_' + this[0]) diff --git a/app/frontend/javascript/validate-paired-tubes/components/TransferVolumes.spec.js b/app/frontend/javascript/validate-paired-tubes/components/TransferVolumes.spec.js index 87b1fbadb..e71bc192a 100644 --- a/app/frontend/javascript/validate-paired-tubes/components/TransferVolumes.spec.js +++ b/app/frontend/javascript/validate-paired-tubes/components/TransferVolumes.spec.js @@ -31,7 +31,7 @@ describe('TransferVolumes', () => { } describe('purposeConfig', () => { - var wrapper + let wrapper beforeEach(() => { purposeConfigForTube.mockClear() @@ -52,7 +52,7 @@ describe('TransferVolumes', () => { }) describe('sourceMolarity', () => { - var wrapper + let wrapper const sourceMolarity = 6 beforeEach(() => { @@ -73,7 +73,7 @@ describe('TransferVolumes', () => { }) describe('targetMolarity', () => { - var wrapper + let wrapper const molarity = 250 beforeEach(() => { @@ -96,7 +96,7 @@ describe('TransferVolumes', () => { }) describe('targetVolume', () => { - var wrapper + let wrapper const volume = 250 beforeEach(() => { @@ -119,7 +119,7 @@ describe('TransferVolumes', () => { }) describe('transferVolumes', () => { - var wrapper + let wrapper const targetMolarity = 4 const targetVolume = 192 const minimumPick = 2 @@ -167,7 +167,7 @@ describe('TransferVolumes', () => { }) describe('sampleVolumeForDisplay', () => { - var wrapper + let wrapper const transferVolumes = { sampleVolume: 150.12345, bufferVolume: 49.87655, @@ -186,7 +186,7 @@ describe('TransferVolumes', () => { }) describe('bufferVolumeForDisplay', () => { - var wrapper + let wrapper const transferVolumes = { sampleVolume: 150.12345, bufferVolume: 49.87655, @@ -203,7 +203,7 @@ describe('TransferVolumes', () => { }) describe('sourceMolarityForDisplay', () => { - var wrapper + let wrapper const sourceMolarity = 3.4567 beforeEach(() => { @@ -219,7 +219,7 @@ describe('TransferVolumes', () => { }) describe('targetMolarityForDisplay', () => { - var wrapper + let wrapper const molarity = 5.6789 beforeEach(() => { @@ -237,7 +237,7 @@ describe('TransferVolumes', () => { }) describe('targetVolumeForDisplay', () => { - var wrapper + let wrapper const volume = 25.6789 beforeEach(() => { @@ -256,7 +256,7 @@ describe('TransferVolumes', () => { describe('belowTargetMolarity', () => { describe('is below target molarity', () => { - var wrapper + let wrapper const transferVolumes = { belowTarget: true, } @@ -274,7 +274,7 @@ describe('TransferVolumes', () => { }) describe('is not below target molarity', () => { - var wrapper + let wrapper const transferVolumes = { belowTarget: false, } @@ -326,7 +326,7 @@ describe('TransferVolumes', () => { }) describe('ready to display result', () => { - var wrapper + let wrapper const targetMolarity = 4 const targetVolume = 192 const transferVolumes = { From f1fab62fd90e9ce449e16ceb8abb3a96334b7c85 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 21 Aug 2024 16:49:18 +0100 Subject: [PATCH 05/26] refactor: manually replace vars with const and let --- app/frontend/javascript/bed_verification.js | 12 ++--- .../components/TubeArraySummary.spec.js | 52 ++++++++----------- .../javascript/multi_plate_pooling.js | 16 +++--- .../pooled_tubes_from_whole_plates.js | 2 +- 4 files changed, 36 insertions(+), 46 deletions(-) diff --git a/app/frontend/javascript/bed_verification.js b/app/frontend/javascript/bed_verification.js index fd70e8080..cc8908747 100644 --- a/app/frontend/javascript/bed_verification.js +++ b/app/frontend/javascript/bed_verification.js @@ -64,7 +64,7 @@ import jQuery from 'jquery' SCAPE.robot_barcode = robot_barcode } - var removeEntry = function () { + const removeEntry = function () { let lw_index, bed_list bed_list = SCAPE.robot_beds[$(this).attr('data-bed')] lw_index = bed_list.indexOf($(this).attr('data-labware')) @@ -91,7 +91,7 @@ import jQuery from 'jquery' } } - var flagBeds = function (beds, message) { + const flagBeds = function (beds, message) { let bad_beds = [] $.each(beds, function (bed_id) { // here we check the validity of each bed in the hash returned from the ruby robot @@ -107,7 +107,7 @@ import jQuery from 'jquery' SCAPE.message('There were problems: ' + message, 'danger') } - var clearFlagFromBeds = function (beds) { + const clearFlagFromBeds = function (beds) { $.each(beds, function (bed_id) { if (beds[bed_id]) { clearFlagFromBed(bed_id) @@ -115,7 +115,7 @@ import jQuery from 'jquery' }) } - var clearFlagFromBed = function (bed_id) { + const clearFlagFromBed = function (bed_id) { $('#bed_list li[data-bed="' + bed_id + '"]').removeClass('bad_bed list-group-item-danger') } @@ -123,13 +123,13 @@ import jQuery from 'jquery' $('#loadingModal').fadeIn(100) } - var pass = function () { + const pass = function () { $('#loadingModal').fadeOut(100) SCAPE.message('No problems detected!', 'success') $('#start-robot').prop('disabled', false) } - var fail = function () { + const fail = function () { $('#loadingModal').fadeOut(100) $('#start-robot').prop('disabled', true) } diff --git a/app/frontend/javascript/multi-stamp-tubes/components/TubeArraySummary.spec.js b/app/frontend/javascript/multi-stamp-tubes/components/TubeArraySummary.spec.js index d4544e16f..68877233a 100644 --- a/app/frontend/javascript/multi-stamp-tubes/components/TubeArraySummary.spec.js +++ b/app/frontend/javascript/multi-stamp-tubes/components/TubeArraySummary.spec.js @@ -24,37 +24,27 @@ describe('TubeArraySummary', () => { let mixtureOfTubesWithDuplicates = [] for (let i = 0; i < 96; i++) { - switch (true) { - case i < 6: - var human_barcode1 = 'NT1001G' - var machine_barcode1 = '1000000000001' - mixtureOfTubesWithDuplicates.push({ - index: i.toString(), - labware: { labware_barcode: { human_barcode: human_barcode1, machine_barcode: machine_barcode1 } }, - state: 'valid', - }) - break - case i < 12: - var human_barcode2 = 'NT1002H' - var machine_barcode2 = '1000000000002' - mixtureOfTubesWithDuplicates.push({ - index: i.toString(), - labware: { labware_barcode: { human_barcode: human_barcode2, machine_barcode: machine_barcode2 } }, - state: 'valid', - }) - break - case i < 18: - var human_barcode3 = 'NT1003I' - var machine_barcode3 = '1000000000003' - mixtureOfTubesWithDuplicates.push({ - index: i.toString(), - labware: { labware_barcode: { human_barcode: human_barcode3, machine_barcode: machine_barcode3 } }, - state: 'valid', - }) - break - default: - mixtureOfTubesWithDuplicates.push({ index: i.toString(), labware: null, state: 'empty' }) - break + let human_barcode, machine_barcode + + if (i < 6) { + human_barcode = 'NT1001G' + machine_barcode = '1000000000001' + } else if (i < 12) { + human_barcode = 'NT1002H' + machine_barcode = '1000000000002' + } else if (i < 18) { + human_barcode = 'NT1003I' + machine_barcode = '1000000000003' + } + + if (human_barcode && machine_barcode) { + mixtureOfTubesWithDuplicates.push({ + index: i.toString(), + labware: { labware_barcode: { human_barcode, machine_barcode } }, + state: 'valid', + }) + } else { + mixtureOfTubesWithDuplicates.push({ index: i.toString(), labware: null, state: 'empty' }) } } diff --git a/app/frontend/javascript/multi_plate_pooling.js b/app/frontend/javascript/multi_plate_pooling.js index f262d6bbe..dcb160d90 100644 --- a/app/frontend/javascript/multi_plate_pooling.js +++ b/app/frontend/javascript/multi_plate_pooling.js @@ -8,7 +8,7 @@ import jQuery from 'jquery' let SCAPE = exports.SCAPE - let WELLS_IN_COLUMN_MAJOR_ORDER = [ + const WELLS_IN_COLUMN_MAJOR_ORDER = [ 'A1', 'B1', 'C1', @@ -107,7 +107,7 @@ import jQuery from 'jquery' 'H12', ] - let SOURCE_STATES = ['passed', 'qc_complete'] + const SOURCE_STATES = ['passed', 'qc_complete'] $(function (_event) { // If we are not on the multi-plate pooling page, don't set anything up. @@ -233,7 +233,7 @@ import jQuery from 'jquery' .hide() } - var walkPreCapPools = function (preCapPools, block) { + const walkPreCapPools = function (preCapPools, block) { let poolNumber = -1 for (let capPool of preCapPools) { poolNumber++ @@ -245,7 +245,7 @@ import jQuery from 'jquery' let renderPoolingSummary = function (plates) { let capPoolOffset = 0 - for (var plate of plates) { + for (let plate of plates) { if (plate === undefined) { // Nothing } else { @@ -268,7 +268,7 @@ import jQuery from 'jquery' let capPoolOffset = 0 for (let plate of SCAPE.plates) { if (plate !== undefined) { - var well + let well capPoolOffset += walkPreCapPools(plate.preCapPools, function (wells, poolNumber) { well = $('.destination-plate .' + WELLS_IN_COLUMN_MAJOR_ORDER[capPoolOffset + poolNumber]) if (wells.length) well.append(SCAPE.newAliquot(capPoolOffset + poolNumber, wells.length)) @@ -279,7 +279,7 @@ import jQuery from 'jquery' SCAPE.renderSourceWells = function () { let capPoolOffset = 0 - for (var plateIndex = 0; plateIndex < SCAPE.plates.length; plateIndex += 1) { + for (let plateIndex = 0; plateIndex < SCAPE.plates.length; plateIndex += 1) { if (SCAPE.plates[plateIndex] === undefined) { $('.plate-id-' + plateIndex).hide() } else { @@ -290,7 +290,7 @@ import jQuery from 'jquery' $('.plate-id-' + plateIndex + ' caption').text(barcode) $('#well-transfers-' + plateIndex).detach() - var newInputs = $(document.createElement('div')).attr('id', 'well-transfers-' + plateIndex) + let newInputs = $(document.createElement('div')).attr('id', 'well-transfers-' + plateIndex) capPoolOffset += walkPreCapPools(preCapPools, function (wells, poolNumber) { let newInput, well @@ -338,7 +338,7 @@ import jQuery from 'jquery' }) } - var updateView = function () { + const updateView = function () { if (SCAPE.calculatePreCapPools()) { plateSummaryHandler() $('#pooling-summary').empty() diff --git a/app/frontend/javascript/pooled_tubes_from_whole_plates.js b/app/frontend/javascript/pooled_tubes_from_whole_plates.js index f8b5bc07a..ce9810131 100644 --- a/app/frontend/javascript/pooled_tubes_from_whole_plates.js +++ b/app/frontend/javascript/pooled_tubes_from_whole_plates.js @@ -20,7 +20,7 @@ import jQuery from 'jquery' // Inefficient and limited Set polyfill for phantomjs and IE10 if (Set === undefined) { - var Set = function () { + let Set = function () { this.array = [] } From fa5b811f222182c0c9b89146b5b2aa66cc1c959a Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 21 Aug 2024 16:53:35 +0100 Subject: [PATCH 06/26] refactor: merge tag_by_tag_plate into tagged-plate --- app/frontend/entrypoints/tagged-plate.js | 206 +++++++++++++++++++ app/frontend/javascript/tag_by_tag_plate.js | 212 -------------------- 2 files changed, 206 insertions(+), 212 deletions(-) delete mode 100644 app/frontend/javascript/tag_by_tag_plate.js diff --git a/app/frontend/entrypoints/tagged-plate.js b/app/frontend/entrypoints/tagged-plate.js index 4eedaac4a..a5604aa70 100644 --- a/app/frontend/entrypoints/tagged-plate.js +++ b/app/frontend/entrypoints/tagged-plate.js @@ -1,3 +1,7 @@ +import $ from 'jquery' +import tagStatusCollector from '@/javascript/lib/tag_collector.js' +import validator from '@/javascript/lib/validator.js' + const configElement = document.getElementById('labware-creator-config') const tagPlatesList = JSON.parse(configElement.dataset.tagPlatesList) const dualRequired = configElement.dataset.dualRequired === 'true' @@ -12,3 +16,205 @@ Object.assign(SCAPE, { dualRequired: dualRequired, enforceSameTemplateWithinPool: enforceSameTemplateWithinPool, }) + +// Previous content from tag_by_tag_plate.js follows below + +// TAG CREATION +let qcableLookup + +//= require lib/ajax_support + +// Set up some null objects +let unknownTemplate = { unknown: true, dual_index: false } +let unknownQcable = { template_uuid: 'not-loaded' } + +qcableLookup = function (barcodeBox, collector) { + if (barcodeBox.length === 0) { + return false + } + + let qc_lookup = this + this.inputBox = barcodeBox + + // the `data` attribute is set when declaring the element in tagged_plate.html.erb + this.infoPanel = $('#' + barcodeBox.data('info-panel')) + this.requiresDualIndexing = barcodeBox.data('requires-dual-indexing') + this.approvedTypes = SCAPE[barcodeBox.data('approved-list')] + this.required = this.inputBox[0].required + + // add an onchange event to the box, to look up the plate + this.inputBox.on('change', function () { + qc_lookup.resetStatus() + qc_lookup.requestPlate(this.value) + }) + this.monitor = collector.register(!this.required, this) + + // set initial values for the tag plate data + this.qcable = unknownQcable + this.template = unknownTemplate +} + +qcableLookup.prototype = { + resetStatus: function () { + this.monitor.fail() + this.infoPanel.find('dd').text('') + this.infoPanel.find('input').val(null) + }, + requestPlate: function (_barcode) { + if (this.inputBox.val() === '' && !this.required) { + return this.monitor.pass() + } + // find the qcable (tag plate) based on the barcode scanned in by the user + $.ajax({ + type: 'POST', + dataType: 'json', + url: '/search/qcables', + data: 'qcable_barcode=' + this.inputBox.val(), + }).then(this.success(), this.error()) + }, + success: function () { + let qc_lookup = this + return function (response) { + if (response.error) { + qc_lookup.message(response.error, 'danger') + } else if (response.qcable) { + // if response is as expected, load some data + qc_lookup.plateFound(response.qcable) + } else { + qc_lookup.message('An unexpected response was received. Please contact support.', 'danger') + } + } + }, + error: function () { + let qc_lookup = this + return function () { + qc_lookup.message( + 'The barcode could not be found. There may be network issues, or problems with Sequencescape.', + 'danger' + ) + } + }, + validators: [ + // `t` is a qcableLookup object + // The data for t.template comes from app/models/labware_creators/tagging/tag_collection.rb + // t.template.dual_index is true if the scanned tag plate contains both i5 and i7 tags together in its wells (is a UDI plate) + // t.requiresDualIndexing is true if there are multiple source plates from the submission, which will be pooled... + // ... and therefore an i5 tag (tag 2) is needed (from a UDI plate) + new validator(function (t) { + return t.qcable.state == 'available' + }, 'The scanned item is not available.'), + new validator(function (t) { + return !t.template.unknown + }, 'It is an unrecognised template.'), + new validator(function (t) { + return t.template.approved + }, 'It is not approved for use with this pipeline.'), + new validator(function (t) { + return !(t.requiresDualIndexing && t.template.used && t.template.dual_index) + }, 'This template has already been used.'), + new validator(function (t) { + return !(t.requiresDualIndexing && !t.template.dual_index) + }, 'Pool is spread across multiple plates. UDI plates must be used.'), + new validator(function (t) { + return SCAPE.enforceSameTemplateWithinPool ? t.template.matches_templates_in_pool : true + }, "It doesn't match those already used for other plates in this submission pool."), + ], + // + // The major function that runs when a tag plate is scanned into the box + // Loads tag plate data into `qcable` and `template` + // Adds visible information to the information panel + // Validates whether the tag plate is suitable + // Updates the plate diagram with tag numbers + // + plateFound: function (qcable) { + this.qcable = qcable + this.template = this.approvedTypes[qcable.template_uuid] || unknownTemplate + this.populateData() + + if (this.validPlate()) { + this.message('The ' + qcable.qcable_type + ' is suitable.', 'success') + SCAPE.update_layout() + this.monitor.pass() + } else { + this.message(' The ' + qcable.qcable_type + ' is not suitable.' + this.errors, 'danger') + this.monitor.fail() + } + }, + populateData: function () { + // add information retrieved about the scanned tag plate to the information panel for the user to see + this.infoPanel.find('dd.lot-number').text(this.qcable.lot_number) + this.infoPanel.find('dd.template').text(this.qcable.tag_layout) + this.infoPanel.find('dd.state').text(this.qcable.state) + this.infoPanel.find('.asset_uuid').val(this.qcable.asset_uuid) + this.infoPanel.find('.template_uuid').val(this.qcable.template_uuid) + }, + validPlate: function () { + // run through the `validators`, and collect any errors + this.errors = '' + for (let i = 0; i < this.validators.length; i += 1) { + let response = this.validators[i].validate(this) + if (!response.valid) { + this.errors += ' ' + response.message + } + } + return this.errors === '' + }, + message: function (message, status) { + this.infoPanel + .find('.qc_validation_report') + .empty() + .append( + $(document.createElement('div')) + .addClass('alert') + .addClass('alert-' + status) + .text(message) + ) + }, + dual: function () { + return this.template.dual_index + }, + errors: '', +} + +let qcCollector = new tagStatusCollector( + SCAPE.dualRequired, + function () { + $('#submit-summary').text('Marks the tag sources as used, and convert the tag plate.') + $('#plate_submit').prop('disabled', false) + }, + function (message) { + $('#submit-summary').text(message) + $('#plate_submit').prop('disabled', true) + } +) + +new qcableLookup($('#plate_tag_plate_barcode'), qcCollector) + +/* Disables form submit (eg. by enter) if the button is disabled. Seems safari doesn't do this by default */ +$('form#plate_new').on('submit', function () { + return !$('input#plate_submit')[0].disabled +}) + +$.extend(SCAPE, { + fetch_tags: function () { + let selected_layout = $('#plate_tag_plate_template_uuid').val() + if (SCAPE.tag_plates_list[selected_layout] === undefined) { + return $([]) + } else { + return $(SCAPE.tag_plates_list[selected_layout].tags) + } + }, + update_layout: function () { + let tags = this.fetch_tags() + + tags.each(function (_index) { + $('#tagging-plate #aliquot_' + this[0]) + .hide('fast') + .text(this[1][1]) + .addClass('aliquot colour-' + this[1][0]) + .addClass('tag-' + this[1][1]) + .show('fast') + }) + }, +}) +SCAPE.update_layout() diff --git a/app/frontend/javascript/tag_by_tag_plate.js b/app/frontend/javascript/tag_by_tag_plate.js deleted file mode 100644 index baafa878d..000000000 --- a/app/frontend/javascript/tag_by_tag_plate.js +++ /dev/null @@ -1,212 +0,0 @@ -// TODO: relocate and refactor the content of this file into tagged-plate.js or a Vue component -import jQuery from 'jquery' -import tagStatusCollector from '@/javascript/lib/tag_collector.js' -import validator from '@/javascript/lib/validator.js' -;(function ($, exports, undefined) { - 'use strict' - - // TAG CREATION - $(function () { - if ($('#tag-creation-page').length === 0) { - return - } - let qcableLookup - - //= require lib/ajax_support - - // Set up some null objects - let unknownTemplate = { unknown: true, dual_index: false } - let unknownQcable = { template_uuid: 'not-loaded' } - - qcableLookup = function (barcodeBox, collector) { - if (barcodeBox.length === 0) { - return false - } - - let qc_lookup = this - this.inputBox = barcodeBox - - // the `data` attribute is set when declaring the element in tagged_plate.html.erb - this.infoPanel = $('#' + barcodeBox.data('info-panel')) - this.requiresDualIndexing = barcodeBox.data('requires-dual-indexing') - this.approvedTypes = SCAPE[barcodeBox.data('approved-list')] - this.required = this.inputBox[0].required - - // add an onchange event to the box, to look up the plate - this.inputBox.on('change', function () { - qc_lookup.resetStatus() - qc_lookup.requestPlate(this.value) - }) - this.monitor = collector.register(!this.required, this) - - // set initial values for the tag plate data - this.qcable = unknownQcable - this.template = unknownTemplate - } - - qcableLookup.prototype = { - resetStatus: function () { - this.monitor.fail() - this.infoPanel.find('dd').text('') - this.infoPanel.find('input').val(null) - }, - requestPlate: function (_barcode) { - if (this.inputBox.val() === '' && !this.required) { - return this.monitor.pass() - } - // find the qcable (tag plate) based on the barcode scanned in by the user - $.ajax({ - type: 'POST', - dataType: 'json', - url: '/search/qcables', - data: 'qcable_barcode=' + this.inputBox.val(), - }).then(this.success(), this.error()) - }, - success: function () { - let qc_lookup = this - return function (response) { - if (response.error) { - qc_lookup.message(response.error, 'danger') - } else if (response.qcable) { - // if response is as expected, load some data - qc_lookup.plateFound(response.qcable) - } else { - qc_lookup.message('An unexpected response was received. Please contact support.', 'danger') - } - } - }, - error: function () { - let qc_lookup = this - return function () { - qc_lookup.message( - 'The barcode could not be found. There may be network issues, or problems with Sequencescape.', - 'danger' - ) - } - }, - validators: [ - // `t` is a qcableLookup object - // The data for t.template comes from app/models/labware_creators/tagging/tag_collection.rb - // t.template.dual_index is true if the scanned tag plate contains both i5 and i7 tags together in its wells (is a UDI plate) - // t.requiresDualIndexing is true if there are multiple source plates from the submission, which will be pooled... - // ... and therefore an i5 tag (tag 2) is needed (from a UDI plate) - new validator(function (t) { - return t.qcable.state == 'available' - }, 'The scanned item is not available.'), - new validator(function (t) { - return !t.template.unknown - }, 'It is an unrecognised template.'), - new validator(function (t) { - return t.template.approved - }, 'It is not approved for use with this pipeline.'), - new validator(function (t) { - return !(t.requiresDualIndexing && t.template.used && t.template.dual_index) - }, 'This template has already been used.'), - new validator(function (t) { - return !(t.requiresDualIndexing && !t.template.dual_index) - }, 'Pool is spread across multiple plates. UDI plates must be used.'), - new validator(function (t) { - return SCAPE.enforceSameTemplateWithinPool ? t.template.matches_templates_in_pool : true - }, "It doesn't match those already used for other plates in this submission pool."), - ], - // - // The major function that runs when a tag plate is scanned into the box - // Loads tag plate data into `qcable` and `template` - // Adds visible information to the information panel - // Validates whether the tag plate is suitable - // Updates the plate diagram with tag numbers - // - plateFound: function (qcable) { - this.qcable = qcable - this.template = this.approvedTypes[qcable.template_uuid] || unknownTemplate - this.populateData() - - if (this.validPlate()) { - this.message('The ' + qcable.qcable_type + ' is suitable.', 'success') - SCAPE.update_layout() - this.monitor.pass() - } else { - this.message(' The ' + qcable.qcable_type + ' is not suitable.' + this.errors, 'danger') - this.monitor.fail() - } - }, - populateData: function () { - // add information retrieved about the scanned tag plate to the information panel for the user to see - this.infoPanel.find('dd.lot-number').text(this.qcable.lot_number) - this.infoPanel.find('dd.template').text(this.qcable.tag_layout) - this.infoPanel.find('dd.state').text(this.qcable.state) - this.infoPanel.find('.asset_uuid').val(this.qcable.asset_uuid) - this.infoPanel.find('.template_uuid').val(this.qcable.template_uuid) - }, - validPlate: function () { - // run through the `validators`, and collect any errors - this.errors = '' - for (let i = 0; i < this.validators.length; i += 1) { - let response = this.validators[i].validate(this) - if (!response.valid) { - this.errors += ' ' + response.message - } - } - return this.errors === '' - }, - message: function (message, status) { - this.infoPanel - .find('.qc_validation_report') - .empty() - .append( - $(document.createElement('div')) - .addClass('alert') - .addClass('alert-' + status) - .text(message) - ) - }, - dual: function () { - return this.template.dual_index - }, - errors: '', - } - - let qcCollector = new tagStatusCollector( - SCAPE.dualRequired, - function () { - $('#submit-summary').text('Marks the tag sources as used, and convert the tag plate.') - $('#plate_submit').prop('disabled', false) - }, - function (message) { - $('#submit-summary').text(message) - $('#plate_submit').prop('disabled', true) - } - ) - - new qcableLookup($('#plate_tag_plate_barcode'), qcCollector) - - /* Disables form submit (eg. by enter) if the button is disabled. Seems safari doesn't do this by default */ - $('form#plate_new').on('submit', function () { - return !$('input#plate_submit')[0].disabled - }) - - $.extend(SCAPE, { - fetch_tags: function () { - let selected_layout = $('#plate_tag_plate_template_uuid').val() - if (SCAPE.tag_plates_list[selected_layout] === undefined) { - return $([]) - } else { - return $(SCAPE.tag_plates_list[selected_layout].tags) - } - }, - update_layout: function () { - let tags = this.fetch_tags() - - tags.each(function (_index) { - $('#tagging-plate #aliquot_' + this[0]) - .hide('fast') - .text(this[1][1]) - .addClass('aliquot colour-' + this[1][0]) - .addClass('tag-' + this[1][1]) - .show('fast') - }) - }, - }) - SCAPE.update_layout() - }) -})(jQuery, window) From fb3e6b5cb42ed31cbc8c4733e5e9f456ad50fe49 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 21 Aug 2024 17:07:31 +0100 Subject: [PATCH 07/26] refactor: remove IIFE from state-machine module --- app/frontend/javascript/state_machine.js | 54 ++++++++++++------------ 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/app/frontend/javascript/state_machine.js b/app/frontend/javascript/state_machine.js index 28c752d86..c653f8759 100644 --- a/app/frontend/javascript/state_machine.js +++ b/app/frontend/javascript/state_machine.js @@ -1,39 +1,37 @@ -import jQuery from 'jquery' -;(function ($, exports, undefined) { - 'use strict' +'use strict' - //= require lib/ajax_support +import $ from 'jquery' - let Events = { - on: function () { - if (!this.o) this.o = $({}) +//= require lib/ajax_support - this.o.on.apply(this.o, arguments) - }, +let Events = { + on: function () { + if (!this.o) this.o = $({}) - trigger: function () { - if (!this.o) this.o = $({}) + this.o.on.apply(this.o, arguments) + }, - this.o.trigger.apply(this.o, arguments) - }, - } + trigger: function () { + if (!this.o) this.o = $({}) - let StateMachine = function () {} + this.o.trigger.apply(this.o, arguments) + }, +} - StateMachine.fn = StateMachine.prototype +let StateMachine = function () {} - $.extend(StateMachine.fn, Events) +StateMachine.fn = StateMachine.prototype - StateMachine.fn.add = function (controller) { - this.on('change', function (e, current) { - if (controller == current) controller.activate() - else controller.deactivate() - }) +$.extend(StateMachine.fn, Events) - controller.active = $.proxy(function () { - this.trigger('change', controller) - }, this) - } +StateMachine.fn.add = function (controller) { + this.on('change', function (e, current) { + if (controller == current) controller.activate() + else controller.deactivate() + }) - exports.StateMachine = StateMachine -})(jQuery, window) + controller.active = $.proxy(function () { + this.trigger('change', controller) + }, this) +} +export { StateMachine } From f3ec8e62f6e3ab341d130d8dc798b4759fd0e1fc Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 21 Aug 2024 17:08:57 +0100 Subject: [PATCH 08/26] refactor: remove IIFE from state-change-reasons module --- .../javascript/state_change_reasons.js | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/app/frontend/javascript/state_change_reasons.js b/app/frontend/javascript/state_change_reasons.js index 5801916f2..e3baed506 100644 --- a/app/frontend/javascript/state_change_reasons.js +++ b/app/frontend/javascript/state_change_reasons.js @@ -1,19 +1,16 @@ -import jQuery from 'jquery' -;(function ($, _exports, undefined) { - 'use strict' +import $ from 'jquery' - let updateDisplay = function (new_state, speed) { - $('.reason') - .not('#' + new_state + '_reasons') - .slideUp(speed) - $('#' + new_state + '_reasons').slideDown(speed) - } +let updateDisplay = function (new_state, speed) { + $('.reason') + .not('#' + new_state + '_reasons') + .slideUp(speed) + $('#' + new_state + '_reasons').slideDown(speed) +} - let displayReason = function () { - updateDisplay(this.value, 'fast') - } +let displayReason = function () { + updateDisplay(this.value, 'fast') +} - $(function () { - $('#state-changer').find('input[type=radio]').on('change', displayReason) - }) -})(jQuery, window) +$(function () { + $('#state-changer').find('input[type=radio]').on('change', displayReason) +}) From 8d5f4720944526856a4b29be006a8f610d00c9eb Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 09:52:46 +0100 Subject: [PATCH 09/26] refactor: remove IIFE from session-scripts module --- app/frontend/javascript/session_scripts.js | 111 ++++++++++----------- 1 file changed, 54 insertions(+), 57 deletions(-) diff --git a/app/frontend/javascript/session_scripts.js b/app/frontend/javascript/session_scripts.js index 4f3ca8d82..56f48e440 100644 --- a/app/frontend/javascript/session_scripts.js +++ b/app/frontend/javascript/session_scripts.js @@ -1,73 +1,70 @@ -import jQuery from 'jquery' +import $ from 'jquery' // Global SCAPE.message method -;(function ($, window, undefined) { - 'use strict' - let loggedIn, userName, wasLoggedIn, logIn, logOut, updateUserName, warning, alert, success +let loggedIn, userName, wasLoggedIn, logIn, logOut, updateUserName, warning, alert, success - loggedIn = function () { - return document.cookie.match(/; user_name=([^;]+)/) !== null - } +loggedIn = function () { + return document.cookie.match(/; user_name=([^;]+)/) !== null +} - userName = function () { - return document.cookie.match(/; user_name=([^;]+)/)[1] - } +userName = function () { + return document.cookie.match(/; user_name=([^;]+)/)[1] +} - updateUserName = function (user_name) { - $('.user_name_placeholder').text(user_name) - } +updateUserName = function (user_name) { + $('.user_name_placeholder').text(user_name) +} - logIn = function () { - let user_name = userName() - if (wasLoggedIn) { - if (user_name !== wasLoggedIn) { - updateUserName(user_name) - warning( - 'The logged in user has changed since this page was last viewed. You are now logged in as ' + user_name + '.' - ) - } - } else { +logIn = function () { + let user_name = userName() + if (wasLoggedIn) { + if (user_name !== wasLoggedIn) { updateUserName(user_name) - $('body').addClass('logged_in').removeClass('logged_out') - success('You were logged in as ' + user_name + ' in another tab.') + warning( + 'The logged in user has changed since this page was last viewed. You are now logged in as ' + user_name + '.' + ) } - wasLoggedIn = user_name + } else { + updateUserName(user_name) + $('body').addClass('logged_in').removeClass('logged_out') + success('You were logged in as ' + user_name + ' in another tab.') } + wasLoggedIn = user_name +} - logOut = function () { - let user_name = 'Guest' - if (wasLoggedIn) { - updateUserName(user_name) - $('body').addClass('logged_out').removeClass('logged_in') - wasLoggedIn = false - warning('An action in another tab or browser window has logged you out!') - } else { - // Nothing changed - wasLoggedIn = false - } +logOut = function () { + let user_name = 'Guest' + if (wasLoggedIn) { + updateUserName(user_name) + $('body').addClass('logged_out').removeClass('logged_in') + wasLoggedIn = false + warning('An action in another tab or browser window has logged you out!') + } else { + // Nothing changed + wasLoggedIn = false } +} - success = function (message) { - alert(message, 'success', 'Notice!') - } +success = function (message) { + alert(message, 'success', 'Notice!') +} - warning = function (message) { - alert(message, 'danger', 'Warning!') - } +warning = function (message) { + alert(message, 'danger', 'Warning!') +} - alert = function (message, category, title_text) { - let newDiv = document.createElement('div') - let title = document.createElement('strong') - title.appendChild(document.createTextNode(title_text + ' ')) - newDiv.appendChild(title) - newDiv.appendChild(document.createTextNode(message)) - newDiv.setAttribute('class', 'alert alert-' + category) - document.getElementById('flashes').appendChild(newDiv) - } +alert = function (message, category, title_text) { + let newDiv = document.createElement('div') + let title = document.createElement('strong') + title.appendChild(document.createTextNode(title_text + ' ')) + newDiv.appendChild(title) + newDiv.appendChild(document.createTextNode(message)) + newDiv.setAttribute('class', 'alert alert-' + category) + document.getElementById('flashes').appendChild(newDiv) +} - wasLoggedIn = loggedIn() && userName() +wasLoggedIn = loggedIn() && userName() - $(window).on('focus', function () { - loggedIn() ? logIn() : logOut() - }) -})(jQuery, window) +$(window).on('focus', function () { + loggedIn() ? logIn() : logOut() +}) From 53013252c7173dadfce05cebf86fddf231dc736a Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 10:53:51 +0100 Subject: [PATCH 10/26] refactor: remove IIFE from pooled-tubes-from-whole-plates --- .../pooled_tubes_from_whole_plates.js | 357 +++++++++--------- .../pooled_tubes_from_whole_plates.html.erb | 1 + .../pooled_tubes_from_whole_tubes.html.erb | 1 + 3 files changed, 176 insertions(+), 183 deletions(-) diff --git a/app/frontend/javascript/pooled_tubes_from_whole_plates.js b/app/frontend/javascript/pooled_tubes_from_whole_plates.js index ce9810131..82c1a834c 100644 --- a/app/frontend/javascript/pooled_tubes_from_whole_plates.js +++ b/app/frontend/javascript/pooled_tubes_from_whole_plates.js @@ -1,194 +1,185 @@ -import jQuery from 'jquery' -;(function ($, _exports, undefined) { - 'use strict' - - let SOURCE_STATES = ['passed', 'qc_complete'] - - $(function (_event) { - if ($('#pooled-tubes-from-whole-plates').length === 0) { - return - } - - //= require lib/array_fill_polyfill - - let Pooler = function (labware_number, button) { - this.tags = Array(labware_number).fill([]) - this.labware_details = Array(labware_number).fill({}) - this.clashing_tags = null - this.button = button - } - - // Inefficient and limited Set polyfill for phantomjs and IE10 - if (Set === undefined) { - let Set = function () { - this.array = [] - } - - Set.prototype = { - // Agh! Can't even use includes in older browsers - has: function (element) { - return this.array.indexOf(element) !== -1 - }, - add: function (element) { - this.array.push(element) - }, +import $ from 'jquery' + +const SOURCE_STATES = ['passed', 'qc_complete'] + +//= require lib/array_fill_polyfill + +let Pooler = function (labware_number, button) { + this.tags = Array(labware_number).fill([]) + this.labware_details = Array(labware_number).fill({}) + this.clashing_tags = null + this.button = button +} + +// Inefficient and limited Set polyfill for phantomjs and IE10 +if (Set === undefined) { + let Set = function () { + this.array = [] + } + + Set.prototype = { + // Agh! Can't even use includes in older browsers + has: function (element) { + return this.array.indexOf(element) !== -1 + }, + add: function (element) { + this.array.push(element) + }, + } +} + +Pooler.prototype = { + retrieveLabware: function (labware) { + this.disable() + labware.ajax = $.ajax({ + dataType: 'json', + url: '/search/', + type: 'POST', + data: 'plate_barcode=' + labware.value, + success: function (data, status) { + labware.checkLabware(data, status, labware.value) + }, + }).fail(function (data, status) { + if (status !== 'abort') { + SCAPE.message('Some problems: ' + status, 'invalid') + labware.badLabware() } + }) + }, + checkLabwares: function () { + if ($('.wait-labware, .bad-labware').length === 0) { + this.enable() + } else { + this.disable() } - - Pooler.prototype = { - retrieveLabware: function (labware) { - this.disable() - labware.ajax = $.ajax({ - dataType: 'json', - url: '/search/', - type: 'POST', - data: 'plate_barcode=' + labware.value, - success: function (data, status) { - labware.checkLabware(data, status, labware.value) - }, - }).fail(function (data, status) { - if (status !== 'abort') { - SCAPE.message('Some problems: ' + status, 'invalid') - labware.badLabware() - } - }) - }, - checkLabwares: function () { - if ($('.wait-labware, .bad-labware').length === 0) { - this.enable() + }, + record: function (labware, position, scanned_barcode) { + this.tags[position] = labware.tags + this.labware_details[position] = { + human_barcode: labware.barcode, + machine_barcode: scanned_barcode, + tags: labware.tags.map((tag) => String(tag)), + } + return this.noDupes() + }, + clearTags: function (position) { + this.tags[position] = [] + this.labware_details[position] = {} + this.clashing_tags = null + }, + disable: function () { + this.button.attr('disabled', 'disabled') + }, + enable: function () { + this.button.attr('disabled', null) + }, + noDupes: function () { + let set = new Set() + let poolerObj = this + return this.tags.every(function (tag_set) { + return tag_set.every(function (tag) { + let tagsAsString = String(tag) + if (set.has(tagsAsString)) { + poolerObj.clashing_tags = tagsAsString + return false } else { - this.disable() + set.add(tagsAsString) + return true } - }, - record: function (labware, position, scanned_barcode) { - this.tags[position] = labware.tags - this.labware_details[position] = { - human_barcode: labware.barcode, - machine_barcode: scanned_barcode, - tags: labware.tags.map((tag) => String(tag)), - } - return this.noDupes() - }, - clearTags: function (position) { - this.tags[position] = [] - this.labware_details[position] = {} - this.clashing_tags = null - }, - disable: function () { - this.button.attr('disabled', 'disabled') - }, - enable: function () { - this.button.attr('disabled', null) - }, - noDupes: function () { - let set = new Set() - let poolerObj = this - return this.tags.every(function (tag_set) { - return tag_set.every(function (tag) { - let tagsAsString = String(tag) - if (set.has(tagsAsString)) { - poolerObj.clashing_tags = tagsAsString - return false - } else { - set.add(tagsAsString) - return true - } - }) - }) - }, - } - - let pooler = new Pooler($('.labware-box').length, $('#create-labware')) - - $('.labware-box').on('change', function () { - // When we scan in a labware - if (this.value === '') { - this.scanLabware() - } else { - this.waitLabware() - pooler.retrieveLabware(this) - } + }) }) + }, +} + +let pooler = new Pooler($('.labware-box').length, $('#create-labware')) + +$('.labware-box').on('change', function () { + // When we scan in a labware + if (this.value === '') { + this.scanLabware() + } else { + this.waitLabware() + pooler.retrieveLabware(this) + } +}) + +$('.labware-box').each(function () { + $.extend(this, { + waitLabware: function () { + this.clearLabware() + this.labwareContainer().removeClass('good-labware bad-labware scan-labware') + this.labwareContainer().addClass('wait-labware') + $('#summary_tab').addClass('ui-disabled') + }, + scanLabware: function () { + this.clearLabware() + this.labwareContainer().removeClass('good-labware wait-labware bad-labware') + this.labwareContainer().addClass('scan-labware') + pooler.checkLabwares() + }, + badLabware: function () { + this.clearLabware() + this.labwareContainer().removeClass('good-labware wait-labware scan-labware') + this.labwareContainer().addClass('bad-labware') + $('#summary_tab').addClass('ui-disabled') + }, + goodLabware: function () { + this.labwareContainer().removeClass('bad-labware wait-labware scan-labware') + this.labwareContainer().addClass('good-labware') + pooler.checkLabwares() + }, + labwareContainer: function () { + return $(this).closest('.labware-container') + }, + checkLabware: function (data, status, scanned_barcode) { + let response = data[this.dataset.labwareType] + if (SOURCE_STATES.indexOf(response.state) === -1) { + this.badLabware() + SCAPE.message('Scanned ' + this.dataset.labwareType + 's are unsuitable', 'invalid') + } else { + let position = $(this).data('position') + if (pooler.record(response, position, scanned_barcode)) { + this.goodLabware() + } else { + let clashing_labware_barcodes = this.findClashingLabwares() - $('.labware-box').each(function () { - $.extend(this, { - waitLabware: function () { - this.clearLabware() - this.labwareContainer().removeClass('good-labware bad-labware scan-labware') - this.labwareContainer().addClass('wait-labware') - $('#summary_tab').addClass('ui-disabled') - }, - scanLabware: function () { - this.clearLabware() - this.labwareContainer().removeClass('good-labware wait-labware bad-labware') - this.labwareContainer().addClass('scan-labware') - pooler.checkLabwares() - }, - badLabware: function () { - this.clearLabware() - this.labwareContainer().removeClass('good-labware wait-labware scan-labware') - this.labwareContainer().addClass('bad-labware') - $('#summary_tab').addClass('ui-disabled') - }, - goodLabware: function () { - this.labwareContainer().removeClass('bad-labware wait-labware scan-labware') - this.labwareContainer().addClass('good-labware') - pooler.checkLabwares() - }, - labwareContainer: function () { - return $(this).closest('.labware-container') - }, - checkLabware: function (data, status, scanned_barcode) { - let response = data[this.dataset.labwareType] - if (SOURCE_STATES.indexOf(response.state) === -1) { - this.badLabware() - SCAPE.message('Scanned ' + this.dataset.labwareType + 's are unsuitable', 'invalid') - } else { - let position = $(this).data('position') - if (pooler.record(response, position, scanned_barcode)) { - this.goodLabware() - } else { - let clashing_labware_barcodes = this.findClashingLabwares() - - this.badLabware() - - let msg = - 'The scanned ' + - this.dataset.labwareType + - ' contains tags that would clash with those in other ' + - this.dataset.labwareType + - 's in the pool. Tag clashes found between: ' + - clashing_labware_barcodes - - SCAPE.message(msg, 'invalid') - } - } - }, - findClashingLabwares: function () { - let clashing_labwares = [] + this.badLabware() - Object.keys(pooler.labware_details).forEach(function (key) { - let current_labware_details = pooler.labware_details[key] - // skip empty labware locations - if (current_labware_details.tags == undefined) { - return - } + let msg = + 'The scanned ' + + this.dataset.labwareType + + ' contains tags that would clash with those in other ' + + this.dataset.labwareType + + 's in the pool. Tag clashes found between: ' + + clashing_labware_barcodes - // check for clashing tags - if (current_labware_details.tags.includes(pooler.clashing_tags)) { - let human_barcode = String(current_labware_details.human_barcode) - let machine_barcode = String(current_labware_details.machine_barcode) - clashing_labwares.push('' + human_barcode + ' (' + machine_barcode + ')') - } - }) + SCAPE.message(msg, 'invalid') + } + } + }, + findClashingLabwares: function () { + let clashing_labwares = [] + + Object.keys(pooler.labware_details).forEach(function (key) { + let current_labware_details = pooler.labware_details[key] + // skip empty labware locations + if (current_labware_details.tags == undefined) { + return + } - return clashing_labwares.join(' and ') - }, - clearLabware: function () { - SCAPE.message('', null) - pooler.clearTags($(this).data('position')) - }, + // check for clashing tags + if (current_labware_details.tags.includes(pooler.clashing_tags)) { + let human_barcode = String(current_labware_details.human_barcode) + let machine_barcode = String(current_labware_details.machine_barcode) + clashing_labwares.push('' + human_barcode + ' (' + machine_barcode + ')') + } }) - }) + + return clashing_labwares.join(' and ') + }, + clearLabware: function () { + SCAPE.message('', null) + pooler.clearTags($(this).data('position')) + }, }) -})(jQuery, window) +}) diff --git a/app/views/tube_creation/pooled_tubes_from_whole_plates.html.erb b/app/views/tube_creation/pooled_tubes_from_whole_plates.html.erb index 7eab487ef..5d491a478 100644 --- a/app/views/tube_creation/pooled_tubes_from_whole_plates.html.erb +++ b/app/views/tube_creation/pooled_tubes_from_whole_plates.html.erb @@ -31,4 +31,5 @@
      <% end %> <% end %> + <%= vite_javascript_tag 'pooled_tubes_from_whole_plates.js' %> <% end %> diff --git a/app/views/tube_creation/pooled_tubes_from_whole_tubes.html.erb b/app/views/tube_creation/pooled_tubes_from_whole_tubes.html.erb index 3de50aae5..64c91a7a4 100644 --- a/app/views/tube_creation/pooled_tubes_from_whole_tubes.html.erb +++ b/app/views/tube_creation/pooled_tubes_from_whole_tubes.html.erb @@ -31,4 +31,5 @@
        <% end %> <% end %> + <%= vite_javascript_tag 'pooled_tubes_from_whole_plates.js' %> <% end %> From 7229fa8df9e4873eb8821a0b3aa4749defce622b Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 10:56:57 +0100 Subject: [PATCH 11/26] refactor: move pooled-tubes-from-whole-plates to entrypoints --- .../pooled_tubes.js} | 4 ++++ .../tube_creation/pooled_tubes_from_whole_plates.html.erb | 2 +- .../tube_creation/pooled_tubes_from_whole_tubes.html.erb | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) rename app/frontend/{javascript/pooled_tubes_from_whole_plates.js => entrypoints/pooled_tubes.js} (97%) diff --git a/app/frontend/javascript/pooled_tubes_from_whole_plates.js b/app/frontend/entrypoints/pooled_tubes.js similarity index 97% rename from app/frontend/javascript/pooled_tubes_from_whole_plates.js rename to app/frontend/entrypoints/pooled_tubes.js index 82c1a834c..c10f073b6 100644 --- a/app/frontend/javascript/pooled_tubes_from_whole_plates.js +++ b/app/frontend/entrypoints/pooled_tubes.js @@ -1,5 +1,9 @@ import $ from 'jquery' +/* global SCAPE */ +// SCAPE is defined in global_message_system.js and inherited from the global namespace +// It should be refactored into a more modular design + const SOURCE_STATES = ['passed', 'qc_complete'] //= require lib/array_fill_polyfill diff --git a/app/views/tube_creation/pooled_tubes_from_whole_plates.html.erb b/app/views/tube_creation/pooled_tubes_from_whole_plates.html.erb index 5d491a478..f3f4f5892 100644 --- a/app/views/tube_creation/pooled_tubes_from_whole_plates.html.erb +++ b/app/views/tube_creation/pooled_tubes_from_whole_plates.html.erb @@ -31,5 +31,5 @@
          <% end %> <% end %> - <%= vite_javascript_tag 'pooled_tubes_from_whole_plates.js' %> + <%= vite_javascript_tag 'pooled_tubes.js' %> <% end %> diff --git a/app/views/tube_creation/pooled_tubes_from_whole_tubes.html.erb b/app/views/tube_creation/pooled_tubes_from_whole_tubes.html.erb index 64c91a7a4..f50512d54 100644 --- a/app/views/tube_creation/pooled_tubes_from_whole_tubes.html.erb +++ b/app/views/tube_creation/pooled_tubes_from_whole_tubes.html.erb @@ -31,5 +31,5 @@
            <% end %> <% end %> - <%= vite_javascript_tag 'pooled_tubes_from_whole_plates.js' %> + <%= vite_javascript_tag 'pooled_tubes.js' %> <% end %> From 9f127b7b1e666d4b594bacd899dacb023d0f076b Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 10:59:13 +0100 Subject: [PATCH 12/26] refactor: remove IIFE from multi-tube-pooling --- app/frontend/javascript/multi_tube_pooling.js | 297 +++++++++--------- app/views/tube_creation/final_tube.html.erb | 1 + 2 files changed, 145 insertions(+), 153 deletions(-) diff --git a/app/frontend/javascript/multi_tube_pooling.js b/app/frontend/javascript/multi_tube_pooling.js index 79ee78582..1c55a96b5 100644 --- a/app/frontend/javascript/multi_tube_pooling.js +++ b/app/frontend/javascript/multi_tube_pooling.js @@ -1,170 +1,161 @@ -import jQuery from 'jquery' +import $ from 'jquery' import statusCollector from '@/javascript/lib/status_collector.js' import { ENTER_KEYCODE, TAB_KEYCODE } from '@/javascript/lib/keycodes.js' -;(function ($, _exports, undefined) { - 'use strict' - $(function (_event) { - if ($('#multi-tube-pooling-page').length === 0) { - return - } - - let newScanned, - tubeCollector, - siblingTube, - barcodeRegister = {} +let newScanned, + tubeCollector, + siblingTube, + barcodeRegister = {} - //= require lib/ajax_support - - siblingTube = function (list_element, collector) { - this.listElement = $(list_element) - this.monitor = collector.register() - barcodeRegister[list_element.dataset.barcode] = this - } +//= require lib/ajax_support - siblingTube.prototype = { - scanned: function () { - this.monitor.pass() - this.setState('good') - this.listElement.find('input').val('1') - this.setMessage('Scanned in and ready to go!') - }, - setState: function (state) { - for (let i = 0; i < this.states.length; i += 1) { - if (state === this.states[i]) { - this.listElement.addClass(this.states[i] + '-tube') - } else { - this.listElement.removeClass(this.states[i] + '-tube') - } - } - }, - setMessage: function (message) { - this.listElement.find('.tube_validation_report').text(message) - }, - states: ['good', 'wait'], - } +siblingTube = function (list_element, collector) { + this.listElement = $(list_element) + this.monitor = collector.register() + barcodeRegister[list_element.dataset.barcode] = this +} - newScanned = function (tube_barcode, collector) { - // Return immediately if the box is empty - let stripped - stripped = tube_barcode.replace(/\s*/g, '') - if (stripped === '') { - return - } else if (barcodeRegister[stripped]) { - barcodeRegister[stripped].scanned() +siblingTube.prototype = { + scanned: function () { + this.monitor.pass() + this.setState('good') + this.listElement.find('input').val('1') + this.setMessage('Scanned in and ready to go!') + }, + setState: function (state) { + for (let i = 0; i < this.states.length; i += 1) { + if (state === this.states[i]) { + this.listElement.addClass(this.states[i] + '-tube') } else { - this.tubeBarcode = stripped - this.monitor = collector.register() - $('#create-tube').prop('disabled', true) - this.addToList() - this.validate() + this.listElement.removeClass(this.states[i] + '-tube') } } + }, + setMessage: function (message) { + this.listElement.find('.tube_validation_report').text(message) + }, + states: ['good', 'wait'], +} + +newScanned = function (tube_barcode, collector) { + // Return immediately if the box is empty + let stripped + stripped = tube_barcode.replace(/\s*/g, '') + if (stripped === '') { + return + } else if (barcodeRegister[stripped]) { + barcodeRegister[stripped].scanned() + } else { + this.tubeBarcode = stripped + this.monitor = collector.register() + $('#create-tube').prop('disabled', true) + this.addToList() + this.validate() + } +} - newScanned.prototype = { - addToList: function () { - $('#scanned_tube_list').append(this.newElement()) - }, - newElement: function () { - let scanned = this - this.listElement = $(document.createElement('li')) - .attr('id', 'listElement[' + this.tubeBarcode + ']') - .attr('class', 'wait-tube') - .attr('data-icon', 'delete') - .data('bed', this.tubeBarcode) - .on('click', function () { - scanned.removeEntry() - }) +newScanned.prototype = { + addToList: function () { + $('#scanned_tube_list').append(this.newElement()) + }, + newElement: function () { + let scanned = this + this.listElement = $(document.createElement('li')) + .attr('id', 'listElement[' + this.tubeBarcode + ']') + .attr('class', 'wait-tube') + .attr('data-icon', 'delete') + .data('bed', this.tubeBarcode) + .on('click', function () { + scanned.removeEntry() + }) + .append( + $(document.createElement('a')) + .attr('href', '#') + .append( + $(document.createElement('h3')) + .attr('class', 'ui-li-heading') + .text('Tube: ' + this.tubeBarcode) + ) + .append($(document.createElement('div')).attr('class', 'tube_validation_report').text('Waiting...')) .append( - $(document.createElement('a')) - .attr('href', '#') - .append( - $(document.createElement('h3')) - .attr('class', 'ui-li-heading') - .text('Tube: ' + this.tubeBarcode) - ) - .append($(document.createElement('div')).attr('class', 'tube_validation_report').text('Waiting...')) - .append( - $(document.createElement('input')) - .attr('type', 'hidden') - .attr('id', 'tube[parents][' + this.tubeBarcode + ']') - .attr('name', 'tube[parents][' + this.tubeBarcode + ']') - .val(1) - ) + $(document.createElement('input')) + .attr('type', 'hidden') + .attr('id', 'tube[parents][' + this.tubeBarcode + ']') + .attr('name', 'tube[parents][' + this.tubeBarcode + ']') + .val(1) ) - return this.listElement - }, - validate: function () { - if (!this.barcodeRex.test(this.tubeBarcode)) { - return this.barcodeError() - } - return this.unrecognized() - }, - unrecognized: function () { - this.setState('bad') - this.monitor.fail() - this.setMessage('This tube is not part of this pool!') - return true - }, - barcodeError: function () { - this.setError("This barcode doesn't look quite right. Barcodes should be 13 digits long.") - return false - }, - setError: function (message) { - this.setState('bad') - this.monitor.fail() - this.setMessage(message) - }, - setMessage: function (message) { - this.listElement.find('.tube_validation_report').text(message) - }, - setState: function (state) { - for (let i = 0; i < this.states.length; i += 1) { - if (state === this.states[i]) { - this.listElement.addClass(this.states[i] + '-tube') - } else { - this.listElement.removeClass(this.states[i] + '-tube') - } - } - }, - removeEntry: function () { - this.listElement.detach() - // This may look odd, but when we remove the tube we effectively no longer need to - // worry about it, so can pass it. - this.monitor.pass() - }, - states: ['good', 'wait', 'bad'], - barcodeRex: /^[0-9]{13}$/, // Matches stings of 13 numbers only. + ) + return this.listElement + }, + validate: function () { + if (!this.barcodeRex.test(this.tubeBarcode)) { + return this.barcodeError() } - - tubeCollector = new statusCollector( - function () { - if ($('#scanned_tube_list').children().length > 0) { - $('#tube_submit').prop('disabled', false) - } else { - $('#tube_submit').prop('disabled', true) - } - }, - function () { - $('#tube_submit').prop('disabled', true) + return this.unrecognized() + }, + unrecognized: function () { + this.setState('bad') + this.monitor.fail() + this.setMessage('This tube is not part of this pool!') + return true + }, + barcodeError: function () { + this.setError("This barcode doesn't look quite right. Barcodes should be 13 digits long.") + return false + }, + setError: function (message) { + this.setState('bad') + this.monitor.fail() + this.setMessage(message) + }, + setMessage: function (message) { + this.listElement.find('.tube_validation_report').text(message) + }, + setState: function (state) { + for (let i = 0; i < this.states.length; i += 1) { + if (state === this.states[i]) { + this.listElement.addClass(this.states[i] + '-tube') + } else { + this.listElement.removeClass(this.states[i] + '-tube') } - ) - - $('.sibling-tube').each(function () { - new siblingTube(this, tubeCollector) - }) + } + }, + removeEntry: function () { + this.listElement.detach() + // This may look odd, but when we remove the tube we effectively no longer need to + // worry about it, so can pass it. + this.monitor.pass() + }, + states: ['good', 'wait', 'bad'], + barcodeRex: /^[0-9]{13}$/, // Matches stings of 13 numbers only. +} +tubeCollector = new statusCollector( + function () { + if ($('#scanned_tube_list').children().length > 0) { + $('#tube_submit').prop('disabled', false) + } else { + $('#tube_submit').prop('disabled', true) + } + }, + function () { $('#tube_submit').prop('disabled', true) + } +) - $('#tube_scan').on('keydown', function (e) { - let code = e.charCode || e.keyCode - if (code === ENTER_KEYCODE || code === TAB_KEYCODE) { - e.preventDefault() - new newScanned(this.value, tubeCollector) - this.value = '' - $(this).focus() - return false - } - }) - }) -})(jQuery, window) +$('.sibling-tube').each(function () { + new siblingTube(this, tubeCollector) +}) + +$('#tube_submit').prop('disabled', true) + +$('#tube_scan').on('keydown', function (e) { + let code = e.charCode || e.keyCode + if (code === ENTER_KEYCODE || code === TAB_KEYCODE) { + e.preventDefault() + new newScanned(this.value, tubeCollector) + this.value = '' + $(this).focus() + return false + } +}) diff --git a/app/views/tube_creation/final_tube.html.erb b/app/views/tube_creation/final_tube.html.erb index 8ed339329..5f5e38eab 100644 --- a/app/views/tube_creation/final_tube.html.erb +++ b/app/views/tube_creation/final_tube.html.erb @@ -52,4 +52,5 @@ <% end %> <% end %> + <%= vite_javascript_tag 'multi_tube_pooling.js' %> <% end %> From 71fa0ce37199f0383c8ed7daa7572292e3c161fe Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 11:02:26 +0100 Subject: [PATCH 13/26] refactor: move multi-tube-pooling to entrypoints --- app/frontend/{javascript => entrypoints}/multi_tube_pooling.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/frontend/{javascript => entrypoints}/multi_tube_pooling.js (100%) diff --git a/app/frontend/javascript/multi_tube_pooling.js b/app/frontend/entrypoints/multi_tube_pooling.js similarity index 100% rename from app/frontend/javascript/multi_tube_pooling.js rename to app/frontend/entrypoints/multi_tube_pooling.js From 5ab596358b19e23c4853ad4fe529b0a5a34ad4a2 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 11:04:50 +0100 Subject: [PATCH 14/26] refactor: remove IIFE from multi-palte-pooling --- .../javascript/multi_plate_pooling.js | 626 +++++++++--------- .../plate_creation/multi_plate_pool.html.erb | 1 + 2 files changed, 309 insertions(+), 318 deletions(-) diff --git a/app/frontend/javascript/multi_plate_pooling.js b/app/frontend/javascript/multi_plate_pooling.js index dcb160d90..05bb5917a 100644 --- a/app/frontend/javascript/multi_plate_pooling.js +++ b/app/frontend/javascript/multi_plate_pooling.js @@ -1,354 +1,344 @@ -import jQuery from 'jquery' -;(function ($, exports, undefined) { - 'use strict' +import $ from 'jquery' - if (exports.SCAPE === undefined) { - exports.SCAPE = {} - } - - let SCAPE = exports.SCAPE +if (exports.SCAPE === undefined) { + exports.SCAPE = {} +} - const WELLS_IN_COLUMN_MAJOR_ORDER = [ - 'A1', - 'B1', - 'C1', - 'D1', - 'E1', - 'F1', - 'G1', - 'H1', - 'A2', - 'B2', - 'C2', - 'D2', - 'E2', - 'F2', - 'G2', - 'H2', - 'A3', - 'B3', - 'C3', - 'D3', - 'E3', - 'F3', - 'G3', - 'H3', - 'A4', - 'B4', - 'C4', - 'D4', - 'E4', - 'F4', - 'G4', - 'H4', - 'A5', - 'B5', - 'C5', - 'D5', - 'E5', - 'F5', - 'G5', - 'H5', - 'A6', - 'B6', - 'C6', - 'D6', - 'E6', - 'F6', - 'G6', - 'H6', - 'A7', - 'B7', - 'C7', - 'D7', - 'E7', - 'F7', - 'G7', - 'H7', - 'A8', - 'B8', - 'C8', - 'D8', - 'E8', - 'F8', - 'G8', - 'H8', - 'A9', - 'B9', - 'C9', - 'D9', - 'E9', - 'F9', - 'G9', - 'H9', - 'A10', - 'B10', - 'C10', - 'D10', - 'E10', - 'F10', - 'G10', - 'H10', - 'A11', - 'B11', - 'C11', - 'D11', - 'E11', - 'F11', - 'G11', - 'H11', - 'A12', - 'B12', - 'C12', - 'D12', - 'E12', - 'F12', - 'G12', - 'H12', - ] +let SCAPE = exports.SCAPE - const SOURCE_STATES = ['passed', 'qc_complete'] +const WELLS_IN_COLUMN_MAJOR_ORDER = [ + 'A1', + 'B1', + 'C1', + 'D1', + 'E1', + 'F1', + 'G1', + 'H1', + 'A2', + 'B2', + 'C2', + 'D2', + 'E2', + 'F2', + 'G2', + 'H2', + 'A3', + 'B3', + 'C3', + 'D3', + 'E3', + 'F3', + 'G3', + 'H3', + 'A4', + 'B4', + 'C4', + 'D4', + 'E4', + 'F4', + 'G4', + 'H4', + 'A5', + 'B5', + 'C5', + 'D5', + 'E5', + 'F5', + 'G5', + 'H5', + 'A6', + 'B6', + 'C6', + 'D6', + 'E6', + 'F6', + 'G6', + 'H6', + 'A7', + 'B7', + 'C7', + 'D7', + 'E7', + 'F7', + 'G7', + 'H7', + 'A8', + 'B8', + 'C8', + 'D8', + 'E8', + 'F8', + 'G8', + 'H8', + 'A9', + 'B9', + 'C9', + 'D9', + 'E9', + 'F9', + 'G9', + 'H9', + 'A10', + 'B10', + 'C10', + 'D10', + 'E10', + 'F10', + 'G10', + 'H10', + 'A11', + 'B11', + 'C11', + 'D11', + 'E11', + 'F11', + 'G11', + 'H11', + 'A12', + 'B12', + 'C12', + 'D12', + 'E12', + 'F12', + 'G12', + 'H12', +] - $(function (_event) { - // If we are not on the multi-plate pooling page, don't set anything up. - if ($('#multi-plate-pooling-page').length === 0) { - return - } +const SOURCE_STATES = ['passed', 'qc_complete'] - $.extend(SCAPE, { - // Make an AJAX call to limber to find the plate. - // Call made to the searches controller, when then redirects the the plate - // show page. Which renders a json template containing the necessary information - retrievePlate: function (plate) { - plate.ajax = $.ajax({ - dataType: 'json', - url: '/search/', - type: 'POST', - data: 'plate_barcode=' + plate.value, - success: function (data, status) { - plate.checkPlate(data, status) - }, - }).fail(function (data, status) { - if (status !== 'abort') { - plate.badPlate() - } - }) - }, - // Enable the create labware button if all plates are valid - // disables it otherwise - // Called when any plate updates its state - checkPlates: function () { - if ($('.wait-labware, .bad-labware').length === 0) { - $('#create-labware').attr('disabled', null) - } else { - $('#create-labware').attr('disabled', 'disabled') - } +$.extend(SCAPE, { + // Make an AJAX call to limber to find the plate. + // Call made to the searches controller, when then redirects the the plate + // show page. Which renders a json template containing the necessary information + retrievePlate: function (plate) { + plate.ajax = $.ajax({ + dataType: 'json', + url: '/search/', + type: 'POST', + data: 'plate_barcode=' + plate.value, + success: function (data, status) { + plate.checkPlate(data, status) }, - plates: [], - }) - - $('.labware-box').on('change', function () { - // When we scan in a plate - if (this.value === '') { - this.scanPlate() - updateView() - } else { - this.waitPlate() - $('#create-labware').attr('disabled', 'disabled') - SCAPE.retrievePlate(this) + }).fail(function (data, status) { + if (status !== 'abort') { + plate.badPlate() } }) + }, + // Enable the create labware button if all plates are valid + // disables it otherwise + // Called when any plate updates its state + checkPlates: function () { + if ($('.wait-labware, .bad-labware').length === 0) { + $('#create-labware').attr('disabled', null) + } else { + $('#create-labware').attr('disabled', 'disabled') + } + }, + plates: [], +}) - $('.labware-box').each(function () { - $.extend(this, { - // Sets the wait state, indicating an AJAX request is in progress - waitPlate: function () { - this.clearPlate() - $(this).closest('.labware-container').removeClass('good-labware bad-labware scan-labware') - $(this).closest('.labware-container').addClass('wait-labware') - $('#summary_tab').addClass('ui-disabled') - }, - // Sets the 'waiting for content' state, which represents no content - scanPlate: function () { - this.clearPlate() - $(this).closest('.labware-container').removeClass('good-labware wait-labware bad-labware') - $(this).closest('.labware-container').addClass('scan-labware') - SCAPE.checkPlates() - }, - // Sets an invalid state, indicating the scanned barcode isn't suitable - badPlate: function () { - this.clearPlate() - $(this).closest('.labware-container').removeClass('good-labware wait-labware scan-labware') - $(this).closest('.labware-container').addClass('bad-labware') - $('#summary_tab').addClass('ui-disabled') - }, - // Sets a valid state, indicating the scanned barcode is good to process - goodPlate: function () { - $(this).closest('.labware-container').removeClass('bad-labware wait-labware scan-labware') - $(this).closest('.labware-container').addClass('good-labware') - SCAPE.checkPlates() - }, - // Passed the response of an ajax call and determines if the plate is suitable - // Currently just checks the plate state. - // Good plates are stored in the plate array. - // Regardless of outcome, the preview is updated via updateView - checkPlate: function (data, _status) { - if (SOURCE_STATES.indexOf(data.plate.state) === -1) { - this.badPlate() - } else { - SCAPE.plates[$(this).data('position')] = data.plate - this.goodPlate() - } - updateView() - }, - // Removes any previously scanned plate from the array - clearPlate: function () { - SCAPE.plates[$(this).data('position')] = undefined - }, - }) - }) +$('.labware-box').on('change', function () { + // When we scan in a plate + if (this.value === '') { + this.scanPlate() + updateView() + } else { + this.waitPlate() + $('#create-labware').attr('disabled', 'disabled') + SCAPE.retrievePlate(this) + } +}) - // Counts the total number of pools on the plate - SCAPE.totalPools = function () { - let poolCount = 0 - for (let plateIndex = 0; plateIndex < SCAPE.plates.length; plateIndex += 1) { - if (SCAPE.plates[plateIndex] !== undefined) { - let preCapPools = SCAPE.plates[plateIndex].preCapPools - poolCount += walkPreCapPools(preCapPools, function () {}) - } +$('.labware-box').each(function () { + $.extend(this, { + // Sets the wait state, indicating an AJAX request is in progress + waitPlate: function () { + this.clearPlate() + $(this).closest('.labware-container').removeClass('good-labware bad-labware scan-labware') + $(this).closest('.labware-container').addClass('wait-labware') + $('#summary_tab').addClass('ui-disabled') + }, + // Sets the 'waiting for content' state, which represents no content + scanPlate: function () { + this.clearPlate() + $(this).closest('.labware-container').removeClass('good-labware wait-labware bad-labware') + $(this).closest('.labware-container').addClass('scan-labware') + SCAPE.checkPlates() + }, + // Sets an invalid state, indicating the scanned barcode isn't suitable + badPlate: function () { + this.clearPlate() + $(this).closest('.labware-container').removeClass('good-labware wait-labware scan-labware') + $(this).closest('.labware-container').addClass('bad-labware') + $('#summary_tab').addClass('ui-disabled') + }, + // Sets a valid state, indicating the scanned barcode is good to process + goodPlate: function () { + $(this).closest('.labware-container').removeClass('bad-labware wait-labware scan-labware') + $(this).closest('.labware-container').addClass('good-labware') + SCAPE.checkPlates() + }, + // Passed the response of an ajax call and determines if the plate is suitable + // Currently just checks the plate state. + // Good plates are stored in the plate array. + // Regardless of outcome, the preview is updated via updateView + checkPlate: function (data, _status) { + if (SOURCE_STATES.indexOf(data.plate.state) === -1) { + this.badPlate() + } else { + SCAPE.plates[$(this).data('position')] = data.plate + this.goodPlate() } - return poolCount - } + updateView() + }, + // Removes any previously scanned plate from the array + clearPlate: function () { + SCAPE.plates[$(this).data('position')] = undefined + }, + }) +}) - SCAPE.calculatePreCapPools = function () { - return SCAPE.totalPools() <= 96 +// Counts the total number of pools on the plate +SCAPE.totalPools = function () { + let poolCount = 0 + for (let plateIndex = 0; plateIndex < SCAPE.plates.length; plateIndex += 1) { + if (SCAPE.plates[plateIndex] !== undefined) { + let preCapPools = SCAPE.plates[plateIndex].preCapPools + poolCount += walkPreCapPools(preCapPools, function () {}) } + } + return poolCount +} - SCAPE.newAliquot = function (poolNumber, aliquotText) { - let poolNumberInt = parseInt(poolNumber, 10) +SCAPE.calculatePreCapPools = function () { + return SCAPE.totalPools() <= 96 +} - return $(document.createElement('div')) - .addClass('aliquot colour-' + (poolNumberInt + 1)) - .text(aliquotText || '\u00A0') - .hide() - } +SCAPE.newAliquot = function (poolNumber, aliquotText) { + let poolNumberInt = parseInt(poolNumber, 10) - const walkPreCapPools = function (preCapPools, block) { - let poolNumber = -1 - for (let capPool of preCapPools) { - poolNumber++ - block(capPool.wells, poolNumber) - } - return poolNumber + 1 - } + return $(document.createElement('div')) + .addClass('aliquot colour-' + (poolNumberInt + 1)) + .text(aliquotText || '\u00A0') + .hide() +} - let renderPoolingSummary = function (plates) { - let capPoolOffset = 0 +const walkPreCapPools = function (preCapPools, block) { + let poolNumber = -1 + for (let capPool of preCapPools) { + poolNumber++ + block(capPool.wells, poolNumber) + } + return poolNumber + 1 +} - for (let plate of plates) { - if (plate === undefined) { - // Nothing - } else { - let preCapPools = plate.preCapPools - capPoolOffset += walkPreCapPools(preCapPools, function (wells, poolNumber) { - let destinationWell = WELLS_IN_COLUMN_MAJOR_ORDER[capPoolOffset + poolNumber] - let listElement = $('
          • ') - .text(destinationWell) - .append('
            ' + wells.length + '
            ') - .append('
            ' + plate.barcode + ': ' + wells.join(', ') + '
            ') - $('#pooling-summary').append(listElement) - }) - } - } +let renderPoolingSummary = function (plates) { + let capPoolOffset = 0 + + for (let plate of plates) { + if (plate === undefined) { + // Nothing + } else { + let preCapPools = plate.preCapPools + capPoolOffset += walkPreCapPools(preCapPools, function (wells, poolNumber) { + let destinationWell = WELLS_IN_COLUMN_MAJOR_ORDER[capPoolOffset + poolNumber] + let listElement = $('
          • ') + .text(destinationWell) + .append('
            ' + wells.length + '
            ') + .append('
            ' + plate.barcode + ': ' + wells.join(', ') + '
            ') + $('#pooling-summary').append(listElement) + }) } + } +} - SCAPE.renderDestinationPools = function () { - $('.destination-plate .well').empty() +SCAPE.renderDestinationPools = function () { + $('.destination-plate .well').empty() - let capPoolOffset = 0 - for (let plate of SCAPE.plates) { - if (plate !== undefined) { - let well - capPoolOffset += walkPreCapPools(plate.preCapPools, function (wells, poolNumber) { - well = $('.destination-plate .' + WELLS_IN_COLUMN_MAJOR_ORDER[capPoolOffset + poolNumber]) - if (wells.length) well.append(SCAPE.newAliquot(capPoolOffset + poolNumber, wells.length)) - }) - } - } + let capPoolOffset = 0 + for (let plate of SCAPE.plates) { + if (plate !== undefined) { + let well + capPoolOffset += walkPreCapPools(plate.preCapPools, function (wells, poolNumber) { + well = $('.destination-plate .' + WELLS_IN_COLUMN_MAJOR_ORDER[capPoolOffset + poolNumber]) + if (wells.length) well.append(SCAPE.newAliquot(capPoolOffset + poolNumber, wells.length)) + }) } + } +} - SCAPE.renderSourceWells = function () { - let capPoolOffset = 0 - for (let plateIndex = 0; plateIndex < SCAPE.plates.length; plateIndex += 1) { - if (SCAPE.plates[plateIndex] === undefined) { - $('.plate-id-' + plateIndex).hide() - } else { - let preCapPools = SCAPE.plates[plateIndex].preCapPools - let barcode = SCAPE.plates[plateIndex].barcode - $('.plate-id-' + plateIndex).show() - $('.plate-id-' + plateIndex + ' .well').empty() - $('.plate-id-' + plateIndex + ' caption').text(barcode) - $('#well-transfers-' + plateIndex).detach() +SCAPE.renderSourceWells = function () { + let capPoolOffset = 0 + for (let plateIndex = 0; plateIndex < SCAPE.plates.length; plateIndex += 1) { + if (SCAPE.plates[plateIndex] === undefined) { + $('.plate-id-' + plateIndex).hide() + } else { + let preCapPools = SCAPE.plates[plateIndex].preCapPools + let barcode = SCAPE.plates[plateIndex].barcode + $('.plate-id-' + plateIndex).show() + $('.plate-id-' + plateIndex + ' .well').empty() + $('.plate-id-' + plateIndex + ' caption').text(barcode) + $('#well-transfers-' + plateIndex).detach() - let newInputs = $(document.createElement('div')).attr('id', 'well-transfers-' + plateIndex) - capPoolOffset += walkPreCapPools(preCapPools, function (wells, poolNumber) { - let newInput, well + let newInputs = $(document.createElement('div')).attr('id', 'well-transfers-' + plateIndex) + capPoolOffset += walkPreCapPools(preCapPools, function (wells, poolNumber) { + let newInput, well - for (let wellName of wells) { - well = $('.plate-id-' + plateIndex + ' .' + wellName) - well.append( - SCAPE.newAliquot(capPoolOffset + poolNumber, WELLS_IN_COLUMN_MAJOR_ORDER[capPoolOffset + poolNumber]) - ) + for (let wellName of wells) { + well = $('.plate-id-' + plateIndex + ' .' + wellName) + well.append( + SCAPE.newAliquot(capPoolOffset + poolNumber, WELLS_IN_COLUMN_MAJOR_ORDER[capPoolOffset + poolNumber]) + ) - newInput = $(document.createElement('input')) - .attr('name', 'plate[transfers][' + SCAPE.plates[plateIndex].uuid + '][' + wellName + ']') - .attr('type', 'hidden') - .val(WELLS_IN_COLUMN_MAJOR_ORDER[capPoolOffset + poolNumber]) + newInput = $(document.createElement('input')) + .attr('name', 'plate[transfers][' + SCAPE.plates[plateIndex].uuid + '][' + wellName + ']') + .attr('type', 'hidden') + .val(WELLS_IN_COLUMN_MAJOR_ORDER[capPoolOffset + poolNumber]) - newInputs.append(newInput) - } - }) - $('#new_plate').append(newInputs) + newInputs.append(newInput) } - } + }) + $('#new_plate').append(newInputs) } + } +} - let plateSummaryHandler = function () { - SCAPE.renderSourceWells() - SCAPE.renderDestinationPools() - - $('.aliquot').fadeIn('slow') - - $('.well').each(function () { - // Handles wells which are part of multiple pre-capture pools - // Causes them to animate between the available states - if ($(this).children().length < 2) { - return - } +let plateSummaryHandler = function () { + SCAPE.renderSourceWells() + SCAPE.renderDestinationPools() - this.pos = 0 + $('.aliquot').fadeIn('slow') - this.slide = function () { - let scrollTo - this.pos = (this.pos + 1) % $(this).children().length - scrollTo = $(this).children()[this.pos].offsetTop - 5 - $(this).delay(1000).animate({ scrollTop: scrollTo }, 500, this.slide) - } - this.slide() - }) + $('.well').each(function () { + // Handles wells which are part of multiple pre-capture pools + // Causes them to animate between the available states + if ($(this).children().length < 2) { + return } - const updateView = function () { - if (SCAPE.calculatePreCapPools()) { - plateSummaryHandler() - $('#pooling-summary').empty() - renderPoolingSummary(SCAPE.plates) - SCAPE.message('Check pooling and create plate', 'valid') - } else { - // Pooling Went wrong - $('#pooling-summary').empty() - SCAPE.message('Too many pools for the target plate.', 'invalid') - } + this.pos = 0 + + this.slide = function () { + let scrollTo + this.pos = (this.pos + 1) % $(this).children().length + scrollTo = $(this).children()[this.pos].offsetTop - 5 + $(this).delay(1000).animate({ scrollTop: scrollTo }, 500, this.slide) } + this.slide() }) -})(jQuery, window) +} + +const updateView = function () { + if (SCAPE.calculatePreCapPools()) { + plateSummaryHandler() + $('#pooling-summary').empty() + renderPoolingSummary(SCAPE.plates) + SCAPE.message('Check pooling and create plate', 'valid') + } else { + // Pooling Went wrong + $('#pooling-summary').empty() + SCAPE.message('Too many pools for the target plate.', 'invalid') + } +} diff --git a/app/views/plate_creation/multi_plate_pool.html.erb b/app/views/plate_creation/multi_plate_pool.html.erb index 346218e6e..faa554cf0 100644 --- a/app/views/plate_creation/multi_plate_pool.html.erb +++ b/app/views/plate_creation/multi_plate_pool.html.erb @@ -44,4 +44,5 @@
              <% end %> <% end %> + <%= vite_javascript_tag 'multi_plate_pooling.js' %> <% end %> From 06352f904feb0a6e4bb5f15e1e77338ece3fb942 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 11:06:23 +0100 Subject: [PATCH 15/26] refactor: move multi-plate-pooling to entrypoints --- app/frontend/{javascript => entrypoints}/multi_plate_pooling.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/frontend/{javascript => entrypoints}/multi_plate_pooling.js (100%) diff --git a/app/frontend/javascript/multi_plate_pooling.js b/app/frontend/entrypoints/multi_plate_pooling.js similarity index 100% rename from app/frontend/javascript/multi_plate_pooling.js rename to app/frontend/entrypoints/multi_plate_pooling.js From 6bdae538cf09d5a6038a7ca4b90e0416f67048aa Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 11:58:23 +0100 Subject: [PATCH 16/26] refactor: remove IIFE from merged-plate --- app/frontend/javascript/merged_plate.js | 8 +------- app/views/plate_creation/merged_plate.html.erb | 1 + 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/frontend/javascript/merged_plate.js b/app/frontend/javascript/merged_plate.js index b2325afaf..e77a03cf3 100644 --- a/app/frontend/javascript/merged_plate.js +++ b/app/frontend/javascript/merged_plate.js @@ -1,9 +1,3 @@ -import jQuery from 'jquery' import { disableEnterKeySubmit } from '@/javascript/lib/disable_enter_key_submit.js' -;(function ($, _exports, undefined) { - 'use strict' - $(function (_event) { - disableEnterKeySubmit('#merged-plate-page', '#new_plate') - }) -})(jQuery, window) +disableEnterKeySubmit('#merged-plate-page', '#new_plate') diff --git a/app/views/plate_creation/merged_plate.html.erb b/app/views/plate_creation/merged_plate.html.erb index 2f4710263..c19a388df 100644 --- a/app/views/plate_creation/merged_plate.html.erb +++ b/app/views/plate_creation/merged_plate.html.erb @@ -49,4 +49,5 @@ <% end %> <% end %> + <%= vite_javascript_tag 'merged_plate.js' %> <% end %> From 828e980d49d92912bebcfc67cd6fb77fbfdf1542 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 11:59:14 +0100 Subject: [PATCH 17/26] refactor: move merged-plate to entrypoints --- app/frontend/{javascript => entrypoints}/merged_plate.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/frontend/{javascript => entrypoints}/merged_plate.js (100%) diff --git a/app/frontend/javascript/merged_plate.js b/app/frontend/entrypoints/merged_plate.js similarity index 100% rename from app/frontend/javascript/merged_plate.js rename to app/frontend/entrypoints/merged_plate.js From e09dedb3f7dad52473db481635f940d97fabf715 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 12:10:45 +0100 Subject: [PATCH 18/26] refactor: modularise script loading --- app/frontend/entrypoints/application.js | 6 ------ app/frontend/entrypoints/{ => pages}/merged_plate.js | 0 app/frontend/entrypoints/{ => pages}/multi_plate_pooling.js | 0 app/frontend/entrypoints/{ => pages}/multi_tube_pooling.js | 0 app/frontend/entrypoints/{ => pages}/pooled_tubes.js | 0 app/frontend/entrypoints/{ => pages}/tagged-plate.js | 0 app/views/layouts/application.html.erb | 4 ++-- app/views/pipelines/index.html.erb | 2 +- app/views/plate_creation/merged_plate.html.erb | 2 +- app/views/plate_creation/multi_plate_pool.html.erb | 2 +- app/views/plate_creation/tagged_plate.html.erb | 4 ++-- app/views/tube_creation/final_tube.html.erb | 2 +- .../tube_creation/pooled_tubes_from_whole_plates.html.erb | 2 +- .../tube_creation/pooled_tubes_from_whole_tubes.html.erb | 2 +- 14 files changed, 10 insertions(+), 16 deletions(-) rename app/frontend/entrypoints/{ => pages}/merged_plate.js (100%) rename app/frontend/entrypoints/{ => pages}/multi_plate_pooling.js (100%) rename app/frontend/entrypoints/{ => pages}/multi_tube_pooling.js (100%) rename app/frontend/entrypoints/{ => pages}/pooled_tubes.js (100%) rename app/frontend/entrypoints/{ => pages}/tagged-plate.js (100%) diff --git a/app/frontend/entrypoints/application.js b/app/frontend/entrypoints/application.js index 1bcf4d1b7..c01858354 100644 --- a/app/frontend/entrypoints/application.js +++ b/app/frontend/entrypoints/application.js @@ -38,20 +38,14 @@ import '@/javascript/bed_verification.js' import '@/javascript/choose_workflow.js' import '@/javascript/global_message_system.js' import '@/javascript/legacy_scripts_a.js' -import '@/javascript/merged_plate.js' -import '@/javascript/multi_plate_pooling.js' -import '@/javascript/multi_tube_pooling.js' -import '@/javascript/pooled_tubes_from_whole_plates.js' import '@/javascript/session_scripts.js' import '@/javascript/state_change_reasons.js' import '@/javascript/state_machine.js' -import '@/javascript/tag_by_tag_plate.js' import '@/javascript/tooltips.js' // Load all javascript files previously in the app/assets/lib directory, these really should // be loaded as required, not globally as previously done. import '@/javascript/lib/ajax_support.js' import '@/javascript/lib/array_fill_polyfill.js' -import '@/javascript/lib/disable_enter_key_submit.js' // Currently setting up each component as its own mini vue app. import '@/javascript/asset-comments/index.js' diff --git a/app/frontend/entrypoints/merged_plate.js b/app/frontend/entrypoints/pages/merged_plate.js similarity index 100% rename from app/frontend/entrypoints/merged_plate.js rename to app/frontend/entrypoints/pages/merged_plate.js diff --git a/app/frontend/entrypoints/multi_plate_pooling.js b/app/frontend/entrypoints/pages/multi_plate_pooling.js similarity index 100% rename from app/frontend/entrypoints/multi_plate_pooling.js rename to app/frontend/entrypoints/pages/multi_plate_pooling.js diff --git a/app/frontend/entrypoints/multi_tube_pooling.js b/app/frontend/entrypoints/pages/multi_tube_pooling.js similarity index 100% rename from app/frontend/entrypoints/multi_tube_pooling.js rename to app/frontend/entrypoints/pages/multi_tube_pooling.js diff --git a/app/frontend/entrypoints/pooled_tubes.js b/app/frontend/entrypoints/pages/pooled_tubes.js similarity index 100% rename from app/frontend/entrypoints/pooled_tubes.js rename to app/frontend/entrypoints/pages/pooled_tubes.js diff --git a/app/frontend/entrypoints/tagged-plate.js b/app/frontend/entrypoints/pages/tagged-plate.js similarity index 100% rename from app/frontend/entrypoints/tagged-plate.js rename to app/frontend/entrypoints/pages/tagged-plate.js diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index d0766b454..f73339e7f 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -11,11 +11,11 @@ <%= vite_stylesheet_tag 'print', media: 'print' %> <%= vite_client_tag %> - <%= vite_javascript_tag 'application' %> + <%= vite_javascript_tag 'application', defer: true %> <% if Rails.application.config.disable_animations %> <%= vite_stylesheet_tag 'disable-animations' %> - <%= vite_javascript_tag 'disable-animations' %> + <%= vite_javascript_tag 'disable-animations', defer: true %> <% end %> diff --git a/app/views/pipelines/index.html.erb b/app/views/pipelines/index.html.erb index 0d5ec8990..a37082b02 100644 --- a/app/views/pipelines/index.html.erb +++ b/app/views/pipelines/index.html.erb @@ -15,4 +15,4 @@ -<%= vite_javascript_tag 'pipeline-graph' %> +<%= vite_javascript_tag 'pipeline-graph', defer: true %> diff --git a/app/views/plate_creation/merged_plate.html.erb b/app/views/plate_creation/merged_plate.html.erb index c19a388df..1273b48b1 100644 --- a/app/views/plate_creation/merged_plate.html.erb +++ b/app/views/plate_creation/merged_plate.html.erb @@ -1,4 +1,5 @@ <%= page('merged-plate-page') do %> + <%= vite_javascript_tag 'entrypoints/pages/merged_plate.js', defer: true %> <%= content do %> <%= card id: 'main-content' do %>
              @@ -49,5 +50,4 @@
              <% end %> <% end %> - <%= vite_javascript_tag 'merged_plate.js' %> <% end %> diff --git a/app/views/plate_creation/multi_plate_pool.html.erb b/app/views/plate_creation/multi_plate_pool.html.erb index faa554cf0..f9170b3c7 100644 --- a/app/views/plate_creation/multi_plate_pool.html.erb +++ b/app/views/plate_creation/multi_plate_pool.html.erb @@ -1,4 +1,5 @@ <%= page('multi-plate-pooling-page') do %> + <%= vite_javascript_tag 'entrypoints/pages/multi_plate_pooling.js', defer: true %> <%= content do %> <%= card without_block: true, id: 'main-content' do %>
              @@ -44,5 +45,4 @@
                <% end %> <% end %> - <%= vite_javascript_tag 'multi_plate_pooling.js' %> <% end %> diff --git a/app/views/plate_creation/tagged_plate.html.erb b/app/views/plate_creation/tagged_plate.html.erb index 0697438e2..563b26151 100644 --- a/app/views/plate_creation/tagged_plate.html.erb +++ b/app/views/plate_creation/tagged_plate.html.erb @@ -1,12 +1,12 @@ <%= page(:'tag-creation-page', prevent_row: true) do -%> + <%= vite_javascript_tag 'entrypoints/pages/tagged-plate.js', defer: true %> +
                - <%= vite_javascript_tag 'tagged-plate' %> - <% form_for(@labware_creator, as: :plate, url: limber_plate_children_path(@labware_creator.parent), html: { class: 'row' }) do |f| %> <%= content do %> <%= card without_block: true, id: 'main-content' do %> diff --git a/app/views/tube_creation/final_tube.html.erb b/app/views/tube_creation/final_tube.html.erb index 5f5e38eab..d14680b87 100644 --- a/app/views/tube_creation/final_tube.html.erb +++ b/app/views/tube_creation/final_tube.html.erb @@ -1,4 +1,5 @@ <%= page(:'multi-tube-pooling-page', prevent_row: true) do %> + <%= vite_javascript_tag 'entrypoints/pages/multi_tube_pooling.js', defer: true %> <% form_for( @labware_creator, url: limber_tube_tubes_path(@labware_creator.parent), @@ -52,5 +53,4 @@ <% end %>
                <% end %> - <%= vite_javascript_tag 'multi_tube_pooling.js' %> <% end %> diff --git a/app/views/tube_creation/pooled_tubes_from_whole_plates.html.erb b/app/views/tube_creation/pooled_tubes_from_whole_plates.html.erb index f3f4f5892..48c4c8c5d 100644 --- a/app/views/tube_creation/pooled_tubes_from_whole_plates.html.erb +++ b/app/views/tube_creation/pooled_tubes_from_whole_plates.html.erb @@ -1,4 +1,5 @@ <%= page(:'pooled-tubes-from-whole-plates') do %> + <%= vite_javascript_tag 'entrypoints/pages/pooled_tubes.js', defer: true %> <%= content do %> <%= render(partial: 'search/inbox', locals: { search_results: @labware_creator.available_plates }) %> <% end %> @@ -31,5 +32,4 @@
                  <% end %> <% end %> - <%= vite_javascript_tag 'pooled_tubes.js' %> <% end %> diff --git a/app/views/tube_creation/pooled_tubes_from_whole_tubes.html.erb b/app/views/tube_creation/pooled_tubes_from_whole_tubes.html.erb index f50512d54..8453eef88 100644 --- a/app/views/tube_creation/pooled_tubes_from_whole_tubes.html.erb +++ b/app/views/tube_creation/pooled_tubes_from_whole_tubes.html.erb @@ -1,4 +1,5 @@ <%= page(:'pooled-tubes-from-whole-plates') do %> + <%= vite_javascript_tag 'entrypoints/pages/pooled_tubes.js', defer: true %> <%= content do %> <%= render(partial: 'search/inbox', locals: { search_results: @labware_creator.available_tubes }) %> <% end %> @@ -31,5 +32,4 @@
                    <% end %> <% end %> - <%= vite_javascript_tag 'pooled_tubes.js' %> <% end %> From 8a2055271e4b26c124841f50b630472dc8443c8e Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 15:31:39 +0100 Subject: [PATCH 19/26] refactor: remove IIFE from legacy-scripts-a --- app/frontend/javascript/legacy_scripts_a.js | 161 ++++++++++---------- 1 file changed, 79 insertions(+), 82 deletions(-) diff --git a/app/frontend/javascript/legacy_scripts_a.js b/app/frontend/javascript/legacy_scripts_a.js index 49be624c2..dfbbf44b2 100644 --- a/app/frontend/javascript/legacy_scripts_a.js +++ b/app/frontend/javascript/legacy_scripts_a.js @@ -1,102 +1,99 @@ -import jQuery from 'jquery' +import $ from 'jquery' import { ENTER_KEYCODE, TAB_KEYCODE } from '@/javascript/lib/keycodes.js' -;(function ($, _exports, undefined) { - 'use strict' - - let PlateViewModel = function (plateElement) { - this['pools-view'] = { - activate: function () { - $('#pools-information li').fadeIn('fast') - plateElement.addClass('pool-colours') - plateElement.find('.aliquot').removeClass('selected-aliquot dimmed') - }, - - deactivate: function () { - plateElement.removeClass('pool-colours') - plateElement.find('.aliquot').removeClass('selected-aliquot dimmed') - }, - } - - this['binned-view'] = { - activate: function () { - plateElement.addClass('binning-colours') - }, - deactivate: function () { - plateElement.removeClass('binning-colours') - }, - } +let PlateViewModel = function (plateElement) { + this['pools-view'] = { + activate: function () { + $('#pools-information li').fadeIn('fast') + plateElement.addClass('pool-colours') + plateElement.find('.aliquot').removeClass('selected-aliquot dimmed') + }, + + deactivate: function () { + plateElement.removeClass('pool-colours') + plateElement.find('.aliquot').removeClass('selected-aliquot dimmed') + }, } - let limberPlateView = function (defaultTab) { - let plateElement = $(this) + this['binned-view'] = { + activate: function () { + plateElement.addClass('binning-colours') + }, - let control = $('#plate-view-control') + deactivate: function () { + plateElement.removeClass('binning-colours') + }, + } +} - let viewModel = new PlateViewModel(plateElement) +let limberPlateView = function (defaultTab) { + let plateElement = $(this) - control.find('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { - let viewName = e.target.dataset.plateView - if (viewModel[viewName]) { - viewModel[viewName].activate() - } - }) + let control = $('#plate-view-control') - control.find('a[data-toggle="tab"]').on('hide.bs.tab', function (e) { - let viewName = e.target.dataset.plateView - if (viewModel[viewName]) { - viewModel[viewName].deactivate() - } - }) + let viewModel = new PlateViewModel(plateElement) - control.find('a[href="' + defaultTab + '"]').tab('show') + control.find('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { + let viewName = e.target.dataset.plateView + if (viewModel[viewName]) { + viewModel[viewName].activate() + } + }) - plateElement.on('click', '.aliquot', function (event) { - let pool = $(event.currentTarget).data('pool') + control.find('a[data-toggle="tab"]').on('hide.bs.tab', function (e) { + let viewName = e.target.dataset.plateView + if (viewModel[viewName]) { + viewModel[viewName].deactivate() + } + }) - control.find('a[data-plate-view="pools-view"]').tab('show') + control.find('a[href="' + defaultTab + '"]').tab('show') - plateElement - .find('.aliquot[data-pool!=' + pool + ']') - .removeClass('selected-aliquot') - .addClass('dimmed') + plateElement.on('click', '.aliquot', function (event) { + let pool = $(event.currentTarget).data('pool') - plateElement - .find('.aliquot[data-pool=' + pool + ']') - .addClass('selected-aliquot') - .removeClass('dimmed') + control.find('a[data-plate-view="pools-view"]').tab('show') - $('#pools-information li[data-pool!=' + pool + ']') - .fadeOut('fast') - .promise() - .done(function () { - $('#pools-information li[data-pool=' + pool + ']').fadeIn('fast') - }) - }) + plateElement + .find('.aliquot[data-pool!=' + pool + ']') + .removeClass('selected-aliquot') + .addClass('dimmed') - // ...we will never break the chain... - return this - } + plateElement + .find('.aliquot[data-pool=' + pool + ']') + .addClass('selected-aliquot') + .removeClass('dimmed') - // Extend jQuery prototype... - $.extend($.fn, { limberPlateView: limberPlateView }) - $(function (_event) { - $('#plate-show-page #plate').limberPlateView(window.location.hash) + $('#pools-information li[data-pool!=' + pool + ']') + .fadeOut('fast') + .promise() + .done(function () { + $('#pools-information li[data-pool=' + pool + ']').fadeIn('fast') + }) }) - // ######################################################################## - // # Page events.... - $(function () { - //= require lib/keycodes - // Trap the carriage return sent by barcode scanner - $(document).on('keydown', '.plate-barcode', function (event) { - let code = event.charCode || event.keyCode - // Check for carrage return (key code ENTER_KEYCODE) - if (code === ENTER_KEYCODE || code === TAB_KEYCODE) { - if ($(event.currentTarget).val().length > 0) { - $(event.currentTarget).closest('.plate-search-form').submit() - } + // ...we will never break the chain... + return this +} + +// Extend jQuery prototype... +$.extend($.fn, { limberPlateView: limberPlateView }) +$(function (_event) { + $('#plate-show-page #plate').limberPlateView(window.location.hash) +}) + +// ######################################################################## +// # Page events.... +$(function () { + //= require lib/keycodes + // Trap the carriage return sent by barcode scanner + $(document).on('keydown', '.plate-barcode', function (event) { + let code = event.charCode || event.keyCode + // Check for carrage return (key code ENTER_KEYCODE) + if (code === ENTER_KEYCODE || code === TAB_KEYCODE) { + if ($(event.currentTarget).val().length > 0) { + $(event.currentTarget).closest('.plate-search-form').submit() } - }) + } }) -})(jQuery, window) +}) From 5e3bf682b4a5ed55dc0ac8752b5a7a48e2a14243 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 15:33:16 +0100 Subject: [PATCH 20/26] refactor: remove IIFE from global-message-system --- .../javascript/global_message_system.js | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/app/frontend/javascript/global_message_system.js b/app/frontend/javascript/global_message_system.js index 1c08cf536..cd597a166 100644 --- a/app/frontend/javascript/global_message_system.js +++ b/app/frontend/javascript/global_message_system.js @@ -1,25 +1,22 @@ -import jQuery from 'jquery' +import $ from 'jquery' // Global SCAPE.message method -;(function ($, exports, undefined) { - 'use strict' - if (exports.SCAPE === undefined) { - exports.SCAPE = {} - } - - exports.SCAPE.message = function (message, status) { - if (message == '') { - $('#validation_report').empty() - return - } +if (exports.SCAPE === undefined) { + exports.SCAPE = {} +} - $('#validation_report') - .empty() - .append( - $(document.createElement('div')) - .addClass('alert') - .addClass('alert-' + status) - .text(message) - ) +exports.SCAPE.message = function (message, status) { + if (message == '') { + $('#validation_report').empty() + return } -})(jQuery, window) + + $('#validation_report') + .empty() + .append( + $(document.createElement('div')) + .addClass('alert') + .addClass('alert-' + status) + .text(message) + ) +} From e33d15feedfda4f165da5960caaff874b891259c Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 15:36:45 +0100 Subject: [PATCH 21/26] refactor: remove IIFE from choose-workflow --- app/frontend/javascript/choose_workflow.js | 9 +-------- app/views/plates/_choose_workflow.html.erb | 1 + 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/app/frontend/javascript/choose_workflow.js b/app/frontend/javascript/choose_workflow.js index cd27261aa..b0357e9ca 100644 --- a/app/frontend/javascript/choose_workflow.js +++ b/app/frontend/javascript/choose_workflow.js @@ -1,10 +1,3 @@ -import jQuery from 'jquery' - import { disableEnterKeySubmit } from '@/javascript/lib/disable_enter_key_submit.js' -;(function ($, _exports, undefined) { - 'use strict' - $(function (_event) { - disableEnterKeySubmit('#choose_workflow_card', '#submission_forms') - }) -})(jQuery, window) +disableEnterKeySubmit('#choose_workflow_card', '#submission_forms') diff --git a/app/views/plates/_choose_workflow.html.erb b/app/views/plates/_choose_workflow.html.erb index 1a9aacaae..8e79275ee 100644 --- a/app/views/plates/_choose_workflow.html.erb +++ b/app/views/plates/_choose_workflow.html.erb @@ -1,3 +1,4 @@ +<%= vite_javascript_tag 'choose_workflow.js' %> <%= card title:'Choose workflow', id: 'choose_workflow_card', css_class: 'suggested-actions logged_in_only' do %>

                    This plate can be processed via multiple workflows and there are currently From 96f3a3ec87230a74ea42ff8027fec5feeff6234a Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 15:38:54 +0100 Subject: [PATCH 22/26] refactor: move choose-workflow to entrypoints --- app/frontend/entrypoints/application.js | 1 - .../{javascript => entrypoints/pages}/choose_workflow.js | 0 app/views/plates/_choose_workflow.html.erb | 2 +- 3 files changed, 1 insertion(+), 2 deletions(-) rename app/frontend/{javascript => entrypoints/pages}/choose_workflow.js (100%) diff --git a/app/frontend/entrypoints/application.js b/app/frontend/entrypoints/application.js index c01858354..ccf6e8e02 100644 --- a/app/frontend/entrypoints/application.js +++ b/app/frontend/entrypoints/application.js @@ -35,7 +35,6 @@ import 'popper.js' // Load all javascript files previously in the app/assets directory import '@/javascript/bed_verification.js' -import '@/javascript/choose_workflow.js' import '@/javascript/global_message_system.js' import '@/javascript/legacy_scripts_a.js' import '@/javascript/session_scripts.js' diff --git a/app/frontend/javascript/choose_workflow.js b/app/frontend/entrypoints/pages/choose_workflow.js similarity index 100% rename from app/frontend/javascript/choose_workflow.js rename to app/frontend/entrypoints/pages/choose_workflow.js diff --git a/app/views/plates/_choose_workflow.html.erb b/app/views/plates/_choose_workflow.html.erb index 8e79275ee..17a88bff6 100644 --- a/app/views/plates/_choose_workflow.html.erb +++ b/app/views/plates/_choose_workflow.html.erb @@ -1,4 +1,4 @@ -<%= vite_javascript_tag 'choose_workflow.js' %> +<%= vite_javascript_tag 'entrypoints/pages/choose_workflow.js', defer: true %> <%= card title:'Choose workflow', id: 'choose_workflow_card', css_class: 'suggested-actions logged_in_only' do %>

                    This plate can be processed via multiple workflows and there are currently From 27fb17ccd1a143dde868cff407eb99600ea3dd5f Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 15:42:43 +0100 Subject: [PATCH 23/26] refactor: remove IIFE from bed-verification --- app/frontend/entrypoints/application.js | 1 - app/frontend/javascript/bed_verification.js | 333 ++++++++++---------- app/views/robots/show.html.erb | 1 + 3 files changed, 163 insertions(+), 172 deletions(-) diff --git a/app/frontend/entrypoints/application.js b/app/frontend/entrypoints/application.js index ccf6e8e02..5d02f0099 100644 --- a/app/frontend/entrypoints/application.js +++ b/app/frontend/entrypoints/application.js @@ -34,7 +34,6 @@ import 'bootstrap' import 'popper.js' // Load all javascript files previously in the app/assets directory -import '@/javascript/bed_verification.js' import '@/javascript/global_message_system.js' import '@/javascript/legacy_scripts_a.js' import '@/javascript/session_scripts.js' diff --git a/app/frontend/javascript/bed_verification.js b/app/frontend/javascript/bed_verification.js index cc8908747..516db2ae8 100644 --- a/app/frontend/javascript/bed_verification.js +++ b/app/frontend/javascript/bed_verification.js @@ -1,177 +1,168 @@ -import jQuery from 'jquery' -;(function ($, exports, undefined) { - 'use strict' - - //////////////////////////////////////////////////////////////////// - // Bed Robot Page - $(function (_event) { - if ($('#robot-verification-bed').length === 0) { - return - } - - //= require lib/ajax_support - - let closeIcon = function () { - return $(document.createElement('a')) - .attr('class', 'close') - .attr('aria-label', 'close') - .append($(document.createElement('span')).attr('aria-hidden', 'true').text('×')) - } - - SCAPE.robot_beds = {} - SCAPE.robot_barcode = '' - - let newScanned = function (bed, labware) { - let new_li - // $('#whole\\['+bed+'\\]').detach(); - new_li = $(document.createElement('li')) - .attr('data-bed', bed) - .attr('data-labware', labware) - .attr('class', 'list-group-item list-group-item-action') - .on('click', removeEntry) +import $ from 'jquery' + +// Bed Robot Page + +//= require lib/ajax_support + +let closeIcon = function () { + return $(document.createElement('a')) + .attr('class', 'close') + .attr('aria-label', 'close') + .append($(document.createElement('span')).attr('aria-hidden', 'true').text('×')) +} + +SCAPE.robot_beds = {} +SCAPE.robot_barcode = '' + +let newScanned = function (bed, labware) { + let new_li + // $('#whole\\['+bed+'\\]').detach(); + new_li = $(document.createElement('li')) + .attr('data-bed', bed) + .attr('data-labware', labware) + .attr('class', 'list-group-item list-group-item-action') + .on('click', removeEntry) + .append( + $(document.createElement('a')) + .attr('href', '#') + .attr('class', 'list-group-item-action') .append( - $(document.createElement('a')) - .attr('href', '#') - .attr('class', 'list-group-item-action') - .append( - $(document.createElement('h3')) - .attr('class', 'ui-li-heading') - .text('Bed: ' + bed) - ) - .append(closeIcon()) - .append( - $(document.createElement('p')) - .attr('class', 'ui-li-desc') - .text('Labware: ' + labware) - ) - .append( - $(document.createElement('input')) - .attr('type', 'hidden') - .attr('id', 'bed_labwares[' + bed + ']') - .attr('name', 'bed_labwares[' + bed + '][]') - .val(labware) - ) + $(document.createElement('h3')) + .attr('class', 'ui-li-heading') + .text('Bed: ' + bed) ) - SCAPE.robot_beds[bed] = SCAPE.robot_beds[bed] || [] - SCAPE.robot_beds[bed].push(labware) - $('#start-robot').prop('disabled', true) - $('#bed_list').append(new_li) - } - - let newRobotScanned = function (robot_barcode) { - $('#robot').text('Robot: ' + robot_barcode) - $('#robot_barcode').val(robot_barcode) - SCAPE.robot_barcode = robot_barcode - } - - const removeEntry = function () { - let lw_index, bed_list - bed_list = SCAPE.robot_beds[$(this).attr('data-bed')] - lw_index = bed_list.indexOf($(this).attr('data-labware')) - bed_list.splice(lw_index, 1) - if (bed_list.length === 0) { - SCAPE.robot_beds[$(this).attr('data-bed')] = undefined - } - $(this).detach() - $('#bed_list') - } - - let checkResponse = function (response) { - if ($('#bed_list').children().length === 0) { - // We don't have any content - $('#loadingModal').fadeOut(100) - } else if (response.valid) { - // Clear all bed error flags when valid - clearFlagFromBeds(response.beds) - pass() - } else { - // Set bed flags according to which are currently valid and which are not - flagBeds(response.beds, response.message) - fail() - } - } - - const flagBeds = function (beds, message) { - let bad_beds = [] - $.each(beds, function (bed_id) { - // here we check the validity of each bed in the hash returned from the ruby robot - // valid_relationships method and if the bed is valid clear the error flags (in case the - // bed was invalid in a previous validate cycle) or if invalid we set the error flags - if (beds[bed_id]) { - clearFlagFromBed(bed_id) - } else { - $('#bed_list li[data-bed="' + bed_id + '"]').addClass('bad_bed list-group-item-danger') - bad_beds.push(bed_id) - } - }) - SCAPE.message('There were problems: ' + message, 'danger') - } - - const clearFlagFromBeds = function (beds) { - $.each(beds, function (bed_id) { - if (beds[bed_id]) { - clearFlagFromBed(bed_id) - } - }) - } - - const clearFlagFromBed = function (bed_id) { - $('#bed_list li[data-bed="' + bed_id + '"]').removeClass('bad_bed list-group-item-danger') - } - - let wait = function () { - $('#loadingModal').fadeIn(100) - } - - const pass = function () { - $('#loadingModal').fadeOut(100) - SCAPE.message('No problems detected!', 'success') - $('#start-robot').prop('disabled', false) + .append(closeIcon()) + .append( + $(document.createElement('p')) + .attr('class', 'ui-li-desc') + .text('Labware: ' + labware) + ) + .append( + $(document.createElement('input')) + .attr('type', 'hidden') + .attr('id', 'bed_labwares[' + bed + ']') + .attr('name', 'bed_labwares[' + bed + '][]') + .val(labware) + ) + ) + SCAPE.robot_beds[bed] = SCAPE.robot_beds[bed] || [] + SCAPE.robot_beds[bed].push(labware) + $('#start-robot').prop('disabled', true) + $('#bed_list').append(new_li) +} + +let newRobotScanned = function (robot_barcode) { + $('#robot').text('Robot: ' + robot_barcode) + $('#robot_barcode').val(robot_barcode) + SCAPE.robot_barcode = robot_barcode +} + +const removeEntry = function () { + let lw_index, bed_list + bed_list = SCAPE.robot_beds[$(this).attr('data-bed')] + lw_index = bed_list.indexOf($(this).attr('data-labware')) + bed_list.splice(lw_index, 1) + if (bed_list.length === 0) { + SCAPE.robot_beds[$(this).attr('data-bed')] = undefined + } + $(this).detach() + $('#bed_list') +} + +let checkResponse = function (response) { + if ($('#bed_list').children().length === 0) { + // We don't have any content + $('#loadingModal').fadeOut(100) + } else if (response.valid) { + // Clear all bed error flags when valid + clearFlagFromBeds(response.beds) + pass() + } else { + // Set bed flags according to which are currently valid and which are not + flagBeds(response.beds, response.message) + fail() + } +} + +const flagBeds = function (beds, message) { + let bad_beds = [] + $.each(beds, function (bed_id) { + // here we check the validity of each bed in the hash returned from the ruby robot + // valid_relationships method and if the bed is valid clear the error flags (in case the + // bed was invalid in a previous validate cycle) or if invalid we set the error flags + if (beds[bed_id]) { + clearFlagFromBed(bed_id) + } else { + $('#bed_list li[data-bed="' + bed_id + '"]').addClass('bad_bed list-group-item-danger') + bad_beds.push(bed_id) } + }) + SCAPE.message('There were problems: ' + message, 'danger') +} - const fail = function () { - $('#loadingModal').fadeOut(100) - $('#start-robot').prop('disabled', true) +const clearFlagFromBeds = function (beds) { + $.each(beds, function (bed_id) { + if (beds[bed_id]) { + clearFlagFromBed(bed_id) } - - $('#plate_scan').on('change', function () { - let plate_barcode, bed_barcode, robot_barcode - plate_barcode = this.value - bed_barcode = $('#bed_scan').val() - robot_barcode = $('#robot_scan').val() - SCAPE.robot_scan = robot_barcode - this.value = '' - $('#bed_scan').val('') - $('#bed_scan').focus() - newScanned(bed_barcode, plate_barcode) - }) - - $('#robot_scan').on('change', function () { - let robot_barcode - robot_barcode = this.value - newRobotScanned(robot_barcode) - }) - - $('#validate_layout').on('click', function () { - wait() - $.ajax({ - dataType: 'json', - url: window.location.pathname + '/verify', - type: 'POST', - data: { - bed_labwares: SCAPE.robot_beds, - robot_barcode: SCAPE.robot_barcode, - }, - success: function (data, _status) { - checkResponse(data) - }, - }).fail(function (_data, _status) { - SCAPE.message( - 'The beds could not be validated. There may be network issues, or problems with Sequencescape.', - 'danger' - ) - fail() - }) - }) }) -})(jQuery, window) +} + +const clearFlagFromBed = function (bed_id) { + $('#bed_list li[data-bed="' + bed_id + '"]').removeClass('bad_bed list-group-item-danger') +} + +let wait = function () { + $('#loadingModal').fadeIn(100) +} + +const pass = function () { + $('#loadingModal').fadeOut(100) + SCAPE.message('No problems detected!', 'success') + $('#start-robot').prop('disabled', false) +} + +const fail = function () { + $('#loadingModal').fadeOut(100) + $('#start-robot').prop('disabled', true) +} + +$('#plate_scan').on('change', function () { + let plate_barcode, bed_barcode, robot_barcode + plate_barcode = this.value + bed_barcode = $('#bed_scan').val() + robot_barcode = $('#robot_scan').val() + SCAPE.robot_scan = robot_barcode + this.value = '' + $('#bed_scan').val('') + $('#bed_scan').focus() + newScanned(bed_barcode, plate_barcode) +}) + +$('#robot_scan').on('change', function () { + let robot_barcode + robot_barcode = this.value + newRobotScanned(robot_barcode) +}) + +$('#validate_layout').on('click', function () { + wait() + $.ajax({ + dataType: 'json', + url: window.location.pathname + '/verify', + type: 'POST', + data: { + bed_labwares: SCAPE.robot_beds, + robot_barcode: SCAPE.robot_barcode, + }, + success: function (data, _status) { + checkResponse(data) + }, + }).fail(function (_data, _status) { + SCAPE.message( + 'The beds could not be validated. There may be network issues, or problems with Sequencescape.', + 'danger' + ) + fail() + }) +}) diff --git a/app/views/robots/show.html.erb b/app/views/robots/show.html.erb index 031bc210d..a6c5d121b 100644 --- a/app/views/robots/show.html.erb +++ b/app/views/robots/show.html.erb @@ -1,4 +1,5 @@ <%= page(:"robot-verification-bed") do %> + <%= vite_javascript_tag 'bed_verification', defer: true %> <%= content do %> <%= card title: robot.name do %>

                    From 8b9c31d95ca28cd7001d05dd36c4b33a77d9b8bb Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 15:44:20 +0100 Subject: [PATCH 24/26] refactor: move bed-verification to entrypoints --- .../{javascript => entrypoints/pages}/bed_verification.js | 4 ++++ app/views/robots/show.html.erb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) rename app/frontend/{javascript => entrypoints/pages}/bed_verification.js (96%) diff --git a/app/frontend/javascript/bed_verification.js b/app/frontend/entrypoints/pages/bed_verification.js similarity index 96% rename from app/frontend/javascript/bed_verification.js rename to app/frontend/entrypoints/pages/bed_verification.js index 516db2ae8..3dfbe655f 100644 --- a/app/frontend/javascript/bed_verification.js +++ b/app/frontend/entrypoints/pages/bed_verification.js @@ -2,6 +2,10 @@ import $ from 'jquery' // Bed Robot Page +/* global SCAPE */ +// SCAPE is defined in global_message_system.js and inherited from the global namespace +// It should be refactored into a more modular design + //= require lib/ajax_support let closeIcon = function () { diff --git a/app/views/robots/show.html.erb b/app/views/robots/show.html.erb index a6c5d121b..1ba6603f8 100644 --- a/app/views/robots/show.html.erb +++ b/app/views/robots/show.html.erb @@ -1,5 +1,5 @@ <%= page(:"robot-verification-bed") do %> - <%= vite_javascript_tag 'bed_verification', defer: true %> + <%= vite_javascript_tag 'entrypoints/pages/bed_verification.js', defer: true %> <%= content do %> <%= card title: robot.name do %>
                    From 26f056db9039d051845cd344094e0edafbc91e4c Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 15:51:45 +0100 Subject: [PATCH 25/26] build: consolidate eslint rules --- .eslintrc.js | 9 ++++-- app/frontend/javascript/.eslintrc.js | 42 ---------------------------- 2 files changed, 6 insertions(+), 45 deletions(-) delete mode 100644 app/frontend/javascript/.eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js index c2c876121..a0ffd71be 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,13 +1,13 @@ module.exports = { env: { + 'vitest/env': true, browser: true, - es6: true, - jasmine: true, node: true, }, - plugins: ['vue'], + plugins: ['vitest', 'vue'], extends: ['eslint:recommended', 'plugin:vue/recommended', 'prettier'], parserOptions: { + ecmaVersion: 'latest', sourceType: 'module', requireConfigFile: false, }, @@ -23,6 +23,9 @@ module.exports = { }, ], 'no-var': 'error', + // We need a proper logging solution (see https://github.com/sanger/limber/issues/836), + // but until then: + 'no-console': ['error', { allow: ['warn', 'error', 'log'] }], // Legacy in from the old days. We should remove these: 'vue/prop-name-casing': ['warn'], }, diff --git a/app/frontend/javascript/.eslintrc.js b/app/frontend/javascript/.eslintrc.js deleted file mode 100644 index 224fad5ba..000000000 --- a/app/frontend/javascript/.eslintrc.js +++ /dev/null @@ -1,42 +0,0 @@ -module.exports = { - env: { - 'vitest/env': true, - browser: true, - es6: true, - node: true, - }, - globals: { - global: true, - SCAPE: true, - }, - plugins: ['vitest', 'vue'], - extends: ['eslint:recommended', 'plugin:vue/recommended', 'prettier'], - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - }, - rules: { - 'linebreak-style': ['error', 'unix'], - 'no-unused-vars': [ - 'error', - { - vars: 'all', - args: 'after-used', - ignoreRestSiblings: false, - argsIgnorePattern: '^_', - }, - ], - 'no-var': 'error', - // We need a proper logging solution (see https://github.com/sanger/limber/issues/836), - // but until then: - 'no-console': ['error', { allow: ['warn', 'error', 'log'] }], - // Legacy in from the old days. We should remove these: - 'vue/prop-name-casing': ['warn'], - }, - overrides: [ - { - files: ['**/*.spec.js'], - plugins: ['vue'], - }, - ], -} From d3814cd3ece2f28bdb1789efecdf1cf6d6cc7c5f Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 22 Aug 2024 16:37:59 +0100 Subject: [PATCH 26/26] refactor: modularise SCAPE imports --- app/frontend/entrypoints/application.js | 1 - app/frontend/entrypoints/pages/bed_verification.js | 5 +---- app/frontend/entrypoints/pages/multi_plate_pooling.js | 7 +------ app/frontend/entrypoints/pages/pooled_tubes.js | 5 +---- app/frontend/entrypoints/pages/tagged-plate.js | 5 +---- .../javascript/{ => lib}/global_message_system.js | 9 +++++---- app/frontend/javascript/session_scripts.js | 1 - 7 files changed, 9 insertions(+), 24 deletions(-) rename app/frontend/javascript/{ => lib}/global_message_system.js (74%) diff --git a/app/frontend/entrypoints/application.js b/app/frontend/entrypoints/application.js index 5d02f0099..dfc9cb98e 100644 --- a/app/frontend/entrypoints/application.js +++ b/app/frontend/entrypoints/application.js @@ -34,7 +34,6 @@ import 'bootstrap' import 'popper.js' // Load all javascript files previously in the app/assets directory -import '@/javascript/global_message_system.js' import '@/javascript/legacy_scripts_a.js' import '@/javascript/session_scripts.js' import '@/javascript/state_change_reasons.js' diff --git a/app/frontend/entrypoints/pages/bed_verification.js b/app/frontend/entrypoints/pages/bed_verification.js index 3dfbe655f..79af43401 100644 --- a/app/frontend/entrypoints/pages/bed_verification.js +++ b/app/frontend/entrypoints/pages/bed_verification.js @@ -1,11 +1,8 @@ import $ from 'jquery' +import SCAPE from '@/javascript/lib/global_message_system.js' // Bed Robot Page -/* global SCAPE */ -// SCAPE is defined in global_message_system.js and inherited from the global namespace -// It should be refactored into a more modular design - //= require lib/ajax_support let closeIcon = function () { diff --git a/app/frontend/entrypoints/pages/multi_plate_pooling.js b/app/frontend/entrypoints/pages/multi_plate_pooling.js index 05bb5917a..441a30133 100644 --- a/app/frontend/entrypoints/pages/multi_plate_pooling.js +++ b/app/frontend/entrypoints/pages/multi_plate_pooling.js @@ -1,10 +1,5 @@ import $ from 'jquery' - -if (exports.SCAPE === undefined) { - exports.SCAPE = {} -} - -let SCAPE = exports.SCAPE +import SCAPE from '@/javascript/lib/global_message_system.js' const WELLS_IN_COLUMN_MAJOR_ORDER = [ 'A1', diff --git a/app/frontend/entrypoints/pages/pooled_tubes.js b/app/frontend/entrypoints/pages/pooled_tubes.js index c10f073b6..8ba28666b 100644 --- a/app/frontend/entrypoints/pages/pooled_tubes.js +++ b/app/frontend/entrypoints/pages/pooled_tubes.js @@ -1,8 +1,5 @@ import $ from 'jquery' - -/* global SCAPE */ -// SCAPE is defined in global_message_system.js and inherited from the global namespace -// It should be refactored into a more modular design +import SCAPE from '@/javascript/lib/global_message_system.js' const SOURCE_STATES = ['passed', 'qc_complete'] diff --git a/app/frontend/entrypoints/pages/tagged-plate.js b/app/frontend/entrypoints/pages/tagged-plate.js index a5604aa70..f401b722f 100644 --- a/app/frontend/entrypoints/pages/tagged-plate.js +++ b/app/frontend/entrypoints/pages/tagged-plate.js @@ -1,16 +1,13 @@ import $ from 'jquery' import tagStatusCollector from '@/javascript/lib/tag_collector.js' import validator from '@/javascript/lib/validator.js' +import SCAPE from '@/javascript/lib/global_message_system.js' const configElement = document.getElementById('labware-creator-config') const tagPlatesList = JSON.parse(configElement.dataset.tagPlatesList) const dualRequired = configElement.dataset.dualRequired === 'true' const enforceSameTemplateWithinPool = configElement.dataset.enforceSameTemplateWithinPool === 'true' -/* global SCAPE */ -// SCAPE is defined in global_message_system.js and inherited from the global namespace -// It should be refactored into a more modular design - Object.assign(SCAPE, { tag_plates_list: tagPlatesList, dualRequired: dualRequired, diff --git a/app/frontend/javascript/global_message_system.js b/app/frontend/javascript/lib/global_message_system.js similarity index 74% rename from app/frontend/javascript/global_message_system.js rename to app/frontend/javascript/lib/global_message_system.js index cd597a166..82fea0d7a 100644 --- a/app/frontend/javascript/global_message_system.js +++ b/app/frontend/javascript/lib/global_message_system.js @@ -1,11 +1,10 @@ import $ from 'jquery' + // Global SCAPE.message method -if (exports.SCAPE === undefined) { - exports.SCAPE = {} -} +let SCAPE = {} -exports.SCAPE.message = function (message, status) { +SCAPE.message = function (message, status) { if (message == '') { $('#validation_report').empty() return @@ -20,3 +19,5 @@ exports.SCAPE.message = function (message, status) { .text(message) ) } + +export default SCAPE diff --git a/app/frontend/javascript/session_scripts.js b/app/frontend/javascript/session_scripts.js index 56f48e440..e72d80654 100644 --- a/app/frontend/javascript/session_scripts.js +++ b/app/frontend/javascript/session_scripts.js @@ -1,5 +1,4 @@ import $ from 'jquery' -// Global SCAPE.message method let loggedIn, userName, wasLoggedIn, logIn, logOut, updateUserName, warning, alert, success