﻿(function() {
  if (!window['google']) return;
  google.load('jquery', '1.4');
  google.setOnLoadCallback(function() {
    // jquery.constrain plugin (v 1.0)
    (function($) {
      $.fn.constrain = function(opt) {
        opt = $.extend(true, {}, {
          limit: {},
          prohibit: { chars: "", regex: false },
          allow: { chars: "", regex: false }
        }, opt);
        function isProhibitedByLimit(input, e) {
          var prohibited = false;
          $.each(opt.limit, function(token, idx) {
            var max = this;
            if (token.charCodeAt(0) == e.which) {
              prohibited = max < 0 ? false : max < $(input).val().split(token).length;
              return false;
            }
          });
          return prohibited;
        };
        function isConfigured(item) {
          return item.chars.length > 0 || (item.regex && item.regex.length > 0);
        };
        function match(item, input, e) {
          var arr = item.chars.split("");
          for (var i in arr) {
            var token = arr[i];
            if (token.charCodeAt(0) == e.which) {
              return true;
            }
          }
          if (item.regex) {
            var re = new RegExp(item.regex);
            if (re.test(String.fromCharCode(e.which))) {
              return true;
            }
          }
          return false;
        };
        function isProhibited(input, e) {
          if (e.which == 0 || e.which == 8 || e.which == 27) {
            return false;
          }
          var prohibit = isConfigured(opt.prohibit) ? match(opt.prohibit, input, e) : false;
          var allow = isConfigured(opt.allow) ? match(opt.allow, input, e) : true;
          var limited = isProhibitedByLimit(input, e);
          return prohibit || !allow || limited;
        };
        return this.each(function() {
          $(this).keypress(function(e) {
            if (isProhibited(this, e)) {
              e.preventDefault();
            }
          });
        });
      };
      $.fn.numeric = function(opt) {
        opt = $.extend(true, {}, {
          onblur: true,
          format: ""
        }, opt);
        var parts = opt.format.split(".");
        var precision = parts.length > 1 ? parts[1].length : false;
        return this.each(function() {
          var allowRe = "\\d";
          if (opt.format.indexOf(".") > -1) {
            allowRe += "\\.";
          }
          if (opt.format.indexOf(",") > -1) {
            allowRe += ",";
          }
          var constraintOptions = {
            allow: { regex: "[" + allowRe + "]" },
            limit: { ".": 1 }
          };
          $(this).constrain(constraintOptions);
          if (precision) {
            $(this).blur(function(e) {
              var n = parseFloat($(this).val());
              if (!isNaN(n)) {
                var val = $(this).val();
                $(this).val($.formatNumber(val, opt.format));
              }
            });
            if (!opt.onblur) {
              var prec = new RegExp("\\d+\\.*\\d{0," + precision + "}");
              $(this).keyup(function(e) {
                if ((e.which < 48 && e.which > 57) ||
                (e.which < 96 && e.which > 105)) {
                  return;
                }
                var val = $(this).val();
                $(this).val(val.match(prec));
              });
            }
          }
        });
      };
    })(jQuery);
    (function($) {
      $.numericFormat = $.numericFormat || {}; $.numericFormat.formats = $.numericFormat.formats || new Array();
      $.extend({
        formatNumber: function(num, format) {
          function _numberFormat(num, format, context) {
            function createNewFormat(format, formatName) {

              var code = "var " + formatName + " = function(num){\n";
              code += "num = num.replace(/,/,'');";
              var formats = format.split(";");
              switch (formats.length) {
                case 1:
                  code += createTerminalFormat(format);
                  break;
                case 2:
                  code += "return (num < 0) ? _numberFormat(num,\""
                + escape(formats[1])
                + "\", 1) : _numberFormat(num,\""
                + escape(formats[0])
                + "\", 2);";
                  break;
                case 3:
                  code += "return (num < 0) ? _numberFormat(num,\""
                + escape(formats[1])
                + "\", 1) : ((num == 0) ? _numberFormat(num,\""
                + escape(formats[2])
                + "\", 2) : _numberFormat(num,\""
                + escape(formats[0])
                + "\", 3));";
                  break;
                default:
                  code += "throw 'Too many semicolons in format string';";
                  break;
              }
              return code + "};";
            };
            function createTerminalFormat(format) {
              if (format.length > 0 && format.search(/[0#?]/) == -1) {
                return "return '" + escape(format) + "';\n";
              }
              var code = "var val = (context == null) ? new Number(num) : Math.abs(num);\n";
              var thousands = false;
              var lodp = format;
              var rodp = "";
              var ldigits = 0;
              var rdigits = 0;
              var scidigits = 0;
              var scishowsign = false;
              var sciletter = "";
              m = format.match(/\..*(e)([+-]?)(0+)/i);
              if (m) {
                sciletter = m[1];
                scishowsign = (m[2] == "+");
                scidigits = m[3].length;
                format = format.replace(/(e)([+-]?)(0+)/i, "");
              }
              var m = format.match(/^([^.]*)\.(.*)$/);
              if (m) {
                lodp = m[1].replace(/\./g, "");
                rodp = m[2].replace(/\./g, "");
              }
              // Look for %
              if (format.indexOf('%') >= 0) {
                code += "val *= 100;\n";
              }
              m = lodp.match(/(,+)(?:$|[^0#?,])/);
              if (m) {
                code += "val /= " + Math.pow(1000, m[1].length) + "\n;";
              }
              if (lodp.search(/[0#?],[0#?]/) >= 0) {
                thousands = true;
              }
              if ((m) || thousands) {
                lodp = lodp.replace(/,/g, "");
              }
              m = lodp.match(/0[0#?]*/);
              if (m) {
                ldigits = m[0].length;
              }
              m = rodp.match(/[0#?]*/);
              if (m) {
                rdigits = m[0].length;
              }
              if (scidigits > 0) {
                code += "var sci = toScientific(num,val,"
                    + ldigits + ", " + rdigits + ", " + scidigits + ", " + scishowsign + ");\n"
                    + "var arr = [sci.l, sci.r];\n";
              }
              else {
                if (format.indexOf('.') < 0) {
                  code += "val = (val > 0) ? Math.ceil(val) : Math.floor(val);\n";
                }
                code += "var arr = round(val," + rdigits + ").toFixed(" + rdigits + ").split('.');\n";
                code += "arr[0] = (val < 0 ? '-' : '') + leftPad((val < 0 ? arr[0].substring(1) : arr[0]), "
                    + ldigits + ", '0');\n";
              }
              if (thousands) {
                code += "arr[0] = addSeparators(arr[0]);\n";
              }
              code += "arr[0] = reverse(injectIntoFormat(reverse(arr[0]), '" + escape(reverse(lodp)) + "', true));\n";
              if (rdigits > 0) {
                code += "arr[1] = injectIntoFormat(arr[1], '" + escape(rodp) + "', false);\n";
              }
              if (scidigits > 0) {
                code += "arr[1] = arr[1].replace(/(\\d{" + rdigits + "})/, '$1" + sciletter + "' + sci.s);\n";
              }
              return code + "return arr.join('.');\n";
            };
            function toScientific(num, val, ldigits, rdigits, scidigits, showsign) {
              var result = { l: "", r: "", s: "" };
              var ex = "";
              var before = Math.abs(val).toFixed(ldigits + rdigits + 1).trim('0');
              var after = Math.round(num, new Number(before.replace(".", "").replace(
                new RegExp("(\\d{" + (ldigits + rdigits) + "})(.*)"), "$1.$2"))).toFixed(0);
              if (after.length >= ldigits) {
                after = after.substring(0, ldigits) + "." + after.substring(ldigits);
              }
              else {
                after += '.';
              }
              result.s = (before.indexOf(".") - before.search(/[1-9]/)) - after.indexOf(".");
              if (result.s < 0) {
                result.s++;
              }
              result.l = (val < 0 ? '-' : '') + leftPad(after.substring(0, after.indexOf(".")), ldigits, "0");
              result.r = after.substring(after.indexOf(".") + 1);
              if (result.s < 0) {
                ex = "-";
              }
              else if (showsign) {
                ex = "+";
              }
              result.s = ex + leftPad(Math.abs(result.s).toFixed(0), scidigits, "0");
              return result;
            };
            function reverse(str) {
              var res = "";
              for (var i = str.length; i > 0; --i) {
                res += str.charAt(i - 1);
              }
              return res;
            };
            function escape(string) {
              return string.replace(/('|\\)/g, "\\$1");
            };
            function leftPad(val, size, ch) {
              var result = new String(val);
              if (ch == null) {
                ch = " ";
              }
              while (result.length < size) {
                result = ch + result;
              }
              return result;
            };
            function round(num, decimals) {
              if (decimals > 0) {
                var m = num.toFixed(decimals + 1).match(
              new RegExp("(-?\\d*)\.(\\d{" + decimals + "})(\\d)\\d*$"));
                if (m && m.length) {
                  return new Number(m[1] + "." + leftPad(Math.round(m[2] + "." + m[3]), decimals, "0"));
                }
              }
              return num;
            };
            function addSeparators(val) {
              var s = reverse(val).replace(/(\d{3})/g, "$1,");
              return reverse(s).replace(/^(-)?,/, "$1");
            };
            function injectIntoFormat(val, format, stuffExtras) {
              var i = 0;
              var j = 0;
              var result = "";
              var revneg = val.charAt(val.length - 1) == '-';
              if (revneg) {
                val = val.substring(0, val.length - 1);
              }
              while (i < format.length && j < val.length && format.substring(i).search(/[0#?]/) >= 0) {
                if (format.charAt(i).match(/[0#?]/)) {
                  if (val.charAt(j) != '-') {
                    result += val.charAt(j);
                  }
                  else {
                    result += "0";
                  }
                  j++;
                }
                else {
                  result += format.charAt(i);
                }
                ++i;
              }
              if (revneg && j == val.length) {
                result += '-';
              }
              if (j < val.length) {
                if (stuffExtras) {
                  result += val.substring(j);
                }
                if (revneg) {
                  result += '-';
                }
              }
              if (i < format.length) {
                result += format.substring(i);
              }
              return result.replace(/#/g, "").replace(/\?/g, " ");
            };
            var formatName = "numFormat" + $.numericFormat.formats.length++;
            eval(createNewFormat(format, formatName));
            return eval(formatName);
          };
          if (!$.numericFormat.formats[format]) {
            $.numericFormat.formats[format] = _numberFormat(num, format);
          };
          return $.numericFormat.formats[format](num);
        }
      });
    })(jQuery);
    // jquery.json plugin (v 2.2)
    (function($) {
      $.toJSON = function(o) {
        if (typeof (JSON) == 'object' && JSON.stringify)
          return JSON.stringify(o);
        var type = typeof (o);
        if (o === null)
          return "null";
        if (type == "undefined")
          return undefined;
        if (type == "number" || type == "boolean")
          return o + "";
        if (type == "string")
          return $.quoteString(o);
        if (type == 'object') {
          if (typeof o.toJSON == "function")
            return $.toJSON(o.toJSON());
          if (o.constructor === Date) {
            var month = o.getUTCMonth() + 1;
            if (month < 10) month = '0' + month;
            var day = o.getUTCDate();
            if (day < 10) day = '0' + day;
            var year = o.getUTCFullYear();
            var hours = o.getUTCHours();
            if (hours < 10) hours = '0' + hours;
            var minutes = o.getUTCMinutes();
            if (minutes < 10) minutes = '0' + minutes;
            var seconds = o.getUTCSeconds();
            if (seconds < 10) seconds = '0' + seconds;
            var milli = o.getUTCMilliseconds();
            if (milli < 100) milli = '0' + milli;
            if (milli < 10) milli = '0' + milli;
            return '"' + year + '-' + month + '-' + day + 'T' + hours + ':' + minutes + ':' + seconds + '.' + milli + 'Z"';
          }
          if (o.constructor === Array) {
            var ret = [];
            for (var i = 0; i < o.length; i++)
              ret.push($.toJSON(o[i]) || "null");
            return "[" + ret.join(",") + "]";
          }
          var pairs = [];
          for (var k in o) {
            var name;
            var type = typeof k;
            if (type == "number")
              name = '"' + k + '"';
            else if (type == "string")
              name = $.quoteString(k);
            else
              continue;  //skip non-string or number keys
            if (typeof o[k] == "function")
              continue;  //skip pairs where the value is a function.
            var val = $.toJSON(o[k]);
            pairs.push(name + ":" + val);
          }
          return "{" + pairs.join(", ") + "}";
        }
      };
      $.evalJSON = function(src) {
        if (typeof (JSON) == 'object' && JSON.parse)
          return JSON.parse(src);
        return eval("(" + src + ")");
      };
      $.secureEvalJSON = function(src) {
        if (typeof (JSON) == 'object' && JSON.parse)
          return JSON.parse(src);
        var filtered = src;
        filtered = filtered.replace(/\\["\\\/bfnrtu]/g, '@');
        filtered = filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
        filtered = filtered.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
        if (/^[\],:{}\s]*$/.test(filtered))
          return eval("(" + src + ")");
        else
          throw new SyntaxError("Error parsing JSON, source is not valid.");
      };
      $.quoteString = function(string) {
        if (string.match(_escapeable)) {
          return '"' + string.replace(_escapeable, function(a) {
            var c = _meta[a];
            if (typeof c === 'string') return c;
            c = a.charCodeAt();
            return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
          }) + '"';
        }
        return '"' + string + '"';
      };
      var _escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
      var _meta = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\' };
    })(jQuery);
    (function($) {
      var SUBSTRUCTURE = 0, EXACT = 1, SIMILARITY = 2, REACTION = 3, SETTINGS = 4,
        DO_SEARCH = 1, GET_PROXY = 2, GET_SETTINGS = 3, GET_COUNT = 4,
        ROW_COUNT = 64, ITEMS_PER_PG = 8, PG_SHIFT = ROW_COUNT / ITEMS_PER_PG, IMG_WIDTH = 670,
        tblResults = null, emptySrc = null, ketcherSrc = null, ketcherTarget = null,
        aProxies = [null, null, null, null, null], aQrs = [{}, {}, {}, {}, {}], aPages = {},
        tabs = null, htms = [], sel = 0, aDivs = [], imgTxt = null,
        ss = { Opts: { Undefined: 1, HighlightSubstructure: 2, TautomerMatching: 4, ResonanceMatching: 8} },
        exct = { Opts: { Undefined: 1, ELE: 2, MAS: 4, STE: 8, FRA: 16, TAU: 32, ALL: 64} },
        sm = { Opts: { Tanimoto: 0, Tversky: 1, Euclid: 2} },
        counter = (function() { var g = 0; return { getNext: function() { return ++g; }, getCount: function() { return g; } }; })();
      aStats = (function() {
        var a = [], o = {
          responseTime: null, results: 0, data: [], total: null,
          curPage: 1, startRow: 1, endRow: ROW_COUNT, firstPage: 1
        };
        $.each([0, 1, 2, 3], function() { a.push(clone(o)); });
        return a;
      })();
      function now() {
        return (new Date()).getTime()
      }
      function clone(obj) {
        function F() { }
        F.prototype = obj;
        return new F();
      }
      function findTags(tag, id, ownr) {
        var s = [tag, "[id$='", id, "']"].join('');
        return (undefined !== ownr) ? $(s, ownr) : $(s);
      }
      function findInps(id, ownr) { return findTags('input', id, ownr); }
      function findDivs(id, ownr) { return findTags('div', id, ownr); }
      function hndl(e) {
        if (undefined !== e) {
          e.preventDefault();
          e.stopPropagation();
        }
      }
      function isValidTab(t) {
        return (undefined !== t && SUBSTRUCTURE <= t && t <= SETTINGS);
      }
      function enable(p, b) {
        if (undefined !== p && null !== p) {
          if (b) { $(p).removeAttr('disabled'); }
          else { $(p).attr('disabled', 'disabled'); }
        }
      }
      function enableArray(a, b) {
        $.each(a, function(i, p) { enable(p, b); });
      }
      function updateOpacity(a) {
        $.each(a, function(i, p) {
          $(p).fadeTo('normal', $(p).attr('disabled') ? 0.4 : 1.0);
        });
      }
      function doEnable(a, b) {
        enableArray(a, b);
        updateOpacity(a);
      }
      function getHtm(t) {
        if (isValidTab(t)) {
          return (t === sel) ? htms[t].hv : htms[t].nm;
        }
        return '';
      }
      function showTab(i) {
        if (isValidTab(i)) {
          var o = sel; sel = i;
          tabs.each(function(j) { $(this).html(getHtm(j)); });
          if (o !== i) { aDivs[o].hide(); aDivs[i].show(); render(); }
        }
      }
      function initTabs() {
        var h = [], pUL = findTags('ul', 'TabUL', findDivs('BingoTabs'));
        $('div', findDivs('hdnImages')).each(function(i) { h.push($(this).html()); });
        if (10 === h.length) {
          htms = [{ hv: h[1], nm: h[0] }, { hv: h[3], nm: h[2] },
            { hv: h[5], nm: h[4] }, { hv: h[7], nm: h[6] }, { hv: h[9], nm: h[8]}];
          tabs = $('li', pUL);
          if (undefined !== tabs && null !== tabs && 0 < tabs.length) {
            tabs.each(function(i) {
              var that = $(this);
              that.html(getHtm(i)).css('display', 'inline')
                .mouseout(function() { that.css('cursor', 'default'); })
                .mouseover(function() { that.css('cursor', ($.browser.msie) ? 'hand' : 'pointer'); })
                .click(function() { showTab(i); });
            });
          }
          pUL.show();
        }
      }
      function getKetcherSrc() {
        if (null === emptySrc) { emptySrc = $('.KetcherIFrame').attr('src'); }
        if (null === ketcherSrc) { ketcherSrc = findInps('KetcherHiddenSrc').val(); }
        return ketcherSrc;
      }
      function getEmptySrc() { return emptySrc; }
      function buildRequest(v, t) {
        v = v || 0; t = t || 0;
        return ['bs.ashx?v=', v.toString(), '&t=', t.toString()].join('');
      }
      function buildGetCountRequest(t, n) {
        var s = buildRequest(GET_COUNT, t);
        return [s, '&cnt=', n.toString()].join('');
      }
      function buildDingoUri(smile, w, h) {
        var wd = w || IMG_WIDTH, hd = h || 300, r = '';
        if (sel === REACTION) { r = '&r=1'; wd *= 2; }
        return ['dingo.ashx?s=', encodeURIComponent(smile),
          '&w=', wd.toString(), '&h=', hd.toString(), r].join('');
      }
      function buildMolfileUri(smile) {
        return ['dingo.ashx?s=', encodeURIComponent(smile), '&w=600&h=300&f=molfile'].join('');
      }
      function getProxy(t) {
        if (isValidTab(t) && t !== SETTINGS && null === aProxies[t]) {
          $.getJSON(buildRequest(GET_PROXY, t), null, function(r) {
            aProxies[t] = r;
            switch (t) {
              case SUBSTRUCTURE: $('.SSQuery').val(r.Query); break;
              case EXACT: $('.XQuery').val(r.Query); break;
              case SIMILARITY: $('.SMQuery').val(r.Query); break;
              case REACTION:
                $('.RQuery').val(r.Query);
                var a = r.Query.split('>>');
                if (undefined !== a && null != a && 2 === a.length) {
                  $('.RReactant').val(a[0]);
                  $('.RProduct').val(a[1]);
                }
                break;
            }
          });
        }
      }
      function populateSettings() {
        $.getJSON(buildRequest(GET_SETTINGS, SETTINGS), null, function(r) {
          function addOption(s, o) {
            try {
              if ($.browser.msie) { s[0].add(new Option(o.text, o.val)); }
              else { s[0].add(new Option(o.text, o.val), null); }
            } catch (e) { }
          }
          function fillSelection(s, a) {
            if (undefined !== s && 0 < s.length) {
              s.empty();
              $.each(a, function(i, v) { addOption(s, v); });
            }
          }
          function fillDbTypes(s, cbx, stgs) {
            var a = [], slctd = null,
              t = $.grep(stgs, function(i) { return (i.DbName === cbx.val()); }),
              dbts = t[0].DbTypeSettings;
            $.each(dbts, function(i, v) {
              a.push({ text: (0 === v.DatabaseType) ? 'Oracle' : 'Sql Server', val: v.DatabaseType });
              if (null === slctd && v.IsDefault) { slctd = v; }
            });
            fillSelection(s, a);
            if (null !== slctd) { s.val(slctd.DatabaseType); }
          }
          var am = [], ar = [], mSel = null, rSel = null,
            mCbx = $('.MDbName'), rCbx = $('.RDbName');
          $.each(r.MolecularSettings, function(i, v) {
            am.push({ text: v.DbName, val: v.DbName });
            if (v.IsDefault && null === mSel) { mSel = v; }
          });
          $.each(r.ReactionSettings, function(i, v) {
            ar.push({ text: v.DbName, val: v.DbName });
            if (v.IsDefault && null === rSel) { rSel = v; }
          });
          fillSelection(mCbx, am); fillSelection(rCbx, ar);
          mCbx.change(function() { fillDbTypes($('.MDbType'), $(this), r.MolecularSettings); });
          rCbx.change(function() { fillDbTypes($('.RDbType'), $(this), r.ReactionSettings); });
          if (null !== mSel) { mCbx.val(mSel.DbName); }
          if (null !== rSel) { rCbx.val(rSel.DbName); }
          mCbx.change(); rCbx.change();
          enable($('.Search'), true);
          uiUnlock();
        });
      }
      function isChecked(chkbx) {
        return $('input', chkbx).is(':checked');
      }
      function setCheck(chkbx) {
        $('input', chkbx).attr('checked', 'checked');
      }
      function setSubstructureBehaviour() {
        var t = $('.SSTautomerOption'),
          r = $('.SSResonanceOption');
        t.click(function() { if (isChecked($(this))) { $('input', r).removeAttr('checked') } });
        r.click(function() { if (isChecked($(this))) { $('input', t).removeAttr('checked') } });
      }
      function fillDbThings(p) {
        if (sel === REACTION) {
          p.DatabaseType = $('.RDbType').val();
          p.DatabaseName = $('.RDbName').val();
        } else {
          p.DatabaseType = $('.MDbType').val();
          p.DatabaseName = $('.MDbName').val();
        }
      }
      function fillSubstructureProxy(p) {
        p.Query = $('.SSQuery').val();
        p.Options = 0;
        if (isChecked($('.SSHighlightOption'))) { p.Options |= ss.Opts.HighlightSubstructure; }
        if (isChecked($('.SSTautomerOption'))) { p.Options |= ss.Opts.TautomerMatching; }
        else if (isChecked($('.SSResonanceOption'))) { p.Options |= ss.Opts.ResonanceMatching; }
        if (0 === p.Options) { p.Options |= ss.Opts.Undefined; }
      }
      function setExactBehaviour() {
        var pTau = $('.XTautomerOption'), pAll = $('.XAllOption'),
          a = ['.XEleOption', '.XMasOption', '.XSteOption', '.XFraOption'];
        pTau.click(function() { enableArray($.merge(['.XAllOption'], a), !isChecked($(this))); });
        pAll.click(function() { if (isChecked($(this))) { $.each(a, function(i, v) { setCheck($(v)); }); } });
        $.each(a, function(i, v) { $(v).click(function() { if (!isChecked($(this))) { $('input', pAll).removeAttr('checked'); } }); });
      }
      function fillExactProxy(p) {
        p.Query = $('.XQuery').val();
        p.Options = 0;
        if (isChecked($('.XTautomerOption'))) {
          p.Options |= exct.Opts.TAU;
        } else {
          if (isChecked($('.XAllOption'))) { p.Options |= exct.Opts.ALL; }
          if (isChecked($('.XEleOption'))) { p.Options |= exct.Opts.ELE; }
          if (isChecked($('.XMasOption'))) { p.Options |= exct.Opts.MAS; }
          if (isChecked($('.XSteOption'))) { p.Options |= exct.Opts.STE; }
          if (isChecked($('.XFraOption'))) { p.Options |= exct.Opts.FRA; }
        }
        if (0 === p.Options) { p.Options |= exct.Opts.Undefined; }
      }
      function setSimilarityBehaviour() {
        $.each([$('.SMTop'), $('.SMBottom')], function(i, v) { v.numeric({ format: "0.00" }); });
      }
      function fillSimilarityProxy(p) {
        function inRange(v) { return 0 <= v && v <= 1; }
        var top = $.trim($('.SMTop').val()),
          btm = $.trim($('.SMBottom').val());
        if (0 < top.length) {
          try {
            p.Top = parseFloat(top);
          } catch (e) { p.Top = -1.0; }
        }
        else { p.Top = -1.0; }
        if (0 < btm.length) {
          try {
            p.Bottom = parseFloat(btm);
          } catch (e) { p.Bottom = -1.0; }
        }
        else { p.Bottom = -1.0; }
        var bOk = (inRange(p.Bottom) && inRange(p.Top) && p.Bottom < p.Top);
        setVisible([$('.SMBottomValid'), $('.SMTopValid')], !bOk);
        if (bOk) {
          p.Query = $('.SMQuery').val();
          p.Options = 0;
          if (isChecked($('.SMTanimotoOption'))) { p.Options |= sm.Opts.Tanimoto; }
          else if (isChecked($('.SMEuclideanOption'))) { p.Options |= sm.Opts.Euclid; }
          else if (isChecked($('.SMTverskyOption'))) { p.Options |= sm.Opts.Tversky; }
          return true;
        }
        return false;
      }
      function mergeReactions(rr, rp) {
        if (undefined === rr) { rr = $('.RReactant') }
        if (undefined === rp) { rp = $('.RProduct') }
        var rLeft = rr.val(), rRght = rp.val(), r = [rLeft, '>>', rRght].join('');
        $('.RQuery').val(r);
        return r;
      }
      function setReactionBehaviour() {
        var rr = $('.RReactant'), rp = $('.RProduct');
        $('.RReactantAction').click(function(e) { hndl(e); callKetcher(rr); return false; });
        $('.RProductAction').click(function(e) { hndl(e); callKetcher(rp); return false; });
        rr.change(function() { mergeReactions(rr, rp); });
        rp.change(function() { mergeReactions(rr, rp); });
      }
      function fillReactionProxy(p) {
        p.Query = mergeReactions();
        p.UseHighlight = isChecked($('.RHighlightOption'));
      }
      function fillProxy(t) {
        if (isValidTab(t) && t !== SETTINGS && null !== aProxies[t]) {
          var p = aProxies[t];
          switch (t) {
            case SUBSTRUCTURE: fillSubstructureProxy(p); break;
            case EXACT: fillExactProxy(p); break;
            case SIMILARITY:
              if (!fillSimilarityProxy(p)) {
                return null;
              }
              break;
            case REACTION: fillReactionProxy(p); break;
          }
          fillDbThings(p);
          p.StartRow = 1;
          p.EndRow = ROW_COUNT;
          return p;
        }
        return null;
      }
      function extractImg() {
        if (null === imgTxt) {
          var p = $('.Progress');
          if (0 < p.length) { imgTxt = p.html(); p.hide().remove(); }
        } else { $('.Progress').hide().remove(); }
      }
      function uiLock() {
        if (null !== imgTxt) {
          var el = $('body'), o = el.offset(), sb = $('.Search').offset();
          $('<div/>', {
            'class': 'locker_css',
            'css': {
              'position': 'absolute', 'top': o.top, 'left': o.left,
              'width': el.outerWidth(), 'height': el.outerHeight() - 5, 'margin': '0px',
              'padding-top': [sb.top.toString(), 'px'].join(''),
              'padding-left': [(sb.left + 24).toString(), 'px'].join(''),
              'z-index': 32000, 'opacity': 0.6, 'color': 'white',
              'background-color': 'white', 'text-align': 'left'
            },
            click: hndl, dblclick: hndl
          }).html(imgTxt).appendTo(el);
        }
      }
      function setVisible(p, b) {
        var f = function(el) { el.css('visibility', b ? 'visible' : 'hidden'); };
        if ($.isArray(p)) { $.each(p, function(i, v) { f(v); }); } else { f(p); }
      }
      function uiUnlock() { $('.locker_css').remove(); }
      function handleDblClickImage(img) {
        var smile = img.attr('alt'), el = $('body'), o = el.offset(), h = [],
          scrollTopOld = img.offset().top,
          wd = el.innerWidth(), hg = screen.height,
          closeImageView = function(e) {
            hndl(e);
            $('.image_view_css').remove();
            $('.BingoRootTable').show();
            $(window).scrollTop(scrollTopOld);
            return false;
          };
        h.push('<img class="ViewImage" alt="Click on image to come back" src="');
        h.push(buildDingoUri(smile, wd, hg)); h.push('"/>');
        $('.BingoRootTable').hide();
        $('<div/>', {
          'class': 'image_view_css',
          'css': {
            'position': 'absolute', 'top': o.top, 'left': o.left,
            'width': wd, 'height': hg, 'margin': '0px', 'padding': '0px',
            'z-index': 32000, 'color': 'white',
            'background-color': 'white', 'text-align': 'left'
          },
          click: closeImageView, dblclick: closeImageView
        }).html(h.join('')).appendTo(el);
      }
      function setupImageView() {
        $('.CellImage').live('click', function(e) {
          hndl(e);
          handleDblClickImage($(this));
          return false;
        });
      }
      function extractPages() {
        var o = $('.Cursor'), p = $('.CursorPage', o);
        $.extend(aPages, { owner: o, pages: p });
        o.hide();
      }
      function renderStat(st, lbl) {
        if (undefined !== st && null !== st) {
          if (undefined === lbl) {
            lbl = $('.SearchStatistic');
          }
          var tot = '...', pgIdx = st.curPage - st.firstPage,
            lft = pgIdx * ITEMS_PER_PG,
            len = Math.min(ITEMS_PER_PG, st.data.length - lft);
          if (null !== st.total) {
            tot = st.total;
          }
          lbl.html(
            ['Response time: ', st.responseTime || '',
            ' sec<br/>Results: ', len.toString(),
            ' of ', tot.toString()].join('')).show();
        }
      }
      function isValidData(st) {
        return (undefined !== st && null !== st && 0 < st.results
          && undefined !== st.data && null !== st.data && 0 < st.data.length);
      }
      function onClickPage(e) {
        hndl(e);
        var st = aStats[sel];
        try {
          st.curPage = parseInt($(this).html());
          renderStat(st);
          renderPages(st);
          renderResults(st);
        } catch (e) { }
      }
      function onClickPrev(e) {
        hndl(e);
        var st = aStats[sel];
        st.firstPage -= PG_SHIFT;
        if (st.firstPage < 1) { st.firstPage = 1; }
        doSearch(sel, true);
      }
      function onClickNext(e) {
        hndl(e);
        var st = aStats[sel];
        st.firstPage += PG_SHIFT;
        doSearch(sel, true);
      }
      function renderPages(st) {
        var pgs = aPages.pages, own = aPages.owner;
        pgs.unbind('click').hide();
        if (isValidData(st)) {
          var maxCnt = ROW_COUNT / ITEMS_PER_PG,
            cnt = st.results / ITEMS_PER_PG,
            rst = st.results % ITEMS_PER_PG,
            hnd = ($.browser.msie) ? 'hand' : 'pointer',
            curPgIdx = st.curPage - st.firstPage + 1;
          if (0 < rst) { cnt++; }
          pgs.each(function(i) {
            if (0 === i) {
              if (1 === st.firstPage) {
                $(this).hide();
              } else {
                $(this).css({ 'color': '#143FFF', 'cursor': hnd, 'font-weight': 'normal' }).show().click(onClickPrev);
              }
            }
            else if (i <= cnt) {
              $(this).html((st.firstPage + i - 1).toString());
              if (i === curPgIdx) {
                $(this).css({ 'color': 'black', 'cursor': 'default', 'font-weight': 'bold' }).show();
              } else {
                $(this).css({ 'color': '#143FFF', 'cursor': hnd, 'font-weight': 'normal' }).show().click(onClickPage);
              }
            } else if (cnt === maxCnt && rst === 0 && i === cnt + 1) {
              $(this).css({ 'color': '#143FFF', 'cursor': hnd, 'font-weight': 'normal' }).show().click(onClickNext);
            } else {
              $(this).hide();
            }
          });
          own.show();
          setVisible(own, true);
        } else {
          own.hide();
          setVisible(own, false);
        }
      }
      function renderResults(st) {
        if (isValidData(st)) {
          var h = [], pgIdx = st.curPage - st.firstPage, lft = pgIdx * ITEMS_PER_PG,
            len = Math.min(ITEMS_PER_PG, st.data.length - lft), rgt = lft + len;
          h.push('<tbody>');
          for (var i = lft; i < rgt; i++) {
            var v = st.data[i], wd = IMG_WIDTH,
              isHlgt = (undefined !== v.HighlightedSmile && null != v.HighlightedSmile && 0 < v.HighlightedSmile.length),
              smile = isHlgt ? v.HighlightedSmile : v.Smile;
            if (sel === REACTION) { wd *= 2; }
            h.push('<tr>'); h.push('<td style="width:');
            h.push(wd.toString()); h.push('px; text-align:left">');
            h.push('<b>ID: </b>'); h.push(v.Id); h.push('<br/>');
            h.push('<b>SMILES: </b>'); h.push(v.Smile); h.push('<br/>');
            h.push('</td>'); h.push('</tr>');
            h.push('<tr>'); h.push('<td style="text-align:center; width:');
            h.push(wd.toString()); h.push('px;">');
            h.push('<img class="CellImage" title="Click to enlarge" src="'); h.push(buildDingoUri(smile)); h.push('" alt="'); h.push(smile); h.push('"/>');
            h.push('</td>'); h.push('</tr>');
          }
          h.push('</tbody>');
          tblResults.html(h.join(''));
        } else {
          tblResults.html('');
        }
      }
      function render() {
        var lbl = $('.SearchStatistic'), tblDiv = $('div.Results');
        if (null === tblResults) {
          tblResults = $('<table border="0" cellPadding="0" cellSpacing="0" align="center" style="table-layout:fixed; width:675px;"/>').appendTo(tblDiv);
        }
        if (sel === SETTINGS) {
          tblResults.html('');
          setVisible([lbl, tblDiv, $('.Search'), tblResults], false);
          renderPages(null);
        } else {
          setVisible($('.Search'), true);
          var st = aStats[sel], bVsbl = (null !== st && null !== st.responseTime);
          renderStat(st, lbl);
          renderPages(st);
          renderResults(st);
          setVisible([lbl, tblDiv, tblResults], bVsbl);
        }
      }
      function doSearch(t, prvOrNxt) {
        if (isValidTab(t) && t !== SETTINGS) {
          var p = null, st = aStats[t], tot = st.total || null;
          if (undefined === prvOrNxt) {
            p = fillProxy(t);
            st.firstPage = 1;
          }
          else {
            p = aProxies[t];
            p.StartRow = (st.firstPage - 1) * ITEMS_PER_PG + 1;
            p.EndRow = p.StartRow + ROW_COUNT - 1;
          }
          if (undefined !== p && null !== p) {
            st.curPage = st.firstPage;
            uiLock();
            var ts = now();
            $.post(buildRequest(DO_SEARCH, t), $.toJSON(p), function(data) {
              var len = (undefined !== data && null !== data) ? data.length : 0;
              uiUnlock();
              $.extend(st, {
                responseTime: $.formatNumber((0.001 * (now() - ts)).toString(), '0.00'),
                results: len, data: data || null,
                total: (function(r, tt) {
                  if (undefined !== prvOrNxt && undefined !== tt && null !== tot) { return tot; }
                  return (ROW_COUNT === r) ? null : r;
                })(len, tot)
              });
              render();
              if (undefined === prvOrNxt && null === st.total && 1 === st.firstPage) {
                $.post(buildGetCountRequest(t, counter.getNext()), $.toJSON(p), function(aCnts) {
                  if (undefined !== aCnts && null !== aCnts && 3 === aCnts.length && counter.getCount() == aCnts[1]) {
                    var tt = aCnts[2];
                    if (st !== aStats[tt]) { aStats[tt].total = aCnts[0]; }
                    else { st.total = aCnts[0]; }
                    renderStat(st);
                  }
                });
              }
            });
          }
        }
      }
      function getKetcherObj() {
        var iFrm = $('.KetcherIFrame');
        if (0 < iFrm.length) {
          return ('contentWindow' in iFrm[0]) ?
            iFrm[0].contentWindow.ketcher
            : document.frames[iFrm.attr('id')].window.ketcher;
        }
        return null;
      }
      function callKetcher(trgt) {
        if (undefined !== trgt) {
          ketcherTarget = trgt;
        } else {
          switch (sel) {
            case SUBSTRUCTURE: ketcherTarget = $('.SSQuery'); break;
            case EXACT: ketcherTarget = $('.XQuery'); break;
            case SIMILARITY: ketcherTarget = $('.SMQuery'); break;
            case REACTION: ketcherTarget = $('.RQuery'); break;
            default: break;
          }
        }
        $('.BingoRootTable').hide();
        $('.KetcherPanel').show();
        $('.KetcherIFrame')
          .load(function() {
            if (undefined !== ketcherTarget && null !== ketcherTarget) {
              $.getJSON(buildMolfileUri(ketcherTarget.val()), null, function(r) {
                try {
                  var kObj = getKetcherObj();
                  if (null != kObj) { kObj.setMolecule(r); }
                } catch (e) { }
                $.each(['.KetcherSmiles', '.CancelBtn'], function(i, v) { $(v).show(); });
              });
            }
          }).attr('src', getKetcherSrc());
      }
      function cancelKetcher(iFrm) {
        $.each(['.KetcherSmiles', '.CancelBtn'], function(i, v) { $(v).hide(); });
        if (undefined === iFrm) { iFrm = $('.KetcherIFrame'); }
        ketcherTarget = null;
        iFrm.attr('src', getEmptySrc());
        $('.KetcherPanel').hide();
        $('.BingoRootTable').show();
      }
      function getSmiles() {
        var iFrm = $('.KetcherIFrame'),
          hasTrgt = (undefined !== ketcherTarget && null !== ketcherTarget),
          trgt = ketcherTarget || null, kObj = getKetcherObj(),
          smiles = (null !== kObj) ? kObj.getSmiles() : '';
        cancelKetcher(iFrm);
        if (undefined !== smiles && null !== smiles && 0 < smiles.length && hasTrgt) {
          trgt.val(smiles);
          if (sel === REACTION) { mergeReactions(); }
        }
      }
      $.ajaxSetup({
        cache: false, dataType: 'json', timeout: 180000,
        contentType: 'application/json',
        error: function(xhr, status, error) {
          // TODO: out error...
          uiUnlock();
        }
      });
      $(function() {
        var sBtn = $('.Search'); enable(sBtn, false);
        sBtn.click(function(e) { hndl(e); doSearch(sel); return false; });
        extractPages();
        extractImg();
        $.each(['.SSPanel', '.XPanel', '.SMPanel', '.RPanel', '.SPanel'], function(i, v) { aDivs.push($(v)); });
        initTabs();
        $('.BingoRootTable').show();
        uiLock();
        $('.Ketcher').click(function(e) { hndl(e); callKetcher(); return false; });
        $('.KetcherSmiles').click(function(e) { hndl(e); getSmiles(); return false; });
        $('.CancelBtn').click(function(e) { hndl(e); cancelKetcher(); return false; });
        setSubstructureBehaviour();
        setExactBehaviour();
        setSimilarityBehaviour();
        setReactionBehaviour();
        render();
        $.each([SUBSTRUCTURE, EXACT, SIMILARITY, REACTION], function(i, v) { getProxy(v); });
        setupImageView();
        populateSettings();
      });
    })(jQuery);
  });
})();
