// Generated by CoffeeScript 1.12.7
(function() {
  var AsyncDataFetcher, JobRunner, SimpleCache, Utils, globalRoot, root,
    slice = [].slice,
    indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
    hasProp = {}.hasOwnProperty;

  if (window.forTrusted == null) {
    window.forTrusted = function(handler) {
      return function(event) {
        if (event != null ? event.isTrusted : void 0) {
          return handler.apply(this, arguments);
        } else {
          return true;
        }
      };
    };
  }

  Utils = {
    isFirefox: (function() {
      var isFirefox, ref, ref1;
      isFirefox = false;
      if (typeof browser !== "undefined" && browser !== null) {
        if ((ref = browser.runtime) != null) {
          if (typeof ref.getBrowserInfo === "function") {
            if ((ref1 = ref.getBrowserInfo()) != null) {
              if (typeof ref1.then === "function") {
                ref1.then(function(browserInfo) {
                  return isFirefox = (browserInfo != null ? browserInfo.name : void 0) === "Firefox";
                });
              }
            }
          }
        }
      }
      return function() {
        return isFirefox;
      };
    })(),
    getCurrentVersion: function() {
      return chrome.runtime.getManifest().version;
    },
    isExtensionPage: function(win) {
      var ref;
      if (win == null) {
        win = window;
      }
      try {
        return ((ref = win.document.location) != null ? ref.origin : void 0) + "/" === chrome.extension.getURL("");
      } catch (error) {}
    },
    isBackgroundPage: function() {
      var base;
      return this.isExtensionPage() && (typeof (base = chrome.extension).getBackgroundPage === "function" ? base.getBackgroundPage() : void 0) === window;
    },
    invokeCommandString: function() {
      var args, component, k, l, len, name, names, obj, ref, str;
      str = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
      ref = str.split('.'), names = 2 <= ref.length ? slice.call(ref, 0, k = ref.length - 1) : (k = 0, []), name = ref[k++];
      obj = window;
      for (l = 0, len = names.length; l < len; l++) {
        component = names[l];
        obj = obj[component];
      }
      return obj[name].apply(obj, args);
    },
    escapeRegexSpecialCharacters: (function() {
      var escapeRegex;
      escapeRegex = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;
      return function(str) {
        return str.replace(escapeRegex, "\\$&");
      };
    })(),
    escapeHtml: function(string) {
      return string.replace(/</g, "&lt;").replace(/>/g, "&gt;");
    },
    createUniqueId: (function() {
      var id;
      id = 0;
      return function() {
        return id += 1;
      };
    })(),
    hasChromePrefix: (function() {
      var chromePrefixes;
      chromePrefixes = ["about:", "view-source:", "extension:", "chrome-extension:", "data:"];
      return function(url) {
        var k, len, prefix;
        for (k = 0, len = chromePrefixes.length; k < len; k++) {
          prefix = chromePrefixes[k];
          if (url.startsWith(prefix)) {
            return true;
          }
        }
        return false;
      };
    })(),
    hasJavascriptPrefix: function(url) {
      return url.startsWith("javascript:");
    },
    hasFullUrlPrefix: (function() {
      var urlPrefix;
      urlPrefix = new RegExp("^[a-z]{3,}://.");
      return function(url) {
        return urlPrefix.test(url);
      };
    })(),
    decodeURIByParts: function(uri) {
      return uri.split(/(?=%)/).map(function(uriComponent) {
        try {
          return decodeURIComponent(uriComponent);
        } catch (error) {
          return uriComponent;
        }
      }).join("");
    },
    createFullUrl: function(partialUrl) {
      if (this.hasFullUrlPrefix(partialUrl)) {
        return partialUrl;
      } else {
        return "http://" + partialUrl;
      }
    },
    isUrl: function(str) {
      var dottedParts, hostName, lastPart, longTlds, match, ref, specialHostNames, urlRegex;
      if (indexOf.call(str, ' ') >= 0) {
        return false;
      }
      if (this.hasFullUrlPrefix(str)) {
        return true;
      }
      urlRegex = new RegExp('^(?:([^:]+)(?::([^:]+))?@)?' + '([^:]+|\\[[^\\]]+\\])' + '(?::(\\d+))?$');
      longTlds = ['arpa', 'asia', 'coop', 'info', 'jobs', 'local', 'mobi', 'museum', 'name', 'onion'];
      specialHostNames = ['localhost'];
      match = urlRegex.exec((str.split('/'))[0]);
      if (!match) {
        return false;
      }
      hostName = match[3];
      if (indexOf.call(specialHostNames, hostName) >= 0) {
        return true;
      }
      if (indexOf.call(hostName, ':') >= 0) {
        return true;
      }
      dottedParts = hostName.split('.');
      if (dottedParts.length > 1) {
        lastPart = dottedParts.pop();
        if ((2 <= (ref = lastPart.length) && ref <= 3) || indexOf.call(longTlds, lastPart) >= 0) {
          return true;
        }
      }
      if (/^(\d{1,3}\.){3}\d{1,3}$/.test(hostName)) {
        return true;
      }
      return false;
    },
    createSearchQuery: function(query) {
      if (typeof query === "string") {
        query = query.split(/\s+/);
      }
      return query.map(encodeURIComponent).join("+");
    },
    createSearchUrl: function(query, searchUrl) {
      if (searchUrl == null) {
        searchUrl = Settings.get("searchUrl");
      }
      if (!(0 <= searchUrl.indexOf("%s"))) {
        searchUrl += "%s";
      }
      return searchUrl.replace(/%s/g, this.createSearchQuery(query));
    },
    extractQuery: (function(_this) {
      return function() {
        var httpProtocolRegexp, queryTerminator;
        queryTerminator = new RegExp("[?&#/]");
        httpProtocolRegexp = new RegExp("^https?://");
        return function(searchUrl, url) {
          var k, len, ref, suffix, suffixTerms;
          url = url.replace(httpProtocolRegexp);
          searchUrl = searchUrl.replace(httpProtocolRegexp);
          ref = searchUrl.split("%s"), searchUrl = ref[0], suffixTerms = 2 <= ref.length ? slice.call(ref, 1) : [];
          if (!url.startsWith(searchUrl)) {
            return null;
          }
          for (k = 0, len = suffixTerms.length; k < len; k++) {
            suffix = suffixTerms[k];
            if (!(0 <= url.indexOf(suffix))) {
              return null;
            }
          }
          try {
            return url.slice(searchUrl.length).split(queryTerminator)[0].split("+").map(decodeURIComponent).join(" ");
          } catch (error) {
            return null;
          }
        };
      };
    })(this)(),
    convertToUrl: function(string) {
      string = string.trim();
      if (Utils.hasChromePrefix(string)) {
        return string;
      } else if (Utils.hasJavascriptPrefix(string)) {
        if (Utils.haveChromeVersion("46.0.2467.2")) {
          return string;
        } else {
          return Utils.decodeURIByParts(string);
        }
      } else if (Utils.isUrl(string)) {
        return Utils.createFullUrl(string);
      } else {
        return Utils.createSearchUrl(string);
      }
    },
    isString: function(obj) {
      return typeof obj === 'string' || obj instanceof String;
    },
    distinctCharacters: function(str) {
      var ch, chars, index;
      chars = str.split("").sort();
      return ((function() {
        var k, len, results;
        results = [];
        for (index = k = 0, len = chars.length; k < len; index = ++k) {
          ch = chars[index];
          if (index === 0 || ch !== chars[index - 1]) {
            results.push(ch);
          }
        }
        return results;
      })()).join("");
    },
    compareVersions: function(versionA, versionB) {
      var a, b, i, k, ref;
      versionA = versionA.split(".");
      versionB = versionB.split(".");
      for (i = k = 0, ref = Math.max(versionA.length, versionB.length); 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
        a = parseInt(versionA[i] || 0, 10);
        b = parseInt(versionB[i] || 0, 10);
        if (a < b) {
          return -1;
        } else if (a > b) {
          return 1;
        }
      }
      return 0;
    },
    haveChromeVersion: function(required) {
      var chromeVersion, ref;
      chromeVersion = (ref = navigator.appVersion.match(/Chrom(e|ium)\/(.*?) /)) != null ? ref[2] : void 0;
      return chromeVersion && 0 <= Utils.compareVersions(chromeVersion, required);
    },
    zip: function(arrays) {
      return arrays[0].map(function(_, i) {
        return arrays.map(function(array) {
          return array[i];
        });
      });
    },
    hasUpperCase: function(s) {
      return s.toLowerCase() !== s;
    },
    matchesAnyRegexp: function(regexps, string) {
      var k, len, re;
      for (k = 0, len = regexps.length; k < len; k++) {
        re = regexps[k];
        if (re.test(string)) {
          return true;
        }
      }
      return false;
    },
    setTimeout: function(ms, func) {
      return setTimeout(func, ms);
    },
    nextTick: function(func) {
      return this.setTimeout(0, func);
    },
    makeIdempotent: function(func) {
      return function() {
        var args, base, previousFunc, ref;
        args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
        return typeof (base = (ref = [func, null], previousFunc = ref[0], func = ref[1], ref))[0] === "function" ? base[0].apply(base, args) : void 0;
      };
    },
    monitorChromeStorage: function(key, setter) {
      return typeof chrome !== "undefined" && chrome !== null ? chrome.storage.local.get(key, (function(_this) {
        return function(obj) {
          if (obj[key] != null) {
            setter(obj[key]);
          }
          return chrome.storage.onChanged.addListener(function(changes, area) {
            var ref;
            if (((ref = changes[key]) != null ? ref.newValue : void 0) != null) {
              return setter(changes[key].newValue);
            }
          });
        };
      })(this)) : void 0;
    }
  };

  Function.prototype.curry = function() {
    var fixedArguments, fn;
    fixedArguments = Array.copy(arguments);
    fn = this;
    return function() {
      return fn.apply(this, fixedArguments.concat(Array.copy(arguments)));
    };
  };

  Array.copy = function(array) {
    return Array.prototype.slice.call(array, 0);
  };

  String.prototype.startsWith = function(str) {
    return this.indexOf(str) === 0;
  };

  String.prototype.ltrim = function() {
    return this.replace(/^\s+/, "");
  };

  String.prototype.rtrim = function() {
    return this.replace(/\s+$/, "");
  };

  String.prototype.reverse = function() {
    return this.split("").reverse().join("");
  };

  globalRoot = typeof window !== "undefined" && window !== null ? window : global;

  globalRoot.extend = function(hash1, hash2) {
    var key;
    for (key in hash2) {
      if (!hasProp.call(hash2, key)) continue;
      hash1[key] = hash2[key];
    }
    return hash1;
  };

  SimpleCache = (function() {
    function SimpleCache(expiry, entries) {
      this.expiry = expiry != null ? expiry : 60 * 60 * 1000;
      this.entries = entries != null ? entries : 1000;
      this.cache = {};
      this.previous = {};
      this.lastRotation = new Date();
    }

    SimpleCache.prototype.has = function(key) {
      this.rotate();
      return (key in this.cache) || key in this.previous;
    };

    SimpleCache.prototype.set = function(key, value) {
      if (value == null) {
        value = null;
      }
      this.rotate();
      delete this.previous[key];
      if (value != null) {
        return this.cache[key] = value;
      } else {
        delete this.cache[key];
        return null;
      }
    };

    SimpleCache.prototype.get = function(key) {
      this.rotate();
      if (key in this.cache) {
        return this.cache[key];
      } else if (key in this.previous) {
        this.cache[key] = this.previous[key];
        delete this.previous[key];
        return this.cache[key];
      } else {
        return null;
      }
    };

    SimpleCache.prototype.rotate = function(force) {
      if (force == null) {
        force = false;
      }
      return Utils.nextTick((function(_this) {
        return function() {
          if (force || _this.entries < Object.keys(_this.cache).length || _this.expiry < new Date() - _this.lastRotation) {
            _this.lastRotation = new Date();
            _this.previous = _this.cache;
            return _this.cache = {};
          }
        };
      })(this));
    };

    SimpleCache.prototype.clear = function() {
      this.rotate(true);
      return this.rotate(true);
    };

    return SimpleCache;

  })();

  AsyncDataFetcher = (function() {
    function AsyncDataFetcher(fetch) {
      this.data = null;
      this.queue = [];
      Utils.nextTick((function(_this) {
        return function() {
          return fetch(function(data) {
            var callback, k, len, ref;
            _this.data = data;
            ref = _this.queue;
            for (k = 0, len = ref.length; k < len; k++) {
              callback = ref[k];
              callback(_this.data);
            }
            return _this.queue = null;
          });
        };
      })(this));
    }

    AsyncDataFetcher.prototype.use = function(callback) {
      if (this.data != null) {
        return callback(this.data);
      } else {
        return this.queue.push(callback);
      }
    };

    return AsyncDataFetcher;

  })();

  JobRunner = (function() {
    function JobRunner(jobs) {
      this.jobs = jobs;
      this.fetcher = new AsyncDataFetcher((function(_this) {
        return function(callback) {
          var job, k, len, ref, results;
          ref = _this.jobs;
          results = [];
          for (k = 0, len = ref.length; k < len; k++) {
            job = ref[k];
            results.push((function(job) {
              return Utils.nextTick(function() {
                return job(function() {
                  _this.jobs = _this.jobs.filter(function(j) {
                    return j !== job;
                  });
                  if (_this.jobs.length === 0) {
                    return callback(true);
                  }
                });
              });
            })(job));
          }
          return results;
        };
      })(this));
    }

    JobRunner.prototype.onReady = function(callback) {
      return this.fetcher.use(callback);
    };

    return JobRunner;

  })();

  root = typeof exports !== "undefined" && exports !== null ? exports : window;

  root.Utils = Utils;

  root.SimpleCache = SimpleCache;

  root.AsyncDataFetcher = AsyncDataFetcher;

  root.JobRunner = JobRunner;

}).call(this);
