// Generated by IcedCoffeeScript 1.7.1-c
(function() {
  var ACCTYPES, BTC, CHECK, Cryptocurrency, E, Link, LinkTable, RemoteProof, Revoke, SHA256, ST, SelfSig, Track, Untrack, Warnings, asyncify, athrow, bitcoyne, cheerio, colors, constants, date_to_unix, db, deq, dict_union, env, format_fingerprint, iced, log, make_email, make_esc, proofs, req, request, scrapemod, util, __iced_k, __iced_k_noop, _ref, _ref1, _ref2,
    __hasProp = {}.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; },
    __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; };

  iced = require('iced-runtime').iced;
  __iced_k = __iced_k_noop = function() {};

  db = require('./db');

  req = require('./req');

  log = require('./log');

  constants = require('./constants').constants;

  SHA256 = require('./keyutils').SHA256;

  E = require('./err').E;

  _ref = require('pgp-utils').util, format_fingerprint = _ref.format_fingerprint, Warnings = _ref.Warnings, asyncify = _ref.asyncify;

  make_esc = require('iced-error').make_esc;

  ST = constants.signature_types;

  ACCTYPES = constants.allowed_cryptocurrency_types;

  _ref1 = require('./util'), dict_union = _ref1.dict_union, date_to_unix = _ref1.date_to_unix, make_email = _ref1.make_email;

  proofs = require('keybase-proofs');

  cheerio = require('cheerio');

  request = require('request');

  colors = require('./colors');

  deq = require('deep-equal');

  util = require('util');

  env = require('./env').env;

  scrapemod = require('./scrapers');

  _ref2 = require('./display'), CHECK = _ref2.CHECK, BTC = _ref2.BTC;

  athrow = require('iced-utils').util.athrow;

  bitcoyne = require('bitcoyne');

  exports.Link = Link = (function() {
    Link.ID_TYPE = constants.ids.sig_chain_link;

    function Link(_arg) {
      this.id = _arg.id, this.obj = _arg.obj;
      this.id || (this.id = this.obj.payload_hash);
      this._revoked = false;
    }

    Link.prototype.export_to_user = function() {
      return {
        seqno: this.seqno(),
        payload_hash: this.id,
        sig_id: this.sig_id()
      };
    };

    Link.prototype.prev = function() {
      return this.obj.prev;
    };

    Link.prototype.seqno = function() {
      return this.obj.seqno;
    };

    Link.prototype.sig = function() {
      return this.obj.sig;
    };

    Link.prototype.payload_json_str = function() {
      return this.obj.payload_json;
    };

    Link.prototype.fingerprint = function() {
      return this.obj.fingerprint.toLowerCase();
    };

    Link.prototype.short_key_id = function() {
      return this.fingerprint().slice(-8).toUpperCase();
    };

    Link.prototype.is_self_sig = function() {
      return false;
    };

    Link.prototype.self_signer = function() {
      var _ref3, _ref4, _ref5;
      return (_ref3 = this.payload_json()) != null ? (_ref4 = _ref3.body) != null ? (_ref5 = _ref4.key) != null ? _ref5.username : void 0 : void 0 : void 0;
    };

    Link.prototype.sig_type = function() {
      return this.obj.sig_type;
    };

    Link.prototype.sig_id = function() {
      return this.obj.sig_id;
    };

    Link.prototype.remote_id = function() {
      return this.obj.remote_id;
    };

    Link.prototype.body = function() {
      var _ref3;
      return (_ref3 = this.payload_json()) != null ? _ref3.body : void 0;
    };

    Link.prototype.ctime = function() {
      return date_to_unix(this.obj.ctime);
    };

    Link.prototype.revoke = function() {
      return this._revoked = true;
    };

    Link.prototype.is_revoked = function() {
      return this._revoked;
    };

    Link.prototype.to_track_obj = function() {
      return {
        seqno: this.seqno(),
        sig_id: this.sig_id(),
        payload_hash: this.id
      };
    };

    Link.prototype.payload_json = function() {
      var e, ret, s;
      if (this._payload_obj == null) {
        s = this.payload_json_str();
        ret = {};
        try {
          ret = JSON.parse(s);
        } catch (_error) {
          e = _error;
          log.error("Error parsing JSON " + s + ": " + e.message);
        }
        this._payload_obj = ret;
      }
      return this._payload_obj;
    };

    Link.prototype.walk = function(_arg) {
      var fn, key, parent;
      fn = _arg.fn, parent = _arg.parent, key = _arg.key;
      return fn({
        value: this,
        key: key,
        parent: parent
      });
    };

    Link.prototype.flatten = function() {
      return [this];
    };

    Link.prototype.is_leaf = function() {
      return true;
    };

    Link.prototype.is_revocable = function() {
      return false;
    };

    Link.prototype.matches = function(rxx) {
      return !!(JSON.stringify(this.condense()).match(rxx));
    };

    Link.prototype.condense = function() {
      return this.payload_json();
    };

    Link.prototype.summary = function() {
      return {
        seqno: this.seqno(),
        id: this.sig_id(),
        type: this.type_str(),
        ctime: this.ctime(),
        live: !(this.is_revoked()),
        payload: this.condense()
      };
    };

    Link.prototype.verify = function() {
      var a, b, err, j;
      err = null;
      if ((a = this.obj.payload_hash) !== (b = this.id)) {
        err = new E.CorruptionError("Link ID mismatch: " + a + " != " + b);
      } else if ((j = SHA256(this.payload_json_str()).toString('hex')) !== this.id) {
        err = new E.CorruptionError("Link has wrong id: " + this.id + " != " + this.j);
      }
      return err;
    };

    Link.prototype.store = function(cb) {
      var err, ___iced_passed_deferral, __iced_deferrals, __iced_k, _ref3;
      __iced_k = __iced_k_noop;
      ___iced_passed_deferral = iced.findDeferral(arguments);
      if (((_ref3 = this.obj.prev) != null ? _ref3.length : void 0) === 0) {
        this.obj.prev = null;
      }
      log.debug("| putting link: " + this.id);
      (function(_this) {
        return (function(__iced_k) {
          __iced_deferrals = new iced.Deferrals(__iced_k, {
            parent: ___iced_passed_deferral,
            filename: "/home/jacko/node-client/src/chainlink.iced",
            funcname: "Link.store"
          });
          db.put({
            type: Link.ID_TYPE,
            key: _this.id,
            value: _this.obj
          }, __iced_deferrals.defer({
            assign_fn: (function() {
              return function() {
                return err = arguments[0];
              };
            })(),
            lineno: 118
          }));
          __iced_deferrals._fulfill();
        });
      })(this)((function(_this) {
        return function() {
          return cb(err);
        };
      })(this));
    };

    Link.prototype.refresh = function(cb) {
      return cb(null);
    };

    Link.alloc = function(_arg) {
      var id, klass, obj;
      obj = _arg.obj, id = _arg.id;
      klass = (function() {
        switch (obj.sig_type) {
          case ST.SELF_SIG:
            return SelfSig;
          case ST.REMOTE_PROOF:
            return RemoteProof;
          case ST.TRACK:
            return Track;
          case ST.CRYPTOCURRENCY:
            return Cryptocurrency;
          case ST.REVOKE:
            return Revoke;
          case ST.UNTRACK:
            return Untrack;
          default:
            return Link;
        }
      })();
      return new klass({
        obj: obj,
        id: id
      });
    };

    Link.load = function(id, cb) {
      var err, obj, ret, ___iced_passed_deferral, __iced_deferrals, __iced_k;
      __iced_k = __iced_k_noop;
      ___iced_passed_deferral = iced.findDeferral(arguments);
      ret = null;
      (function(_this) {
        return (function(__iced_k) {
          __iced_deferrals = new iced.Deferrals(__iced_k, {
            parent: ___iced_passed_deferral,
            filename: "/home/jacko/node-client/src/chainlink.iced",
            funcname: "Link.load"
          });
          db.get({
            type: Link.ID_TYPE,
            key: id
          }, __iced_deferrals.defer({
            assign_fn: (function() {
              return function() {
                err = arguments[0];
                return obj = arguments[1];
              };
            })(),
            lineno: 144
          }));
          __iced_deferrals._fulfill();
        });
      })(this)((function(_this) {
        return function() {
          if (typeof err !== "undefined" && err !== null) {

          } else if (typeof obj !== "undefined" && obj !== null) {
            ret = Link.alloc({
              id: id,
              obj: obj
            });
            if ((err = ret.verify()) != null) {
              ret = null;
            }
          }
          return cb(err, ret);
        };
      })(this));
    };

    Link.prototype.insert_into_table = function() {
      return log.warn("unhandled public sig type: " + (this.sig_type()));
    };

    return Link;

  })();

  SelfSig = (function(_super) {
    __extends(SelfSig, _super);

    function SelfSig() {
      return SelfSig.__super__.constructor.apply(this, arguments);
    }

    SelfSig.prototype.is_self_sig = function() {
      return true;
    };

    SelfSig.prototype.condense = function() {
      return "self";
    };

    SelfSig.prototype.type_str = function() {
      return "self";
    };

    SelfSig.prototype.is_revocable = function() {
      return true;
    };

    SelfSig.prototype.insert_into_table = function(_arg) {
      var table;
      table = _arg.table;
      return table.insert(this.sig_type(), this);
    };

    return SelfSig;

  })(Link);

  RemoteProof = (function(_super) {
    __extends(RemoteProof, _super);

    function RemoteProof() {
      return RemoteProof.__super__.constructor.apply(this, arguments);
    }

    RemoteProof.prototype.proof_service_object = function() {
      var _ref3, _ref4;
      return (_ref3 = this.payload_json()) != null ? (_ref4 = _ref3.body) != null ? _ref4.service : void 0 : void 0;
    };

    RemoteProof.prototype.proof_type = function() {
      return this.obj.proof_type;
    };

    RemoteProof.prototype.proof_state = function() {
      return this.obj.proof_state;
    };

    RemoteProof.prototype.remote_username = function() {
      var _ref3;
      return (_ref3 = this.proof_service_object()) != null ? _ref3.username : void 0;
    };

    RemoteProof.prototype.api_url = function() {
      return this.obj.api_url;
    };

    RemoteProof.prototype.human_url = function() {
      return this.obj.human_url;
    };

    RemoteProof.prototype.proof_text_check = function() {
      return this.obj.proof_text_check;
    };

    RemoteProof.prototype.type_str = function() {
      return "proof";
    };

    RemoteProof.prototype.is_revocable = function() {
      return true;
    };

    RemoteProof.prototype.condense = function() {
      var key, pso, val;
      pso = this.proof_service_object();
      key = pso.name || pso.protocol;
      if (key.slice(-1)[0] !== ':') {
        key += ":";
      }
      val = pso.username || pso.domain || pso.hostname;
      return [key, val].join('//');
    };

    RemoteProof.prototype.insert_into_table = function(_arg) {
      var S, index, keys, opts, states, sub_id, table, _ref3;
      table = _arg.table, index = _arg.index, opts = _arg.opts;
      log.debug("+ RemoteProof::insert_into_table");
      S = constants.proof_state;
      states = [S.OK, S.TEMP_FAILURE, S.LOOKING];
      if (opts != null ? opts.show_perm_failures : void 0) {
        states.push(S.PERM_FAILURE);
      }
      if (_ref3 = this.proof_state(), __indexOf.call(states, _ref3) >= 0) {
        keys = [this.sig_type(), this.proof_type()];
        if ((sub_id = this.get_sub_id()) != null) {
          keys.push(sub_id);
        }
        table.insert_path(keys, this);
      } else {
        log.debug("Skipping remote proof in state " + (this.proof_state()) + ": " + (this.payload_json_str()));
      }
      return log.debug("- RemoteProof::insert_into_table");
    };

    RemoteProof.prototype.get_sub_id = function() {
      var _ref3;
      return (_ref3 = scrapemod.alloc_stub(this.proof_type())) != null ? _ref3.get_sub_id(this.proof_service_object()) : void 0;
    };

    RemoteProof.prototype.check_remote_proof = function(_arg, cb) {
      var arg, e2, err, errmsg, esc, msg, ok, proof_vec, rc, rsc, scraper, skip, type, type_s, username, warnings, ___iced_passed_deferral, __iced_deferrals, __iced_k;
      __iced_k = __iced_k_noop;
      ___iced_passed_deferral = iced.findDeferral(arguments);
      skip = _arg.skip, type = _arg.type, warnings = _arg.warnings, proof_vec = _arg.proof_vec;
      username = this.self_signer();
      esc = make_esc(cb, "SigChain::Link::check_remote_proof'");
      (function(_this) {
        return (function(__iced_k) {
          if ((type_s = proofs.proof_type_to_string[type]) == null) {
            err = new E.VerifyError("Unknown proof type (" + type + ") found; consider a `keybase update`");
            (function(__iced_k) {
              __iced_deferrals = new iced.Deferrals(__iced_k, {
                parent: ___iced_passed_deferral,
                filename: "/home/jacko/node-client/src/chainlink.iced",
                funcname: "RemoteProof.check_remote_proof"
              });
              athrow(err, esc(__iced_deferrals.defer({
                lineno: 228
              })));
              __iced_deferrals._fulfill();
            })(__iced_k);
          } else {
            return __iced_k();
          }
        });
      })(this)((function(_this) {
        return function() {
          log.debug("+ " + username + ": checking remote " + type_s + " proof");
          (function(__iced_k) {
            if (!skip && !_this.api_url()) {
              (function(__iced_k) {
                __iced_deferrals = new iced.Deferrals(__iced_k, {
                  parent: ___iced_passed_deferral,
                  filename: "/home/jacko/node-client/src/chainlink.iced",
                  funcname: "RemoteProof.check_remote_proof"
                });
                _this.refresh(__iced_deferrals.defer({
                  assign_fn: (function() {
                    return function() {
                      return e2 = arguments[0];
                    };
                  })(),
                  lineno: 233
                }));
                __iced_deferrals._fulfill();
              })(function() {
                return __iced_k(typeof e2 !== "undefined" && e2 !== null ? log.warn("Error fetching URL for proof: " + e2.message) : void 0);
              });
            } else {
              return __iced_k();
            }
          })(function() {
            rsc = JSON.stringify(_this.proof_service_object());
            log.debug("| remote service desc is " + rsc);
            (function(__iced_k) {
              __iced_deferrals = new iced.Deferrals(__iced_k, {
                parent: ___iced_passed_deferral,
                filename: "/home/jacko/node-client/src/chainlink.iced",
                funcname: "RemoteProof.check_remote_proof"
              });
              scrapemod.alloc(type, esc(__iced_deferrals.defer({
                assign_fn: (function() {
                  return function() {
                    return scraper = arguments[0];
                  };
                })(),
                lineno: 240
              })));
              __iced_deferrals._fulfill();
            })(function() {
              arg = {
                api_url: _this.api_url(),
                signature: _this.sig(),
                proof_text_check: _this.proof_text_check(),
                remote_id: "" + _this.remote_id(),
                human_url: _this.human_url()
              };
              arg = dict_union(arg, _this.proof_service_object());
              proof_vec.push(scraper.to_proof(arg));
              errmsg = "";
              (function(__iced_k) {
                if (skip) {
                  return __iced_k(rc = proofs.constants.v_codes.OK);
                } else {
                  (function(__iced_k) {
                    if (!_this.api_url()) {
                      return __iced_k(rc = proofs.constants.v_codes.NOT_FOUND);
                    } else {
                      log.debug("+ Calling into scraper -> " + rsc + "@" + type_s + " -> " + (_this.api_url()));
                      (function(__iced_k) {
                        __iced_deferrals = new iced.Deferrals(__iced_k, {
                          parent: ___iced_passed_deferral,
                          filename: "/home/jacko/node-client/src/chainlink.iced",
                          funcname: "RemoteProof.check_remote_proof"
                        });
                        scraper.validate(arg, __iced_deferrals.defer({
                          assign_fn: (function() {
                            return function() {
                              err = arguments[0];
                              return rc = arguments[1];
                            };
                          })(),
                          lineno: 259
                        }));
                        __iced_deferrals._fulfill();
                      })(function() {
                        log.debug("- Called scraper -> " + rc);
                        return __iced_k(typeof err !== "undefined" && err !== null ? errmsg = ": " + err.message : void 0);
                      });
                    }
                  })(__iced_k);
                }
              })(function() {
                ok = false;
                if (rc !== proofs.constants.v_codes.OK) {
                  warnings.push(new E.RemoteCheckError("Remote check failed (code: " + rc + ")"));
                  _this.obj.proof_state = rc;
                } else {
                  ok = true;
                  log.debug("| proof checked out");
                }
                msg = scraper.format_msg({
                  arg: arg,
                  ok: ok
                });
                if (skip) {
                  msg.push("(you've recently OK'ed this proof)");
                }
                if (!ok) {
                  msg.push("(failed with code " + rc + errmsg + ")");
                }
                log.lconsole("error", log["package"]().INFO, msg.join(' '));
                log.debug("- " + username + ": checked remote " + type_s + " proof");
                return cb(null);
              });
            });
          });
        };
      })(this));
    };

    RemoteProof.prototype.remote_proof_to_track_obj = function() {
      var _ref3, _ref4;
      return {
        ctime: this.obj.ctime,
        etime: this.obj.etime,
        seqno: this.obj.seqno,
        curr: this.id,
        sig_type: this.obj.sig_type,
        sig_id: this.obj.sig_id,
        remote_key_proof: {
          check_data_json: (_ref3 = this.payload_json()) != null ? (_ref4 = _ref3.body) != null ? _ref4.service : void 0 : void 0,
          state: this.obj.proof_state,
          proof_type: this.obj.proof_type
        }
      };
    };

    RemoteProof.prototype.to_list_display = function(opts) {
      var name, _ref3;
      name = (_ref3 = scrapemod.alloc_stub(this.proof_type())) != null ? _ref3.to_list_display(this.proof_service_object()) : void 0;
      if ((opts != null ? opts.with_sig_ids : void 0) || ((opts != null ? opts.with_proof_states : void 0) != null)) {
        return {
          name: name,
          sig_id: this.sig_id(),
          proof_state: this.proof_state()
        };
      } else {
        return name;
      }
    };

    RemoteProof.prototype.refresh = function(cb) {
      var arg, body, err, row, u, ___iced_passed_deferral, __iced_deferrals, __iced_k;
      __iced_k = __iced_k_noop;
      ___iced_passed_deferral = iced.findDeferral(arguments);
      err = null;
      log.debug("+ refresh RemoteProof link");
      (function(_this) {
        return (function(__iced_k) {
          if (_this.api_url() == null) {
            log.debug("| Proof_id = " + _this.obj.proof_id);
            arg = {
              endpoint: "sig/remote_proof",
              args: {
                proof_id: _this.obj.proof_id
              }
            };
            log.debug("| request proof refresh for id=" + _this.obj.proof_id);
            (function(__iced_k) {
              __iced_deferrals = new iced.Deferrals(__iced_k, {
                parent: ___iced_passed_deferral,
                filename: "/home/jacko/node-client/src/chainlink.iced",
                funcname: "RemoteProof.refresh"
              });
              req.get(arg, __iced_deferrals.defer({
                assign_fn: (function() {
                  return function() {
                    err = arguments[0];
                    return body = arguments[1];
                  };
                })(),
                lineno: 315
              }));
              __iced_deferrals._fulfill();
            })(function() {
              (function(__iced_k) {
                if ((err == null) && ((row = typeof body !== "undefined" && body !== null ? body.row : void 0) != null) && ((u = row.api_url) != null)) {
                  log.debug("| Refreshed with api_url -> " + u);
                  _this.obj.api_url = u;
                  _this.obj.human_url = row.human_url;
                  (function(__iced_k) {
                    __iced_deferrals = new iced.Deferrals(__iced_k, {
                      parent: ___iced_passed_deferral,
                      filename: "/home/jacko/node-client/src/chainlink.iced",
                      funcname: "RemoteProof.refresh"
                    });
                    _this.store(__iced_deferrals.defer({
                      assign_fn: (function() {
                        return function() {
                          return err = arguments[0];
                        };
                      })(),
                      lineno: 320
                    }));
                    __iced_deferrals._fulfill();
                  })(__iced_k);
                } else {
                  return __iced_k();
                }
              })(__iced_k);
            });
          } else {
            return __iced_k();
          }
        });
      })(this)((function(_this) {
        return function() {
          log.debug("- refresh RemoteProof link");
          return cb(err);
        };
      })(this));
    };

    RemoteProof.prototype.is_self_sig = function() {
      return true;
    };

    return RemoteProof;

  })(Link);

  Track = (function(_super) {
    __extends(Track, _super);

    function Track() {
      return Track.__super__.constructor.apply(this, arguments);
    }

    Track.prototype.to_table_obj = function() {
      var ret;
      ret = this.body().track;
      ret.ctime = this.ctime();
      return ret;
    };

    Track.prototype.is_self_sig = function() {
      return true;
    };

    Track.prototype.condense = function() {
      return this.body().track.basics.username;
    };

    Track.prototype.type_str = function() {
      return "track";
    };

    Track.prototype.insert_into_table = function(_arg) {
      var id, opts, path, table, _ref3, _ref4;
      table = _arg.table, opts = _arg.opts;
      log.debug("+ Track::insert_into_table " + (this.sig_id()));
      if ((id = (_ref3 = this.body()) != null ? (_ref4 = _ref3.track) != null ? _ref4.id : void 0 : void 0) == null) {
        log.warn("Missing track in signature");
        log.debug("Full JSON in signature:");
        log.debug(this.payload_json_str());
      } else {
        path = [this.sig_type(), id];
        if (opts.show_revoked) {
          path.push(this.seqno());
        }
        table.insert_path(path, this);
      }
      return log.debug("- Track::insert_into_table " + (this.sig_id()) + " (uid=" + id + ")");
    };

    return Track;

  })(Link);

  Cryptocurrency = (function(_super) {
    __extends(Cryptocurrency, _super);

    function Cryptocurrency() {
      return Cryptocurrency.__super__.constructor.apply(this, arguments);
    }

    Cryptocurrency.prototype.to_cryptocurrency = function(opts) {
      var _ref3;
      return (_ref3 = this.body()) != null ? _ref3.cryptocurrency : void 0;
    };

    Cryptocurrency.prototype.condense = function() {
      var d, ret;
      d = this.body().cryptocurrency;
      ret = {};
      ret[d.type] = d.address;
      return ret;
    };

    Cryptocurrency.prototype.type_str = function() {
      return "currency";
    };

    Cryptocurrency.prototype.is_revocable = function() {
      return true;
    };

    Cryptocurrency.prototype.display_cryptocurrency = function(opts, cb) {
      var cc, msg;
      cc = this.to_cryptocurrency(opts);
      msg = [BTC, cc.type, colors.green(cc.address)];
      log.lconsole("error", log["package"]().INFO, msg.join(' '));
      return cb(null);
    };

    Cryptocurrency.prototype.insert_into_table = function(_arg) {
      var err, id, index, opts, path, ret, table, _ref3, _ref4, _ref5;
      table = _arg.table, index = _arg.index, opts = _arg.opts;
      log.debug("+ Cryptocurrency::insert_into_table " + (this.sig_id()));
      if ((id = (_ref3 = this.body()) != null ? (_ref4 = _ref3.cryptocurrency) != null ? _ref4.address : void 0 : void 0) == null) {
        log.warn("Missing Cryptocurrency address");
        log.debug("Full JSON in signature:");
        log.debug(this.payload_json_str());
      } else if ((_ref5 = bitcoyne.address.check(id, {
        version: ACCTYPES
      }), err = _ref5[0], ret = _ref5[1], _ref5) == null) {
        log.error("Error in checking cryptocurrency address: " + id);
      } else if (err != null) {
        log.warn("Error in cryptocurrency address: " + err.message);
      } else {
        path = [this.sig_type(), ret.version];
        if (opts != null ? opts.show_revoked : void 0) {
          path.push(this.seqno());
        }
        table.insert_path(path, this);
      }
      return log.debug("- Cryptocurrency::insert_into_table " + (this.sig_id()));
    };

    return Cryptocurrency;

  })(Link);

  Revoke = (function(_super) {
    __extends(Revoke, _super);

    function Revoke() {
      return Revoke.__super__.constructor.apply(this, arguments);
    }

    Revoke.prototype.insert_into_table = function(_arg) {
      var index;
      index = _arg.index;
      log.debug("+ Revoke::insert_into_table");
      return log.debug("- Revoke::insert_into_table");
    };

    return Revoke;

  })(Link);

  Untrack = (function(_super) {
    __extends(Untrack, _super);

    function Untrack() {
      return Untrack.__super__.constructor.apply(this, arguments);
    }

    Untrack.prototype.insert_into_table = function(_arg) {
      var id, index, link, links, opts, table, _i, _len, _ref3, _ref4, _ref5;
      table = _arg.table, index = _arg.index, opts = _arg.opts;
      log.debug("+ Untrack::insert_into_table");
      if ((id = (_ref3 = this.body()) != null ? (_ref4 = _ref3.untrack) != null ? _ref4.id : void 0 : void 0) == null) {
        log.warn("Mssing untrack in signature: " + (this.payload_json_str()));
      } else if ((link = (_ref5 = table.get(ST.TRACK)) != null ? _ref5.get(id) : void 0) == null) {
        log.debug("Unexpected untrack of " + id + " in signature chain");
      } else if (!(link.is_leaf()) && !(opts != null ? opts.show_revoked : void 0)) {
        log.warn("Unexpected multi-follow");
      } else {
        links = link.is_leaf() ? [link] : link.flatten();
        for (_i = 0, _len = links.length; _i < _len; _i++) {
          link = links[_i];
          if (link.is_revoked()) {
            log.debug("| Tracking was already revoked for " + id + " (ignoring untrack)");
          } else {
            link.revoke();
          }
        }
      }
      return log.debug("- Untrack::insert_into_table");
    };

    return Untrack;

  })(Link);

  exports.LinkTable = LinkTable = (function() {
    function LinkTable(table) {
      this.table = table != null ? table : {};
    }

    LinkTable.prototype.insert = function(key, value) {
      return this.table[key] = value;
    };

    LinkTable.prototype.insert_path = function(path, value) {
      var d, k, v, _i, _len, _ref3;
      d = this;
      _ref3 = path.slice(0, -1);
      for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
        k = _ref3[_i];
        if ((v = d.get(k)) == null) {
          v = new LinkTable();
          d.insert(k, v);
        }
        d = v;
      }
      return d.insert(path.slice(-1)[0], value);
    };

    LinkTable.prototype.get = function(key) {
      return this.table[key];
    };

    LinkTable.prototype.keys = function() {
      return Object.keys(this.table);
    };

    LinkTable.prototype.get_path = function(path) {
      var p, v, _i, _len;
      v = this;
      for (_i = 0, _len = path.length; _i < _len; _i++) {
        p = path[_i];
        if (v != null) {
          v = v.get(p);
        }
      }
      return v;
    };

    LinkTable.prototype.remove = function(key) {
      return delete this.table[key];
    };

    LinkTable.prototype.to_dict = function() {
      return this.table;
    };

    LinkTable.prototype.is_leaf = function() {
      return false;
    };

    LinkTable.prototype.walk = function(_arg) {
      var fn, k, v, _ref3, _results;
      fn = _arg.fn;
      _ref3 = this.table;
      _results = [];
      for (k in _ref3) {
        v = _ref3[k];
        _results.push(v.walk({
          fn: fn,
          parent: this,
          key: k
        }));
      }
      return _results;
    };

    LinkTable.prototype.flatten = function() {
      var fn, out;
      out = [];
      fn = function(_arg) {
        var key, parent, value;
        key = _arg.key, value = _arg.value, parent = _arg.parent;
        return out.push(value);
      };
      this.walk({
        fn: fn
      });
      return out;
    };

    LinkTable.prototype.prune = function(prune_condition) {
      var fn;
      fn = function(_arg) {
        var key, parent, value;
        key = _arg.key, value = _arg.value, parent = _arg.parent;
        if (prune_condition(value)) {
          return parent.remove(key);
        }
      };
      return this.walk({
        fn: fn
      });
    };

    LinkTable.prototype.select = function(keys) {
      var k, out, _i, _len;
      out = new LinkTable;
      for (_i = 0, _len = keys.length; _i < _len; _i++) {
        k = keys[_i];
        out.insert(k, this.get(k));
      }
      return out;
    };

    return LinkTable;

  })();

}).call(this);
