Team:EPF-Lausanne/PrimerDesignHelper.js
From 2012.igem.org
(3 intermediate revisions not shown) | |||
Line 2: | Line 2: | ||
Immutable class that makes it possible to work with (very simplified) DNA sequences | Immutable class that makes it possible to work with (very simplified) DNA sequences | ||
*/ | */ | ||
- | |||
var DNA, | var DNA, | ||
- | __indexOf = | + | __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; |
DNA = (function() { | DNA = (function() { | ||
Line 221: | Line 220: | ||
XmaI: "CCCGGG", | XmaI: "CCCGGG", | ||
XmaIII: "CGGCCG", | XmaIII: "CGGCCG", | ||
- | |||
XmnI: "GAANNNNTTC" | XmnI: "GAANNNNTTC" | ||
+ | }; | ||
+ | |||
+ | DNA.cutPositions = { | ||
+ | AatI: 3, | ||
+ | AatII: 5, | ||
+ | AccI: 2, | ||
+ | AccIII: 1, | ||
+ | Acc65I: 1, | ||
+ | AcsI: 1, | ||
+ | AcyI: 2, | ||
+ | AflI: 1, | ||
+ | AflII: 1, | ||
+ | AflIII: 1, | ||
+ | AgeI: 1, | ||
+ | AhaII: 2, | ||
+ | AhaIII: 3, | ||
+ | AluI: 2, | ||
+ | Alw44I: 1, | ||
+ | AlwNI: 6, | ||
+ | AocI: 2, | ||
+ | AosI: 3, | ||
+ | ApaI: 5, | ||
+ | ApaLI: 1, | ||
+ | ApoI: 1, | ||
+ | ApyI: 0, | ||
+ | AscI: 2, | ||
+ | AseI: 2, | ||
+ | AsnI: 2, | ||
+ | AspI: 4, | ||
+ | Asp700: 5, | ||
+ | Asp718: 1, | ||
+ | AspEI: 6, | ||
+ | AspHI: 5, | ||
+ | AsuII: 2, | ||
+ | AvaI: 1, | ||
+ | AvaII: 1, | ||
+ | AviII: 3, | ||
+ | AvrII: 1, | ||
+ | BalI: 3, | ||
+ | BamHI: 1, | ||
+ | BanI: 1, | ||
+ | BanII: 5, | ||
+ | BbrPI: 3, | ||
+ | BclI: 1, | ||
+ | BfaI: 1, | ||
+ | BfrI: 1, | ||
+ | BglI: 7, | ||
+ | BglII: 1, | ||
+ | BinI: 1, | ||
+ | BmyI: 5, | ||
+ | Bpu1102I: 2, | ||
+ | BsaAI: 3, | ||
+ | BsaBI: 5, | ||
+ | BsaHI: 2, | ||
+ | BsaJI: 1, | ||
+ | BseAI: 1, | ||
+ | BsePI: 1, | ||
+ | BsiEI: 4, | ||
+ | BsiWI: 1, | ||
+ | BsiYI: 7, | ||
+ | BslI: 7, | ||
+ | Bsp1286I: 5, | ||
+ | Bsp1407I: 1, | ||
+ | BspDI: 2, | ||
+ | BspEI: 1, | ||
+ | BspHI: 1, | ||
+ | BspLU11I: 1, | ||
+ | BsrFI: 1, | ||
+ | BssGI: 8, | ||
+ | BssHII: 1, | ||
+ | Bst1107I: 3, | ||
+ | BstBI: 2, | ||
+ | BstEII: 1, | ||
+ | BstNI: 2, | ||
+ | BstUI: 2, | ||
+ | BstXI: 8, | ||
+ | BstYI: 1, | ||
+ | Bsu36I: 2, | ||
+ | CelII: 2, | ||
+ | CfoI: 3, | ||
+ | CfrI: 1, | ||
+ | Cfr10I: 1, | ||
+ | ClaI: 2, | ||
+ | DdeI: 1, | ||
+ | DpnII: 0, | ||
+ | DraI: 3, | ||
+ | DraII: 2, | ||
+ | DraIII: 6, | ||
+ | DrdI: 7, | ||
+ | DsaI: 1, | ||
+ | EaeI: 1, | ||
+ | EagI: 1, | ||
+ | Eam1105I: 6, | ||
+ | Ecl136II: 3, | ||
+ | EclXI: 1, | ||
+ | Eco47III: 3, | ||
+ | EcoNI: 5, | ||
+ | EcoO109I: 2, | ||
+ | EcoRI: 1, | ||
+ | EcoRII: 0, | ||
+ | EcoRV: 3, | ||
+ | EspI: 2, | ||
+ | FnuDII: 2, | ||
+ | Fnu4HI: 2, | ||
+ | FseI: 6, | ||
+ | FspI: 3, | ||
+ | HaeII: 5, | ||
+ | HaeIII: 2, | ||
+ | HgiAI: 5, | ||
+ | HhaI: 3, | ||
+ | HincII: 3, | ||
+ | HindII: 3, | ||
+ | HindIII: 1, | ||
+ | HinfI: 1, | ||
+ | HinPI: 1, | ||
+ | HpaI: 3, | ||
+ | HpaII: 1, | ||
+ | ItaI: 2, | ||
+ | KasI: 1, | ||
+ | KpnI: 5, | ||
+ | KspI: 4, | ||
+ | MaeI: 1, | ||
+ | MaeII: 1, | ||
+ | MaeIII: 0, | ||
+ | MamI: 5, | ||
+ | MboI: 0, | ||
+ | MfeI: 1, | ||
+ | MluI: 1, | ||
+ | MluNI: 3, | ||
+ | MroI: 1, | ||
+ | MscI: 3, | ||
+ | MseI: 1, | ||
+ | MspI: 1, | ||
+ | MstI: 3, | ||
+ | MstII: 2, | ||
+ | MunI: 1, | ||
+ | MvaI: 2, | ||
+ | MvnI: 2, | ||
+ | NaeI: 3, | ||
+ | NarI: 2, | ||
+ | NciI: 2, | ||
+ | NcoI: 1, | ||
+ | NdeI: 2, | ||
+ | NdeII: 0, | ||
+ | NgoMI: 1, | ||
+ | NheI: 1, | ||
+ | NlaIII: 4, | ||
+ | NlaIV: 3, | ||
+ | NotI: 2, | ||
+ | NruI: 3, | ||
+ | NsiI: 5, | ||
+ | NspBII: 3, | ||
+ | NspI: 5, | ||
+ | NspII: 5, | ||
+ | NspV: 2, | ||
+ | PacI: 5, | ||
+ | PaeR7I: 1, | ||
+ | PflMI: 7, | ||
+ | PinAI: 1, | ||
+ | PsiI: 3, | ||
+ | PmaCI: 3, | ||
+ | PmeI: 4, | ||
+ | PmlI: 3, | ||
+ | PpuMI: 2, | ||
+ | Psp1406I: 2, | ||
+ | PstI: 5, | ||
+ | PvuI: 4, | ||
+ | PvuII: 3, | ||
+ | RcaI: 1, | ||
+ | RmaI: 1, | ||
+ | RsaI: 2, | ||
+ | RsrII: 2, | ||
+ | SacI: 5, | ||
+ | SacII: 4, | ||
+ | SalI: 1, | ||
+ | SauI: 2, | ||
+ | Sau3AI: 0, | ||
+ | Sau96I: 1, | ||
+ | ScaI: 3, | ||
+ | ScrFI: 2, | ||
+ | SexAI: 1, | ||
+ | SfcI: 1, | ||
+ | SfiI: 8, | ||
+ | SfuI: 2, | ||
+ | SgrAI: 2, | ||
+ | SmaI: 3, | ||
+ | SnaBI: 3, | ||
+ | SnoI: 1, | ||
+ | SpeI: 1, | ||
+ | SphI: 5, | ||
+ | SrfI: 4, | ||
+ | Sse8387I: 6, | ||
+ | SspI: 3, | ||
+ | SspBI: 1, | ||
+ | SstI: 5, | ||
+ | SstII: 4, | ||
+ | StuI: 3, | ||
+ | StyI: 1, | ||
+ | SwaI: 4, | ||
+ | TaqI: 1, | ||
+ | TfiI: 1, | ||
+ | ThaI: 2, | ||
+ | Tru9I: 1, | ||
+ | Tth111I: 4, | ||
+ | Van91I: 7, | ||
+ | XbaI: 1, | ||
+ | XcmI: 8, | ||
+ | XhoI: 1, | ||
+ | XhoII: 1, | ||
+ | XmaI: 1, | ||
+ | XmaIII: 1, | ||
+ | XmaCI: 1, | ||
+ | XmnI: 5 | ||
}; | }; | ||
Line 228: | Line 439: | ||
DNA.getRSFlipped = function() { | DNA.getRSFlipped = function() { | ||
- | var name, seq, _ref; | + | var key, name, seq, value, _ref, _ref2; |
if (this.RSFlipped === void 0) { | if (this.RSFlipped === void 0) { | ||
this.RSFlipped = {}; | this.RSFlipped = {}; | ||
Line 235: | Line 446: | ||
seq = _ref[name]; | seq = _ref[name]; | ||
if (this.RSFlipped[seq]) { | if (this.RSFlipped[seq]) { | ||
- | this.RSFlipped[seq] | + | this.RSFlipped[seq].push(name); |
} else { | } else { | ||
- | this.RSFlipped[seq] = name; | + | this.RSFlipped[seq] = [name]; |
+ | } | ||
+ | } | ||
+ | _ref2 = this.RSFlipped; | ||
+ | for (key in _ref2) { | ||
+ | value = _ref2[key]; | ||
+ | if (value.length <= 2) { | ||
+ | this.RSFlipped[key] = value.join(" or "); | ||
+ | } else { | ||
+ | this.RSFlipped[key] = value.slice(0, value.length - 1).join(", ") + " or " + value[value.length - 1]; | ||
} | } | ||
} | } | ||
} | } | ||
return this.RSFlipped; | return this.RSFlipped; | ||
+ | }; | ||
+ | |||
+ | DNA.getCutPosition = function(rs) { | ||
+ | var k, v, _ref; | ||
+ | if (DNA.cutPositions[rs]) { | ||
+ | return DNA.cutPositions[rs]; | ||
+ | } else { | ||
+ | _ref = DNA.cutPositions; | ||
+ | for (k in _ref) { | ||
+ | v = _ref[k]; | ||
+ | if (k.toLowerCase() === rs.toLowerCase()) return v; | ||
+ | } | ||
+ | return -1; | ||
+ | } | ||
}; | }; | ||
Line 265: | Line 499: | ||
var found, name, seq, sequence, _ref; | var found, name, seq, sequence, _ref; | ||
found = false; | found = false; | ||
- | _ref = this.RS; | + | if (data != null) { |
- | + | _ref = this.RS; | |
- | + | for (name in _ref) { | |
- | + | sequence = _ref[name]; | |
- | + | if (name.toLowerCase() === data.toLowerCase()) { | |
- | + | seq = new DNA(sequence.split('')); | |
+ | found = true; | ||
+ | } | ||
} | } | ||
+ | } else { | ||
+ | data = ""; | ||
} | } | ||
- | if (!found) | + | if (!found) seq = new DNA(data.toUpperCase().replace(/\s/ig, "").split('')); |
- | + | ||
- | + | ||
seq.validateData(); | seq.validateData(); | ||
return seq; | return seq; | ||
Line 286: | Line 522: | ||
} else { | } else { | ||
data = (function() { | data = (function() { | ||
- | var | + | var _results; |
_results = []; | _results = []; | ||
- | for (i | + | for (i = 1; 1 <= length ? i <= length : i >= length; 1 <= length ? i++ : i--) { |
_results.push(DNA.allowed[Math.floor(Math.random() * DNA.allowed.length)]); | _results.push(DNA.allowed[Math.floor(Math.random() * DNA.allowed.length)]); | ||
} | } | ||
Line 301: | Line 537: | ||
optimization ^^). Anyway, the DNA.fromString function is probably what you were looking for! | optimization ^^). Anyway, the DNA.fromString function is probably what you were looking for! | ||
*/ | */ | ||
- | |||
function DNA(data) { | function DNA(data) { | ||
Line 325: | Line 560: | ||
DNA.prototype.matches = function(seq2) { | DNA.prototype.matches = function(seq2) { | ||
var combined; | var combined; | ||
- | if (this.length() !== seq2.length()) | + | if (this.length() !== seq2.length()) return false; |
- | + | ||
- | + | ||
combined = _.zip(this.data, seq2.data); | combined = _.zip(this.data, seq2.data); | ||
return _.all(combined, function(_arg) { | return _.all(combined, function(_arg) { | ||
- | var i, j, n1, n1Matches, n2, n2Matches | + | var i, j, n1, n1Matches, n2, n2Matches, _ref, _ref2; |
n1 = _arg[0], n2 = _arg[1]; | n1 = _arg[0], n2 = _arg[1]; | ||
if (n1 === n2) { | if (n1 === n2) { | ||
Line 343: | Line 576: | ||
n1Matches = DNA.nucleotideMatches[n1]; | n1Matches = DNA.nucleotideMatches[n1]; | ||
n2Matches = DNA.nucleotideMatches[n2]; | n2Matches = DNA.nucleotideMatches[n2]; | ||
- | for (i | + | for (i = 0, _ref = n1Matches.length - 1; 0 <= _ref ? i <= _ref : i >= _ref; 0 <= _ref ? i++ : i--) { |
- | for (j | + | for (j = 0, _ref2 = n2Matches.length - 1; 0 <= _ref2 ? j <= _ref2 : j >= _ref2; 0 <= _ref2 ? j++ : j--) { |
- | if (n1Matches[i] === n2Matches[j]) | + | if (n1Matches[i] === n2Matches[j]) return true; |
- | + | ||
- | + | ||
} | } | ||
} | } | ||
Line 355: | Line 586: | ||
}; | }; | ||
- | DNA.prototype.findRS = function() { | + | DNA.prototype.findRS = function(nameOnly) { |
- | var key, name, rs, seq, _ref; | + | var key, name, rs, rsDNA, seq, _ref; |
+ | if (nameOnly == null) nameOnly = false; | ||
+ | if (this.length() > 15) return ""; | ||
key = this.data.join(""); | key = this.data.join(""); | ||
rs = DNA.getRSFlipped(); | rs = DNA.getRSFlipped(); | ||
if (rs[key]) { | if (rs[key]) { | ||
- | return rs[key] + " (" + key + ")"; | + | if (nameOnly) { |
+ | return rs[key]; | ||
+ | } else { | ||
+ | return rs[key] + " (5' → 3': " + key + ")"; | ||
+ | } | ||
} else { | } else { | ||
_ref = DNA.RS; | _ref = DNA.RS; | ||
for (seq in _ref) { | for (seq in _ref) { | ||
name = _ref[seq]; | name = _ref[seq]; | ||
- | if (this.matches( | + | rsDNA = DNA.fromString(seq); |
- | + | if (this.matches(rsDNA)) { | |
+ | if (nameOnly) { | ||
+ | name; | ||
+ | } else { | ||
+ | name + " (5' → 3': " + key + ")"; | ||
+ | } | ||
} | } | ||
} | } | ||
return ""; | return ""; | ||
} | } | ||
+ | }; | ||
+ | |||
+ | DNA.prototype.getCutPosition = function() { | ||
+ | return DNA.getCutPosition(this.findRS(true)); | ||
+ | }; | ||
+ | |||
+ | DNA.prototype.containsRS = function(names) { | ||
+ | var c, comparisons, i, lengths, name, parts, pos, rs, _i, _j, _len, _len2, _ref; | ||
+ | comparisons = []; | ||
+ | for (_i = 0, _len = names.length; _i < _len; _i++) { | ||
+ | name = names[_i]; | ||
+ | comparisons.push(name.toLowerCase()); | ||
+ | } | ||
+ | lengths = (function() { | ||
+ | var _results; | ||
+ | _results = []; | ||
+ | for (i = 0; i <= 10; i++) { | ||
+ | _results.push(""); | ||
+ | } | ||
+ | return _results; | ||
+ | })(); | ||
+ | rs = DNA.getRSFlipped(); | ||
+ | pos = 0; | ||
+ | _ref = this.data; | ||
+ | for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) { | ||
+ | c = _ref[_j]; | ||
+ | for (i = 4; i <= 10; i++) { | ||
+ | lengths[i] += c; | ||
+ | if (i <= pos) lengths[i] = lengths[i].substr(1); | ||
+ | if (rs[lengths[i]]) { | ||
+ | parts = rs[lengths[i]].toLowerCase().split(" "); | ||
+ | if (_.some(comparisons, function(obj) { | ||
+ | return __indexOf.call(parts, obj) >= 0; | ||
+ | })) { | ||
+ | return true; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | pos++; | ||
+ | } | ||
+ | return false; | ||
}; | }; | ||
Line 386: | Line 669: | ||
DNA.prototype.reverseComplement = function() { | DNA.prototype.reverseComplement = function() { | ||
- | var complement, k, l, revCompl, v | + | var complement, k, l, revCompl, v, _len, _ref; |
revCompl = []; | revCompl = []; | ||
complement = function(x) { | complement = function(x) { | ||
Line 404: | Line 687: | ||
l = this.data.length - 1; | l = this.data.length - 1; | ||
_ref = this.data; | _ref = this.data; | ||
- | for (k | + | for (k = 0, _len = _ref.length; k < _len; k++) { |
v = _ref[k]; | v = _ref[k]; | ||
revCompl[l - k] = complement(v); | revCompl[l - k] = complement(v); | ||
Line 421: | Line 704: | ||
DNA.prototype.toString = function() { | DNA.prototype.toString = function() { | ||
return this.data.join(""); | return this.data.join(""); | ||
+ | }; | ||
+ | |||
+ | DNA.prototype.toJSON = function() { | ||
+ | return this.toString(); | ||
+ | }; | ||
+ | |||
+ | DNA.prototype.clone = function() { | ||
+ | return new DNA(this.data.slice(0)); | ||
}; | }; | ||
Line 426: | Line 717: | ||
})(); | })(); | ||
+ | var DNAInput, DNARSInput, DNASmallInput, Input, InputForm, NumberInput, RadioInput, SelectInput, Separator, Submit, TemperatureInput, | ||
+ | __hasProp = Object.prototype.hasOwnProperty, | ||
+ | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }; | ||
+ | |||
+ | InputForm = (function() { | ||
+ | |||
+ | InputForm.i = 0; | ||
+ | |||
+ | /* | ||
+ | [{ | ||
+ | name: "tm", | ||
+ | readableName: "Tm", | ||
+ | type: "TemperatureInput", | ||
+ | label: "Tm" | ||
+ | }] | ||
+ | */ | ||
+ | |||
+ | InputForm.create = function(options, objects) { | ||
+ | var f; | ||
+ | f = new InputForm(options); | ||
+ | objects.forEach(function(x) { | ||
+ | return f.addInput(x); | ||
+ | }); | ||
+ | return f; | ||
+ | }; | ||
+ | |||
+ | function InputForm(_arg) { | ||
+ | this.update = _arg.update, this.submit = _arg.submit, this.name = _arg.name; | ||
+ | this.id = "inputform-" + InputForm.i++; | ||
+ | this.inputs = []; | ||
+ | this.inputDict = {}; | ||
+ | this.lastValue = {}; | ||
+ | } | ||
+ | |||
+ | InputForm.prototype.save = function(data) { | ||
+ | var k, tempData, v; | ||
+ | try { | ||
+ | if (JSON && localStorage) { | ||
+ | tempData = {}; | ||
+ | for (k in data) { | ||
+ | v = data[k]; | ||
+ | if (k === "leftRS" || k === "rightRS") v = v.rs; | ||
+ | tempData[k] = v; | ||
+ | } | ||
+ | return localStorage.setItem("values", JSON.stringify(tempData)); | ||
+ | } | ||
+ | } catch (e) { | ||
+ | |||
+ | } | ||
+ | }; | ||
+ | |||
+ | InputForm.prototype.restore = function() { | ||
+ | var k, oldVals, v, _results; | ||
+ | try { | ||
+ | if (JSON && localStorage) { | ||
+ | oldVals = localStorage.getItem("values"); | ||
+ | if (oldVals != null) { | ||
+ | oldVals = JSON.parse(oldVals); | ||
+ | _results = []; | ||
+ | for (k in oldVals) { | ||
+ | v = oldVals[k]; | ||
+ | _results.push(this.inputDict[k].value(v)); | ||
+ | } | ||
+ | return _results; | ||
+ | } | ||
+ | } | ||
+ | } catch (e) { | ||
+ | |||
+ | } | ||
+ | }; | ||
+ | |||
+ | InputForm.prototype.attach = function(elem) { | ||
+ | $(elem).append(this.generateDOM()); | ||
+ | $(".help-block").hide(); | ||
+ | this.restore(); | ||
+ | return this.updateCallback(); | ||
+ | }; | ||
+ | |||
+ | InputForm.prototype.submitCallback = function() { | ||
+ | var changed, data, error, messages, _ref; | ||
+ | _ref = this.getFormData(), data = _ref.data, messages = _ref.messages, error = _ref.error, changed = _ref.changed; | ||
+ | return typeof this.submit === "function" ? this.submit(data, messages, error) : void 0; | ||
+ | }; | ||
+ | |||
+ | InputForm.prototype.updateCallback = function() { | ||
+ | var changed, data, error, messages, _ref; | ||
+ | if (this.disableAuto) return; | ||
+ | _ref = this.getFormData(), data = _ref.data, messages = _ref.messages, error = _ref.error, changed = _ref.changed; | ||
+ | if (changed) { | ||
+ | return typeof this.update === "function" ? this.update(data, messages, error) : void 0; | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | InputForm.prototype.getFormData = function() { | ||
+ | var changed, data, error, messages; | ||
+ | data = {}; | ||
+ | messages = []; | ||
+ | error = false; | ||
+ | this.inputs.forEach(function(x) { | ||
+ | var val; | ||
+ | val = void 0; | ||
+ | try { | ||
+ | val = x.value(); | ||
+ | } catch (e) { | ||
+ | messages.push("Error: " + e.message); | ||
+ | } | ||
+ | if (!x.validate(val)) { | ||
+ | error = true; | ||
+ | messages.push("The value of " + x.get("label") + " is invalid"); | ||
+ | } | ||
+ | if (x.get("name") != null) return data[x.get("name")] = val; | ||
+ | }); | ||
+ | changed = false; | ||
+ | if (!_.isEqual(this.lastValue, data)) { | ||
+ | changed = true; | ||
+ | this.inputs.forEach(function(x) { | ||
+ | if (x.visible(data)) { | ||
+ | return x.show(); | ||
+ | } else { | ||
+ | return x.hide(); | ||
+ | } | ||
+ | }); | ||
+ | this.lastValue = data; | ||
+ | this.save(data); | ||
+ | } | ||
+ | return { | ||
+ | data: data, | ||
+ | messages: messages, | ||
+ | error: error, | ||
+ | changed: changed | ||
+ | }; | ||
+ | }; | ||
+ | |||
+ | InputForm.prototype.addInput = function(input) { | ||
+ | var callback, inputObj, | ||
+ | _this = this; | ||
+ | if (input === "Separator") { | ||
+ | this.inputs.push(new Separator()); | ||
+ | return; | ||
+ | } | ||
+ | if (!window[input.type] instanceof Function) { | ||
+ | throw new Error("Passed input can't be instantiated"); | ||
+ | } | ||
+ | callback = function() { | ||
+ | return _this.updateCallback.apply(_this, arguments); | ||
+ | }; | ||
+ | if (input.type === "Submit") { | ||
+ | callback = function() { | ||
+ | return _this.submitCallback.apply(_this, arguments); | ||
+ | }; | ||
+ | } | ||
+ | inputObj = new window[input.type](callback, input); | ||
+ | if (!inputObj instanceof Input) throw new Error("Object isn't an input"); | ||
+ | this.inputDict[input.name] = inputObj; | ||
+ | this.inputs.push(inputObj); | ||
+ | return this; | ||
+ | }; | ||
+ | |||
+ | InputForm.prototype.get = function(field) { | ||
+ | return this.inputDict[field]; | ||
+ | }; | ||
+ | |||
+ | InputForm.prototype.generateDOM = function() { | ||
+ | var fieldset, form, | ||
+ | _this = this; | ||
+ | form = $("<form>").attr("id", this.id).attr("class", "form-horizontal"); | ||
+ | if (this.name != null) { | ||
+ | fieldset = $("<fieldset>").append($("<legend>").text(this.name)); | ||
+ | form.append(fieldset); | ||
+ | } else { | ||
+ | fieldset = form; | ||
+ | } | ||
+ | this.inputs.forEach(function(input) { | ||
+ | return fieldset.append(input.generateDOM()); | ||
+ | }); | ||
+ | fieldset.prepend($("<hr>")).prepend($("<div>").attr("class", "control-group " + this.rowClass).append($("<label>").attr("class", "control-label").attr("for", "help-checkbox").html("Show help blocks")).append($("<div>").attr("class", "controls").append($("<input>").attr("class", "input-medium").attr("type", "checkbox").attr("checked", false).attr("id", "help-checkbox").on("change", function() { | ||
+ | if ($("#help-checkbox").attr("checked")) { | ||
+ | return $(".help-block").show(); | ||
+ | } else { | ||
+ | return $(".help-block").hide(); | ||
+ | } | ||
+ | })))).prepend($("<div>").attr("class", "control-group " + this.rowClass).append($("<label>").attr("class", "control-label").attr("for", "autoUpdate-checkbox").html("Automatically update results (CPU-heavy, but way more useable)")).append($("<div>").attr("class", "controls").append($("<input>").attr("class", "input-medium").attr("type", "checkbox").attr("checked", "checked").attr("id", "autoUpdate-checkbox").on("change", function() { | ||
+ | if ($("#autoUpdate-checkbox").attr("checked")) { | ||
+ | return _this.disableAuto = false; | ||
+ | } else { | ||
+ | return _this.disableAuto = true; | ||
+ | } | ||
+ | })))); | ||
+ | return form; | ||
+ | }; | ||
+ | |||
+ | return InputForm; | ||
+ | |||
+ | })(); | ||
+ | |||
+ | Input = (function() { | ||
+ | |||
+ | Input.i = 0; | ||
+ | |||
+ | function Input(callback, data, rowClass) { | ||
+ | this.callback = callback; | ||
+ | this.data = data; | ||
+ | this.rowClass = rowClass != null ? rowClass : ""; | ||
+ | this.id = "input-" + Input.i++; | ||
+ | this.type = "text"; | ||
+ | } | ||
+ | |||
+ | Input.prototype.generateDOM = function() { | ||
+ | return $("<div>").attr("id", "row-" + this.id).attr("class", "control-group " + this.rowClass).append(this.generateLabelDOM()).append($("<div>").attr("class", "controls").append(this.generateElementDOM()).append(this.generateHelpDOM())); | ||
+ | }; | ||
+ | |||
+ | Input.prototype.generateLabelDOM = function() { | ||
+ | return $("<label>").attr("class", "control-label").attr("for", this.id).html(this.data.label); | ||
+ | }; | ||
+ | |||
+ | Input.prototype.generateInputElementDOM = function(id, type, value, callback) { | ||
+ | var element; | ||
+ | element = $("<input>").attr("class", "input-medium").attr("type", type).attr("id", id).on("change", callback).on("keyup", callback); | ||
+ | element.val(value); | ||
+ | return element; | ||
+ | }; | ||
+ | |||
+ | Input.prototype.generateElementDOM = function() { | ||
+ | return this.generateInputElementDOM(this.id, this.type, this.data.value, this.callback); | ||
+ | }; | ||
+ | |||
+ | Input.prototype.generateHelpDOM = function(helpData) { | ||
+ | if (helpData == null) helpData = this.data.help; | ||
+ | if ((helpData != null) && helpData.length > 0) { | ||
+ | return $("<p>").attr("class", "help-block").html(helpData); | ||
+ | } else { | ||
+ | return $("<p>").attr("class", "help-block").attr("style", "display: none"); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | Input.prototype.get = function(key) { | ||
+ | return this.data[key]; | ||
+ | }; | ||
+ | |||
+ | Input.prototype.visible = function(vals) { | ||
+ | if (this.data.visible != null) { | ||
+ | return this.data.visible(vals); | ||
+ | } else { | ||
+ | return true; | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | Input.prototype.hide = function() { | ||
+ | return $("#row-" + this.id).hide(); | ||
+ | }; | ||
+ | |||
+ | Input.prototype.show = function() { | ||
+ | return $("#row-" + this.id).show(); | ||
+ | }; | ||
+ | |||
+ | Input.prototype.value = function(value) { | ||
+ | if (value === void 0) { | ||
+ | return $("#" + this.id).val(); | ||
+ | } else { | ||
+ | return $("#" + this.id).val(value); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | Input.prototype.validate = function(val) { | ||
+ | return true; | ||
+ | }; | ||
+ | |||
+ | return Input; | ||
+ | |||
+ | })(); | ||
+ | |||
+ | Separator = (function(_super) { | ||
+ | |||
+ | __extends(Separator, _super); | ||
+ | |||
+ | function Separator() { | ||
+ | Separator.__super__.constructor.apply(this, arguments); | ||
+ | } | ||
+ | |||
+ | Separator.prototype.generateDOM = function() { | ||
+ | return $("<hr>").attr("id", "row-" + this.id); | ||
+ | }; | ||
+ | |||
+ | Separator.prototype.value = function() {}; | ||
+ | |||
+ | Separator.prototype.validate = function() { | ||
+ | return true; | ||
+ | }; | ||
+ | |||
+ | return Separator; | ||
+ | |||
+ | })(Input); | ||
+ | |||
+ | Submit = (function(_super) { | ||
+ | |||
+ | __extends(Submit, _super); | ||
+ | |||
+ | function Submit() { | ||
+ | Submit.__super__.constructor.apply(this, arguments); | ||
+ | } | ||
+ | |||
+ | Submit.prototype.generateElementDOM = function() { | ||
+ | return $("<input>").attr("type", "submit").attr("value", this.data.value).attr("class", "btn").click(this.callback, function(e) { | ||
+ | e.preventDefault(); | ||
+ | e.data(e); | ||
+ | return false; | ||
+ | }); | ||
+ | }; | ||
+ | |||
+ | Submit.prototype.value = function() {}; | ||
+ | |||
+ | Submit.prototype.validate = function() { | ||
+ | return true; | ||
+ | }; | ||
+ | |||
+ | return Submit; | ||
+ | |||
+ | })(Input); | ||
+ | |||
+ | NumberInput = (function(_super) { | ||
+ | |||
+ | __extends(NumberInput, _super); | ||
+ | |||
+ | function NumberInput() { | ||
+ | NumberInput.__super__.constructor.apply(this, arguments); | ||
+ | this.type = "number"; | ||
+ | } | ||
+ | |||
+ | NumberInput.prototype.value = function(value) { | ||
+ | if (value === void 0) { | ||
+ | return parseInt(NumberInput.__super__.value.call(this), 10); | ||
+ | } else { | ||
+ | return NumberInput.__super__.value.call(this, parseInt(value, 10)); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | NumberInput.prototype.validate = function(val) { | ||
+ | return !isNaN(val); | ||
+ | }; | ||
+ | |||
+ | return NumberInput; | ||
+ | |||
+ | })(Input); | ||
+ | |||
+ | TemperatureInput = (function(_super) { | ||
+ | |||
+ | __extends(TemperatureInput, _super); | ||
+ | |||
+ | function TemperatureInput() { | ||
+ | TemperatureInput.__super__.constructor.apply(this, arguments); | ||
+ | } | ||
+ | |||
+ | TemperatureInput.prototype.generateElementDOM = function() { | ||
+ | var input; | ||
+ | input = TemperatureInput.__super__.generateElementDOM.call(this); | ||
+ | return $("<div>").attr("class", "input-append").append(input).append($("<span>").attr("class", "add-on").text("\u00B0C")); | ||
+ | }; | ||
+ | |||
+ | return TemperatureInput; | ||
+ | |||
+ | })(NumberInput); | ||
+ | |||
+ | DNAInput = (function(_super) { | ||
+ | |||
+ | __extends(DNAInput, _super); | ||
+ | |||
+ | function DNAInput() { | ||
+ | DNAInput.__super__.constructor.apply(this, arguments); | ||
+ | } | ||
+ | |||
+ | DNAInput.prototype.generateElementDOM = function() { | ||
+ | var textarea; | ||
+ | textarea = $("<textarea>").attr("class", "input-xlarge").attr("id", this.id).on("change", this.callback).on("keyup", this.callback); | ||
+ | textarea.val(this.data.value); | ||
+ | return textarea; | ||
+ | }; | ||
+ | |||
+ | DNAInput.prototype.value = function(value) { | ||
+ | if (value === void 0) { | ||
+ | return DNA.fromString(DNAInput.__super__.value.call(this)); | ||
+ | } else { | ||
+ | return DNAInput.__super__.value.call(this, value.toString()); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | DNAInput.prototype.validate = function(val) { | ||
+ | return val !== void 0; | ||
+ | }; | ||
+ | |||
+ | return DNAInput; | ||
+ | |||
+ | })(Input); | ||
+ | |||
+ | DNASmallInput = (function(_super) { | ||
+ | |||
+ | __extends(DNASmallInput, _super); | ||
+ | |||
+ | function DNASmallInput() { | ||
+ | DNASmallInput.__super__.constructor.apply(this, arguments); | ||
+ | } | ||
+ | |||
+ | DNASmallInput.prototype.generateElementDOM = function() { | ||
+ | var input; | ||
+ | input = DNASmallInput.__super__.generateElementDOM.call(this); | ||
+ | return $("<div>").attr("class", "input-prepend input-append").append($("<span>").attr("class", "add-on").text("5'")).append(this.generateInputElementDOM(this.id, "text", this.data.value, this.callback)).append($("<span>").attr("class", "add-on").text("3'")); | ||
+ | }; | ||
+ | |||
+ | return DNASmallInput; | ||
+ | |||
+ | })(DNAInput); | ||
+ | |||
+ | DNARSInput = (function(_super) { | ||
+ | |||
+ | __extends(DNARSInput, _super); | ||
+ | |||
+ | function DNARSInput() { | ||
+ | DNARSInput.__super__.constructor.apply(this, arguments); | ||
+ | } | ||
+ | |||
+ | DNARSInput.prototype.generateElementDOM = function() { | ||
+ | var input; | ||
+ | input = DNARSInput.__super__.generateElementDOM.call(this); | ||
+ | return $("<div>").attr("class", "input-prepend input-append").append($("<span>").attr("class", "add-on").text("5'")).append(this.generateInputElementDOM(this.id, "text", this.data.value, this.callback)).append($("<span>").attr("class", "add-on").text("3'")).append($("<span>").attr("id", this.id + "-rs").attr("class", "add-on").text("append 2")); | ||
+ | }; | ||
+ | |||
+ | DNARSInput.prototype.value = function(value) { | ||
+ | var val; | ||
+ | val = DNARSInput.__super__.value.call(this, value); | ||
+ | if ((val != null) && val instanceof DNA) { | ||
+ | $("#" + this.id + "-rs").html(val.toString()); | ||
+ | } | ||
+ | if (val === void 0) val = DNA.fromString(""); | ||
+ | return { | ||
+ | dna: val, | ||
+ | rs: $("#" + this.id).val() | ||
+ | }; | ||
+ | }; | ||
+ | |||
+ | return DNARSInput; | ||
+ | |||
+ | })(DNAInput); | ||
+ | |||
+ | SelectInput = (function(_super) { | ||
+ | |||
+ | __extends(SelectInput, _super); | ||
+ | |||
+ | function SelectInput(callback, obj) { | ||
+ | SelectInput.__super__.constructor.apply(this, arguments); | ||
+ | this.choices = obj.choices; | ||
+ | } | ||
+ | |||
+ | SelectInput.prototype.generateElementDOM = function() { | ||
+ | var container, input, | ||
+ | _this = this; | ||
+ | input = SelectInput.__super__.generateElementDOM.call(this); | ||
+ | container = $("<select>").attr("class", "input-medium").attr("id", this.id).on("change", this.callback).on("click", this.callback); | ||
+ | this.choices.forEach(function(_arg) { | ||
+ | var name, value; | ||
+ | name = _arg.name, value = _arg.value; | ||
+ | return container.append($("<option>").attr("value", name).text(value)); | ||
+ | }); | ||
+ | return container; | ||
+ | }; | ||
+ | |||
+ | return SelectInput; | ||
+ | |||
+ | })(Input); | ||
+ | |||
+ | RadioInput = (function(_super) { | ||
+ | |||
+ | __extends(RadioInput, _super); | ||
+ | |||
+ | function RadioInput(callback, obj) { | ||
+ | RadioInput.__super__.constructor.apply(this, arguments); | ||
+ | this.choices = obj.choices; | ||
+ | } | ||
+ | |||
+ | RadioInput.prototype.generateElementDOM = function() { | ||
+ | var container, i, input, | ||
+ | _this = this; | ||
+ | input = RadioInput.__super__.generateElementDOM.call(this); | ||
+ | container = $("<div>"); | ||
+ | i = 0; | ||
+ | this.choices.forEach(function(_arg) { | ||
+ | var elem, help, label, name; | ||
+ | name = _arg.name, label = _arg.label, help = _arg.help; | ||
+ | elem = $("<label>").attr("class", "radio").text(label).prepend($("<input>").attr("checked", _this.data.value === name).attr("type", "radio").attr("id", _this.id + "-" + name).attr("name", _this.id).on("change", _this.callback).on("click", _this.callback)).append(_this.generateHelpDOM(help)); | ||
+ | return container.append(elem); | ||
+ | }); | ||
+ | return container; | ||
+ | }; | ||
+ | |||
+ | RadioInput.prototype.value = function(value) { | ||
+ | var output, | ||
+ | _this = this; | ||
+ | if (value === void 0) { | ||
+ | output = {}; | ||
+ | this.choices.forEach(function(_arg) { | ||
+ | var name; | ||
+ | name = _arg.name; | ||
+ | return output[name] = $("#" + _this.id + "-" + name).attr("checked") === "checked"; | ||
+ | }); | ||
+ | return output; | ||
+ | } else { | ||
+ | if (value instanceof String) { | ||
+ | this.choices.forEach(function(_arg) { | ||
+ | var name; | ||
+ | name = _arg.name; | ||
+ | return $("#" + _this.id + "-" + name).attr("checked", name === value); | ||
+ | }); | ||
+ | } else { | ||
+ | this.choices.forEach(function(_arg) { | ||
+ | var name; | ||
+ | name = _arg.name; | ||
+ | return $("#" + _this.id + "-" + name).attr("checked", value[name]); | ||
+ | }); | ||
+ | } | ||
+ | return value; | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | return RadioInput; | ||
+ | |||
+ | })(Input); | ||
+ | |||
+ | $(function() { | ||
+ | window.output = new Output("Primer 1 (<b>{{primer1Tm}}\u00B0C</b>, {{primer1TailLength}} + {{primer1Length}}bp): 5' → 3'<br />\n<pre class='primer'>{{primer1}}</pre>\nPrimer 2 (<b>{{primer2Tm}}\u00B0C</b>, {{primer2TailLength}} + {{primer2Length}}bp): 5' → 3'<br />\n<pre class='primer'>{{primer2}}</pre>\n<b>Annealing temperature:</b> {{annealingTm}}\u00B0C\n\n<br /><br />\n<h3>Messages</h3>\n{{_messages}}\n\n<br />\n<h3>Digestion result</h3>\n{{RSDisplay:}}\n<br />\n<h3>PCR Product</h3>\n{{DNADisplay:full}}\n<!-- <br />\n<h3>Self Dimer</h3>\n{{SelfDimerDisplay:primers}} -->"); | ||
+ | window.output.attach("#outputAttach"); | ||
+ | window.primerFinder = new PrimerFinder({ | ||
+ | output: function(data, messages) { | ||
+ | window.output.setMessages(messages); | ||
+ | return window.output.set(data); | ||
+ | } | ||
+ | }); | ||
+ | window.form = InputForm.create({ | ||
+ | name: "Primer design helper", | ||
+ | submit: function(data, messages, error) { | ||
+ | return window.primerFinder.calc(data, messages, error); | ||
+ | }, | ||
+ | update: function(data, messages, error) { | ||
+ | return window.primerFinder.calc(data, messages, error); | ||
+ | } | ||
+ | }, [ | ||
+ | { | ||
+ | name: "dna", | ||
+ | label: "DNA Sequence of interest", | ||
+ | type: "DNAInput", | ||
+ | value: "", | ||
+ | help: "Most of the time you should just paste the DNA sequence (in the 5' → 3' direction) you want to extract/edit in this field" | ||
+ | }, { | ||
+ | type: "Separator" | ||
+ | }, { | ||
+ | name: "type", | ||
+ | label: "Primer type", | ||
+ | type: "RadioInput", | ||
+ | choices: [ | ||
+ | { | ||
+ | name: "normal", | ||
+ | label: "Primers that extract the whole sequence", | ||
+ | help: "Running a PCR using these primers will extract the entire sequence" | ||
+ | }, { | ||
+ | name: "rs", | ||
+ | label: "Add restriction sites", | ||
+ | help: "Running a PCR using these primers will result in the sequence with added standardized restriction sites on both sides." | ||
+ | }, { | ||
+ | name: "known", | ||
+ | label: "Add a standard prefix/suffix to the sequence", | ||
+ | help: "Running a PCR using these primers will result in the sequence with added standardized tails on both sides (for example for biobricking)." | ||
+ | }, { | ||
+ | name: "custom", | ||
+ | label: "Add custom tails to the sequence", | ||
+ | help: "Running a PCR using these primers will result in the sequence with added tails on both sides." | ||
+ | } | ||
+ | ], | ||
+ | value: "normal" | ||
+ | }, { | ||
+ | type: "Separator" | ||
+ | }, { | ||
+ | name: "standardTail", | ||
+ | label: "Standardized tails", | ||
+ | type: "SelectInput", | ||
+ | value: "biobrickGene", | ||
+ | choices: [ | ||
+ | { | ||
+ | name: "biobrickGene", | ||
+ | value: "Biobrick (gene)" | ||
+ | }, { | ||
+ | name: "biobrickOther", | ||
+ | value: "Biobrick (other)" | ||
+ | } | ||
+ | ], | ||
+ | visible: function(_arg) { | ||
+ | var type; | ||
+ | type = _arg.type; | ||
+ | return type.known; | ||
+ | } | ||
+ | }, { | ||
+ | name: "randomBases", | ||
+ | label: "Random Bases", | ||
+ | type: "NumberInput", | ||
+ | value: "4", | ||
+ | help: "This is the number of random bases will be added on both ends.", | ||
+ | visible: function(_arg) { | ||
+ | var type; | ||
+ | type = _arg.type; | ||
+ | return type.rs; | ||
+ | } | ||
+ | }, { | ||
+ | name: "leftRS", | ||
+ | label: "Left restriction site", | ||
+ | type: "DNARSInput", | ||
+ | value: "", | ||
+ | help: "You can also enter the name of the restriction enzyme (i.e. EcoRI).", | ||
+ | visible: function(_arg) { | ||
+ | var type; | ||
+ | type = _arg.type; | ||
+ | return type.rs; | ||
+ | } | ||
+ | }, { | ||
+ | name: "rightRS", | ||
+ | label: "Right restriction site", | ||
+ | type: "DNARSInput", | ||
+ | value: "", | ||
+ | help: "You can also enter the name of the restriction enzyme (i.e. EcoRI).", | ||
+ | visible: function(_arg) { | ||
+ | var type; | ||
+ | type = _arg.type; | ||
+ | return type.rs; | ||
+ | } | ||
+ | }, { | ||
+ | name: "leftTail", | ||
+ | label: "Left tail", | ||
+ | type: "DNASmallInput", | ||
+ | value: "", | ||
+ | visible: function(_arg) { | ||
+ | var type; | ||
+ | type = _arg.type; | ||
+ | return type.custom; | ||
+ | } | ||
+ | }, { | ||
+ | name: "rightTail", | ||
+ | label: "Right tail", | ||
+ | type: "DNASmallInput", | ||
+ | value: "", | ||
+ | visible: function(_arg) { | ||
+ | var type; | ||
+ | type = _arg.type; | ||
+ | return type.custom; | ||
+ | } | ||
+ | }, { | ||
+ | type: "Separator", | ||
+ | visible: function(_arg) { | ||
+ | var type; | ||
+ | type = _arg.type; | ||
+ | return !type.normal; | ||
+ | } | ||
+ | }, { | ||
+ | name: "tm", | ||
+ | label: "Target Tm", | ||
+ | type: "TemperatureInput", | ||
+ | value: "50", | ||
+ | help: "The program will try to create primers that have an annealing temperature close to this one." | ||
+ | }, { | ||
+ | value: "Generate", | ||
+ | type: "Submit" | ||
+ | } | ||
+ | ]); | ||
+ | return window.form.attach($("#formAttach")); | ||
+ | }); | ||
window.tails = [ | window.tails = [ | ||
- | + | { | |
- | + | name: "Biobrick (gene)", | |
- | + | value: "biobrickGene", | |
- | + | primer1Tail: "GTT TCT TCG AAT TCG CGG CCG CTT CTA G", | |
- | + | primer2Tail: "GTT TCT TCC TGC AGC GGC CGC TAC TAG TA TTA TTA", | |
- | + | messages: ["Don't forget to include the first ATG and to exclude the stop codon!", "The recommended minimum length of a BioBrick primer is 20bp", "The recommended Tm of a BioBrick primer is between 55 and 65\u00B0C", "Check that the following restriction sites are absent: EcoRI, SpeI, XbaI, PstI, NotI", "Check out <a href='http://openwetware.org/wiki/Synthetic_Biology:BioBricks/Part_fabrication'>OpenWetWare's reference page</a>"], | |
- | + | validate: function(primer1, primer2, dna) { | |
- | + | var end, start; | |
- | + | start = primer1.substr(0, 3).toUpperCase(); | |
- | + | if (start !== "ATG") return "<b>The starting ATG seems to be absent.</b>"; | |
- | + | end = primer2.substr(0, 3).toUpperCase(); | |
- | + | if (end === "TTA" || end === "TCA" || end === "CTA") { | |
- | + | return "<b>Remove the stop codons from the sequence!</b>"; | |
- | + | } | |
- | + | if (dna.containsRS(["EcoRI", "SpeI", "XbaI", "PstI", "NotI"])) { | |
- | + | return "<b>One of the following restriction sites is present (and shouldn't be): EcoRI, SpeI, XbaI, PstI, NotI</b>"; | |
- | + | } | |
- | + | return; | |
- | + | } | |
- | + | }, { | |
- | + | name: "Biobrick (other)", | |
- | + | value: "biobrickOther", | |
- | + | primer1Tail: "GTT TCT TCG AAT TCG CGG CCG CTT CTA GAG", | |
- | + | primer2Tail: "GTT TCT TCC TGC AGC GGC CGC TAC TAG TA", | |
- | + | messages: ["The recommended minimum length of a BioBrick primer is 20bp", "The recommended Tm of a BioBrick primer is between 55 and 65\u00B0C", "Check that the following restriction sites are absent: EcoRI, SpeI, XbaI, PstI, NotI", "Check out <a href='http://openwetware.org/wiki/Synthetic_Biology:BioBricks/Part_fabrication'>OpenWetWare's reference page</a>"], | |
- | + | validate: function(primer1, primer2, dna) { | |
- | + | if (dna.containsRS(["EcoRI", "SpeI", "XbaI", "PstI", "NotI"])) { | |
- | + | return "<b>One of the following restriction sites is present (and shouldn't be): EcoRI, SpeI, XbaI, PstI, NotI</b>"; | |
- | + | } | |
- | + | return; | |
- | + | } | |
- | + | } | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
]; | ]; | ||
- | + | var DNADisplay, Display, Output, RSDisplay, SelfDimerDisplay, | |
- | + | __hasProp = Object.prototype.hasOwnProperty, | |
- | + | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }; | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | }; | + | |
- | window.pad = function(str, width, | + | Output = (function() { |
- | + | ||
- | + | Output.i = 0; | |
- | + | ||
- | + | Output.j = 0; | |
- | + | ||
- | + | function Output(template) { | |
- | + | this.template = template; | |
- | + | this.id = "output-" + Output.i++; | |
- | + | this.fields = {}; | |
- | } | + | this.matches = {}; |
+ | this.displays = {}; | ||
+ | this.parsedTemplate = this.parseTemplate(this.template); | ||
+ | } | ||
+ | |||
+ | Output.prototype.attach = function(elem) { | ||
+ | var displayClass, displayObj, k, v, _ref, _results; | ||
+ | this.matches = {}; | ||
+ | $(elem).html(this.parsedTemplate); | ||
+ | _ref = this.displays; | ||
+ | _results = []; | ||
+ | for (k in _ref) { | ||
+ | v = _ref[k]; | ||
+ | _results.push((function() { | ||
+ | var _i, _len, _ref2, _results2; | ||
+ | _results2 = []; | ||
+ | for (_i = 0, _len = v.length; _i < _len; _i++) { | ||
+ | _ref2 = v[_i], displayClass = _ref2.displayClass, displayObj = _ref2.displayObj; | ||
+ | _results2.push(displayObj.attach("." + displayClass)); | ||
+ | } | ||
+ | return _results2; | ||
+ | })()); | ||
+ | } | ||
+ | return _results; | ||
+ | }; | ||
+ | |||
+ | Output.prototype.setMessages = function(messages) { | ||
+ | var output; | ||
+ | output = messages; | ||
+ | if (messages.length === 0) output = ["No messages"]; | ||
+ | return this.getFields("_messages").html("<ul><li>" + output.join("</li><li>") + "</li></ul>"); | ||
+ | }; | ||
+ | |||
+ | Output.prototype.wipe = function() { | ||
+ | var k, v, _ref; | ||
+ | _ref = this.fields; | ||
+ | for (k in _ref) { | ||
+ | v = _ref[k]; | ||
+ | this.updateValue(k, void 0); | ||
+ | } | ||
+ | return this.fields = {}; | ||
+ | }; | ||
+ | |||
+ | Output.prototype.set = function(values) { | ||
+ | var k, v; | ||
+ | for (k in values) { | ||
+ | v = values[k]; | ||
+ | this.updateValue(k, v); | ||
+ | } | ||
+ | this.updateValue("__all__", this.fields); | ||
+ | return this.fields; | ||
+ | }; | ||
+ | |||
+ | Output.prototype.updateValue = function(k, v) { | ||
+ | var displayObj, _i, _len, _ref, _results; | ||
+ | if (this.fields[k] !== v) { | ||
+ | this.fields[k] = v; | ||
+ | if (v != null) { | ||
+ | this.getFields(k).html(v.toString()); | ||
+ | } else { | ||
+ | this.getFields(k).html(""); | ||
+ | } | ||
+ | } | ||
+ | if (this.displays[k] != null) { | ||
+ | _ref = this.displays[k]; | ||
+ | _results = []; | ||
+ | for (_i = 0, _len = _ref.length; _i < _len; _i++) { | ||
+ | displayObj = _ref[_i].displayObj; | ||
+ | _results.push(displayObj.set(v)); | ||
+ | } | ||
+ | return _results; | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | Output.prototype.getFields = function(name) { | ||
+ | if (!this.matches[name]) { | ||
+ | this.matches[name] = $("." + this.id + "-match-" + name); | ||
+ | } | ||
+ | return this.matches[name]; | ||
+ | }; | ||
+ | |||
+ | Output.prototype.parseTemplate = function(template) { | ||
+ | var c1, c2, display, displayClass, displayObj, displayPart, i, inName, key, name, output, skipNext, started, _ref; | ||
+ | c1 = template[0]; | ||
+ | c2 = template[1]; | ||
+ | output = template + ""; | ||
+ | skipNext = false; | ||
+ | inName = false; | ||
+ | started = -1; | ||
+ | name = ""; | ||
+ | for (i = 1, _ref = template.length - 1; 1 <= _ref ? i <= _ref : i >= _ref; 1 <= _ref ? i++ : i--) { | ||
+ | c2 = template[i]; | ||
+ | if (!skipNext) { | ||
+ | if (c1 === "{" && c2 === "{") { | ||
+ | if (inName) { | ||
+ | throw new Error("Invalid template, opened brackets in an already opened context"); | ||
+ | } | ||
+ | name = ""; | ||
+ | started = i; | ||
+ | skipNext = true; | ||
+ | inName = true; | ||
+ | } else if (c1 === "}" && c2 === "}") { | ||
+ | if (!inName) { | ||
+ | throw new Error("Invalid template, closed unopened brackets"); | ||
+ | } | ||
+ | displayPart = name.indexOf(":"); | ||
+ | if (displayPart >= 0) { | ||
+ | display = name.substr(0, displayPart); | ||
+ | key = name.substr(displayPart + 1); | ||
+ | if (key === "") key = "__all__"; | ||
+ | displayClass = this.id + "-display-" + display + "-" + key; | ||
+ | output = output.replace("{{" + name + "}}", "<span class='" + displayClass + "'></span>"); | ||
+ | displayObj = new window[display](key); | ||
+ | if (!(this.displays[key] != null)) this.displays[key] = []; | ||
+ | this.displays[key].push({ | ||
+ | displayClass: displayClass, | ||
+ | displayObj: displayObj | ||
+ | }); | ||
+ | } else { | ||
+ | output = output.replace("{{" + name + "}}", "<span class='" + this.id + "-match-" + name + "'></span>"); | ||
+ | } | ||
+ | skipNext = true; | ||
+ | inName = false; | ||
+ | } else { | ||
+ | if (inName) name += c1; | ||
+ | } | ||
+ | } else { | ||
+ | skipNext = false; | ||
+ | } | ||
+ | c1 = c2; | ||
+ | } | ||
+ | return output; | ||
+ | }; | ||
+ | |||
+ | return Output; | ||
+ | |||
+ | })(); | ||
+ | |||
+ | Display = (function() { | ||
+ | |||
+ | function Display(key) { | ||
+ | this.key = key; | ||
+ | this.value = void 0; | ||
+ | } | ||
+ | |||
+ | Display.prototype.attach = function(id) { | ||
+ | throw new Error("Not implemented"); | ||
+ | }; | ||
+ | |||
+ | Display.prototype.set = function(values) { | ||
+ | return this.value = values[this.key]; | ||
+ | }; | ||
+ | |||
+ | Display.prototype.pad = function(str, width, padLeft) { | ||
+ | var length; | ||
+ | if (str == null) str = ""; | ||
+ | if (width == null) width = 5; | ||
+ | if (padLeft == null) padLeft = false; | ||
+ | str = str.toString(); | ||
+ | length = str.length; | ||
+ | while (length < width) { | ||
+ | if (padLeft) { | ||
+ | str = " " + str; | ||
+ | } else { | ||
+ | str += " "; | ||
+ | } | ||
+ | length++; | ||
+ | } | ||
+ | return str; | ||
+ | }; | ||
+ | |||
+ | Display.prototype.wrap = function(str, width) { | ||
+ | var i, length, output; | ||
+ | if (str == null) str = ""; | ||
+ | if (width == null) width = 40; | ||
+ | str = str.toString(); | ||
+ | length = str.length; | ||
+ | output = []; | ||
+ | i = 0; | ||
+ | while (length >= width) { | ||
+ | output.push(str.substr(i, width)); | ||
+ | length -= width; | ||
+ | i += width; | ||
+ | } | ||
+ | if (length > 0) output.push(str.substr(i)); | ||
+ | return output; | ||
+ | }; | ||
+ | |||
+ | return Display; | ||
+ | |||
+ | })(); | ||
+ | |||
+ | DNADisplay = (function(_super) { | ||
+ | |||
+ | __extends(DNADisplay, _super); | ||
+ | |||
+ | DNADisplay.i = 0; | ||
+ | |||
+ | function DNADisplay(key) { | ||
+ | var that, | ||
+ | _this = this; | ||
+ | DNADisplay.__super__.constructor.apply(this, arguments); | ||
+ | this["class"] = "dnadisplay-" + DNADisplay.i++; | ||
+ | this.objects = []; | ||
+ | that = this; | ||
+ | this.dom = $("<div>").attr("class", this["class"]).append($("<label>").append($("<input>").attr("class", this["class"] + "-toggleLines").attr("type", "checkbox").attr("checked", "on").on("change", function() { | ||
+ | if ($("." + _this["class"] + "-toggleLines").attr("checked")) { | ||
+ | $("." + _this["class"] + "-lines").hide(); | ||
+ | $("." + _this["class"] + "-numberedLines").show(); | ||
+ | } else { | ||
+ | $("." + _this["class"] + "-lines").show(); | ||
+ | $("." + _this["class"] + "-numberedLines").hide(); | ||
+ | } | ||
+ | return; | ||
+ | })).append(" Line Numbers")).append($("<pre>").attr("class", this["class"] + "-lines product").hide()).append($("<pre>").attr("class", this["class"] + "-numberedLines product")); | ||
+ | } | ||
+ | |||
+ | DNADisplay.prototype.attach = function(elem) { | ||
+ | $(elem).append(this.dom); | ||
+ | return this.objects = $("." + this["class"]); | ||
+ | }; | ||
+ | |||
+ | DNADisplay.prototype.set = function(value) { | ||
+ | var i, line, lines, numberWidth, numberedLines, outputLines, width, widthNumbered, _len; | ||
+ | width = 60; | ||
+ | numberWidth = 6; | ||
+ | widthNumbered = 40; | ||
+ | lines = this.wrap(value, width); | ||
+ | numberedLines = this.wrap(value, widthNumbered); | ||
+ | outputLines = []; | ||
+ | for (i = 0, _len = numberedLines.length; i < _len; i++) { | ||
+ | line = numberedLines[i]; | ||
+ | if (line.length > 0) { | ||
+ | outputLines[i] = this.pad(i * widthNumbered + 1, numberWidth, true) + " - " + this.pad((i + 1) * widthNumbered, numberWidth) + " " + line; | ||
+ | } | ||
+ | } | ||
+ | this.full = lines.join("\n"); | ||
+ | this.fullNumbered = outputLines.join("\n"); | ||
+ | $("." + this["class"] + "-lines").text(this.full); | ||
+ | return $("." + this["class"] + "-numberedLines").text(this.fullNumbered); | ||
+ | }; | ||
+ | |||
+ | return DNADisplay; | ||
+ | |||
+ | })(Display); | ||
+ | |||
+ | RSDisplay = (function(_super) { | ||
+ | |||
+ | __extends(RSDisplay, _super); | ||
+ | |||
+ | RSDisplay.i = 0; | ||
+ | |||
+ | function RSDisplay(key) { | ||
+ | var that; | ||
+ | RSDisplay.__super__.constructor.apply(this, arguments); | ||
+ | this["class"] = "rsdisplay-" + RSDisplay.i++; | ||
+ | this.objects = []; | ||
+ | that = this; | ||
+ | this.dom = $("<div>").attr("class", this["class"]).append($("<pre>").attr("class", this["class"] + "-pre")); | ||
+ | } | ||
+ | |||
+ | RSDisplay.prototype.attach = function(elem) { | ||
+ | $(elem).append(this.dom); | ||
+ | return this.objects = $("." + this["class"]); | ||
+ | }; | ||
+ | |||
+ | RSDisplay.prototype.set = function(_arg) { | ||
+ | var c, complement, full, left, leftCompStr, leftCut, leftRS, leftRSInput, leftStr, overhang, randomBases, right, rightCompStr, rightCut, rightRS, rightRSInput, rightStr, rs, text, type, _i, _j, _len, _len2, _ref, _ref2; | ||
+ | rs = _arg.rs, type = _arg.type, leftRS = _arg.leftRS, rightRS = _arg.rightRS; | ||
+ | if (!type.rs) { | ||
+ | $("." + this["class"] + "-pre").html("Not applicable"); | ||
+ | return; | ||
+ | } | ||
+ | leftRSInput = leftRS.rs; | ||
+ | rightRSInput = rightRS.rs; | ||
+ | left = rs.left, right = rs.right, full = rs.full, randomBases = rs.randomBases; | ||
+ | leftCut = DNA.getCutPosition(leftRSInput); | ||
+ | if (leftCut < 0) { | ||
+ | leftRS = DNA.fromString(left).findRS(true); | ||
+ | leftCut = DNA.getCutPosition(leftRS); | ||
+ | } else { | ||
+ | leftRS = leftRSInput; | ||
+ | } | ||
+ | if (leftRS.length === 0) leftRS = "?"; | ||
+ | rightCut = DNA.getCutPosition(rightRSInput); | ||
+ | if (rightCut < 0) { | ||
+ | rightRS = DNA.fromString(right).findRS(true); | ||
+ | rightCut = DNA.getCutPosition(rightRS); | ||
+ | } else { | ||
+ | rightRS = rightRSInput; | ||
+ | } | ||
+ | if (rightRS.length === 0) rightRS = "?"; | ||
+ | leftStr = full.substr(0, randomBases + left.length + 10); | ||
+ | rightStr = full.substr(full.length - (randomBases + right.length + 10)); | ||
+ | if (randomBases > 6) { | ||
+ | leftStr = "..." + leftStr.substr(randomBases - 3); | ||
+ | rightStr = rightStr.substr(0, rightStr.length - (randomBases - 3)) + "..."; | ||
+ | randomBases = 6; | ||
+ | } | ||
+ | leftCompStr = ""; | ||
+ | rightCompStr = ""; | ||
+ | complement = function(c) { | ||
+ | switch (c) { | ||
+ | case "A": | ||
+ | return "T"; | ||
+ | case "T": | ||
+ | return "A"; | ||
+ | case "C": | ||
+ | return "G"; | ||
+ | case "G": | ||
+ | return "C"; | ||
+ | default: | ||
+ | return c; | ||
+ | } | ||
+ | }; | ||
+ | _ref = leftStr.split(""); | ||
+ | for (_i = 0, _len = _ref.length; _i < _len; _i++) { | ||
+ | c = _ref[_i]; | ||
+ | leftCompStr += complement(c); | ||
+ | } | ||
+ | _ref2 = rightStr.split(""); | ||
+ | for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { | ||
+ | c = _ref2[_j]; | ||
+ | rightCompStr += complement(c); | ||
+ | } | ||
+ | if (leftCut >= 0) { | ||
+ | overhang = Math.ceil(Math.abs(left.length / 2 - leftCut)) + 1; | ||
+ | leftStr = leftStr.substr(0, randomBases + leftCut) + this.pad("", overhang + 1) + leftStr.substr(randomBases + leftCut); | ||
+ | leftCompStr = leftCompStr.substr(0, randomBases + left.length - leftCut) + this.pad("", overhang + 1) + leftCompStr.substr(randomBases + left.length - leftCut); | ||
+ | } else { | ||
+ | leftStr = leftStr.substr(0, randomBases) + " " + leftStr.substr(randomBases, left.length) + " " + leftStr.substr(randomBases + left.length); | ||
+ | leftCompStr = leftCompStr.substr(0, randomBases) + " " + leftCompStr.substr(randomBases, left.length) + " " + leftCompStr.substr(randomBases + left.length); | ||
+ | } | ||
+ | if (rightCut >= 0) { | ||
+ | overhang = Math.ceil(Math.abs(right.length / 2 - rightCut)) + 1; | ||
+ | rightStr = rightStr.substr(0, rightStr.length - (randomBases + right.length - rightCut)) + this.pad("", overhang + 1) + rightStr.substr(rightStr.length - (randomBases + right.length - rightCut)); | ||
+ | rightCompStr = rightCompStr.substr(0, rightCompStr.length - (randomBases + rightCut)) + this.pad("", overhang + 1) + rightCompStr.substr(rightCompStr.length - (randomBases + rightCut)); | ||
+ | } else { | ||
+ | rightStr = rightStr.substr(0, rightStr.length - (randomBases + right.length)) + " " + rightStr.substr(rightStr.length - (randomBases + right.length), right.length) + " " + rightStr.substr(rightStr.length - randomBases); | ||
+ | rightCompStr = rightCompStr.substr(0, rightCompStr.length - (randomBases + right.length)) + " " + rightCompStr.substr(rightCompStr.length - (randomBases + right.length), right.length) + " " + rightCompStr.substr(rightCompStr.length - randomBases); | ||
+ | } | ||
+ | text = []; | ||
+ | text.push("Left restriction site: " + leftRS + (leftCut < 0 ? " (cutting site unknown)" : "")); | ||
+ | text.push("Right restriction site: " + rightRS + (rightCut < 0 ? " (cutting site unknown)" : "")); | ||
+ | text.push(""); | ||
+ | text.push("5'" + this.pad("3'", leftStr.length + rightStr.length + 1, true)); | ||
+ | text.push(leftStr + "..." + rightStr); | ||
+ | text.push(leftCompStr + "..." + rightCompStr); | ||
+ | text.push("3'" + this.pad("5'", leftStr.length + rightStr.length + 1, true)); | ||
+ | return $("." + this["class"] + "-pre").text(text.join("\n")); | ||
+ | }; | ||
+ | |||
+ | return RSDisplay; | ||
+ | |||
+ | })(Display); | ||
+ | |||
+ | SelfDimerDisplay = (function(_super) { | ||
+ | |||
+ | __extends(SelfDimerDisplay, _super); | ||
+ | |||
+ | SelfDimerDisplay.i = 0; | ||
+ | |||
+ | function SelfDimerDisplay(key) { | ||
+ | var that, | ||
+ | _this = this; | ||
+ | SelfDimerDisplay.__super__.constructor.apply(this, arguments); | ||
+ | this["class"] = "selfdimerdisplay-" + RSDisplay.i++; | ||
+ | this.objects = []; | ||
+ | that = this; | ||
+ | this.dom = $("<div>").attr("class", this["class"]).append($("<span>").text("This calculation is quite heavy and is done on demand. Press on the button to start the calculations:")).append("<br>").append($("<input>").attr("type", "button").attr("value", "Calculate").attr("class", "btn").click(function() { | ||
+ | return _this.click(); | ||
+ | })).append("<br>").append("<br>").append($("<pre>").attr("class", this["class"] + "-pre").html("Press the calculate button to update this data")); | ||
+ | } | ||
+ | |||
+ | SelfDimerDisplay.prototype.attach = function(elem) { | ||
+ | $(elem).append(this.dom); | ||
+ | return this.objects = $("." + this["class"] + "-pre"); | ||
+ | }; | ||
+ | |||
+ | SelfDimerDisplay.prototype.set = function(_arg) { | ||
+ | var primer1, primer2; | ||
+ | primer1 = _arg.primer1, primer2 = _arg.primer2; | ||
+ | if (primer1 !== this.primer1 || primer2 !== this.primer2) { | ||
+ | this.primer1 = primer1; | ||
+ | this.primer2 = primer2; | ||
+ | return this.objects.html("Press the calculate button to update this data"); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | SelfDimerDisplay.prototype.click = function() { | ||
+ | var report; | ||
+ | report = this.generateReport("Primer 1", this.primer1) + "\n\n" + this.generateReport("Primer 2", this.primer2); | ||
+ | return this.objects.html(report); | ||
+ | }; | ||
+ | |||
+ | SelfDimerDisplay.prototype.generateReport = function(name, primer) { | ||
+ | var character, folds, lineC, lineN, lines, matches, number, p, pairs, rna, _i, _j, _len, _ref, _ref2, _results; | ||
+ | lines = []; | ||
+ | lines.push(name + ":"); | ||
+ | rna = RNA.fromString(primer); | ||
+ | _ref = rna.findKnots(), matches = _ref.matches, folds = _ref.folds; | ||
+ | lines.push("Amount of matches: " + matches); | ||
+ | pairs = _.zip((function() { | ||
+ | _results = []; | ||
+ | for (var _i = 1, _ref2 = primer.length; 1 <= _ref2 ? _i <= _ref2 : _i >= _ref2; 1 <= _ref2 ? _i++ : _i--){ _results.push(_i); } | ||
+ | return _results; | ||
+ | }).apply(this), primer.split("")); | ||
+ | lineC = ""; | ||
+ | lineN = ""; | ||
+ | for (_j = 0, _len = pairs.length; _j < _len; _j++) { | ||
+ | p = pairs[_j]; | ||
+ | number = p[0] + " "; | ||
+ | character = this.pad(p[1], number.length - 1, true) + " "; | ||
+ | if (lineC.length + number.length > 60) { | ||
+ | lines.push(lineC); | ||
+ | lines.push(lineN); | ||
+ | lines.push(""); | ||
+ | lineC = ""; | ||
+ | lineN = ""; | ||
+ | } | ||
+ | lineC += character; | ||
+ | lineN += number; | ||
+ | } | ||
+ | if (lineC.length > 0) { | ||
+ | lines.push(lineC); | ||
+ | lines.push(lineN); | ||
+ | lines.push(""); | ||
+ | } | ||
+ | return lines.join("\n"); | ||
+ | }; | ||
- | + | return SelfDimerDisplay; | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | })(Display); | |
- | + | var Primer; | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
Primer = (function() { | Primer = (function() { | ||
Line 703: | Line 1,898: | ||
function Primer(data) { | function Primer(data) { | ||
- | this.seq = DNA.fromString(data); | + | if (data instanceof DNA) { |
+ | this.seq = data.clone(); | ||
+ | } else { | ||
+ | this.seq = DNA.fromString(data); | ||
+ | } | ||
this.tail1 = new DNA([]); | this.tail1 = new DNA([]); | ||
this.tail2 = new DNA([]); | this.tail2 = new DNA([]); | ||
Line 723: | Line 1,922: | ||
Primer.prototype.generate = function(goal) { | Primer.prototype.generate = function(goal) { | ||
- | var diff, l1, l2, maxLength, primer1Length, primer1Temp, primer2Length, primer2Temp, rand1, rand2, revCompl, temp | + | var diff, l1, l2, maxLength, primer1Length, primer1Temp, primer2Length, primer2Temp, rand1, rand2, revCompl, temp, _ref, _ref2; |
if (2 * Primer.minLength > this.seq.length()) { | if (2 * Primer.minLength > this.seq.length()) { | ||
throw new Error("The given sequence is too short"); | throw new Error("The given sequence is too short"); | ||
Line 738: | Line 1,937: | ||
primer2Length = -1; | primer2Length = -1; | ||
primer2Temp = 0; | primer2Temp = 0; | ||
- | for (l1 | + | for (l1 = _ref = Primer.minLength; _ref <= maxLength ? l1 <= maxLength : l1 >= maxLength; _ref <= maxLength ? l1++ : l1--) { |
temp = this.seq.snip(l1).tm(); | temp = this.seq.snip(l1).tm(); | ||
diff = Math.abs(temp - goal); | diff = Math.abs(temp - goal); | ||
Line 745: | Line 1,944: | ||
primer1Temp = temp; | primer1Temp = temp; | ||
} | } | ||
- | if (temp >= goal) | + | if (temp >= goal) break; |
- | + | ||
- | + | ||
} | } | ||
- | for (l2 = | + | for (l2 = _ref2 = Primer.minLength; _ref2 <= maxLength ? l2 <= maxLength : l2 >= maxLength; _ref2 <= maxLength ? l2++ : l2--) { |
temp = revCompl.snip(l2).tm(); | temp = revCompl.snip(l2).tm(); | ||
diff = Math.abs(temp - goal); | diff = Math.abs(temp - goal); | ||
Line 756: | Line 1,953: | ||
primer2Temp = temp; | primer2Temp = temp; | ||
} | } | ||
- | if (temp >= goal) | + | if (temp >= goal) break; |
- | + | ||
- | + | ||
} | } | ||
if (primer1Length < 0 || primer2Length < 0) { | if (primer1Length < 0 || primer2Length < 0) { | ||
Line 766: | Line 1,961: | ||
rand2 = DNA.fromRandom(this.randEdgeSize); | rand2 = DNA.fromRandom(this.randEdgeSize); | ||
return { | return { | ||
+ | rs: { | ||
+ | left: this.tail1.toString(), | ||
+ | right: this.tail2.toString(), | ||
+ | randomBases: this.randEdgeSize, | ||
+ | full: rand1.toString() + this.tail1.toString() + this.seq.toString() + this.tail2.reverseComplement().toString() + rand2.reverseComplement().toString() | ||
+ | }, | ||
primer1: rand1.toString() + " " + this.tail1.toString() + " " + this.seq.snip(primer1Length).toString(), | primer1: rand1.toString() + " " + this.tail1.toString() + " " + this.seq.snip(primer1Length).toString(), | ||
+ | primers: { | ||
+ | primer1: rand1.toString() + this.tail1.toString() + this.seq.snip(primer1Length).toString(), | ||
+ | primer2: rand2.toString() + this.tail2.toString() + revCompl.snip(primer2Length).toString() | ||
+ | }, | ||
primer1NoTail: this.seq.snip(primer1Length).toString(), | primer1NoTail: this.seq.snip(primer1Length).toString(), | ||
- | + | primer1Tm: primer1Temp, | |
- | + | primer1Length: primer1Length, | |
- | + | primer1TailLength: rand1.length() + this.tail1.length(), | |
primer2: rand2.toString() + " " + this.tail2.toString() + " " + revCompl.snip(primer2Length).toString(), | primer2: rand2.toString() + " " + this.tail2.toString() + " " + revCompl.snip(primer2Length).toString(), | ||
primer2NoTail: revCompl.snip(primer2Length).toString(), | primer2NoTail: revCompl.snip(primer2Length).toString(), | ||
- | + | primer2Tm: primer2Temp, | |
- | + | primer2Length: primer2Length, | |
- | + | primer2TailLength: rand2.length() + this.tail2.length(), | |
full: rand1.toString() + this.tail1.toString() + this.seq.toString() + this.tail2.reverseComplement().toString() + rand2.reverseComplement().toString() | full: rand1.toString() + this.tail1.toString() + this.seq.toString() + this.tail2.reverseComplement().toString() + rand2.reverseComplement().toString() | ||
}; | }; | ||
Line 781: | Line 1,986: | ||
return Primer; | return Primer; | ||
+ | |||
+ | })(); | ||
+ | var PrimerFinder; | ||
+ | |||
+ | PrimerFinder = (function() { | ||
+ | |||
+ | function PrimerFinder(_arg) { | ||
+ | this.output = _arg.output; | ||
+ | this.defaults = { | ||
+ | primer1: "No data", | ||
+ | primer1Tm: "?", | ||
+ | primer1Length: 0, | ||
+ | primer2: "No data", | ||
+ | primer2Tm: "?", | ||
+ | primer2Length: 0, | ||
+ | annealingTm: "?" | ||
+ | }; | ||
+ | } | ||
+ | |||
+ | PrimerFinder.prototype.calc = function(data, messages, error) { | ||
+ | var k, match1, match2, p, primers, result, tail, tail1, tail2, v, validate, _i, _len, _ref, _ref2; | ||
+ | if (error) { | ||
+ | messages = ["A fatal error occured:"].concat(messages); | ||
+ | this.output(data, messages); | ||
+ | return; | ||
+ | } | ||
+ | _ref = this.defaults; | ||
+ | for (k in _ref) { | ||
+ | v = _ref[k]; | ||
+ | if (!(data[k] != null)) data[k] = v; | ||
+ | } | ||
+ | try { | ||
+ | p = new Primer(data.dna); | ||
+ | validate = function(primer1, primer2, dna) {}; | ||
+ | if (data.type.custom) p.setTails(data.leftTail, data.rightTail); | ||
+ | if (data.type.rs) { | ||
+ | match1 = data.leftRS.dna.findRS(); | ||
+ | match2 = data.rightRS.dna.findRS(); | ||
+ | if ((match1 != null) && match1.length > 0) { | ||
+ | messages.push("The left restriction site is " + match1); | ||
+ | } else { | ||
+ | messages.push("The left restriction site wasn't recognized"); | ||
+ | } | ||
+ | if ((match2 != null) && match2.length > 0) { | ||
+ | messages.push("The right restriction site is " + match2); | ||
+ | } else { | ||
+ | messages.push("The right restriction site wasn't recognized"); | ||
+ | } | ||
+ | p.setTails(data.leftRS.dna, data.rightRS.dna.reverseComplement()); | ||
+ | p.addRandomEdges(data.randomBases); | ||
+ | messages.push("Click the 'Generate' button to regenerate the random bases added on the end of the primers."); | ||
+ | messages.push("In the case of a plasmid where the piece between A and B is cut out, and A is used for the primer 1 RS (and B for the second).\nThe generated primers will put the RS such that the given DNA starts at the A side and ends at the B side (in short,\nit will probably work as you expected, but still check what happens with some other tool)."); | ||
+ | } | ||
+ | if (data.type.known) { | ||
+ | tail1 = ""; | ||
+ | tail2 = ""; | ||
+ | _ref2 = window.tails; | ||
+ | for (_i = 0, _len = _ref2.length; _i < _len; _i++) { | ||
+ | tail = _ref2[_i]; | ||
+ | if (tail.value === data.standardTail) { | ||
+ | tail1 = tail.primer1Tail; | ||
+ | tail2 = tail.primer2Tail; | ||
+ | if (tail.messages != null) messages = [].concat(tail.messages); | ||
+ | if (tail.validate != null) validate = tail.validate; | ||
+ | } | ||
+ | } | ||
+ | p.setTails(DNA.fromString(tail1), DNA.fromString(tail2)); | ||
+ | } | ||
+ | primers = p.generate(data.tm); | ||
+ | result = validate(primers.primer1NoTail, primers.primer2NoTail, data.dna); | ||
+ | if (result != null) messages.push(result); | ||
+ | for (k in primers) { | ||
+ | v = primers[k]; | ||
+ | data[k] = v; | ||
+ | } | ||
+ | messages = messages; | ||
+ | } catch (e) { | ||
+ | messages = [e.message]; | ||
+ | } | ||
+ | data.annealingTm = Math.min(data.primer1Tm, data.primer2Tm); | ||
+ | if (Math.abs(data.primer1Tm - data.primer2Tm) > 5) { | ||
+ | messages.push("The difference between the primer's Tm is more than 5\u00B0C"); | ||
+ | } | ||
+ | if (data.annealingTm > 70) { | ||
+ | messages.push("Consider using the two-step PCR protocol"); | ||
+ | } | ||
+ | if (data.annealingTm > 72) { | ||
+ | messages.push("Annealing temperature shouldn't exceed 72\u00B0C"); | ||
+ | data.annealingTm = 72; | ||
+ | } | ||
+ | if (data.annealingTm < 45) { | ||
+ | messages.push("The annealing temperature should be more than 45\u00B0C"); | ||
+ | } | ||
+ | return this.output(data, messages); | ||
+ | }; | ||
+ | |||
+ | return PrimerFinder; | ||
})(); | })(); | ||
Line 805: | Line 2,107: | ||
Note: Just like for the DNA class, you probably didn't want to use the constructor. Use fromDNA or fromString instead! | Note: Just like for the DNA class, you probably didn't want to use the constructor. Use fromDNA or fromString instead! | ||
*/ | */ | ||
- | |||
function RNA(data) { | function RNA(data) { | ||
Line 824: | Line 2,125: | ||
RNA.prototype.isTooSelfComplementary = function() { | RNA.prototype.isTooSelfComplementary = function() { | ||
- | var matches; | + | var fold, folds, i, matches, seq, _i, _len, _ref, _ref2; |
- | + | _ref = this.findKnots(), matches = _ref.matches, folds = _ref.folds; | |
- | + | for (_i = 0, _len = folds.length; _i < _len; _i++) { | |
+ | fold = folds[_i]; | ||
+ | seq = []; | ||
+ | console.log(fold); | ||
+ | for (i = 0, _ref2 = fold.length - 1; 0 <= _ref2 ? i <= _ref2 : i >= _ref2; 0 <= _ref2 ? i++ : i--) { | ||
+ | if (fold[i] === "(") { | ||
+ | if (i > 0 && fold[i - 1] === "(") { | ||
+ | seq[seq.length - 1] += this.data[i]; | ||
+ | } else { | ||
+ | seq[seq.length] = this.data[i]; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | console.log(seq); | ||
+ | } | ||
+ | return false; | ||
}; | }; | ||
RNA.prototype.findKnots = function(minSize) { | RNA.prototype.findKnots = function(minSize) { | ||
- | var HM, M, a, a1, a2, createSolution, hashSet, hm, hs, i, j, k, l, m, max, output, p, sol, str, tmp, _i, _j, _k, _l, _len | + | var HM, M, a, a1, a2, createSolution, hashSet, hm, hs, i, j, k, l, m, max, output, p, sol, str, tmp, _i, _j, _k, _l, _len, _len2, _len3, _len4, _ref, _ref10, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, |
_this = this; | _this = this; | ||
- | if (minSize == null) | + | if (minSize == null) minSize = 2; |
- | + | if (this.knots !== void 0) return this.knots; | |
- | + | ||
- | if (this.knots !== void 0) | + | |
- | + | ||
- | + | ||
createSolution = function() { | createSolution = function() { | ||
- | var i | + | var i, _ref, _results; |
_results = []; | _results = []; | ||
- | for (i | + | for (i = 1, _ref = _this.n; 1 <= _ref ? i <= _ref : i >= _ref; 1 <= _ref ? i++ : i--) { |
_results.push("."); | _results.push("."); | ||
} | } | ||
Line 847: | Line 2,159: | ||
}; | }; | ||
HM = []; | HM = []; | ||
- | for (l | + | for (l = 0, _ref = this.n - 1; 0 <= _ref ? l <= _ref : l >= _ref; 0 <= _ref ? l++ : l--) { |
hm = []; | hm = []; | ||
- | for (m | + | for (m = 0, _ref2 = this.n - 1; 0 <= _ref2 ? m <= _ref2 : m >= _ref2; 0 <= _ref2 ? m++ : m--) { |
hm[m] = [createSolution()]; | hm[m] = [createSolution()]; | ||
} | } | ||
Line 855: | Line 2,167: | ||
} | } | ||
M = (function() { | M = (function() { | ||
- | var | + | var _ref3, _results; |
_results = []; | _results = []; | ||
- | for (i | + | for (i = 1, _ref3 = this.n; 1 <= _ref3 ? i <= _ref3 : i >= _ref3; 1 <= _ref3 ? i++ : i--) { |
_results.push((function() { | _results.push((function() { | ||
- | var | + | var _ref4, _results2; |
- | + | _results2 = []; | |
- | for (j | + | for (j = 1, _ref4 = this.n; 1 <= _ref4 ? j <= _ref4 : j >= _ref4; 1 <= _ref4 ? j++ : j--) { |
- | + | _results2.push(0); | |
} | } | ||
- | return | + | return _results2; |
}).call(this)); | }).call(this)); | ||
} | } | ||
return _results; | return _results; | ||
}).call(this); | }).call(this); | ||
- | for (i = | + | for (i = _ref3 = this.n - 1; _ref3 <= 0 ? i <= 0 : i >= 0; _ref3 <= 0 ? i++ : i--) { |
- | for (j | + | for (j = i, _ref4 = this.n - 1; i <= _ref4 ? j <= _ref4 : j >= _ref4; i <= _ref4 ? j++ : j--) { |
max = 0; | max = 0; | ||
if (i < j - minSize) { | if (i < j - minSize) { | ||
max = M[i + 1][j - 1] + this.P(i, j); | max = M[i + 1][j - 1] + this.P(i, j); | ||
hs = []; | hs = []; | ||
- | + | _ref5 = HM[i + 1][j - 1]; | |
- | for ( | + | for (_i = 0, _len = _ref5.length; _i < _len; _i++) { |
- | sol = | + | sol = _ref5[_i]; |
if (this.P(i, j) === 1) { | if (this.P(i, j) === 1) { | ||
sol[i] = "("; | sol[i] = "("; | ||
Line 885: | Line 2,197: | ||
sol[j] = "."; | sol[j] = "."; | ||
} | } | ||
- | if (hs.length < 50) | + | if (hs.length < 50) hs.push(sol); |
- | + | ||
- | + | ||
} | } | ||
- | for (k | + | for (k = i, _ref6 = j - 1; i <= _ref6 ? k <= _ref6 : k >= _ref6; i <= _ref6 ? k++ : k--) { |
tmp = M[i][k] + M[k + 1][j]; | tmp = M[i][k] + M[k + 1][j]; | ||
if (tmp > max) { | if (tmp > max) { | ||
Line 896: | Line 2,206: | ||
} | } | ||
if (tmp >= max) { | if (tmp >= max) { | ||
- | + | _ref7 = HM[i][k]; | |
- | for ( | + | for (_j = 0, _len2 = _ref7.length; _j < _len2; _j++) { |
- | a1 = | + | a1 = _ref7[_j]; |
- | + | _ref8 = HM[k + 1][j]; | |
- | for ( | + | for (_k = 0, _len3 = _ref8.length; _k < _len3; _k++) { |
- | a2 = | + | a2 = _ref8[_k]; |
a = createSolution(this.n); | a = createSolution(this.n); | ||
- | for (p | + | for (p = 0; 0 <= k ? p <= k : p >= k; 0 <= k ? p++ : p--) { |
a[p] = a1[p]; | a[p] = a1[p]; | ||
} | } | ||
- | for (p = | + | for (p = _ref9 = k + 1; _ref9 <= j ? p <= j : p >= j; _ref9 <= j ? p++ : p--) { |
a[p] = a2[p]; | a[p] = a2[p]; | ||
} | } | ||
- | if (hs.length < 50) | + | if (hs.length < 50) hs.push(a); |
- | + | ||
- | + | ||
} | } | ||
} | } | ||
Line 923: | Line 2,231: | ||
output = []; | output = []; | ||
hashSet = {}; | hashSet = {}; | ||
- | + | _ref10 = HM[0][this.n - 1]; | |
- | for ( | + | for (_l = 0, _len4 = _ref10.length; _l < _len4; _l++) { |
- | sol = | + | sol = _ref10[_l]; |
str = sol.join(""); | str = sol.join(""); | ||
if (hashSet[str] === void 0) { | if (hashSet[str] === void 0) { | ||
Line 945: | Line 2,253: | ||
algorithm. | algorithm. | ||
*/ | */ | ||
- | |||
var TmCalc; | var TmCalc; | ||
Line 1,084: | Line 2,391: | ||
/* See (Breslauer et al. 1986): http://www.pnas.org/content/83/11/3746.full.pdf | /* See (Breslauer et al. 1986): http://www.pnas.org/content/83/11/3746.full.pdf | ||
*/ | */ | ||
- | + | var R, dh, ds, dsinit, i, n1, n2, naConc, predG, predH, predS, primerConc, primerc_local, _ref; | |
- | var R, i, n1, n2, naConc, predG, predH, predS, primerConc, | + | |
predH = 0; | predH = 0; | ||
predG = -(5 + 0.4); | predG = -(5 + 0.4); | ||
predS = 0; | predS = 0; | ||
- | primerConc = 500 * 10e- | + | primerConc = 500 * 10e-3; |
- | naConc = | + | naConc = 50 * 10e-3; |
R = 1.987; | R = 1.987; | ||
n1 = data[0]; | n1 = data[0]; | ||
- | for (i | + | for (i = 1, _ref = data.length - 1; 1 <= _ref ? i <= _ref : i >= _ref; 1 <= _ref ? i++ : i--) { |
n2 = data[i]; | n2 = data[i]; | ||
- | predH | + | predH -= this.getH(n1, n2); |
- | predG | + | predG -= this.getG(n1, n2); |
- | predS | + | predS -= this.getS(n1, n2); |
n1 = n2; | n1 = n2; | ||
} | } | ||
- | + | dh = 1000 * predH; | |
+ | ds = predS; | ||
+ | dsinit = -10.8; | ||
+ | primerc_local = primerConc / 4 * 1e-6; | ||
+ | return dh / (dsinit + ds + R * Math.log(primerc_local / 10)) - 273.15 + 16.6 * Math.log(naConc / 10) / Math.LN10; | ||
}; | }; | ||
Line 1,137: | Line 2,447: | ||
freq | freq | ||
*/ | */ | ||
- | |||
return TmCalc; | return TmCalc; | ||
Line 1,160: | Line 2,469: | ||
freq | freq | ||
*/ | */ | ||
- | |||
// Underscore.js 1.3.3 | // Underscore.js 1.3.3 | ||
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. | // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. |
Latest revision as of 11:14, 28 July 2012
/* Immutable class that makes it possible to work with (very simplified) DNA sequences
- /
var DNA,
__indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
DNA = (function() {
DNA.allowed = ["A", "C", "G", "T"];
DNA.extendedAllowed = ["A", "C", "G", "T", "R", "Y", "K", "M", "S", "W", "B", "D", "H", "V", "N"];
DNA.RS = { AatI: "AGGCCT", AatII: "GACGTC", AccI: "GTMKAC", AccIII: "TCCGGA", Acc65I: "GGTACC", AcsI: "RAATTY", AcyI: "GRCGYC", AflI: "GGWCC", AflII: "CTTAAG", AflIII: "ACRYGT", AgeI: "ACCGGT", AhaII: "GRCGYC", AhaIII: "TTTAAA", AluI: "AGCT", Alw44I: "GTGCAC", AlwNI: "CAGNNNCTG", AocI: "CCTNAGG", AosI: "TGCGCA", ApaI: "GGGCCC", ApaLI: "GTGCAC", ApoI: "RAATTY", ApyI: "CCWGG", AscI: "GGCGCGCC", AseI: "ATTAAT", AsnI: "ATTAAT", AspI: "GACNNNGTC", Asp700: "GAANNNNTTC", Asp718: "GGTACC", AspEI: "GACNNNNNGTC", AspHI: "GWGCWC", AsuII: "TTCGAA", AvaI: "CYCGRG", AvaII: "GGWCC", AviII: "TGCGCA", AvrII: "CCTAGG", BalI: "TGGCCA", BamHI: "GGATCC", BanI: "GGYRCC", BanII: "GRGCYC", BbrPI: "CACGTG", BclI: "TGATCA", BfaI: "CTAG", BfrI: "CTTAAG", BglI: "GCCNNNNNGGC", BglII: "AGATCT", BinI: "CCTAGG", BmyI: "GDGCHC", Bpu1102I: "GCTNAGC", BsaAI: "YACGTR", BsaBI: "GATNNNNATC", BsaHI: "GRCGYC", BsaJI: "CCNNGG", BseAI: "TCCGGA", BsePI: "GCGCGC", BsiEI: "CGRYCG", BsiWI: "CGTACG", BsiYI: "CCNNNNNNNGG", BslI: "CCNNNNNNNGG", Bsp1286I: "GDGCHC", Bsp1407I: "TGTACA", BspDI: "ATCGAT", BspEI: "TCCGGA", BspHI: "TCATGA", BspLU11I: "ACATGT", BsrFI: "RCCGGY", BssGI: "CCANNNNNNTGG", BssHII: "GCGCGC", Bst1107I: "GTATAC", BstBI: "TTCGAA", BstEII: "GGTNACC", BstNI: "CCWGG", BstUI: "CGCG", BstXI: "CCANNNNNNTGG", BstYI: "RGATCY", Bsu36I: "CCTNAGG", CelII: "GCTNAGC", CfoI: "GCGC", CfrI: "YGGCCR", Cfr10I: "RCCGGY", ClaI: "ATCGAT", DdeI: "CTNAG", DpnII: "GATC", DraI: "TTTAAA", DraII: "RGGNCCY", DraIII: "CACNNNGTG", DrdI: "GACNNNNNNGTC", DsaI: "CCRYGG", EaeI: "YGGCCR", EagI: "CGGCCG", Eam1105I: "GACNNNNNGTC", Ecl136II: "GAGCTC", EclXI: "CGGCCG", Eco47III: "AGCGCT", EcoNI: "CCTNNNNNAGG", EcoO109I: "RGGNCCY", EcoRI: "GAATTC", EcoRII: "CCWGG", EcoRV: "GATATC", EspI: "GCTNAGC", FnuDII: "CGCG", Fnu4HI: "GCNGC", FseI: "GGCCGGCC", FspI: "TGCGCA", HaeII: "RGCGCY", HaeIII: "GGCC", HgiAI: "GWGCWC", HhaI: "GCGC", HincII: "GTYRAC", HindII: "GTYRAC", HindIII: "AAGCTT", HinfI: "GANTC", HinPI: "GCGC", HpaI: "GTTAAC", HpaII: "CCGG", ItaI: "GCNGC", KasI: "GGCGCC", KpnI: "GGTACC", KspI: "CCGCGG", MaeI: "CTAG", MaeII: "ACGT", MaeIII: "GTNAC", MamI: "GATNNNNATC", MboI: "GATC", MfeI: "CAATTG", MluI: "ACGCGT", MluNI: "TGGCCA", MroI: "TCCGGA", MscI: "TGGCCA", MseI: "TTAA", MspI: "CCGG", MstI: "TGCGCA", MstII: "CCTNAGG", MunI: "CAATTG", MvaI: "CCWGG", MvnI: "CGCG", NaeI: "GCCGGC", NarI: "GGCGCC", NciI: "CCSGG", NcoI: "CCATGG", NdeI: "CATATG", NdeII: "GATC", NgoMI: "GCCGGC", NheI: "GCTAGC", NlaIII: "CATG", NlaIV: "GGNNCC", NotI: "GCGGCCGC", NruI: "TCGCGA", NsiI: "ATGCAT", NspBII: "CMGCKG", NspI: "RCATGY", NspII: "GDGCHC", NspV: "TTCGAA", PacI: "TTAATTAA", PaeR7I: "CTCGAG", PflMI: "CCANNNNNTGG", PinAI: "ACCGGT", PsiI: "TTATAA", PmaCI: "CACGTG", PmeI: "GTTTAAAC", PmlI: "CACGTG", PpuMI: "RGGWCCY", Psp1406I: "AACGTT", PstI: "CTGCAG", PvuI: "CGATCG", PvuII: "CAGCTG", RcaI: "TCATGA", RmaI: "CTAG", RsaI: "GTAC", RsrII: "CGGWCCG", SacI: "GAGCTC", SacII: "CCGCGG", SalI: "GTCGAC", SauI: "CCTNAGG", Sau3AI: "GATC", Sau96I: "GGNCC", ScaI: "AGTACT", ScrFI: "CCNGG", SexAI: "ACCWGGT", SfcI: "CTRYAG", SfiI: "GGCCNNNNNGGCC", SfuI: "TTCGAA", SgrAI: "CRCCGGYG", SmaI: "CCCGGG", SnaBI: "TACGTA", SnoI: "GTGCAC", SpeI: "ACTAGT", SphI: "GCATGC", SrfI: "GCCCGGGC", Sse8387I: "CCTGCAGG", SspI: "AATATT", SspBI: "TGTACA", SstI: "GAGCTC", SstII: "CCGCGG", StuI: "AGGCCT", StyI: "CCWWGG", SwaI: "ATTTAAAT", TaqI: "TCGA", TfiI: "GAWTC", ThaI: "CGCG", Tru9I: "TTAA", Tth111I: "GACNNNGTC", Van91I: "CCANNNNNTGG", XbaI: "TCTAGA", XcmI: "CCANNNNNNNNNTGG", XhoI: "CTCGAG", XhoII: "RGATCY", XmaI: "CCCGGG", XmaIII: "CGGCCG", XmnI: "GAANNNNTTC" };
DNA.cutPositions = { AatI: 3, AatII: 5, AccI: 2, AccIII: 1, Acc65I: 1, AcsI: 1, AcyI: 2, AflI: 1, AflII: 1, AflIII: 1, AgeI: 1, AhaII: 2, AhaIII: 3, AluI: 2, Alw44I: 1, AlwNI: 6, AocI: 2, AosI: 3, ApaI: 5, ApaLI: 1, ApoI: 1, ApyI: 0, AscI: 2, AseI: 2, AsnI: 2, AspI: 4, Asp700: 5, Asp718: 1, AspEI: 6, AspHI: 5, AsuII: 2, AvaI: 1, AvaII: 1, AviII: 3, AvrII: 1, BalI: 3, BamHI: 1, BanI: 1, BanII: 5, BbrPI: 3, BclI: 1, BfaI: 1, BfrI: 1, BglI: 7, BglII: 1, BinI: 1, BmyI: 5, Bpu1102I: 2, BsaAI: 3, BsaBI: 5, BsaHI: 2, BsaJI: 1, BseAI: 1, BsePI: 1, BsiEI: 4, BsiWI: 1, BsiYI: 7, BslI: 7, Bsp1286I: 5, Bsp1407I: 1, BspDI: 2, BspEI: 1, BspHI: 1, BspLU11I: 1, BsrFI: 1, BssGI: 8, BssHII: 1, Bst1107I: 3, BstBI: 2, BstEII: 1, BstNI: 2, BstUI: 2, BstXI: 8, BstYI: 1, Bsu36I: 2, CelII: 2, CfoI: 3, CfrI: 1, Cfr10I: 1, ClaI: 2, DdeI: 1, DpnII: 0, DraI: 3, DraII: 2, DraIII: 6, DrdI: 7, DsaI: 1, EaeI: 1, EagI: 1, Eam1105I: 6, Ecl136II: 3, EclXI: 1, Eco47III: 3, EcoNI: 5, EcoO109I: 2, EcoRI: 1, EcoRII: 0, EcoRV: 3, EspI: 2, FnuDII: 2, Fnu4HI: 2, FseI: 6, FspI: 3, HaeII: 5, HaeIII: 2, HgiAI: 5, HhaI: 3, HincII: 3, HindII: 3, HindIII: 1, HinfI: 1, HinPI: 1, HpaI: 3, HpaII: 1, ItaI: 2, KasI: 1, KpnI: 5, KspI: 4, MaeI: 1, MaeII: 1, MaeIII: 0, MamI: 5, MboI: 0, MfeI: 1, MluI: 1, MluNI: 3, MroI: 1, MscI: 3, MseI: 1, MspI: 1, MstI: 3, MstII: 2, MunI: 1, MvaI: 2, MvnI: 2, NaeI: 3, NarI: 2, NciI: 2, NcoI: 1, NdeI: 2, NdeII: 0, NgoMI: 1, NheI: 1, NlaIII: 4, NlaIV: 3, NotI: 2, NruI: 3, NsiI: 5, NspBII: 3, NspI: 5, NspII: 5, NspV: 2, PacI: 5, PaeR7I: 1, PflMI: 7, PinAI: 1, PsiI: 3, PmaCI: 3, PmeI: 4, PmlI: 3, PpuMI: 2, Psp1406I: 2, PstI: 5, PvuI: 4, PvuII: 3, RcaI: 1, RmaI: 1, RsaI: 2, RsrII: 2, SacI: 5, SacII: 4, SalI: 1, SauI: 2, Sau3AI: 0, Sau96I: 1, ScaI: 3, ScrFI: 2, SexAI: 1, SfcI: 1, SfiI: 8, SfuI: 2, SgrAI: 2, SmaI: 3, SnaBI: 3, SnoI: 1, SpeI: 1, SphI: 5, SrfI: 4, Sse8387I: 6, SspI: 3, SspBI: 1, SstI: 5, SstII: 4, StuI: 3, StyI: 1, SwaI: 4, TaqI: 1, TfiI: 1, ThaI: 2, Tru9I: 1, Tth111I: 4, Van91I: 7, XbaI: 1, XcmI: 8, XhoI: 1, XhoII: 1, XmaI: 1, XmaIII: 1, XmaCI: 1, XmnI: 5 };
DNA.RSFlipped = void 0;
DNA.getRSFlipped = function() { var key, name, seq, value, _ref, _ref2; if (this.RSFlipped === void 0) { this.RSFlipped = {}; _ref = this.RS; for (name in _ref) { seq = _ref[name]; if (this.RSFlipped[seq]) { this.RSFlipped[seq].push(name); } else { this.RSFlipped[seq] = [name]; } } _ref2 = this.RSFlipped; for (key in _ref2) { value = _ref2[key]; if (value.length <= 2) { this.RSFlipped[key] = value.join(" or "); } else { this.RSFlipped[key] = value.slice(0, value.length - 1).join(", ") + " or " + value[value.length - 1]; } } } return this.RSFlipped; };
DNA.getCutPosition = function(rs) { var k, v, _ref; if (DNA.cutPositions[rs]) { return DNA.cutPositions[rs]; } else { _ref = DNA.cutPositions; for (k in _ref) { v = _ref[k]; if (k.toLowerCase() === rs.toLowerCase()) return v; } return -1; } };
DNA.nucleotideMatches = { A: ["A", "R", "M", "W", "D", "H", "V", "N"], C: ["C", "Y", "M", "S", "B", "H", "V", "N"], G: ["G", "R", "K", "S", "B", "D", "V", "N"], T: ["T", "Y", "K", "W", "B", "D", "H", "N"], R: ["A", "G"], Y: ["C", "T"], K: ["G", "T"], M: ["A", "C"], S: ["C", "G"], W: ["A", "T"], B: ["C", "G", "T"], D: ["A", "G", "T"], H: ["A", "C", "T"], V: ["A", "C", "G"], N: ["A", "C", "G", "T"] };
DNA.fromString = function(data) { var found, name, seq, sequence, _ref; found = false; if (data != null) { _ref = this.RS; for (name in _ref) { sequence = _ref[name]; if (name.toLowerCase() === data.toLowerCase()) { seq = new DNA(sequence.split()); found = true; } } } else { data = ""; } if (!found) seq = new DNA(data.toUpperCase().replace(/\s/ig, "").split()); seq.validateData(); return seq; };
DNA.fromRandom = function(length) { var data, i; if (length === 0 || length === "0" || !length || length === void 0) { return new DNA([]); } else { data = (function() { var _results; _results = []; for (i = 1; 1 <= length ? i <= length : i >= length; 1 <= length ? i++ : i--) { _results.push(DNA.allowed[Math.floor(Math.random() * DNA.allowed.length)]); } return _results; })(); return new DNA(data); } };
/* The constructor assumes the data has been validated (for performance reasons -> premature and non-consequent optimization ^^). Anyway, the DNA.fromString function is probably what you were looking for! */
function DNA(data) { this.data = data; }
DNA.prototype.validateData = function(strict) { if (strict) { if (this.data.some(function(x) { return __indexOf.call(DNA.allowed, x) < 0; })) { throw new Error("Invalid character encountered"); } } else { if (this.data.some(function(x) { return __indexOf.call(DNA.extendedAllowed, x) < 0; })) { throw new Error("Invalid character encountered"); } } };
DNA.prototype.matches = function(seq2) { var combined; if (this.length() !== seq2.length()) return false; combined = _.zip(this.data, seq2.data); return _.all(combined, function(_arg) { var i, j, n1, n1Matches, n2, n2Matches, _ref, _ref2; n1 = _arg[0], n2 = _arg[1]; if (n1 === n2) { return true; } else { if (__indexOf.call(DNA.allowed, n1) >= 0) { return __indexOf.call(DNA.nucleotideMatches[n1], n2) >= 0; } if (__indexOf.call(DNA.allowed, n2) >= 0) { return __indexOf.call(DNA.nucleotideMatches[n2], n1) >= 0; } n1Matches = DNA.nucleotideMatches[n1]; n2Matches = DNA.nucleotideMatches[n2]; for (i = 0, _ref = n1Matches.length - 1; 0 <= _ref ? i <= _ref : i >= _ref; 0 <= _ref ? i++ : i--) { for (j = 0, _ref2 = n2Matches.length - 1; 0 <= _ref2 ? j <= _ref2 : j >= _ref2; 0 <= _ref2 ? j++ : j--) { if (n1Matches[i] === n2Matches[j]) return true; } } return false; } }); };
DNA.prototype.findRS = function(nameOnly) { var key, name, rs, rsDNA, seq, _ref; if (nameOnly == null) nameOnly = false; if (this.length() > 15) return ""; key = this.data.join(""); rs = DNA.getRSFlipped(); if (rs[key]) { if (nameOnly) { return rs[key]; } else { return rs[key] + " (5' → 3': " + key + ")"; } } else { _ref = DNA.RS; for (seq in _ref) { name = _ref[seq]; rsDNA = DNA.fromString(seq); if (this.matches(rsDNA)) { if (nameOnly) { name; } else { name + " (5' → 3': " + key + ")"; } } } return ""; } };
DNA.prototype.getCutPosition = function() { return DNA.getCutPosition(this.findRS(true)); };
DNA.prototype.containsRS = function(names) { var c, comparisons, i, lengths, name, parts, pos, rs, _i, _j, _len, _len2, _ref; comparisons = []; for (_i = 0, _len = names.length; _i < _len; _i++) { name = names[_i]; comparisons.push(name.toLowerCase()); } lengths = (function() { var _results; _results = []; for (i = 0; i <= 10; i++) { _results.push(""); } return _results; })(); rs = DNA.getRSFlipped(); pos = 0; _ref = this.data; for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) { c = _ref[_j]; for (i = 4; i <= 10; i++) { lengths[i] += c; if (i <= pos) lengths[i] = lengths[i].substr(1); if (rs[lengths[i]]) { parts = rs[lengths[i]].toLowerCase().split(" "); if (_.some(comparisons, function(obj) { return __indexOf.call(parts, obj) >= 0; })) { return true; } } } pos++; } return false; };
DNA.prototype.length = function() { return this.data.length; };
DNA.prototype.snip = function(length) { if (length >= 0 && length <= this.data.length) { return new DNA(this.data.slice(0, length)); } else { throw new Error("Invalid snip length (tried to snip a piece of length " + length + " in a sequence of " + this.length() + "bp)"); } };
DNA.prototype.reverseComplement = function() { var complement, k, l, revCompl, v, _len, _ref; revCompl = []; complement = function(x) { switch (x) { case "A": return "T"; case "T": return "A"; case "C": return "G"; case "G": return "C"; default: throw new Error("Invalid character encountered (" + x + ")"); } }; l = this.data.length - 1; _ref = this.data; for (k = 0, _len = _ref.length; k < _len; k++) { v = _ref[k]; revCompl[l - k] = complement(v); } return new DNA(revCompl); };
DNA.prototype.append = function(seqToAdd) { return new DNA(this.data.concat(seqToAdd.data)); };
DNA.prototype.tm = function() { return TmCalc.calc(this.data); };
DNA.prototype.toString = function() { return this.data.join(""); };
DNA.prototype.toJSON = function() { return this.toString(); };
DNA.prototype.clone = function() { return new DNA(this.data.slice(0)); };
return DNA;
})(); var DNAInput, DNARSInput, DNASmallInput, Input, InputForm, NumberInput, RadioInput, SelectInput, Separator, Submit, TemperatureInput,
__hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
InputForm = (function() {
InputForm.i = 0;
/* [{ name: "tm", readableName: "Tm", type: "TemperatureInput", label: "Tm" }] */
InputForm.create = function(options, objects) { var f; f = new InputForm(options); objects.forEach(function(x) { return f.addInput(x); }); return f; };
function InputForm(_arg) { this.update = _arg.update, this.submit = _arg.submit, this.name = _arg.name; this.id = "inputform-" + InputForm.i++; this.inputs = []; this.inputDict = {}; this.lastValue = {}; }
InputForm.prototype.save = function(data) { var k, tempData, v; try { if (JSON && localStorage) { tempData = {}; for (k in data) { v = data[k]; if (k === "leftRS" || k === "rightRS") v = v.rs; tempData[k] = v; } return localStorage.setItem("values", JSON.stringify(tempData)); } } catch (e) {
} };
InputForm.prototype.restore = function() { var k, oldVals, v, _results; try { if (JSON && localStorage) { oldVals = localStorage.getItem("values"); if (oldVals != null) { oldVals = JSON.parse(oldVals); _results = []; for (k in oldVals) { v = oldVals[k]; _results.push(this.inputDict[k].value(v)); } return _results; } } } catch (e) {
} };
InputForm.prototype.attach = function(elem) { $(elem).append(this.generateDOM()); $(".help-block").hide(); this.restore(); return this.updateCallback(); };
InputForm.prototype.submitCallback = function() { var changed, data, error, messages, _ref; _ref = this.getFormData(), data = _ref.data, messages = _ref.messages, error = _ref.error, changed = _ref.changed; return typeof this.submit === "function" ? this.submit(data, messages, error) : void 0; };
InputForm.prototype.updateCallback = function() { var changed, data, error, messages, _ref; if (this.disableAuto) return; _ref = this.getFormData(), data = _ref.data, messages = _ref.messages, error = _ref.error, changed = _ref.changed; if (changed) { return typeof this.update === "function" ? this.update(data, messages, error) : void 0; } };
InputForm.prototype.getFormData = function() { var changed, data, error, messages; data = {}; messages = []; error = false; this.inputs.forEach(function(x) { var val; val = void 0; try { val = x.value(); } catch (e) { messages.push("Error: " + e.message); } if (!x.validate(val)) { error = true; messages.push("The value of " + x.get("label") + " is invalid"); } if (x.get("name") != null) return data[x.get("name")] = val; }); changed = false; if (!_.isEqual(this.lastValue, data)) { changed = true; this.inputs.forEach(function(x) { if (x.visible(data)) { return x.show(); } else { return x.hide(); } }); this.lastValue = data; this.save(data); } return { data: data, messages: messages, error: error, changed: changed }; };
InputForm.prototype.addInput = function(input) { var callback, inputObj, _this = this; if (input === "Separator") { this.inputs.push(new Separator()); return; } if (!window[input.type] instanceof Function) { throw new Error("Passed input can't be instantiated"); } callback = function() { return _this.updateCallback.apply(_this, arguments); }; if (input.type === "Submit") { callback = function() { return _this.submitCallback.apply(_this, arguments); }; } inputObj = new window[input.type](callback, input); if (!inputObj instanceof Input) throw new Error("Object isn't an input"); this.inputDict[input.name] = inputObj; this.inputs.push(inputObj); return this; };
InputForm.prototype.get = function(field) { return this.inputDict[field]; };
InputForm.prototype.generateDOM = function() { var fieldset, form, _this = this; form = $("<form>").attr("id", this.id).attr("class", "form-horizontal"); if (this.name != null) { fieldset = $("<fieldset>").append($("<legend>").text(this.name)); form.append(fieldset); } else { fieldset = form; } this.inputs.forEach(function(input) { return fieldset.append(input.generateDOM()); });fieldset.prepend($("
")).prepend($("
if ($("#help-checkbox").attr("checked")) { return $(".help-block").show(); } else { return $(".help-block").hide(); }})))).prepend($("
if ($("#autoUpdate-checkbox").attr("checked")) { return _this.disableAuto = false; } else { return _this.disableAuto = true; } })))); return form; };
return InputForm;
})();
Input = (function() {
Input.i = 0;
function Input(callback, data, rowClass) { this.callback = callback; this.data = data; this.rowClass = rowClass != null ? rowClass : ""; this.id = "input-" + Input.i++; this.type = "text"; }
Input.prototype.generateDOM = function() {return $("
};
Input.prototype.generateLabelDOM = function() { return $("<label>").attr("class", "control-label").attr("for", this.id).html(this.data.label); };
Input.prototype.generateInputElementDOM = function(id, type, value, callback) { var element; element = $("<input>").attr("class", "input-medium").attr("type", type).attr("id", id).on("change", callback).on("keyup", callback); element.val(value); return element; };
Input.prototype.generateElementDOM = function() { return this.generateInputElementDOM(this.id, this.type, this.data.value, this.callback); };
Input.prototype.generateHelpDOM = function(helpData) { if (helpData == null) helpData = this.data.help; if ((helpData != null) && helpData.length > 0) {return $("
").attr("class", "help-block").html(helpData); } else { return $("<p>").attr("class", "help-block").attr("style", "display: none"); } }; Input.prototype.get = function(key) { return this.data[key]; }; Input.prototype.visible = function(vals) { if (this.data.visible != null) { return this.data.visible(vals); } else { return true; } }; Input.prototype.hide = function() { return $("#row-" + this.id).hide(); }; Input.prototype.show = function() { return $("#row-" + this.id).show(); }; Input.prototype.value = function(value) { if (value === void 0) { return $("#" + this.id).val(); } else { return $("#" + this.id).val(value); } }; Input.prototype.validate = function(val) { return true; }; return Input; })(); Separator = (function(_super) { __extends(Separator, _super); function Separator() { Separator.__super__.constructor.apply(this, arguments); } Separator.prototype.generateDOM = function() { return $("
").attr("id", "row-" + this.id);
};
Separator.prototype.value = function() {};
Separator.prototype.validate = function() { return true; };
return Separator;
})(Input);
Submit = (function(_super) {
__extends(Submit, _super);
function Submit() { Submit.__super__.constructor.apply(this, arguments); }
Submit.prototype.generateElementDOM = function() { return $("<input>").attr("type", "submit").attr("value", this.data.value).attr("class", "btn").click(this.callback, function(e) { e.preventDefault(); e.data(e); return false; }); };
Submit.prototype.value = function() {};
Submit.prototype.validate = function() { return true; };
return Submit;
})(Input);
NumberInput = (function(_super) {
__extends(NumberInput, _super);
function NumberInput() { NumberInput.__super__.constructor.apply(this, arguments); this.type = "number"; }
NumberInput.prototype.value = function(value) { if (value === void 0) { return parseInt(NumberInput.__super__.value.call(this), 10); } else { return NumberInput.__super__.value.call(this, parseInt(value, 10)); } };
NumberInput.prototype.validate = function(val) { return !isNaN(val); };
return NumberInput;
})(Input);
TemperatureInput = (function(_super) {
__extends(TemperatureInput, _super);
function TemperatureInput() { TemperatureInput.__super__.constructor.apply(this, arguments); }
TemperatureInput.prototype.generateElementDOM = function() { var input; input = TemperatureInput.__super__.generateElementDOM.call(this);return $("
};
return TemperatureInput;
})(NumberInput);
DNAInput = (function(_super) {
__extends(DNAInput, _super);
function DNAInput() { DNAInput.__super__.constructor.apply(this, arguments); }
DNAInput.prototype.generateElementDOM = function() { var textarea; textarea = $("<textarea>").attr("class", "input-xlarge").attr("id", this.id).on("change", this.callback).on("keyup", this.callback); textarea.val(this.data.value); return textarea; };
DNAInput.prototype.value = function(value) { if (value === void 0) { return DNA.fromString(DNAInput.__super__.value.call(this)); } else { return DNAInput.__super__.value.call(this, value.toString()); } };
DNAInput.prototype.validate = function(val) { return val !== void 0; };
return DNAInput;
})(Input);
DNASmallInput = (function(_super) {
__extends(DNASmallInput, _super);
function DNASmallInput() { DNASmallInput.__super__.constructor.apply(this, arguments); }
DNASmallInput.prototype.generateElementDOM = function() { var input; input = DNASmallInput.__super__.generateElementDOM.call(this);return $("
};
return DNASmallInput;
})(DNAInput);
DNARSInput = (function(_super) {
__extends(DNARSInput, _super);
function DNARSInput() { DNARSInput.__super__.constructor.apply(this, arguments); }
DNARSInput.prototype.generateElementDOM = function() { var input; input = DNARSInput.__super__.generateElementDOM.call(this);return $("
};
DNARSInput.prototype.value = function(value) { var val; val = DNARSInput.__super__.value.call(this, value); if ((val != null) && val instanceof DNA) { $("#" + this.id + "-rs").html(val.toString()); } if (val === void 0) val = DNA.fromString(""); return { dna: val, rs: $("#" + this.id).val() }; };
return DNARSInput;
})(DNAInput);
SelectInput = (function(_super) {
__extends(SelectInput, _super);
function SelectInput(callback, obj) { SelectInput.__super__.constructor.apply(this, arguments); this.choices = obj.choices; }
SelectInput.prototype.generateElementDOM = function() { var container, input, _this = this; input = SelectInput.__super__.generateElementDOM.call(this); container = $("<select>").attr("class", "input-medium").attr("id", this.id).on("change", this.callback).on("click", this.callback); this.choices.forEach(function(_arg) { var name, value; name = _arg.name, value = _arg.value; return container.append($("<option>").attr("value", name).text(value)); }); return container; };
return SelectInput;
})(Input);
RadioInput = (function(_super) {
__extends(RadioInput, _super);
function RadioInput(callback, obj) { RadioInput.__super__.constructor.apply(this, arguments); this.choices = obj.choices; }
RadioInput.prototype.generateElementDOM = function() { var container, i, input, _this = this; input = RadioInput.__super__.generateElementDOM.call(this);container = $("
i = 0; this.choices.forEach(function(_arg) { var elem, help, label, name; name = _arg.name, label = _arg.label, help = _arg.help; elem = $("<label>").attr("class", "radio").text(label).prepend($("<input>").attr("checked", _this.data.value === name).attr("type", "radio").attr("id", _this.id + "-" + name).attr("name", _this.id).on("change", _this.callback).on("click", _this.callback)).append(_this.generateHelpDOM(help)); return container.append(elem); }); return container; };
RadioInput.prototype.value = function(value) { var output, _this = this; if (value === void 0) { output = {}; this.choices.forEach(function(_arg) { var name; name = _arg.name; return output[name] = $("#" + _this.id + "-" + name).attr("checked") === "checked"; }); return output; } else { if (value instanceof String) { this.choices.forEach(function(_arg) { var name; name = _arg.name; return $("#" + _this.id + "-" + name).attr("checked", name === value); }); } else { this.choices.forEach(function(_arg) { var name; name = _arg.name; return $("#" + _this.id + "-" + name).attr("checked", value[name]); }); } return value; } };
return RadioInput;
})(Input);
$(function() {
window.output = new Output("Primer 1 (Template:Primer1Tm\u00B0C, Template:Primer1TailLength + Template:Primer1Lengthbp): 5' → 3'\n
{{primer1}}\nPrimer 2 (Template:Primer2Tm\u00B0C, Template:Primer2TailLength + Template:Primer2Lengthbp): 5' → 3'
\n
{{primer2}}\nAnnealing temperature: Template:AnnealingTm\u00B0C\n\n
\n
Messages
\nTemplate:Messages\n\n\n
Digestion result
\nTemplate:RSDisplay:\n\n
PCR Product
\nTemplate:DNADisplay:full\n");window.output.attach("#outputAttach"); window.primerFinder = new PrimerFinder({ output: function(data, messages) { window.output.setMessages(messages); return window.output.set(data); } }); window.form = InputForm.create({ name: "Primer design helper", submit: function(data, messages, error) { return window.primerFinder.calc(data, messages, error); }, update: function(data, messages, error) { return window.primerFinder.calc(data, messages, error); } }, [ { name: "dna", label: "DNA Sequence of interest", type: "DNAInput", value: "", help: "Most of the time you should just paste the DNA sequence (in the 5' → 3' direction) you want to extract/edit in this field" }, { type: "Separator" }, { name: "type", label: "Primer type", type: "RadioInput", choices: [ { name: "normal", label: "Primers that extract the whole sequence", help: "Running a PCR using these primers will extract the entire sequence" }, { name: "rs", label: "Add restriction sites", help: "Running a PCR using these primers will result in the sequence with added standardized restriction sites on both sides." }, { name: "known", label: "Add a standard prefix/suffix to the sequence", help: "Running a PCR using these primers will result in the sequence with added standardized tails on both sides (for example for biobricking)." }, { name: "custom", label: "Add custom tails to the sequence", help: "Running a PCR using these primers will result in the sequence with added tails on both sides." } ], value: "normal" }, { type: "Separator" }, { name: "standardTail", label: "Standardized tails", type: "SelectInput", value: "biobrickGene", choices: [ { name: "biobrickGene", value: "Biobrick (gene)" }, { name: "biobrickOther", value: "Biobrick (other)" } ], visible: function(_arg) { var type; type = _arg.type; return type.known; } }, { name: "randomBases", label: "Random Bases", type: "NumberInput", value: "4", help: "This is the number of random bases will be added on both ends.", visible: function(_arg) { var type; type = _arg.type; return type.rs; } }, { name: "leftRS", label: "Left restriction site", type: "DNARSInput", value: "", help: "You can also enter the name of the restriction enzyme (i.e. EcoRI).", visible: function(_arg) { var type; type = _arg.type; return type.rs; } }, { name: "rightRS", label: "Right restriction site", type: "DNARSInput", value: "", help: "You can also enter the name of the restriction enzyme (i.e. EcoRI).", visible: function(_arg) { var type; type = _arg.type; return type.rs; } }, { name: "leftTail", label: "Left tail", type: "DNASmallInput", value: "", visible: function(_arg) { var type; type = _arg.type; return type.custom; } }, { name: "rightTail", label: "Right tail", type: "DNASmallInput", value: "", visible: function(_arg) { var type; type = _arg.type; return type.custom; } }, { type: "Separator", visible: function(_arg) { var type; type = _arg.type; return !type.normal; } }, { name: "tm", label: "Target Tm", type: "TemperatureInput", value: "50", help: "The program will try to create primers that have an annealing temperature close to this one." }, { value: "Generate", type: "Submit" } ]); return window.form.attach($("#formAttach"));
});
window.tails = [
{ name: "Biobrick (gene)", value: "biobrickGene", primer1Tail: "GTT TCT TCG AAT TCG CGG CCG CTT CTA G", primer2Tail: "GTT TCT TCC TGC AGC GGC CGC TAC TAG TA TTA TTA", messages: ["Don't forget to include the first ATG and to exclude the stop codon!", "The recommended minimum length of a BioBrick primer is 20bp", "The recommended Tm of a BioBrick primer is between 55 and 65\u00B0C", "Check that the following restriction sites are absent: EcoRI, SpeI, XbaI, PstI, NotI", "Check out <a href='http://openwetware.org/wiki/Synthetic_Biology:BioBricks/Part_fabrication'>OpenWetWare's reference page</a>"], validate: function(primer1, primer2, dna) { var end, start; start = primer1.substr(0, 3).toUpperCase(); if (start !== "ATG") return "The starting ATG seems to be absent."; end = primer2.substr(0, 3).toUpperCase(); if (end === "TTA" || end === "TCA" || end === "CTA") { return "Remove the stop codons from the sequence!"; } if (dna.containsRS(["EcoRI", "SpeI", "XbaI", "PstI", "NotI"])) { return "One of the following restriction sites is present (and shouldn't be): EcoRI, SpeI, XbaI, PstI, NotI"; } return; } }, { name: "Biobrick (other)", value: "biobrickOther", primer1Tail: "GTT TCT TCG AAT TCG CGG CCG CTT CTA GAG", primer2Tail: "GTT TCT TCC TGC AGC GGC CGC TAC TAG TA", messages: ["The recommended minimum length of a BioBrick primer is 20bp", "The recommended Tm of a BioBrick primer is between 55 and 65\u00B0C", "Check that the following restriction sites are absent: EcoRI, SpeI, XbaI, PstI, NotI", "Check out <a href='http://openwetware.org/wiki/Synthetic_Biology:BioBricks/Part_fabrication'>OpenWetWare's reference page</a>"], validate: function(primer1, primer2, dna) { if (dna.containsRS(["EcoRI", "SpeI", "XbaI", "PstI", "NotI"])) { return "One of the following restriction sites is present (and shouldn't be): EcoRI, SpeI, XbaI, PstI, NotI"; } return; } }
]; var DNADisplay, Display, Output, RSDisplay, SelfDimerDisplay,
__hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
Output = (function() {
Output.i = 0;
Output.j = 0;
function Output(template) { this.template = template; this.id = "output-" + Output.i++; this.fields = {}; this.matches = {}; this.displays = {}; this.parsedTemplate = this.parseTemplate(this.template); }
Output.prototype.attach = function(elem) { var displayClass, displayObj, k, v, _ref, _results; this.matches = {}; $(elem).html(this.parsedTemplate); _ref = this.displays; _results = []; for (k in _ref) { v = _ref[k]; _results.push((function() { var _i, _len, _ref2, _results2; _results2 = []; for (_i = 0, _len = v.length; _i < _len; _i++) { _ref2 = v[_i], displayClass = _ref2.displayClass, displayObj = _ref2.displayObj; _results2.push(displayObj.attach("." + displayClass)); } return _results2; })()); } return _results; };
Output.prototype.setMessages = function(messages) { var output; output = messages; if (messages.length === 0) output = ["No messages"];return this.getFields("_messages").html("
- " + output.join("
- ") + "
};
Output.prototype.wipe = function() { var k, v, _ref; _ref = this.fields; for (k in _ref) { v = _ref[k]; this.updateValue(k, void 0); } return this.fields = {}; };
Output.prototype.set = function(values) { var k, v; for (k in values) { v = values[k]; this.updateValue(k, v); } this.updateValue("__all__", this.fields); return this.fields; };
Output.prototype.updateValue = function(k, v) { var displayObj, _i, _len, _ref, _results; if (this.fields[k] !== v) { this.fields[k] = v; if (v != null) { this.getFields(k).html(v.toString()); } else { this.getFields(k).html(""); } } if (this.displays[k] != null) { _ref = this.displays[k]; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { displayObj = _ref[_i].displayObj; _results.push(displayObj.set(v)); } return _results; } };
Output.prototype.getFields = function(name) { if (!this.matches[name]) { this.matches[name] = $("." + this.id + "-match-" + name); } return this.matches[name]; };
Output.prototype.parseTemplate = function(template) { var c1, c2, display, displayClass, displayObj, displayPart, i, inName, key, name, output, skipNext, started, _ref; c1 = template[0]; c2 = template[1]; output = template + ""; skipNext = false; inName = false; started = -1; name = ""; for (i = 1, _ref = template.length - 1; 1 <= _ref ? i <= _ref : i >= _ref; 1 <= _ref ? i++ : i--) { c2 = template[i]; if (!skipNext) { if (c1 === "{" && c2 === "{") { if (inName) { throw new Error("Invalid template, opened brackets in an already opened context"); } name = ""; started = i; skipNext = true; inName = true; } else if (c1 === "}" && c2 === "}") { if (!inName) { throw new Error("Invalid template, closed unopened brackets"); } displayPart = name.indexOf(":"); if (displayPart >= 0) { display = name.substr(0, displayPart); key = name.substr(displayPart + 1); if (key === "") key = "__all__"; displayClass = this.id + "-display-" + display + "-" + key; output = output.replace("Template:" + name + "", ""); displayObj = new window[display](key); if (!(this.displays[key] != null)) this.displays[key] = []; this.displays[key].push({ displayClass: displayClass, displayObj: displayObj }); } else { output = output.replace("Template:" + name + "", ""); } skipNext = true; inName = false; } else { if (inName) name += c1; } } else { skipNext = false; } c1 = c2; } return output; };
return Output;
})();
Display = (function() {
function Display(key) { this.key = key; this.value = void 0; }
Display.prototype.attach = function(id) { throw new Error("Not implemented"); };
Display.prototype.set = function(values) { return this.value = values[this.key]; };
Display.prototype.pad = function(str, width, padLeft) { var length; if (str == null) str = ""; if (width == null) width = 5; if (padLeft == null) padLeft = false; str = str.toString(); length = str.length; while (length < width) { if (padLeft) { str = " " + str; } else { str += " "; } length++; } return str; };
Display.prototype.wrap = function(str, width) { var i, length, output; if (str == null) str = ""; if (width == null) width = 40; str = str.toString(); length = str.length; output = []; i = 0; while (length >= width) { output.push(str.substr(i, width)); length -= width; i += width; } if (length > 0) output.push(str.substr(i)); return output; };
return Display;
})();
DNADisplay = (function(_super) {
__extends(DNADisplay, _super);
DNADisplay.i = 0;
function DNADisplay(key) { var that, _this = this; DNADisplay.__super__.constructor.apply(this, arguments); this["class"] = "dnadisplay-" + DNADisplay.i++; this.objects = []; that = this;this.dom = $("
if ($("." + _this["class"] + "-toggleLines").attr("checked")) { $("." + _this["class"] + "-lines").hide(); $("." + _this["class"] + "-numberedLines").show(); } else { $("." + _this["class"] + "-lines").show(); $("." + _this["class"] + "-numberedLines").hide(); } return;})).append(" Line Numbers")).append($("
").attr("class", this["class"] + "-lines product").hide()).append($("<pre>").attr("class", this["class"] + "-numberedLines product")); } DNADisplay.prototype.attach = function(elem) { $(elem).append(this.dom); return this.objects = $("." + this["class"]); }; DNADisplay.prototype.set = function(value) { var i, line, lines, numberWidth, numberedLines, outputLines, width, widthNumbered, _len; width = 60; numberWidth = 6; widthNumbered = 40; lines = this.wrap(value, width); numberedLines = this.wrap(value, widthNumbered); outputLines = []; for (i = 0, _len = numberedLines.length; i < _len; i++) { line = numberedLines[i]; if (line.length > 0) { outputLines[i] = this.pad(i * widthNumbered + 1, numberWidth, true) + " - " + this.pad((i + 1) * widthNumbered, numberWidth) + " " + line; } } this.full = lines.join("\n"); this.fullNumbered = outputLines.join("\n"); $("." + this["class"] + "-lines").text(this.full); return $("." + this["class"] + "-numberedLines").text(this.fullNumbered); }; return DNADisplay; })(Display); RSDisplay = (function(_super) { __extends(RSDisplay, _super); RSDisplay.i = 0; function RSDisplay(key) { var that; RSDisplay.__super__.constructor.apply(this, arguments); this["class"] = "rsdisplay-" + RSDisplay.i++; this.objects = []; that = this; this.dom = $("<div>").attr("class", this["class"]).append($("<pre>").attr("class", this["class"] + "-pre")); } RSDisplay.prototype.attach = function(elem) { $(elem).append(this.dom); return this.objects = $("." + this["class"]); }; RSDisplay.prototype.set = function(_arg) { var c, complement, full, left, leftCompStr, leftCut, leftRS, leftRSInput, leftStr, overhang, randomBases, right, rightCompStr, rightCut, rightRS, rightRSInput, rightStr, rs, text, type, _i, _j, _len, _len2, _ref, _ref2; rs = _arg.rs, type = _arg.type, leftRS = _arg.leftRS, rightRS = _arg.rightRS; if (!type.rs) { $("." + this["class"] + "-pre").html("Not applicable"); return; } leftRSInput = leftRS.rs; rightRSInput = rightRS.rs; left = rs.left, right = rs.right, full = rs.full, randomBases = rs.randomBases; leftCut = DNA.getCutPosition(leftRSInput); if (leftCut < 0) { leftRS = DNA.fromString(left).findRS(true); leftCut = DNA.getCutPosition(leftRS); } else { leftRS = leftRSInput; } if (leftRS.length === 0) leftRS = "?"; rightCut = DNA.getCutPosition(rightRSInput); if (rightCut < 0) { rightRS = DNA.fromString(right).findRS(true); rightCut = DNA.getCutPosition(rightRS); } else { rightRS = rightRSInput; } if (rightRS.length === 0) rightRS = "?"; leftStr = full.substr(0, randomBases + left.length + 10); rightStr = full.substr(full.length - (randomBases + right.length + 10)); if (randomBases > 6) { leftStr = "..." + leftStr.substr(randomBases - 3); rightStr = rightStr.substr(0, rightStr.length - (randomBases - 3)) + "..."; randomBases = 6; } leftCompStr = ""; rightCompStr = ""; complement = function(c) { switch (c) { case "A": return "T"; case "T": return "A"; case "C": return "G"; case "G": return "C"; default: return c; } }; _ref = leftStr.split(""); for (_i = 0, _len = _ref.length; _i < _len; _i++) { c = _ref[_i]; leftCompStr += complement(c); } _ref2 = rightStr.split(""); for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { c = _ref2[_j]; rightCompStr += complement(c); } if (leftCut >= 0) { overhang = Math.ceil(Math.abs(left.length / 2 - leftCut)) + 1; leftStr = leftStr.substr(0, randomBases + leftCut) + this.pad("", overhang + 1) + leftStr.substr(randomBases + leftCut); leftCompStr = leftCompStr.substr(0, randomBases + left.length - leftCut) + this.pad("", overhang + 1) + leftCompStr.substr(randomBases + left.length - leftCut); } else { leftStr = leftStr.substr(0, randomBases) + " " + leftStr.substr(randomBases, left.length) + " " + leftStr.substr(randomBases + left.length); leftCompStr = leftCompStr.substr(0, randomBases) + " " + leftCompStr.substr(randomBases, left.length) + " " + leftCompStr.substr(randomBases + left.length); } if (rightCut >= 0) { overhang = Math.ceil(Math.abs(right.length / 2 - rightCut)) + 1; rightStr = rightStr.substr(0, rightStr.length - (randomBases + right.length - rightCut)) + this.pad("", overhang + 1) + rightStr.substr(rightStr.length - (randomBases + right.length - rightCut)); rightCompStr = rightCompStr.substr(0, rightCompStr.length - (randomBases + rightCut)) + this.pad("", overhang + 1) + rightCompStr.substr(rightCompStr.length - (randomBases + rightCut)); } else { rightStr = rightStr.substr(0, rightStr.length - (randomBases + right.length)) + " " + rightStr.substr(rightStr.length - (randomBases + right.length), right.length) + " " + rightStr.substr(rightStr.length - randomBases); rightCompStr = rightCompStr.substr(0, rightCompStr.length - (randomBases + right.length)) + " " + rightCompStr.substr(rightCompStr.length - (randomBases + right.length), right.length) + " " + rightCompStr.substr(rightCompStr.length - randomBases); } text = []; text.push("Left restriction site: " + leftRS + (leftCut < 0 ? " (cutting site unknown)" : "")); text.push("Right restriction site: " + rightRS + (rightCut < 0 ? " (cutting site unknown)" : "")); text.push(""); text.push("5'" + this.pad("3'", leftStr.length + rightStr.length + 1, true)); text.push(leftStr + "..." + rightStr); text.push(leftCompStr + "..." + rightCompStr); text.push("3'" + this.pad("5'", leftStr.length + rightStr.length + 1, true)); return $("." + this["class"] + "-pre").text(text.join("\n")); }; return RSDisplay; })(Display); SelfDimerDisplay = (function(_super) { __extends(SelfDimerDisplay, _super); SelfDimerDisplay.i = 0; function SelfDimerDisplay(key) { var that, _this = this; SelfDimerDisplay.__super__.constructor.apply(this, arguments); this["class"] = "selfdimerdisplay-" + RSDisplay.i++; this.objects = []; that = this; this.dom = $("<div>").attr("class", this["class"]).append($("<span>").text("This calculation is quite heavy and is done on demand. Press on the button to start the calculations:")).append("<br>").append($("<input>").attr("type", "button").attr("value", "Calculate").attr("class", "btn").click(function() { return _this.click(); })).append("<br>").append("<br>").append($("<pre>").attr("class", this["class"] + "-pre").html("Press the calculate button to update this data")); } SelfDimerDisplay.prototype.attach = function(elem) { $(elem).append(this.dom); return this.objects = $("." + this["class"] + "-pre"); }; SelfDimerDisplay.prototype.set = function(_arg) { var primer1, primer2; primer1 = _arg.primer1, primer2 = _arg.primer2; if (primer1 !== this.primer1 || primer2 !== this.primer2) { this.primer1 = primer1; this.primer2 = primer2; return this.objects.html("Press the calculate button to update this data"); } }; SelfDimerDisplay.prototype.click = function() { var report; report = this.generateReport("Primer 1", this.primer1) + "\n\n" + this.generateReport("Primer 2", this.primer2); return this.objects.html(report); }; SelfDimerDisplay.prototype.generateReport = function(name, primer) { var character, folds, lineC, lineN, lines, matches, number, p, pairs, rna, _i, _j, _len, _ref, _ref2, _results; lines = []; lines.push(name + ":"); rna = RNA.fromString(primer); _ref = rna.findKnots(), matches = _ref.matches, folds = _ref.folds; lines.push("Amount of matches: " + matches); pairs = _.zip((function() { _results = []; for (var _i = 1, _ref2 = primer.length; 1 <= _ref2 ? _i <= _ref2 : _i >= _ref2; 1 <= _ref2 ? _i++ : _i--){ _results.push(_i); } return _results; }).apply(this), primer.split("")); lineC = ""; lineN = ""; for (_j = 0, _len = pairs.length; _j < _len; _j++) { p = pairs[_j]; number = p[0] + " "; character = this.pad(p[1], number.length - 1, true) + " "; if (lineC.length + number.length > 60) { lines.push(lineC); lines.push(lineN); lines.push(""); lineC = ""; lineN = ""; } lineC += character; lineN += number; } if (lineC.length > 0) { lines.push(lineC); lines.push(lineN); lines.push(""); } return lines.join("\n"); }; return SelfDimerDisplay; })(Display); var Primer; Primer = (function() { Primer.minLength = 8; Primer.maxLength = 50; Primer.maxDiff = 8; Primer.lastData = ""; Primer.lastTail1 = ""; Primer.lastTail2 = ""; Primer.lastTm = 0; Primer.lastRandEdgeSize = -1; Primer.reset = function() { this.lastData = ""; this.lastTm = 0; this.lastTail1 = ""; this.lastTail2 = ""; return this.lastRandEdgeSize = -1; }; function Primer(data) { if (data instanceof DNA) { this.seq = data.clone(); } else { this.seq = DNA.fromString(data); } this.tail1 = new DNA([]); this.tail2 = new DNA([]); this.randEdgeSize = 0; } Primer.prototype.setTails = function(tail1, tail2) { this.tail1 = tail1; this.tail2 = tail2; }; Primer.prototype.addRandomEdges = function(randEdgeSize) { this.randEdgeSize = randEdgeSize; }; Primer.prototype.isSameAsLast = function(data, tm) { return Primer.lastTail1.toString() === this.tail1.toString() && Primer.lastTail2.toString() === this.tail2.toString() && Primer.lastData === DNA.fromString(data).toString() && Primer.lastTm === tm && Primer.lastRandEdgeSize === this.randEdgeSize; }; Primer.prototype.generate = function(goal) { var diff, l1, l2, maxLength, primer1Length, primer1Temp, primer2Length, primer2Temp, rand1, rand2, revCompl, temp, _ref, _ref2; if (2 * Primer.minLength > this.seq.length()) { throw new Error("The given sequence is too short"); } Primer.lastData = this.seq.toString(); Primer.lastTm = goal; Primer.lastTail1 = this.tail1; Primer.lastTail2 = this.tail2; Primer.lastRandEdgeSize = this.randEdgeSize; maxLength = Math.min(Math.floor(this.seq.length() / 2), Primer.maxLength); revCompl = this.seq.reverseComplement(); primer1Length = -1; primer1Temp = 0; primer2Length = -1; primer2Temp = 0; for (l1 = _ref = Primer.minLength; _ref <= maxLength ? l1 <= maxLength : l1 >= maxLength; _ref <= maxLength ? l1++ : l1--) { temp = this.seq.snip(l1).tm(); diff = Math.abs(temp - goal); if (diff <= Primer.maxDiff && (primer1Length === -1 || Math.abs(primer1Temp - goal) > diff)) { primer1Length = l1; primer1Temp = temp; } if (temp >= goal) break; } for (l2 = _ref2 = Primer.minLength; _ref2 <= maxLength ? l2 <= maxLength : l2 >= maxLength; _ref2 <= maxLength ? l2++ : l2--) { temp = revCompl.snip(l2).tm(); diff = Math.abs(temp - goal); if (diff <= Primer.maxDiff && (primer2Length === -1 || Math.abs(primer2Temp - goal) > diff)) { primer2Length = l2; primer2Temp = temp; } if (temp >= goal) break; } if (primer1Length < 0 || primer2Length < 0) { throw new Error("Couldn't find a primer of correct length that satisfies the conditions"); } rand1 = DNA.fromRandom(this.randEdgeSize); rand2 = DNA.fromRandom(this.randEdgeSize); return { rs: { left: this.tail1.toString(), right: this.tail2.toString(), randomBases: this.randEdgeSize, full: rand1.toString() + this.tail1.toString() + this.seq.toString() + this.tail2.reverseComplement().toString() + rand2.reverseComplement().toString() }, primer1: rand1.toString() + " " + this.tail1.toString() + " " + this.seq.snip(primer1Length).toString(), primers: { primer1: rand1.toString() + this.tail1.toString() + this.seq.snip(primer1Length).toString(), primer2: rand2.toString() + this.tail2.toString() + revCompl.snip(primer2Length).toString() }, primer1NoTail: this.seq.snip(primer1Length).toString(), primer1Tm: primer1Temp, primer1Length: primer1Length, primer1TailLength: rand1.length() + this.tail1.length(), primer2: rand2.toString() + " " + this.tail2.toString() + " " + revCompl.snip(primer2Length).toString(), primer2NoTail: revCompl.snip(primer2Length).toString(), primer2Tm: primer2Temp, primer2Length: primer2Length, primer2TailLength: rand2.length() + this.tail2.length(), full: rand1.toString() + this.tail1.toString() + this.seq.toString() + this.tail2.reverseComplement().toString() + rand2.reverseComplement().toString() }; }; return Primer; })(); var PrimerFinder; PrimerFinder = (function() { function PrimerFinder(_arg) { this.output = _arg.output; this.defaults = { primer1: "No data", primer1Tm: "?", primer1Length: 0, primer2: "No data", primer2Tm: "?", primer2Length: 0, annealingTm: "?" }; } PrimerFinder.prototype.calc = function(data, messages, error) { var k, match1, match2, p, primers, result, tail, tail1, tail2, v, validate, _i, _len, _ref, _ref2; if (error) { messages = ["A fatal error occured:"].concat(messages); this.output(data, messages); return; } _ref = this.defaults; for (k in _ref) { v = _ref[k]; if (!(data[k] != null)) data[k] = v; } try { p = new Primer(data.dna); validate = function(primer1, primer2, dna) {}; if (data.type.custom) p.setTails(data.leftTail, data.rightTail); if (data.type.rs) { match1 = data.leftRS.dna.findRS(); match2 = data.rightRS.dna.findRS(); if ((match1 != null) && match1.length > 0) { messages.push("The left restriction site is " + match1); } else { messages.push("The left restriction site wasn't recognized"); } if ((match2 != null) && match2.length > 0) { messages.push("The right restriction site is " + match2); } else { messages.push("The right restriction site wasn't recognized"); } p.setTails(data.leftRS.dna, data.rightRS.dna.reverseComplement()); p.addRandomEdges(data.randomBases); messages.push("Click the 'Generate' button to regenerate the random bases added on the end of the primers."); messages.push("In the case of a plasmid where the piece between A and B is cut out, and A is used for the primer 1 RS (and B for the second).\nThe generated primers will put the RS such that the given DNA starts at the A side and ends at the B side (in short,\nit will probably work as you expected, but still check what happens with some other tool)."); } if (data.type.known) { tail1 = ""; tail2 = ""; _ref2 = window.tails; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { tail = _ref2[_i]; if (tail.value === data.standardTail) { tail1 = tail.primer1Tail; tail2 = tail.primer2Tail; if (tail.messages != null) messages = [].concat(tail.messages); if (tail.validate != null) validate = tail.validate; } } p.setTails(DNA.fromString(tail1), DNA.fromString(tail2)); } primers = p.generate(data.tm); result = validate(primers.primer1NoTail, primers.primer2NoTail, data.dna); if (result != null) messages.push(result); for (k in primers) { v = primers[k]; data[k] = v; } messages = messages; } catch (e) { messages = [e.message]; } data.annealingTm = Math.min(data.primer1Tm, data.primer2Tm); if (Math.abs(data.primer1Tm - data.primer2Tm) > 5) { messages.push("The difference between the primer's Tm is more than 5\u00B0C"); } if (data.annealingTm > 70) { messages.push("Consider using the two-step PCR protocol"); } if (data.annealingTm > 72) { messages.push("Annealing temperature shouldn't exceed 72\u00B0C"); data.annealingTm = 72; } if (data.annealingTm < 45) { messages.push("The annealing temperature should be more than 45\u00B0C"); } return this.output(data, messages); }; return PrimerFinder; })(); var RNA; RNA = (function() { RNA.fromDNA = function(dna) { return new RNA(dna.data.map(function(x) { switch (x) { case "T": return "U"; default: return x; } })); }; RNA.fromString = function(data) { return RNA.fromDNA(DNA.fromString(data)); }; /* Note: Just like for the DNA class, you probably didn't want to use the constructor. Use fromDNA or fromString instead! */ function RNA(data) { this.data = data; this.n = this.data.length; this.knots = void 0; } RNA.prototype.P = function(i, j) { if ((this.data[i] === "A" && this.data[j] === "U") || (this.data[i] === "U" && this.data[j] === "C")) { return 1; } else if ((this.data[i] === "G" && this.data[j] === "C") || (this.data[i] === "C" && this.data[j] === "G")) { return 1; } else { return 0; } }; RNA.prototype.isTooSelfComplementary = function() { var fold, folds, i, matches, seq, _i, _len, _ref, _ref2; _ref = this.findKnots(), matches = _ref.matches, folds = _ref.folds; for (_i = 0, _len = folds.length; _i < _len; _i++) { fold = folds[_i]; seq = []; console.log(fold); for (i = 0, _ref2 = fold.length - 1; 0 <= _ref2 ? i <= _ref2 : i >= _ref2; 0 <= _ref2 ? i++ : i--) { if (fold[i] === "(") { if (i > 0 && fold[i - 1] === "(") { seq[seq.length - 1] += this.data[i]; } else { seq[seq.length] = this.data[i]; } } } console.log(seq); } return false; }; RNA.prototype.findKnots = function(minSize) { var HM, M, a, a1, a2, createSolution, hashSet, hm, hs, i, j, k, l, m, max, output, p, sol, str, tmp, _i, _j, _k, _l, _len, _len2, _len3, _len4, _ref, _ref10, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _this = this; if (minSize == null) minSize = 2; if (this.knots !== void 0) return this.knots; createSolution = function() { var i, _ref, _results; _results = []; for (i = 1, _ref = _this.n; 1 <= _ref ? i <= _ref : i >= _ref; 1 <= _ref ? i++ : i--) { _results.push("."); } return _results; }; HM = []; for (l = 0, _ref = this.n - 1; 0 <= _ref ? l <= _ref : l >= _ref; 0 <= _ref ? l++ : l--) { hm = []; for (m = 0, _ref2 = this.n - 1; 0 <= _ref2 ? m <= _ref2 : m >= _ref2; 0 <= _ref2 ? m++ : m--) { hm[m] = [createSolution()]; } HM[l] = hm; } M = (function() { var _ref3, _results; _results = []; for (i = 1, _ref3 = this.n; 1 <= _ref3 ? i <= _ref3 : i >= _ref3; 1 <= _ref3 ? i++ : i--) { _results.push((function() { var _ref4, _results2; _results2 = []; for (j = 1, _ref4 = this.n; 1 <= _ref4 ? j <= _ref4 : j >= _ref4; 1 <= _ref4 ? j++ : j--) { _results2.push(0); } return _results2; }).call(this)); } return _results; }).call(this); for (i = _ref3 = this.n - 1; _ref3 <= 0 ? i <= 0 : i >= 0; _ref3 <= 0 ? i++ : i--) { for (j = i, _ref4 = this.n - 1; i <= _ref4 ? j <= _ref4 : j >= _ref4; i <= _ref4 ? j++ : j--) { max = 0; if (i < j - minSize) { max = M[i + 1][j - 1] + this.P(i, j); hs = []; _ref5 = HM[i + 1][j - 1]; for (_i = 0, _len = _ref5.length; _i < _len; _i++) { sol = _ref5[_i]; if (this.P(i, j) === 1) { sol[i] = "("; sol[j] = ")"; } else { sol[i] = "."; sol[j] = "."; } if (hs.length < 50) hs.push(sol); } for (k = i, _ref6 = j - 1; i <= _ref6 ? k <= _ref6 : k >= _ref6; i <= _ref6 ? k++ : k--) { tmp = M[i][k] + M[k + 1][j]; if (tmp > max) { max = tmp; hs = []; } if (tmp >= max) { _ref7 = HM[i][k]; for (_j = 0, _len2 = _ref7.length; _j < _len2; _j++) { a1 = _ref7[_j]; _ref8 = HM[k + 1][j]; for (_k = 0, _len3 = _ref8.length; _k < _len3; _k++) { a2 = _ref8[_k]; a = createSolution(this.n); for (p = 0; 0 <= k ? p <= k : p >= k; 0 <= k ? p++ : p--) { a[p] = a1[p]; } for (p = _ref9 = k + 1; _ref9 <= j ? p <= j : p >= j; _ref9 <= j ? p++ : p--) { a[p] = a2[p]; } if (hs.length < 50) hs.push(a); } } } } M[i][j] = max; HM[i][j] = hs; } } } output = []; hashSet = {}; _ref10 = HM[0][this.n - 1]; for (_l = 0, _len4 = _ref10.length; _l < _len4; _l++) { sol = _ref10[_l]; str = sol.join(""); if (hashSet[str] === void 0) { hashSet[str] = true; output.push(str); } } return this.knots = { matches: M[0][this.n - 1], folds: output }; }; return RNA; })(); /* Container class that helps with Tm calculations. It has been seperated to enable easy improvements to the algorithm. */ var TmCalc; TmCalc = (function() { function TmCalc() {} TmCalc.maxCacheSize = 500; TmCalc.cache = {}; TmCalc.calc = function(data) { var key, keys, result; if (!this.cache[data]) { result = Math.round(this.calcFull(data) * 10) / 10; if (this.cache.length >= this.maxCacheSize) { keys = Object.keys(TmCalc.cache); key = keys[Math.floor(Math.random() * keys.length)]; delete TmCalc.cache[key]; } this.cache[data] = result; } return this.cache[data]; }; TmCalc.complement = function(n) { switch (n) { case "A": return "T"; case "T": return "A"; case "C": return "G"; case "G": return "C"; default: throw new Error("Invalid character encountered (" + n + ")"); } }; TmCalc.neighborH = { AA: 9.1, AT: 8.6, TA: 6.0, CA: 5.8, GT: 6.5, CT: 7.8, GA: 5.6, CG: 11.9, GC: 11.1, GG: 11.0 }; TmCalc.getH = function(n1, n2) { return this.get(this.neighborH, n1, n2); }; TmCalc.neighborG = { AA: 1.9, AT: 1.5, TA: 0.9, CA: 1.9, GT: 1.3, CT: 1.6, GA: 1.6, CG: 3.6, GC: 3.1, GG: 3.1 }; TmCalc.getG = function(n1, n2) { return this.get(this.neighborG, n1, n2); }; TmCalc.neighborS = { AA: 24.0, AT: 23.9, TA: 16.9, CA: 12.9, GT: 17.3, CT: 20.8, GA: 13.5, CG: 27.8, GC: 26.7, GG: 26.6 }; TmCalc.getS = function(n1, n2) { return this.get(this.neighborS, n1, n2); }; TmCalc.get = function(searchVar, n1, n2) { switch (n1 + n2) { case "AA": return searchVar["AA"]; case "TT": return searchVar["AA"]; case "AT": return searchVar["AT"]; case "TA": return searchVar["TA"]; case "TT": return searchVar["TA"]; case "CA": return searchVar["CA"]; case "TG": return searchVar["CA"]; case "GT": return searchVar["GT"]; case "AC": return searchVar["GT"]; case "CT": return searchVar["CT"]; case "AG": return searchVar["CT"]; case "GA": return searchVar["GA"]; case "TC": return searchVar["GA"]; case "CG": return searchVar["CG"]; case "TC": return searchVar["CG"]; case "GC": return searchVar["GC"]; case "TC": return searchVar["GC"]; case "GG": return searchVar["GG"]; case "CC": return searchVar["GG"]; default: throw new Error("No idea what happened... (tried getting " + n1 + n2 + ")"); } }; TmCalc.calcFull = function(data) { /* See (Breslauer et al. 1986): http://www.pnas.org/content/83/11/3746.full.pdf */ var R, dh, ds, dsinit, i, n1, n2, naConc, predG, predH, predS, primerConc, primerc_local, _ref; predH = 0; predG = -(5 + 0.4); predS = 0; primerConc = 500 * 10e-3; naConc = 50 * 10e-3; R = 1.987; n1 = data[0]; for (i = 1, _ref = data.length - 1; 1 <= _ref ? i <= _ref : i >= _ref; 1 <= _ref ? i++ : i--) { n2 = data[i]; predH -= this.getH(n1, n2); predG -= this.getG(n1, n2); predS -= this.getS(n1, n2); n1 = n2; } dh = 1000 * predH; ds = predS; dsinit = -10.8; primerc_local = primerConc / 4 * 1e-6; return dh / (dsinit + ds + R * Math.log(primerc_local / 10)) - 273.15 + 16.6 * Math.log(naConc / 10) / Math.LN10; }; /* Again, old @calcFull: (data, options) -> ## For more details see: http://www.basic.northwestern.edu/biotools/oligocalc.html ## freq = @calcFrequencies(data) if not options options = {} if not options.Na options.Na = 0.05 GC = freq["G"] + freq["C"] AT = freq["A"] + freq["T"] N = data.length if N < 14 AT*2 + GC*4 - 16.6*Math.log(0.050)/Math.log(10) + 16.6*Math.log(options.Na)/Math.log(10) else if N < 50 #18 100.5 + (41 * GC/N) - (820/N) + 16.6*Math.log(options.Na)/Math.log(10) #else if N < 50 # 81.5 + (41 * GC/N) - (500/N) + 16.6*Math.log(options.Na) - 0.62*options.F else 79.8 + 18.5*Math.log(options.Na)/Math.log(10) + (58.4 * GC/N) + (11.8 * (GC/N) * (GC/N)) - (820/N) @calcFrequencies: (data) -> freq = {A: 0, C: 0, G: 0, T: 0} data.forEach (x) -> freq[x]++ freq */ return TmCalc; })(); /* Old version (not very accurate): @calc: (data) -> Math.round(@calcFull(data)*100)/100 @calcFull: (data) -> freq = @calcFrequencies(data) if data.length < 14 4 * (freq["G"] + freq["C"]) + 2 * (freq["A"] + freq["T"]) else 64.9 + 41 * (freq["G"] + freq["C"] - 16.4)/data.length @calcFrequencies: (data) -> freq = {A: 0, C: 0, G: 0, T: 0} data.forEach (x) -> freq[x]++ freq */ // Underscore.js 1.3.3 // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. // Underscore is freely distributable under the MIT license. // Portions of Underscore are inspired or borrowed from Prototype, // Oliver Steele's Functional, and John Resig's Micro-Templating. // For all details and documentation: // http://documentcloud.github.com/underscore (function(){function r(a,c,d){if(a===c)return 0!==a||1/a==1/c;if(null==a||null==c)return a===c;a._chain&&(a=a._wrapped);c._chain&&(c=c._wrapped);if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return!1;switch(e){case "[object String]":return a==""+c;case "[object Number]":return a!=+a?c!=+c:0==a?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source== c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if("object"!=typeof a||"object"!=typeof c)return!1;for(var f=d.length;f--;)if(d[f]==a)return!0;d.push(a);var f=0,g=!0;if("[object Array]"==e){if(f=a.length,g=f==c.length)for(;f--&&(g=f in a==f in c&&r(a[f],c[f],d)););}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return!1;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,h)&&!f--)break; g=!f}}d.pop();return g}var s=this,I=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,J=k.unshift,l=p.toString,K=p.hasOwnProperty,y=k.forEach,z=k.map,A=k.reduce,B=k.reduceRight,C=k.filter,D=k.every,E=k.some,q=k.indexOf,F=k.lastIndexOf,p=Array.isArray,L=Object.keys,t=Function.prototype.bind,b=function(a){return new m(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):s._=b;b.VERSION="1.3.3";var j=b.each=b.forEach=function(a, c,d){if(a!=null)if(y&&a.forEach===y)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===o)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===o)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.map===z)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(A&& a.reduce===A){e&&(c=b.bind(c,e));return f?a.reduce(c,d):a.reduce(c)}j(a,function(a,b,i){if(f)d=c.call(e,d,a,b,i);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(B&&a.reduceRight===B){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a, c,b){var e;G(a,function(a,g,h){if(c.call(b,a,g,h)){e=a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(C&&a.filter===C)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(D&&a.every===D)return a.every(c,b);j(a,function(a,g,h){if(!(e=e&&c.call(b, a,g,h)))return o});return!!e};var G=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(E&&a.some===E)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(q&&a.indexOf===q)return a.indexOf(c)!=-1;return b=G(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck= function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&& (e={value:a,computed:b})});return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){d=Math.floor(Math.random()*(f+1));b[f]=b[d];b[d]=a});return b};b.sortBy=function(a,c,d){var e=b.isFunction(c)?c:function(a){return a[c]};return b.pluck(b.map(a,function(a,b,c){return{value:a,criteria:e.call(d,a,b,c)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c===void 0?1:d===void 0?-1:c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]}; j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:b.isArray(a)||b.isArguments(a)?i.call(a):a.toArray&&b.isFunction(a.toArray)?a.toArray():b.values(a)};b.size=function(a){return b.isArray(a)?a.length:b.keys(a).length};b.first=b.head=b.take=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a, 0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a, e=[];a.length<3&&(c=true);b.reduce(d,function(d,g,h){if(c?b.last(d)!==g||!d.length:!b.include(d,g)){d.push(g);e.push(a[h])}return d},[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1),true);return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a= i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d){d=b.sortedIndex(a,c);return a[d]===c?d:-1}if(q&&a.indexOf===q)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(F&&a.lastIndexOf===F)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){if(arguments.length<= 1){b=a||0;a=0}for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;){g[f++]=a;a=a+d}return g};var H=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));H.prototype=a.prototype;var b=new H,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c= i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(null,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i,j=b.debounce(function(){h= g=false},c);return function(){d=this;e=arguments;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);j()},c));g?h=true:i=a.apply(d,e);j();g=true;return i}};b.debounce=function(a,b,d){var e;return function(){var f=this,g=arguments;d&&!e&&a.apply(f,g);clearTimeout(e);e=setTimeout(function(){e=null;d||a.apply(f,g)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0)); return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=L||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&& c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={};j(b.flatten(i.call(arguments,1)),function(b){b in a&&(c[b]=a[b])});return c};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty= function(a){if(a==null)return true;if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFunction=function(a){return l.call(a)=="[object Function]"}; b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isFinite=function(a){return b.isNumber(a)&&isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a, b){return K.call(a,b)};b.noConflict=function(){s._=I;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){j(b.functions(a),function(c){M(c,b[c]=a[c])})};var N=0;b.uniqueId= function(a){var b=N++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var u=/.^/,n={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},v;for(v in n)n[n[v]]=v;var O=/\\|'|\r|\n|\t|\u2028|\u2029/g,P=/\\(\\|'|r|n|t|u2028|u2029)/g,w=function(a){return a.replace(P,function(a,b){return n[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(O,function(a){return"\\"+n[a]}).replace(d.escape|| u,function(a,b){return"'+\n_.escape("+w(b)+")+\n'"}).replace(d.interpolate||u,function(a,b){return"'+\n("+w(b)+")+\n'"}).replace(d.evaluate||u,function(a,b){return"';\n"+w(b)+"\n;__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",e=new Function(d.variable||"obj","_",a);if(c)return e(c,b);c=function(a){return e.call(this,a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c}; b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var x=function(a,c){return c?b(a).chain():a},M=function(a,c){m.prototype[a]=function(){var a=i.call(arguments);J.call(a,this._wrapped);return x(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return x(d, this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return x(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);