text: "use strict";
  
  Object.defineProperty(exports, "__esModule", {
  	value: true
  });
  
  var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  
  var _core = require("./utils/core");
  
  var _queue = require("./utils/queue");
  
  var _queue2 = _interopRequireDefault(_queue);
  
  var _epubcfi = require("./epubcfi");
  
  var _epubcfi2 = _interopRequireDefault(_epubcfi);
  
  var _constants = require("./utils/constants");
  
  var _eventEmitter = require("event-emitter");
  
  var _eventEmitter2 = _interopRequireDefault(_eventEmitter);
  
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  
  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  
  /**
   * Find Locations for a Book
   * @param {Spine} spine
   * @param {request} request
   */
  var Locations = function () {
  	function Locations(spine, request, pause) {
  		_classCallCheck(this, Locations);
  
  		this.spine = spine;
  		this.request = request;
  		this.pause = pause || 100;
  
  		this.q = new _queue2.default(this);
  		this.epubcfi = new _epubcfi2.default();
  
  		this._locations = [];
  		this.total = 0;
  
  		this.break = 150;
  
  		this._current = 0;
  
  		this.currentLocation = '';
  		this._currentCfi = '';
  		this.processingTimeout = undefined;
  	}
  
  	/**
    * Load all of sections in the book to generate locations
    * @param  {int} chars how many chars to split on
    * @return {object} locations
    */
  
  
  	_createClass(Locations, [{
  		key: "generate",
  		value: function generate(chars) {
  
  			if (chars) {
  				this.break = chars;
  			}
  
  			this.q.pause();
  
  			this.spine.each(function (section) {
  				if (section.linear) {
  					this.q.enqueue(this.process.bind(this), section);
  				}
  			}.bind(this));
  
  			return this.q.run().then(function () {
  				this.total = this._locations.length - 1;
  
  				if (this._currentCfi) {
  					this.currentLocation = this._currentCfi;
  				}
  
  				return this._locations;
  				// console.log(this.percentage(this.book.rendition.location.start), this.percentage(this.book.rendition.location.end));
  			}.bind(this));
  		}
  	}, {
  		key: "createRange",
  		value: function createRange() {
  			return {
  				startContainer: undefined,
  				startOffset: undefined,
  				endContainer: undefined,
  				endOffset: undefined
  			};
  		}
  	}, {
  		key: "process",
  		value: function process(section) {
  
  			return section.load(this.request).then(function (contents) {
  				var completed = new _core.defer();
  				var locations = this.parse(contents, section.cfiBase);
  				this._locations = this._locations.concat(locations);
  
  				section.unload();
  
  				this.processingTimeout = setTimeout(function () {
  					return completed.resolve(locations);
  				}, this.pause);
  				return completed.promise;
  			}.bind(this));
  		}
  	}, {
  		key: "parse",
  		value: function parse(contents, cfiBase, chars) {
  			var locations = [];
  			var range;
  			var doc = contents.ownerDocument;
  			var body = (0, _core.qs)(doc, "body");
  			var counter = 0;
  			var prev;
  			var _break = chars || this.break;
  			var parser = function parser(node) {
  				var len = node.length;
  				var dist;
  				var pos = 0;
  
  				if (node.textContent.trim().length === 0) {
  					return false; // continue
  				}
  
  				// Start range
  				if (counter == 0) {
  					range = this.createRange();
  					range.startContainer = node;
  					range.startOffset = 0;
  				}
  
  				dist = _break - counter;
  
  				// Node is smaller than a break,
  				// skip over it
  				if (dist > len) {
  					counter += len;
  					pos = len;
  				}
  
  				while (pos < len) {
  					dist = _break - counter;
  
  					if (counter === 0) {
  						// Start new range
  						pos += 1;
  						range = this.createRange();
  						range.startContainer = node;
  						range.startOffset = pos;
  					}
  
  					// pos += dist;
  
  					// Gone over
  					if (pos + dist >= len) {
  						// Continue counter for next node
  						counter += len - pos;
  						// break
  						pos = len;
  						// At End
  					} else {
  						// Advance pos
  						pos += dist;
  
  						// End the previous range
  						range.endContainer = node;
  						range.endOffset = pos;
  						// cfi = section.cfiFromRange(range);
  						var cfi = new _epubcfi2.default(range, cfiBase).toString();
  						locations.push(cfi);
  						counter = 0;
  					}
  				}
  				prev = node;
  			};
  
  			(0, _core.sprint)(body, parser.bind(this));
  
  			// Close remaining
  			if (range && range.startContainer && prev) {
  				range.endContainer = prev;
  				range.endOffset = prev.length;
  				var cfi = new _epubcfi2.default(range, cfiBase).toString();
  				locations.push(cfi);
  				counter = 0;
  			}
  
  			return locations;
  		}
  	}, {
  		key: "locationFromCfi",
  		value: function locationFromCfi(cfi) {
  			var loc = void 0;
  			if (_epubcfi2.default.prototype.isCfiString(cfi)) {
  				cfi = new _epubcfi2.default(cfi);
  			}
  			// Check if the location has not been set yet
  			if (this._locations.length === 0) {
  				return -1;
  			}
  
  			loc = (0, _core.locationOf)(cfi, this._locations, this.epubcfi.compare);
  
  			if (loc > this.total) {
  				return this.total;
  			}
  
  			return loc;
  		}
  	}, {
  		key: "percentageFromCfi",
  		value: function percentageFromCfi(cfi) {
  			if (this._locations.length === 0) {
  				return null;
  			}
  			// Find closest cfi
  			var loc = this.locationFromCfi(cfi);
  			// Get percentage in total
  			return this.percentageFromLocation(loc);
  		}
  	}, {
  		key: "percentageFromLocation",
  		value: function percentageFromLocation(loc) {
  			if (!loc || !this.total) {
  				return 0;
  			}
  
  			return loc / this.total;
  		}
  	}, {
  		key: "cfiFromLocation",
  		value: function cfiFromLocation(loc) {
  			var cfi = -1;
  			// check that pg is an int
  			if (typeof loc != "number") {
  				loc = parseInt(loc);
  			}
  
  			if (loc >= 0 && loc < this._locations.length) {
  				cfi = this._locations[loc];
  			}
  
  			return cfi;
  		}
  	}, {
  		key: "cfiFromPercentage",
  		value: function cfiFromPercentage(percentage) {
  			var loc = void 0;
  			if (percentage > 1) {
  				console.warn("Normalize cfiFromPercentage value to between 0 - 1");
  			}
  
  			// Make sure 1 goes to very end
  			if (percentage >= 1) {
  				var cfi = new _epubcfi2.default(this._locations[this.total]);
  				cfi.collapse();
  				return cfi.toString();
  			}
  
  			loc = Math.ceil(this.total * percentage);
  			return this.cfiFromLocation(loc);
  		}
  	}, {
  		key: "load",
  		value: function load(locations) {
  			if (typeof locations === "string") {
  				this._locations = JSON.parse(locations);
  			} else {
  				this._locations = locations;
  			}
  			this.total = this._locations.length - 1;
  			return this._locations;
  		}
  	}, {
  		key: "save",
  		value: function save(json) {
  			return JSON.stringify(this._locations);
  		}
  	}, {
  		key: "getCurrent",
  		value: function getCurrent(json) {
  			return this._current;
  		}
  	}, {
  		key: "setCurrent",
  		value: function setCurrent(curr) {
  			var loc;
  
  			if (typeof curr == "string") {
  				this._currentCfi = curr;
  			} else if (typeof curr == "number") {
  				this._current = curr;
  			} else {
  				return;
  			}
  
  			if (this._locations.length === 0) {
  				return;
  			}
  
  			if (typeof curr == "string") {
  				loc = this.locationFromCfi(curr);
  				this._current = loc;
  			} else {
  				loc = curr;
  			}
  
  			this.emit(_constants.EVENTS.LOCATIONS.CHANGED, {
  				percentage: this.percentageFromLocation(loc)
  			});
  		}
  	}, {
  		key: "length",
  		value: function length() {
  			return this._locations.length;
  		}
  	}, {
  		key: "destroy",
  		value: function destroy() {
  			this.spine = undefined;
  			this.request = undefined;
  			this.pause = undefined;
  
  			this.q.stop();
  			this.q = undefined;
  			this.epubcfi = undefined;
  
  			this._locations = undefined;
  			this.total = undefined;
  
  			this.break = undefined;
  			this._current = undefined;
  
  			this.currentLocation = undefined;
  			this._currentCfi = undefined;
  			clearTimeout(this.processingTimeout);
  		}
  	}, {
  		key: "currentLocation",
  		get: function get() {
  			return this._current;
  		},
  		set: function set(curr) {
  			this.setCurrent(curr);
  		}
  	}]);
  
  	return Locations;
  }();
  
  (0, _eventEmitter2.default)(Locations.prototype);
  
  exports.default = Locations;
  module.exports = exports["default"];