Skip to content

Instantly share code, notes, and snippets.

@jnothman
Created August 28, 2018 05:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jnothman/f2cae6c8a957a0c98e71240bfefb9832 to your computer and use it in GitHub Desktop.
Save jnothman/f2cae6c8a957a0c98e71240bfefb9832 to your computer and use it in GitHub Desktop.
Stoichiometry widget for LabArchives
/* Stoichiometry Widget implemented by Joel Nothman at the Sydney Informatics Hub
Copyright (c) 2018, The University of Sydney
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
must display the following acknowledgement:
This product includes software developed by the University of Sydney.
4. Neither the name of the University of Sydney nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
my_widget_script =
{
init:function (mode, json_data) {
//this method is called when the form is being constructed
// parameters
// mode = if it equals 'view' than it should not be editable
// if it equals 'edit' then it will be used for entry
// if it equals 'view_dev' same as view, does some additional checks that may slow things down in production
// if it equals 'edit_dev' same as edit, does some additional checks that may slow things down in production
// json_data will contain the data to populate the form with, it will be in the form of the data
// returned from a call to to_json or empty if this is a new form.
//By default it calls the parent_class's init.
//TO DO write code specific to your form
this.parent_class.init(mode, json_data);
if (mode.indexOf('view') > -1) {
var isEmpty = function(tr) {
var inputs = $('input', tr);
for (var i = 0; i < inputs.length; i++) {
if ($(inputs[i]).val()) {
return false;
}
}
return true;
}
$('#the_form tbody tr').each(function() {
if (isEmpty(this)) {
$(this).remove();
}
})
return;
}
var nFixed = 2;
$('#the_form input[name^=amount]').on('keyup change', function() {
var tr = $(this).closest('tr');
var equiv = $('input[name^=equivalents]', tr);
if (equiv.length != 1)
throw ('Found the incorrect number of equiv inputs: ' + equiv.length)
equiv = equiv.first();
var moles1 = $('#the_form input[name=moles1_formula]');
var fw = $('input[name^=fw]', tr);
if ($(this).val() && fw.val() && moles1.val())
equiv.val(($(this).val() / fw.val() / moles1.val()).toFixed(nFixed))
});
$('#the_form input[name^=fw]').on('keyup change', function() {
var tr = $(this).closest('tr');
var amount = $('input[name^=amount]', tr);
var fw = $('input[name^=fw]', tr);
var moles = $('input[name^=moles]', tr);
var equiv = $('input[name^=equivalents]', tr);
if (moles.val() && fw.val()) {
amount.val((moles.val() * fw.val()).toFixed(nFixed))
}
});
$('#the_form input[name^=fw]').on('change', function() {
var tr = $(this).closest('tr');
var amount = $('input[name^=amount]', tr);
var equiv = $('input[name^=equivalents]', tr);
if (!equiv.val()) {
amount.change();
}
});
$('#the_form input[name^=equivalents]').on('keyup change', function() {
var tr = $(this).closest('tr');
var amount = $('input[name^=amount]', tr);
var fw = $('input[name^=fw]', tr);
var moles = $('input[name^=moles]', tr);
var moles1 = $('#the_form input[name=moles1_formula]');
var equiv = $('input[name^=equivalents]', tr);
if (equiv.val() && fw.val() && moles1.val()) {
moles.val((moles1.val() * equiv.val()).toFixed(nFixed));
fw.change();
}
});
},
to_json:function () {
//should return a json string containing the data entered into the form by the user
//whatever is return from the method is persisted in LabArchives. must not be binary data.
//called when the user hits the save button, when adding or editing an entry
//TO DO write code specific to your form
return this.parent_class.to_json();
},
from_json:function (json_data) {
//populates the form with json_data
//TO DO write code specific to your form
this.parent_class.from_json(json_data);
},
test_data:function () {
//during development this method is called to populate your form while in preview mode
//TO DO write code specific to your form
return this.parent_class.test_data();
},
is_valid:function (b_suppress_message) {
//called when the user hits the save button, to allow for form validation.
//returns an array of dom elements that are not valid - default is those elements marked as mandatory
// that have no data in them.
//You can modify this method, to highlight bad form elements etc...
//LA calls this method with b_suppress_message and relies on your code to communicate issues to the user
//Returning an empty array [] or NULL equals no error
//TO DO write code specific to your form
return this.parent_class.is_valid(b_suppress_message);
},
is_edited:function () {
//should return true if the form has been edited since it was loaded or since reset_edited was called
return this.parent_class.is_edited();
},
reset_edited:function () {
//typically called have a save
//TO DO write code specific to your form
return this.parent_class.reset_edited();
}
}
<table border="0" cellpadding="1" cellspacing="1" height="100" style="font-family: Times;" width="930">
<thead>
<tr>
<th scope="col" style="width: 35px;"><strong>Substance</strong></th>
<th scope="col" style="width: 8px;"><strong>Amount<br />
(mg)</strong></th>
<th scope="col" style="width: 8px;"><strong>FW<br />
(g/mol)</strong></th>
<th scope="col" style="width: 8px;"><strong>Moles<br />
(mmol)</strong></th>
<th scope="col" style="width: 5px;"><strong>Equiv.</strong></th>
<th scope="col" style="width: 8px;"><strong>Volume<br />
(uL)</strong></th>
<th scope="col" style="width: 5px;"><strong>Density<br />
(g/mL)</strong></th>
<th scope="col" style="width: 35px;"><strong>Comments</strong></th>
</tr>
</thead>
<tbody>
<tr>
<th scope="col" style="text-align: left; width: 35px;"><input name="substance1" size="35" type="text" /></th>
<td scope="col" style="text-align: right; width: 8px;"><input name="amount1_number" size="8" type="text" /></td>
<th scope="col" style="text-align: right; width: 8px;"><input name="fw1_number" size="8" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="moles1_formula" size="8" type="text" value="(#{amount1_number}/#{fw1_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="equivalents1_number" size="5" type="text" value="1" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="volume1_formula" size="8" type="text" value="(#{amount1_number}/#{density1_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="density1_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right;"><input name="comments1" size="35" type="text" /></th>
</tr>
<tr>
<th scope="col" style="text-align: left; width: 35px;"><input name="substance2" size="35" type="text" /></th>
<td scope="col" style="text-align: right; width: 8px;"><input name="amount2_number" size="8" type="text" value="" /></td>
<th scope="col" style="text-align: right; width: 8px;"><input name="fw2_number" size="8" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="moles2_formula" size="8" type="text" value="(#{moles1_formula}*#{equivalents2_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="equivalents2_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="volume2_formula" size="8" type="text" value="(#{amount2_number}/#{density2_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="density2_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right;"><input name="comments2" size="35" type="text" /></th>
</tr>
<tr>
<th scope="col" style="text-align: left; width: 35px;"><input name="substance3" size="35" type="text" /></th>
<td scope="col" style="text-align: right; width: 8px;"><input name="amount3_number" size="8" type="text" /></td>
<th scope="col" style="text-align: right; width: 8px;"><input name="fw3_number" size="8" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="moles3_formula" size="8" type="text" value="(#{moles1_formula}*#{equivalents3_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="equivalents3_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="volume3_formula" size="8" type="text" value="(#{amount3_number}/#{density3_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="density3_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right;"><input name="comments3" size="35" type="text" /></th>
</tr>
<tr>
<th scope="col" style="text-align: left; width: 35px;"><input name="substance4" size="35" type="text" /></th>
<td scope="col" style="text-align: right; width: 8px;"><input name="amount4_number" size="8" type="text" /></td>
<th scope="col" style="text-align: right; width: 8px;"><input name="fw4_number" size="8" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="moles4_formula" size="8" type="text" value="(#{moles1_formula}*#{equivalents4_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="equivalents4_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="volume4_formula" size="8" type="text" value="(#{amount4_number}/#{density4_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="density4_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right;"><input name="comments4" size="35" type="text" /></th>
</tr>
<tr>
<th scope="col" style="text-align: left; width: 35px;"><input name="substance5" size="35" type="text" /></th>
<td scope="col" style="text-align: right; width: 8px;"><input name="amount5_number" size="8" type="text" /></td>
<th scope="col" style="text-align: right; width: 8px;"><input name="fw5_number" size="8" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="moles5_formula" size="8" type="text" value="(#{moles1_formula}*#{equivalents5_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="equivalents5_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="volume5_formula" size="8" type="text" value="(#{amount5_number}/#{density5_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="density5_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right;"><input name="comments5" size="35" type="text" /></th>
</tr>
<tr>
<th scope="col" style="text-align: left; width: 35px;"><input name="substance6" size="35" type="text" /></th>
<td scope="col" style="text-align: right; width: 8px;"><input name="amount6_number" size="8" type="text" /></td>
<th scope="col" style="text-align: right; width: 8px;"><input name="fw6_number" size="8" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="moles6_formula" size="8" type="text" value="(#{moles1_formula}*#{equivalents6_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="equivalents6_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="volume6_formula" size="8" type="text" value="(#{amount6_number}/#{density6_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="density6_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right;"><input name="comments6" size="35" type="text" /></th>
</tr>
<tr>
<th scope="col" style="text-align: left; width: 35px;"><input name="substance7" size="35" type="text" /></th>
<td scope="col" style="text-align: right; width: 8px;"><input name="amount7_number" size="8" type="text" /></td>
<th scope="col" style="text-align: right; width: 8px;"><input name="fw7_number" size="8" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="moles7_formula" size="8" type="text" value="(#{moles1_formula}*#{equivalents7_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="equivalents7_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="volume7_formula" size="8" type="text" value="(#{amount7_number}/#{density7_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="density7_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right;"><input name="comments7" size="35" type="text" /></th>
</tr>
<tr>
<th scope="col" style="text-align: left; width: 35px;"><input name="substance8" size="35" type="text" /></th>
<td scope="col" style="text-align: right; width: 8px;"><input name="amount8_number" size="8" type="text" /></td>
<th scope="col" style="text-align: right; width: 8px;"><input name="fw8_number" size="8" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="moles8_formula" size="8" type="text" value="(#{moles1_formula}*#{equivalents8_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="equivalents8_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="volume8_formula" size="8" type="text" value="(#{amount8_number}/#{density8_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="density8_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right;"><input name="comments8" size="35" type="text" /></th>
</tr>
<tr>
<th scope="col" style="text-align: left; width: 35px;"><input name="substance9" size="35" type="text" /></th>
<td scope="col" style="text-align: right; width: 8px;"><input name="amount9_number" size="8" type="text" /></td>
<th scope="col" style="text-align: right; width: 8px;"><input name="fw9_number" size="8" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="moles9_formula" size="8" type="text" value="(#{moles1_formula}*#{equivalents9_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="equivalents9_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="volume9_formula" size="8" type="text" value="(#{amount9_number}/#{density9_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="density9_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right;"><input name="comments9" size="35" type="text" /></th>
</tr>
<tr>
<th scope="col" style="text-align: left; width: 35px;"><input name="substance10" size="35" type="text" /></th>
<td scope="col" style="text-align: right; width: 8px;"><input name="amount10_number" size="8" type="text" /></td>
<th scope="col" style="text-align: right; width: 8px;"><input name="fw10_number" size="8" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="moles10_formula" size="8" type="text" value="(#{moles1_formula}*#{equivalents10_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="equivalents10_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right; width: 8px;"><input name="volume10_formula" size="8" type="text" value="(#{amount10_number}/#{density10_number}).toFixed(2)" /></th>
<th scope="col" style="text-align: right; width: 5px;"><input name="density10_number" size="5" type="text" /></th>
<th scope="col" style="text-align: right;"><input name="comments10" size="35" type="text" /></th>
</tr>
</tbody>
</table>
@jnothman
Copy link
Author

jnothman commented Feb 14, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment