/**
 * FITS.js - JavaScript library for reading FITS (Flexible Image Transport System) files
 * 
 * Source: https://github.com/astrojs/fitsjs
 * License: MIT
 * 
 * This file was downloaded from the official GitHub repository since it could not be 
 * loaded via CDN. FITS.js provides client-side parsing and processing of 
 * astronomical FITS image files in web browsers.
 * 
 * Usage: See documentation at https://github.com/astrojs/fitsjs
 */

// Generated by CoffeeScript 1.6.3
(function() {
  var Base, BinaryTable, CompressedImage, DataUnit, Decompress, FITS, HDU, Header, HeaderVerify, Image, ImageUtils, Parser, Table, Tabular, _ref, _ref1,
    __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; },
    __slice = [].slice;

  if (this.astro == null) {
    this.astro = {};
  }

  Base = (function() {
    function Base() {}

    Base.include = function(obj) {
      var key, value;
      for (key in obj) {
        value = obj[key];
        this.prototype[key] = value;
      }
      return this;
    };

    Base.extend = function(obj) {
      var key, value;
      for (key in obj) {
        value = obj[key];
        this[key] = value;
      }
      return this;
    };

    Base.prototype.proxy = function(func) {
      var _this = this;
      return function() {
        return func.apply(_this, arguments);
      };
    };

    Base.prototype.invoke = function(callback, opts, data) {
      var context;
      context = (opts != null ? opts.context : void 0) != null ? opts.context : this;
      if (callback != null) {
        return callback.call(context, data, opts);
      }
    };

    return Base;

  })();

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

    Parser.prototype.LINEWIDTH = 80;

    Parser.prototype.BLOCKLENGTH = 2880;

    File.prototype.slice = File.prototype.slice || File.prototype.webkitSlice;

    Blob.prototype.slice = Blob.prototype.slice || Blob.prototype.webkitSlice;

    function Parser(arg, callback, opts) {
      var xhr,
        _this = this;
      this.arg = arg;
      this.callback = callback;
      this.opts = opts;
      this.hdus = [];
      this.blockCount = 0;
      this.begin = 0;
      this.end = this.BLOCKLENGTH;
      this.offset = 0;
      this.headerStorage = new Uint8Array();
      if (typeof this.arg === 'string') {
        this.readNextBlock = this._readBlockFromBuffer;
        xhr = new XMLHttpRequest();
        xhr.open('GET', this.arg);
        xhr.responseType = 'arraybuffer';
        xhr.onload = function() {
          if (xhr.status !== 200) {
            _this.invoke(_this.callback, _this.opts);
            return;
          }
          _this.arg = xhr.response;
          _this.length = _this.arg.byteLength;
          return _this.readFromBuffer();
        };
        xhr.send();
      } else {
        this.length = this.arg.size;
        this.readNextBlock = this._readBlockFromFile;
        this.readFromFile();
      }
    }

    Parser.prototype.readFromBuffer = function() {
      var block;
      block = this.arg.slice(this.begin + this.offset, this.end + this.offset);
      return this.readBlock(block);
    };

    Parser.prototype.readFromFile = function() {
      var block,
        _this = this;
      this.reader = new FileReader();
      this.reader.onloadend = function(e) {
        return _this.readBlock(e.target.result);
      };
      block = this.arg.slice(this.begin + this.offset, this.end + this.offset);
      return this.reader.readAsArrayBuffer(block);
    };

    Parser.prototype.readBlock = function(block) {
      var arr, dataLength, dataunit, header, rowIndex, rows, s, slice, tmp, value, _i, _len, _ref;
      arr = new Uint8Array(block);
      tmp = new Uint8Array(this.headerStorage);
      this.headerStorage = new Uint8Array(this.end);
      this.headerStorage.set(tmp, 0);
      this.headerStorage.set(arr, this.begin);
      rows = this.BLOCKLENGTH / this.LINEWIDTH;
      while (rows--) {
        rowIndex = rows * this.LINEWIDTH;
        if (arr[rowIndex] === 32) {
          continue;
        }
        if (arr[rowIndex] === 69 && arr[rowIndex + 1] === 78 && arr[rowIndex + 2] === 68 && arr[rowIndex + 3] === 32) {
          s = '';
          _ref = this.headerStorage;
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            value = _ref[_i];
            s += String.fromCharCode(value);
          }
          header = new Header(s);
          this.start = this.end + this.offset;
          dataLength = header.getDataLength();
          slice = this.arg.slice(this.start, this.start + dataLength);
          if (header.hasDataUnit()) {
            dataunit = this.createDataUnit(header, slice);
          }
          this.hdus.push(new HDU(header, dataunit));
          this.offset += this.end + dataLength + this.excessBytes(dataLength);
          if (this.offset === this.length) {
            this.headerStorage = null;
            this.invoke(this.callback, this.opts, this);
            return;
          }
          this.blockCount = 0;
          this.begin = this.blockCount * this.BLOCKLENGTH;
          this.end = this.begin + this.BLOCKLENGTH;
          this.headerStorage = new Uint8Array();
          block = this.arg.slice(this.begin + this.offset, this.end + this.offset);
          this.readNextBlock(block);
          return;
        }
        break;
      }
      this.blockCount += 1;
      this.begin = this.blockCount * this.BLOCKLENGTH;
      this.end = this.begin + this.BLOCKLENGTH;
      block = this.arg.slice(this.begin + this.offset, this.end + this.offset);
      this.readNextBlock(block);
    };

    Parser.prototype._readBlockFromBuffer = function(block) {
      return this.readBlock(block);
    };

    Parser.prototype._readBlockFromFile = function(block) {
      return this.reader.readAsArrayBuffer(block);
    };

    Parser.prototype.createDataUnit = function(header, blob) {
      var type;
      type = header.getDataType();
      return new astro.FITS[type](header, blob);
    };

    Parser.prototype.excessBytes = function(length) {
      return (this.BLOCKLENGTH - (length % this.BLOCKLENGTH)) % this.BLOCKLENGTH;
    };

    Parser.prototype.isEOF = function() {
      if (this.offset === this.length) {
        return true;
      } else {
        return false;
      }
    };

    return Parser;

  })(Base);

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

    function FITS(arg, callback, opts) {
      var parser,
        _this = this;
      this.arg = arg;
      parser = new Parser(this.arg, function(fits) {
        _this.hdus = parser.hdus;
        return _this.invoke(callback, opts, _this);
      });
    }

    FITS.prototype.getHDU = function(index) {
      var hdu, _i, _len, _ref;
      if ((index != null) && (this.hdus[index] != null)) {
        return this.hdus[index];
      }
      _ref = this.hdus;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        hdu = _ref[_i];
        if (hdu.hasData()) {
          return hdu;
        }
      }
    };

    FITS.prototype.getHeader = function(index) {
      return this.getHDU(index).header;
    };

    FITS.prototype.getDataUnit = function(index) {
      return this.getHDU(index).data;
    };

    return FITS;

  })(Base);

  FITS.version = '0.6.5';

  this.astro.FITS = FITS;

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

    DataUnit.swapEndian = {
      B: function(value) {
        return value;
      },
      I: function(value) {
        return (value << 8) | (value >> 8);
      },
      J: function(value) {
        return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value >> 8) & 0xFF00) | ((value >> 24) & 0xFF);
      }
    };

    DataUnit.swapEndian[8] = DataUnit.swapEndian['B'];

    DataUnit.swapEndian[16] = DataUnit.swapEndian['I'];

    DataUnit.swapEndian[32] = DataUnit.swapEndian['J'];

    function DataUnit(header, data) {
      if (data instanceof ArrayBuffer) {
        this.buffer = data;
      } else {
        this.blob = data;
      }
    }

    return DataUnit;

  })(Base);

  this.astro.FITS.DataUnit = DataUnit;

  HeaderVerify = {
    verifyOrder: function(keyword, order) {
      if (order !== this.cardIndex) {
        return console.warn("" + keyword + " should appear at index " + this.cardIndex + " in the FITS header");
      }
    },
    verifyBetween: function(keyword, value, lower, upper) {
      if (!(value >= lower && value <= upper)) {
        throw "The " + keyword + " value of " + value + " is not between " + lower + " and " + upper;
      }
    },
    verifyBoolean: function(value) {
      if (value === "T") {
        return true;
      } else {
        return false;
      }
    },
    VerifyFns: {
      SIMPLE: function() {
        var args, value;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        value = arguments[0];
        this.primary = true;
        this.verifyOrder("SIMPLE", 0);
        return this.verifyBoolean(value);
      },
      XTENSION: function() {
        var args;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        this.extension = true;
        this.extensionType = arguments[0];
        this.verifyOrder("XTENSION", 0);
        return this.extensionType;
      },
      BITPIX: function() {
        var args, key, value;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        key = "BITPIX";
        value = parseInt(arguments[0]);
        this.verifyOrder(key, 1);
        if (value !== 8 && value !== 16 && value !== 32 && value !== (-32) && value !== (-64)) {
          throw "" + key + " value " + value + " is not permitted";
        }
        return value;
      },
      NAXIS: function() {
        var args, array, key, required, value, _ref;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        key = "NAXIS";
        value = parseInt(arguments[0]);
        array = arguments[1];
        if (!array) {
          this.verifyOrder(key, 2);
          this.verifyBetween(key, value, 0, 999);
          if (this.isExtension()) {
            if ((_ref = this.extensionType) === "TABLE" || _ref === "BINTABLE") {
              required = 2;
              if (value !== required) {
                throw "" + key + " must be " + required + " for TABLE and BINTABLE extensions";
              }
            }
          }
        }
        return value;
      },
      PCOUNT: function() {
        var args, key, order, required, value, _ref;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        key = "PCOUNT";
        value = parseInt(arguments[0]);
        order = 1 + 1 + 1 + this.get("NAXIS");
        this.verifyOrder(key, order);
        if (this.isExtension()) {
          if ((_ref = this.extensionType) === "IMAGE" || _ref === "TABLE") {
            required = 0;
            if (value !== required) {
              throw "" + key + " must be " + required + " for the " + this.extensionType + " extensions";
            }
          }
        }
        return value;
      },
      GCOUNT: function() {
        var args, key, order, required, value, _ref;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        key = "GCOUNT";
        value = parseInt(arguments[0]);
        order = 1 + 1 + 1 + this.get("NAXIS") + 1;
        this.verifyOrder(key, order);
        if (this.isExtension()) {
          if ((_ref = this.extensionType) === "IMAGE" || _ref === "TABLE" || _ref === "BINTABLE") {
            required = 1;
            if (value !== required) {
              throw "" + key + " must be " + required + " for the " + this.extensionType + " extensions";
            }
          }
        }
        return value;
      },
      EXTEND: function() {
        var args, value;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        value = arguments[0];
        if (!this.isPrimary()) {
          throw "EXTEND must only appear in the primary header";
        }
        return this.verifyBoolean(value);
      },
      BSCALE: function() {
        var args;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        return parseFloat(arguments[0]);
      },
      BZERO: function() {
        var args;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        return parseFloat(arguments[0]);
      },
      BLANK: function() {
        var args, value;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        value = arguments[0];
        if (!(this.get("BITPIX") > 0)) {
          console.warn("BLANK is not to be used for BITPIX = " + (this.get('BITPIX')));
        }
        return parseInt(value);
      },
      DATAMIN: function() {
        var args;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        return parseFloat(arguments[0]);
      },
      DATAMAX: function() {
        var args;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        return parseFloat(arguments[0]);
      },
      EXTVER: function() {
        var args;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        return parseInt(arguments[0]);
      },
      EXTLEVEL: function() {
        var args;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        return parseInt(arguments[0]);
      },
      TFIELDS: function() {
        var args, value;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        value = parseInt(arguments[0]);
        this.verifyBetween("TFIELDS", value, 0, 999);
        return value;
      },
      TBCOL: function() {
        var args, index, value;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        value = arguments[0];
        index = arguments[2];
        this.verifyBetween("TBCOL", index, 0, this.get("TFIELDS"));
        return value;
      },
      ZIMAGE: function() {
        var args;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        return this.verifyBoolean(arguments[0]);
      },
      ZCMPTYPE: function() {
        var args, value;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        value = arguments[0];
        if (value !== 'GZIP_1' && value !== 'RICE_1' && value !== 'PLIO_1' && value !== 'HCOMPRESS_1') {
          throw "ZCMPTYPE value " + value + " is not permitted";
        }
        if (value !== 'RICE_1') {
          throw "Compress type " + value + " is not yet implement";
        }
        return value;
      },
      ZBITPIX: function() {
        var args, value;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        value = parseInt(arguments[0]);
        if (value !== 8 && value !== 16 && value !== 32 && value !== 64 && value !== (-32) && value !== (-64)) {
          throw "ZBITPIX value " + value + " is not permitted";
        }
        return value;
      },
      ZNAXIS: function() {
        var args, array, value;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        value = parseInt(arguments[0]);
        array = arguments[1];
        value = value;
        if (!array) {
          this.verifyBetween("ZNAXIS", value, 0, 999);
        }
        return value;
      },
      ZTILE: function() {
        var args;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        return parseInt(arguments[0]);
      },
      ZSIMPLE: function() {
        var args;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        if (arguments[0] === "T") {
          return true;
        } else {
          return false;
        }
      },
      ZPCOUNT: function() {
        var args;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        return parseInt(arguments[0]);
      },
      ZGCOUNT: function() {
        var args;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        return parseInt(arguments[0]);
      },
      ZDITHER0: function() {
        var args;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        return parseInt(arguments[0]);
      }
    }
  };

  this.astro.FITS.HeaderVerify = HeaderVerify;

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

    Header.include(HeaderVerify);

    Header.prototype.arrayPattern = /(\D+)(\d+)/;

    Header.prototype.maxLines = 600;

    function Header(block) {
      var method, name, _ref;
      this.primary = false;
      this.extension = false;
      this.verifyCard = {};
      _ref = this.VerifyFns;
      for (name in _ref) {
        method = _ref[name];
        this.verifyCard[name] = this.proxy(method);
      }
      this.cards = {};
      this.cards["COMMENT"] = [];
      this.cards["HISTORY"] = [];
      this.cardIndex = 0;
      this.block = block;
      this.readBlock(block);
    }

    Header.prototype.get = function(key) {
      if (this.contains(key)) {
        return this.cards[key].value;
      } else {
        return null;
      }
    };

    Header.prototype.set = function(key, value, comment) {
      comment = comment || '';
      this.cards[key] = {
        index: this.cardIndex,
        value: value,
        comment: comment
      };
      return this.cardIndex += 1;
    };

    Header.prototype.contains = function(key) {
      return this.cards.hasOwnProperty(key);
    };

    Header.prototype.readLine = function(l) {
      var blank, comment, firstByte, indicator, key, value, _ref;
      key = l.slice(0, 8).trim();
      blank = key === '';
      if (blank) {
        return;
      }
      indicator = l.slice(8, 10);
      value = l.slice(10);
      if (indicator !== "= ") {
        if (key === 'COMMENT' || key === 'HISTORY') {
          this.cards[key].push(value.trim());
        }
        return;
      }
      _ref = value.split(' /'), value = _ref[0], comment = _ref[1];
      value = value.trim();
      firstByte = value[0];
      if (firstByte === "'") {
        value = value.slice(1, -1).trim();
      } else {
        if (value !== 'T' && value !== 'F') {
          value = parseFloat(value);
        }
      }
      value = this.validate(key, value);
      return this.set(key, value, comment);
    };

    Header.prototype.validate = function(key, value) {
      var baseKey, index, isArray, match, _ref;
      index = null;
      baseKey = key;
      isArray = this.arrayPattern.test(key);
      if (isArray) {
        match = this.arrayPattern.exec(key);
        _ref = match.slice(1), baseKey = _ref[0], index = _ref[1];
      }
      if (baseKey in this.verifyCard) {
        value = this.verifyCard[baseKey](value, isArray, index);
      }
      return value;
    };

    Header.prototype.readBlock = function(block) {
      var i, line, lineWidth, nLines, _i, _ref, _results;
      lineWidth = 80;
      nLines = block.length / lineWidth;
      nLines = nLines < this.maxLines ? nLines : this.maxLines;
      _results = [];
      for (i = _i = 0, _ref = nLines - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
        line = block.slice(i * lineWidth, (i + 1) * lineWidth);
        _results.push(this.readLine(line));
      }
      return _results;
    };

    Header.prototype.hasDataUnit = function() {
      if (this.get("NAXIS") === 0) {
        return false;
      } else {
        return true;
      }
    };

    Header.prototype.getDataLength = function() {
      var i, length, naxis, _i, _ref;
      if (!this.hasDataUnit()) {
        return 0;
      }
      naxis = [];
      for (i = _i = 1, _ref = this.get("NAXIS"); 1 <= _ref ? _i <= _ref : _i >= _ref; i = 1 <= _ref ? ++_i : --_i) {
        naxis.push(this.get("NAXIS" + i));
      }
      length = naxis.reduce(function(a, b) {
        return a * b;
      }) * Math.abs(this.get("BITPIX")) / 8;
      length += this.get("PCOUNT");
      return length;
    };

    Header.prototype.getDataType = function() {
      switch (this.extensionType) {
        case 'BINTABLE':
          if (this.contains('ZIMAGE')) {
            return 'CompressedImage';
          }
          return 'BinaryTable';
        case 'TABLE':
          return 'Table';
        default:
          if (this.hasDataUnit()) {
            return 'Image';
          } else {
            return null;
          }
      }
    };

    Header.prototype.isPrimary = function() {
      return this.primary;
    };

    Header.prototype.isExtension = function() {
      return this.extension;
    };

    return Header;

  })(Base);

  this.astro.FITS.Header = Header;

  ImageUtils = {
    getExtent: function(arr) {
      var index, max, min, value;
      index = arr.length;
      while (index--) {
        value = arr[index];
        if (isNaN(value)) {
          continue;
        }
        min = max = value;
        break;
      }
      if (index === -1) {
        return [NaN, NaN];
      }
      while (index--) {
        value = arr[index];
        if (isNaN(value)) {
          continue;
        }
        if (value < min) {
          min = value;
        }
        if (value > max) {
          max = value;
        }
      }
      return [min, max];
    },
    getPixel: function(arr, x, y) {
      return arr[y * this.width + x];
    }
  };

  this.astro.FITS.ImageUtils = ImageUtils;

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

    Image.include(ImageUtils);

    Image.prototype.allocationSize = 16777216;

    function Image(header, data) {
      var begin, frame, i, naxis, _i, _j, _ref;
      Image.__super__.constructor.apply(this, arguments);
      naxis = header.get("NAXIS");
      this.bitpix = header.get("BITPIX");
      this.naxis = [];
      for (i = _i = 1; 1 <= naxis ? _i <= naxis : _i >= naxis; i = 1 <= naxis ? ++_i : --_i) {
        this.naxis.push(header.get("NAXIS" + i));
      }
      this.width = header.get("NAXIS1");
      this.height = header.get("NAXIS2") || 1;
      this.depth = header.get("NAXIS3") || 1;
      this.bzero = header.get("BZERO") || 0;
      this.bscale = header.get("BSCALE") || 1;
      this.bytes = Math.abs(this.bitpix) / 8;
      this.length = this.naxis.reduce(function(a, b) {
        return a * b;
      }) * Math.abs(this.bitpix) / 8;
      this.frame = 0;
      this.frameOffsets = [];
      this.frameLength = this.bytes * this.width * this.height;
      this.nBuffers = this.buffer != null ? 1 : 2;
      for (i = _j = 0, _ref = this.depth - 1; 0 <= _ref ? _j <= _ref : _j >= _ref; i = 0 <= _ref ? ++_j : --_j) {
        begin = i * this.frameLength;
        frame = {
          begin: begin
        };
        if (this.buffer != null) {
          frame.buffers = [this.buffer.slice(begin, begin + this.frameLength)];
        }
        this.frameOffsets.push(frame);
      }
    }

    Image.prototype._getFrame = function(buffer, bitpix, bzero, bscale) {
      var arr, bytes, dataType, i, nPixels, swapEndian, tmp, value;
      bytes = Math.abs(bitpix) / 8;
      nPixels = i = buffer.byteLength / bytes;
      dataType = Math.abs(bitpix);
      if (bitpix > 0) {
        switch (bitpix) {
          case 8:
            tmp = new Uint8Array(buffer);
            tmp = new Uint16Array(tmp);
            swapEndian = function(value) {
              return value;
            };
            break;
          case 16:
            tmp = new Int16Array(buffer);
            swapEndian = function(value) {
              return ((value & 0xFF) << 8) | ((value >> 8) & 0xFF);
            };
            break;
          case 32:
            tmp = new Int32Array(buffer);
            swapEndian = function(value) {
              return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value >> 8) & 0xFF00) | ((value >> 24) & 0xFF);
            };
        }
        if (!(parseInt(bzero) === bzero && parseInt(bscale) === bscale)) {
          arr = new Float32Array(tmp.length);
        } else {
          arr = tmp;
        }
        while (nPixels--) {
          tmp[nPixels] = swapEndian(tmp[nPixels]);
          arr[nPixels] = bzero + bscale * tmp[nPixels];
        }
      } else {
        arr = new Uint32Array(buffer);
        swapEndian = function(value) {
          return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value >> 8) & 0xFF00) | ((value >> 24) & 0xFF);
        };
        while (i--) {
          value = arr[i];
          arr[i] = swapEndian(value);
        }
        arr = new Float32Array(buffer);
        while (nPixels--) {
          arr[nPixels] = bzero + bscale * arr[nPixels];
        }
      }
      return arr;
    };

    Image.prototype._getFrameAsync = function(buffers, callback, opts) {
      var URL, blobGetFrame, blobOnMessage, fn1, fn2, i, mime, msg, onmessage, pixels, start, urlGetFrame, urlOnMessage, worker,
        _this = this;
      onmessage = function(e) {
        var arr, bitpix, bscale, buffer, bzero, data, url;
        data = e.data;
        buffer = data.buffer;
        bitpix = data.bitpix;
        bzero = data.bzero;
        bscale = data.bscale;
        url = data.url;
        importScripts(url);
        arr = _getFrame(buffer, bitpix, bzero, bscale);
        return postMessage(arr);
      };
      fn1 = onmessage.toString().replace('return postMessage', 'postMessage');
      fn1 = "onmessage = " + fn1;
      fn2 = this._getFrame.toString();
      fn2 = fn2.replace('function', 'function _getFrame');
      mime = "application/javascript";
      blobOnMessage = new Blob([fn1], {
        type: mime
      });
      blobGetFrame = new Blob([fn2], {
        type: mime
      });
      URL = window.URL || window.webkitURL;
      urlOnMessage = URL.createObjectURL(blobOnMessage);
      urlGetFrame = URL.createObjectURL(blobGetFrame);
      worker = new Worker(urlOnMessage);
      msg = {
        buffer: buffers[0],
        bitpix: this.bitpix,
        bzero: this.bzero,
        bscale: this.bscale,
        url: urlGetFrame
      };
      i = 0;
      pixels = null;
      start = 0;
      worker.onmessage = function(e) {
        var arr;
        arr = e.data;
        if (pixels == null) {
          pixels = new arr.constructor(_this.width * _this.height);
        }
        pixels.set(arr, start);
        start += arr.length;
        i += 1;
        if (i === _this.nBuffers) {
          _this.invoke(callback, opts, pixels);
          URL.revokeObjectURL(urlOnMessage);
          URL.revokeObjectURL(urlGetFrame);
          return worker.terminate();
        } else {
          msg.buffer = buffers[i];
          return worker.postMessage(msg, [buffers[i]]);
        }
      };
      worker.postMessage(msg, [buffers[0]]);
    };

    Image.prototype.getFrame = function(frame, callback, opts) {
      var begin, blobFrame, blobs, buffers, bytesPerBuffer, frameInfo, i, nRowsPerBuffer, reader, start, _i, _ref,
        _this = this;
      this.frame = frame || this.frame;
      frameInfo = this.frameOffsets[this.frame];
      buffers = frameInfo.buffers;
      if ((buffers != null ? buffers.length : void 0) === this.nBuffers) {
        return this._getFrameAsync(buffers, callback, opts);
      } else {
        this.frameOffsets[this.frame].buffers = [];
        begin = frameInfo.begin;
        blobFrame = this.blob.slice(begin, begin + this.frameLength);
        blobs = [];
        nRowsPerBuffer = Math.floor(this.height / this.nBuffers);
        bytesPerBuffer = nRowsPerBuffer * this.bytes * this.width;
        for (i = _i = 0, _ref = this.nBuffers - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
          start = i * bytesPerBuffer;
          if (i === this.nBuffers - 1) {
            blobs.push(blobFrame.slice(start));
          } else {
            blobs.push(blobFrame.slice(start, start + bytesPerBuffer));
          }
        }
        buffers = [];
        reader = new FileReader();
        reader.frame = this.frame;
        i = 0;
        reader.onloadend = function(e) {
          var buffer;
          frame = e.target.frame;
          buffer = e.target.result;
          _this.frameOffsets[frame].buffers.push(buffer);
          i += 1;
          if (i === _this.nBuffers) {
            return _this.getFrame(frame, callback, opts);
          } else {
            return reader.readAsArrayBuffer(blobs[i]);
          }
        };
        return reader.readAsArrayBuffer(blobs[0]);
      }
    };

    Image.prototype.getFrames = function(frame, number, callback, opts) {
      var cb,
        _this = this;
      cb = function(arr, opts) {
        _this.invoke(callback, opts, arr);
        number -= 1;
        frame += 1;
        if (!number) {
          return;
        }
        return _this.getFrame(frame, cb, opts);
      };
      return this.getFrame(frame, cb, opts);
    };

    Image.prototype.isDataCube = function() {
      if (this.naxis.length > 2) {
        return true;
      } else {
        return false;
      }
    };

    return Image;

  })(DataUnit);

  this.astro.FITS.Image = Image;

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

    Tabular.prototype.maxMemory = 1048576;

    function Tabular(header, data) {
      Tabular.__super__.constructor.apply(this, arguments);
      this.rowByteSize = header.get("NAXIS1");
      this.rows = header.get("NAXIS2");
      this.cols = header.get("TFIELDS");
      this.length = this.rowByteSize * this.rows;
      this.heapLength = header.get("PCOUNT");
      this.columns = this.getColumns(header);
      if (this.buffer != null) {
        this.rowsInMemory = this._rowsInMemoryBuffer;
        this.heap = this.buffer.slice(this.length, this.length + this.heapLength);
      } else {
        this.rowsInMemory = this._rowsInMemoryBlob;
        this.firstRowInBuffer = this.lastRowInBuffer = 0;
        this.nRowsInBuffer = Math.floor(this.maxMemory / this.rowByteSize);
      }
      this.accessors = [];
      this.descriptors = [];
      this.elementByteLengths = [];
      this.setAccessors(header);
    }

    Tabular.prototype._rowsInMemoryBuffer = function() {
      return true;
    };

    Tabular.prototype._rowsInMemoryBlob = function(firstRow, lastRow) {
      if (firstRow < this.firstRowInBuffer) {
        return false;
      }
      if (lastRow > this.lastRowInBuffer) {
        return false;
      }
      return true;
    };

    Tabular.prototype.getColumns = function(header) {
      var columns, i, key, _i, _ref;
      columns = [];
      for (i = _i = 1, _ref = this.cols; 1 <= _ref ? _i <= _ref : _i >= _ref; i = 1 <= _ref ? ++_i : --_i) {
        key = "TTYPE" + i;
        if (!header.contains(key)) {
          return null;
        }
        columns.push(header.get(key));
      }
      return columns;
    };

    Tabular.prototype.getColumn = function(name, callback, opts) {
      var accessor, cb, column, descriptor, elementByteLength, elementByteOffset, factor, i, index, iterations, rowsPerIteration,
        _this = this;
      if (this.blob != null) {
        index = this.columns.indexOf(name);
        descriptor = this.descriptors[index];
        accessor = this.accessors[index];
        elementByteLength = this.elementByteLengths[index];
        elementByteOffset = this.elementByteLengths.slice(0, index);
        if (elementByteOffset.length === 0) {
          elementByteOffset = 0;
        } else {
          elementByteOffset = elementByteOffset.reduce(function(a, b) {
            return a + b;
          });
        }
        column = this.typedArray[descriptor] != null ? new this.typedArray[descriptor](this.rows) : [];
        rowsPerIteration = ~~(this.maxMemory / this.rowByteSize);
        rowsPerIteration = Math.min(rowsPerIteration, this.rows);
        factor = this.rows / rowsPerIteration;
        iterations = Math.floor(factor) === factor ? factor : Math.floor(factor) + 1;
        i = 0;
        index = 0;
        cb = function(buffer, opts) {
          var nRows, offset, startRow, view;
          nRows = buffer.byteLength / _this.rowByteSize;
          view = new DataView(buffer);
          offset = elementByteOffset;
          while (nRows--) {
            column[i] = accessor(view, offset)[0];
            i += 1;
            offset += _this.rowByteSize;
          }
          iterations -= 1;
          index += 1;
          if (iterations) {
            startRow = index * rowsPerIteration;
            return _this.getTableBuffer(startRow, rowsPerIteration, cb, opts);
          } else {
            _this.invoke(callback, opts, column);
          }
        };
        return this.getTableBuffer(0, rowsPerIteration, cb, opts);
      } else {
        cb = function(rows, opts) {
          column = rows.map(function(d) {
            return d[name];
          });
          return _this.invoke(callback, opts, column);
        };
        return this.getRows(0, this.rows, cb, opts);
      }
    };

    Tabular.prototype.getTableBuffer = function(row, number, callback, opts) {
      var begin, blobRows, end, reader,
        _this = this;
      number = Math.min(this.rows - row, number);
      begin = row * this.rowByteSize;
      end = begin + number * this.rowByteSize;
      blobRows = this.blob.slice(begin, end);
      reader = new FileReader();
      reader.row = row;
      reader.number = number;
      reader.onloadend = function(e) {
        return _this.invoke(callback, opts, e.target.result);
      };
      return reader.readAsArrayBuffer(blobRows);
    };

    Tabular.prototype.getRows = function(row, number, callback, opts) {
      var begin, blobRows, buffer, end, reader, rows,
        _this = this;
      if (this.rowsInMemory(row, row + number)) {
        if (this.blob != null) {
          buffer = this.buffer;
        } else {
          begin = row * this.rowByteSize;
          end = begin + number * this.rowByteSize;
          buffer = this.buffer.slice(begin, end);
        }
        rows = this._getRows(buffer, number);
        this.invoke(callback, opts, rows);
        return rows;
      } else {
        begin = row * this.rowByteSize;
        end = begin + Math.max(this.nRowsInBuffer * this.rowByteSize, number * this.rowByteSize);
        blobRows = this.blob.slice(begin, end);
        reader = new FileReader();
        reader.row = row;
        reader.number = number;
        reader.onloadend = function(e) {
          var target;
          target = e.target;
          _this.buffer = target.result;
          _this.firstRowInBuffer = _this.lastRowInBuffer = target.row;
          _this.lastRowInBuffer += target.number;
          return _this.getRows(row, number, callback, opts);
        };
        return reader.readAsArrayBuffer(blobRows);
      }
    };

    return Tabular;

  })(DataUnit);

  this.astro.FITS.Tabular = Tabular;

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

    function Table() {
      _ref = Table.__super__.constructor.apply(this, arguments);
      return _ref;
    }

    Table.prototype.dataAccessors = {
      A: function(value) {
        return value.trim();
      },
      I: function(value) {
        return parseInt(value);
      },
      F: function(value) {
        return parseFloat(value);
      },
      E: function(value) {
        return parseFloat(value);
      },
      D: function(value) {
        return parseFloat(value);
      }
    };

    Table.prototype.setAccessors = function(header) {
      var descriptor, form, i, match, pattern, type, _i, _ref1, _results,
        _this = this;
      pattern = /([AIFED])(\d+)\.*(\d+)*/;
      _results = [];
      for (i = _i = 1, _ref1 = this.cols; 1 <= _ref1 ? _i <= _ref1 : _i >= _ref1; i = 1 <= _ref1 ? ++_i : --_i) {
        form = header.get("TFORM" + i);
        type = header.get("TTYPE" + i);
        match = pattern.exec(form);
        descriptor = match[1];
        _results.push((function(descriptor) {
          var accessor;
          accessor = function(value) {
            return _this.dataAccessors[descriptor](value);
          };
          return _this.accessors.push(accessor);
        })(descriptor));
      }
      return _results;
    };

    Table.prototype._getRows = function(buffer) {
      var accessor, arr, begin, end, i, index, line, nRows, row, rows, subarray, value, _i, _j, _k, _len, _len1, _ref1, _ref2;
      nRows = buffer.byteLength / this.rowByteSize;
      arr = new Uint8Array(buffer);
      rows = [];
      for (i = _i = 0, _ref1 = nRows - 1; 0 <= _ref1 ? _i <= _ref1 : _i >= _ref1; i = 0 <= _ref1 ? ++_i : --_i) {
        begin = i * this.rowByteSize;
        end = begin + this.rowByteSize;
        subarray = arr.subarray(begin, end);
        line = '';
        for (_j = 0, _len = subarray.length; _j < _len; _j++) {
          value = subarray[_j];
          line += String.fromCharCode(value);
        }
        line = line.trim().split(/\s+/);
        row = {};
        _ref2 = this.accessors;
        for (index = _k = 0, _len1 = _ref2.length; _k < _len1; index = ++_k) {
          accessor = _ref2[index];
          value = line[index];
          row[this.columns[index]] = accessor(value);
        }
        rows.push(row);
      }
      return rows;
    };

    return Table;

  })(Tabular);

  this.astro.FITS.Table = Table;

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

    function BinaryTable() {
      _ref1 = BinaryTable.__super__.constructor.apply(this, arguments);
      return _ref1;
    }

    BinaryTable.prototype.typedArray = {
      B: Uint8Array,
      I: Uint16Array,
      J: Uint32Array,
      E: Float32Array,
      D: Float64Array,
      1: Uint8Array,
      2: Uint16Array,
      4: Uint32Array
    };

    BinaryTable.offsets = {
      L: 1,
      B: 1,
      I: 2,
      J: 4,
      K: 8,
      A: 1,
      E: 4,
      D: 8,
      C: 8,
      M: 16
    };

    BinaryTable.prototype.dataAccessors = {
      L: function(view, offset) {
        var val, x;
        x = view.getInt8(offset);
        offset += 1;
        val = x === 84 ? true : false;
        return [val, offset];
      },
      B: function(view, offset) {
        var val;
        val = view.getUint8(offset);
        offset += 1;
        return [val, offset];
      },
      I: function(view, offset) {
        var val;
        val = view.getInt16(offset);
        offset += 2;
        return [val, offset];
      },
      J: function(view, offset) {
        var val;
        val = view.getInt32(offset);
        offset += 4;
        return [val, offset];
      },
      K: function(view, offset) {
        var factor, highByte, lowByte, mod, val;
        highByte = Math.abs(view.getInt32(offset));
        offset += 4;
        lowByte = Math.abs(view.getInt32(offset));
        offset += 4;
        mod = highByte % 10;
        factor = mod ? -1 : 1;
        highByte -= mod;
        val = factor * ((highByte << 32) | lowByte);
        return [val, offset];
      },
      A: function(view, offset) {
        var val;
        val = view.getUint8(offset);
        val = String.fromCharCode(val);
        offset += 1;
        return [val, offset];
      },
      E: function(view, offset) {
        var val;
        val = view.getFloat32(offset);
        offset += 4;
        return [val, offset];
      },
      D: function(view, offset) {
        var val;
        val = view.getFloat64(offset);
        offset += 8;
        return [val, offset];
      },
      C: function(view, offset) {
        var val, val1, val2;
        val1 = view.getFloat32(offset);
        offset += 4;
        val2 = view.getFloat32(offset);
        offset += 4;
        val = [val1, val2];
        return [val, offset];
      },
      M: function(view, offset) {
        var val, val1, val2;
        val1 = view.getFloat64(offset);
        offset += 8;
        val2 = view.getFloat64(offset);
        offset += 8;
        val = [val1, val2];
        return [val, offset];
      }
    };

    BinaryTable.prototype.toBits = function(byte) {
      var arr, i;
      arr = [];
      i = 128;
      while (i >= 1) {
        arr.push((byte & i ? 1 : 0));
        i /= 2;
      }
      return arr;
    };

    BinaryTable.prototype.getFromHeap = function(view, offset, descriptor) {
      var arr, heapOffset, heapSlice, i, length;
      length = view.getInt32(offset);
      offset += 4;
      heapOffset = view.getInt32(offset);
      offset += 4;
      heapSlice = this.heap.slice(heapOffset, heapOffset + length);
      arr = new this.typedArray[descriptor](heapSlice);
      i = arr.length;
      while (i--) {
        arr[i] = this.constructor.swapEndian[descriptor](arr[i]);
      }
      return [arr, offset];
    };

    BinaryTable.prototype.setAccessors = function(header) {
      var count, descriptor, form, i, isArray, match, pattern, type, _i, _ref2, _results,
        _this = this;
      pattern = /(\d*)([P|Q]*)([L|X|B|I|J|K|A|E|D|C|M]{1})/;
      _results = [];
      for (i = _i = 1, _ref2 = this.cols; 1 <= _ref2 ? _i <= _ref2 : _i >= _ref2; i = 1 <= _ref2 ? ++_i : --_i) {
        form = header.get("TFORM" + i);
        type = header.get("TTYPE" + i);
        match = pattern.exec(form);
        count = parseInt(match[1]) || 1;
        isArray = match[2];
        descriptor = match[3];
        _results.push((function(descriptor, count) {
          var accessor, nBytes;
          _this.descriptors.push(descriptor);
          _this.elementByteLengths.push(_this.constructor.offsets[descriptor] * count);
          if (isArray) {
            switch (type) {
              case "COMPRESSED_DATA":
                accessor = function(view, offset) {
                  var arr, pixels, _ref3;
                  _ref3 = _this.getFromHeap(view, offset, descriptor), arr = _ref3[0], offset = _ref3[1];
                  pixels = new _this.typedArray[_this.algorithmParameters["BYTEPIX"]](_this.ztile[0]);
                  Decompress.Rice(arr, _this.algorithmParameters["BLOCKSIZE"], _this.algorithmParameters["BYTEPIX"], pixels, _this.ztile[0], Decompress.RiceSetup);
                  return [pixels, offset];
                };
                break;
              case "GZIP_COMPRESSED_DATA":
                accessor = function(view, offset) {
                  var arr;
                  arr = new Float32Array(_this.width);
                  i = arr.length;
                  while (i--) {
                    arr[i] = NaN;
                  }
                  return [arr, offset];
                };
                break;
              default:
                accessor = function(view, offset) {
                  return _this.getFromHeap(view, offset, descriptor);
                };
            }
          } else {
            if (count === 1) {
              accessor = function(view, offset) {
                var value, _ref3;
                _ref3 = _this.dataAccessors[descriptor](view, offset), value = _ref3[0], offset = _ref3[1];
                return [value, offset];
              };
            } else {
              if (descriptor === 'X') {
                nBytes = Math.log(count) / Math.log(2);
                accessor = function(view, offset) {
                  var arr, bits, buffer, byte, bytes, _j, _len;
                  buffer = view.buffer.slice(offset, offset + nBytes);
                  bytes = new Uint8Array(buffer);
                  bits = [];
                  for (_j = 0, _len = bytes.length; _j < _len; _j++) {
                    byte = bytes[_j];
                    arr = _this.toBits(byte);
                    bits = bits.concat(arr);
                  }
                  offset += nBytes;
                  return [bits.slice(0, +(count - 1) + 1 || 9e9), offset];
                };
              } else if (descriptor === 'A') {
                accessor = function(view, offset) {
                  var arr, buffer, s, value, _j, _len;
                  buffer = view.buffer.slice(offset, offset + count);
                  arr = new Uint8Array(buffer);
                  s = '';
                  for (_j = 0, _len = arr.length; _j < _len; _j++) {
                    value = arr[_j];
                    s += String.fromCharCode(value);
                  }
                  s = s.trim();
                  offset += count;
                  return [s, offset];
                };
              } else {
                accessor = function(view, offset) {
                  var data, value, _ref3;
                  i = count;
                  data = [];
                  while (i--) {
                    _ref3 = _this.dataAccessors[descriptor](view, offset), value = _ref3[0], offset = _ref3[1];
                    data.push(value);
                  }
                  return [data, offset];
                };
              }
            }
          }
          return _this.accessors.push(accessor);
        })(descriptor, count));
      }
      return _results;
    };

    BinaryTable.prototype._getRows = function(buffer, nRows) {
      var accessor, index, offset, row, rows, value, view, _i, _len, _ref2, _ref3;
      view = new DataView(buffer);
      offset = 0;
      rows = [];
      while (nRows--) {
        row = {};
        _ref2 = this.accessors;
        for (index = _i = 0, _len = _ref2.length; _i < _len; index = ++_i) {
          accessor = _ref2[index];
          _ref3 = accessor(view, offset), value = _ref3[0], offset = _ref3[1];
          row[this.columns[index]] = value;
        }
        rows.push(row);
      }
      return rows;
    };

    return BinaryTable;

  })(Tabular);

  this.astro.FITS.BinaryTable = BinaryTable;

  Decompress = {
    RiceSetup: {
      1: function(array) {
        var fsbits, fsmax, lastpix, pointer;
        pointer = 1;
        fsbits = 3;
        fsmax = 6;
        lastpix = array[0];
        return [fsbits, fsmax, lastpix, pointer];
      },
      2: function(array) {
        var bytevalue, fsbits, fsmax, lastpix, pointer;
        pointer = 2;
        fsbits = 4;
        fsmax = 14;
        lastpix = 0;
        bytevalue = array[0];
        lastpix = lastpix | (bytevalue << 8);
        bytevalue = array[1];
        lastpix = lastpix | bytevalue;
        return [fsbits, fsmax, lastpix, pointer];
      },
      4: function(array) {
        var bytevalue, fsbits, fsmax, lastpix, pointer;
        pointer = 4;
        fsbits = 5;
        fsmax = 25;
        lastpix = 0;
        bytevalue = array[0];
        lastpix = lastpix | (bytevalue << 24);
        bytevalue = array[1];
        lastpix = lastpix | (bytevalue << 16);
        bytevalue = array[2];
        lastpix = lastpix | (bytevalue << 8);
        bytevalue = array[3];
        lastpix = lastpix | bytevalue;
        return [fsbits, fsmax, lastpix, pointer];
      }
    },
    Rice: function(array, blocksize, bytepix, pixels, nx, setup) {
      var b, bbits, diff, fs, fsbits, fsmax, i, imax, k, lastpix, nbits, nonzeroCount, nzero, pointer, _ref2, _ref3;
      bbits = 1 << fsbits;
      _ref2 = setup[bytepix](array), fsbits = _ref2[0], fsmax = _ref2[1], lastpix = _ref2[2], pointer = _ref2[3];
      nonzeroCount = new Uint8Array(256);
      nzero = 8;
      _ref3 = [128, 255], k = _ref3[0], i = _ref3[1];
      while (i >= 0) {
        while (i >= k) {
          nonzeroCount[i] = nzero;
          i -= 1;
        }
        k = k / 2;
        nzero -= 1;
      }
      nonzeroCount[0] = 0;
      b = array[pointer++];
      nbits = 8;
      i = 0;
      while (i < nx) {
        nbits -= fsbits;
        while (nbits < 0) {
          b = (b << 8) | array[pointer++];
          nbits += 8;
        }
        fs = (b >> nbits) - 1;
        b &= (1 << nbits) - 1;
        imax = i + blocksize;
        if (imax > nx) {
          imax = nx;
        }
        if (fs < 0) {
          while (i < imax) {
            pixels[i] = lastpix;
            i += 1;
          }
        } else if (fs === fsmax) {
          while (i < imax) {
            k = bbits - nbits;
            diff = b << k;
            k -= 8;
            while (k >= 0) {
              b = array[pointer++];
              diff |= b << k;
              k -= 8;
            }
            if (nbits > 0) {
              b = array[pointer++];
              diff |= b >> (-k);
              b &= (1 << nbits) - 1;
            } else {
              b = 0;
            }
            if ((diff & 1) === 0) {
              diff = diff >> 1;
            } else {
              diff = ~(diff >> 1);
            }
            pixels[i] = diff + lastpix;
            lastpix = pixels[i];
            i++;
          }
        } else {
          while (i < imax) {
            while (b === 0) {
              nbits += 8;
              b = array[pointer++];
            }
            nzero = nbits - nonzeroCount[b];
            nbits -= nzero + 1;
            b ^= 1 << nbits;
            nbits -= fs;
            while (nbits < 0) {
              b = (b << 8) | array[pointer++];
              nbits += 8;
            }
            diff = (nzero << fs) | (b >> nbits);
            b &= (1 << nbits) - 1;
            if ((diff & 1) === 0) {
              diff = diff >> 1;
            } else {
              diff = ~(diff >> 1);
            }
            pixels[i] = diff + lastpix;
            lastpix = pixels[i];
            i++;
          }
        }
      }
      return pixels;
    }
  };

  this.astro.FITS.Decompress = Decompress;

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

    CompressedImage.include(ImageUtils);

    CompressedImage.extend(Decompress);

    CompressedImage.randomGenerator = function() {
      var a, i, m, random, seed, temp, _i;
      a = 16807;
      m = 2147483647;
      seed = 1;
      random = new Float32Array(10000);
      for (i = _i = 0; _i <= 9999; i = ++_i) {
        temp = a * seed;
        seed = temp - m * parseInt(temp / m);
        random[i] = seed / m;
      }
      return random;
    };

    CompressedImage.randomSequence = CompressedImage.randomGenerator();

    function CompressedImage(header, data) {
      var i, key, value, ztile, _i, _ref2;
      CompressedImage.__super__.constructor.apply(this, arguments);
      this.zcmptype = header.get("ZCMPTYPE");
      this.zbitpix = header.get("ZBITPIX");
      this.znaxis = header.get("ZNAXIS");
      this.zblank = header.get("ZBLANK");
      this.blank = header.get("BLANK");
      this.zdither = header.get('ZDITHER0') || 0;
      this.ztile = [];
      for (i = _i = 1, _ref2 = this.znaxis; 1 <= _ref2 ? _i <= _ref2 : _i >= _ref2; i = 1 <= _ref2 ? ++_i : --_i) {
        ztile = header.contains("ZTILE" + i) ? header.get("ZTILE" + i) : i === 1 ? header.get("ZNAXIS1") : 1;
        this.ztile.push(ztile);
      }
      this.width = header.get("ZNAXIS1");
      this.height = header.get("ZNAXIS2") || 1;
      this.algorithmParameters = {};
      if (this.zcmptype === 'RICE_1') {
        this.algorithmParameters["BLOCKSIZE"] = 32;
        this.algorithmParameters["BYTEPIX"] = 4;
      }
      i = 1;
      while (true) {
        key = "ZNAME" + i;
        if (!header.contains(key)) {
          break;
        }
        value = "ZVAL" + i;
        this.algorithmParameters[header.get(key)] = header.get(value);
        i += 1;
      }
      this.zmaskcmp = header.get("ZMASKCMP");
      this.zquantiz = header.get("ZQUANTIZ") || "LINEAR_SCALING";
      this.bzero = header.get("BZERO") || 0;
      this.bscale = header.get("BSCALE") || 1;
    }

    CompressedImage.prototype._getRows = function(buffer, nRows) {
      var accessor, arr, blank, data, i, index, nTile, offset, r, rIndex, row, scale, seed0, seed1, value, view, zero, _i, _j, _len, _len1, _ref2, _ref3;
      view = new DataView(buffer);
      offset = 0;
      arr = new Float32Array(this.width * this.height);
      while (nRows--) {
        row = {};
        _ref2 = this.accessors;
        for (index = _i = 0, _len = _ref2.length; _i < _len; index = ++_i) {
          accessor = _ref2[index];
          _ref3 = accessor(view, offset), value = _ref3[0], offset = _ref3[1];
          row[this.columns[index]] = value;
        }
        data = row['COMPRESSED_DATA'] || row['UNCOMPRESSED_DATA'] || row['GZIP_COMPRESSED_DATA'];
        blank = row['ZBLANK'] || this.zblank;
        scale = row['ZSCALE'] || this.bscale;
        zero = row['ZZERO'] || this.bzero;
        nTile = this.height - nRows;
        seed0 = nTile + this.zdither - 1;
        seed1 = (seed0 - 1) % 10000;
        rIndex = parseInt(this.constructor.randomSequence[seed1] * 500);
        for (index = _j = 0, _len1 = data.length; _j < _len1; index = ++_j) {
          value = data[index];
          i = (nTile - 1) * this.width + index;
          if (value === -2147483647) {
            arr[i] = NaN;
          } else if (value === -2147483646) {
            arr[i] = 0;
          } else {
            r = this.constructor.randomSequence[rIndex];
            arr[i] = (value - r + 0.5) * scale + zero;
          }
          rIndex += 1;
          if (rIndex === 10000) {
            seed1 = (seed1 + 1) % 10000;
            rIndex = parseInt(this.randomSequence[seed1] * 500);
          }
        }
      }
      return arr;
    };

    CompressedImage.prototype.getFrame = function(nFrame, callback, opts) {
      var heapBlob, reader,
        _this = this;
      if (this.heap) {
        this.frame = nFrame || this.frame;
        return this.getRows(0, this.rows, callback, opts);
      } else {
        heapBlob = this.blob.slice(this.length, this.length + this.heapLength);
        reader = new FileReader();
        reader.onloadend = function(e) {
          _this.heap = e.target.result;
          return _this.getFrame(nFrame, callback, opts);
        };
        return reader.readAsArrayBuffer(heapBlob);
      }
    };

    return CompressedImage;

  })(BinaryTable);

  this.astro.FITS.CompressedImage = CompressedImage;

  HDU = (function() {
    function HDU(header, data) {
      this.header = header;
      this.data = data;
    }

    HDU.prototype.hasData = function() {
      if (this.data != null) {
        return true;
      } else {
        return false;
      }
    };

    return HDU;

  })();

  this.astro.FITS.HDU = HDU;

}).call(this);
