var Leaf, Normalizer, Range, Selection, dom, _; _ = require('lodash'); dom = require('../lib/dom'); Leaf = require('./leaf'); Normalizer = require('../lib/normalizer'); Range = require('../lib/range'); Selection = (function() { function Selection(doc, iframe, emitter) { this.doc = doc; this.iframe = iframe; this.emitter = emitter; this.document = this.doc.root.ownerDocument; this.focus = false; this.range = new Range(0, 0); this.nullDelay = false; this.update('silent'); } Selection.prototype.checkFocus = function() { return this.document.activeElement === this.doc.root && document.activeElement === this.iframe; }; Selection.prototype.getRange = function(ignoreFocus) { var end, nativeRange, start; if (ignoreFocus == null) { ignoreFocus = false; } if (this.checkFocus()) { nativeRange = this._getNativeRange(); if (nativeRange == null) { return null; } start = this._positionToIndex(nativeRange.startContainer, nativeRange.startOffset); if (nativeRange.startContainer === nativeRange.endContainer && nativeRange.startOffset === nativeRange.endOffset) { end = start; } else { end = this._positionToIndex(nativeRange.endContainer, nativeRange.endOffset); } return new Range(Math.min(start, end), Math.max(start, end)); } else if (ignoreFocus) { return this.range; } else { return null; } }; Selection.prototype.preserve = function(fn) { var endNode, endOffset, nativeRange, startNode, startOffset, _ref, _ref1, _ref2, _ref3; nativeRange = this._getNativeRange(); if ((nativeRange != null) && this.checkFocus()) { _ref = this._encodePosition(nativeRange.startContainer, nativeRange.startOffset), startNode = _ref[0], startOffset = _ref[1]; _ref1 = this._encodePosition(nativeRange.endContainer, nativeRange.endOffset), endNode = _ref1[0], endOffset = _ref1[1]; fn(); _ref2 = this._decodePosition(startNode, startOffset), startNode = _ref2[0], startOffset = _ref2[1]; _ref3 = this._decodePosition(endNode, endOffset), endNode = _ref3[0], endOffset = _ref3[1]; return this._setNativeRange(startNode, startOffset, endNode, endOffset); } else { return fn(); } }; Selection.prototype.setRange = function(range, source) { var endNode, endOffset, startNode, startOffset, _ref, _ref1, _ref2; if (range != null) { _ref = this._indexToPosition(range.start), startNode = _ref[0], startOffset = _ref[1]; if (range.isCollapsed()) { _ref1 = [startNode, startOffset], endNode = _ref1[0], endOffset = _ref1[1]; } else { _ref2 = this._indexToPosition(range.end), endNode = _ref2[0], endOffset = _ref2[1]; } this._setNativeRange(startNode, startOffset, endNode, endOffset); } else { this._setNativeRange(null); } return this.update(source); }; Selection.prototype.shiftAfter = function(index, length, fn) { var range; range = this.getRange(); fn(); if (range != null) { range.shift(index, length); return this.setRange(range, 'silent'); } }; Selection.prototype.update = function(source) { var emit, focus, range, toEmit; focus = this.checkFocus(); range = this.getRange(true); emit = source !== 'silent' && (!Range.compare(range, this.range) || focus !== this.focus); toEmit = focus ? range : null; if (toEmit === null && source === 'user' && !this.nullDelay) { return this.nullDelay = true; } else { this.nullDelay = false; this.range = range; this.focus = focus; if (emit) { return this.emitter.emit(this.emitter.constructor.events.SELECTION_CHANGE, toEmit, source); } } }; Selection.prototype._decodePosition = function(node, offset) { var childIndex; if (dom(node).isElement()) { childIndex = _.indexOf(dom(node.parentNode).childNodes(), node); offset += childIndex; node = node.parentNode; } return [node, offset]; }; Selection.prototype._encodePosition = function(node, offset) { var text; while (true) { if (dom(node).isTextNode() || node.tagName === dom.DEFAULT_BREAK_TAG || (dom.EMBED_TAGS[node.tagName] != null)) { return [node, offset]; } else if (offset < node.childNodes.length) { node = node.childNodes[offset]; offset = 0; } else if (node.childNodes.length === 0) { if (Normalizer.TAGS[node.tagName] == null) { text = node.ownerDocument.createTextNode(''); node.appendChild(text); node = text; } return [node, 0]; } else { node = node.lastChild; if (dom(node).isElement()) { if (node.tagName === dom.DEFAULT_BREAK_TAG || (dom.EMBED_TAGS[node.tagName] != null)) { return [node, 1]; } else { offset = node.childNodes.length; } } else { return [node, dom(node).length()]; } } } }; Selection.prototype._getNativeRange = function() { var range, selection; selection = this.document.getSelection(); if ((selection != null ? selection.rangeCount : void 0) > 0) { range = selection.getRangeAt(0); if (dom(range.startContainer).isAncestor(this.doc.root, true)) { if (range.startContainer === range.endContainer || dom(range.endContainer).isAncestor(this.doc.root, true)) { return range; } } } return null; }; Selection.prototype._indexToPosition = function(index) { var leaf, offset, _ref; if (this.doc.lines.length === 0) { return [this.doc.root, 0]; } _ref = this.doc.findLeafAt(index, true), leaf = _ref[0], offset = _ref[1]; return this._decodePosition(leaf.node, offset); }; Selection.prototype._positionToIndex = function(node, offset) { var leaf, leafNode, leafOffset, line, lineOffset, _ref; _ref = this._encodePosition(node, offset), leafNode = _ref[0], offset = _ref[1]; line = this.doc.findLine(leafNode); if (line == null) { return 0; } leaf = line.findLeaf(leafNode); lineOffset = 0; while (line.prev != null) { line = line.prev; lineOffset += line.length; } if (leaf == null) { return lineOffset; } leafOffset = 0; while (leaf.prev != null) { leaf = leaf.prev; leafOffset += leaf.length; } return lineOffset + leafOffset + offset; }; Selection.prototype._setNativeRange = function(startNode, startOffset, endNode, endOffset) { var nativeRange, selection; selection = this.document.getSelection(); if (!selection) { return; } if (startNode != null) { if (!this.checkFocus()) { this.doc.root.focus(); } nativeRange = this._getNativeRange(); if ((nativeRange == null) || startNode !== nativeRange.startContainer || startOffset !== nativeRange.startOffset || endNode !== nativeRange.endContainer || endOffset !== nativeRange.endOffset) { selection.removeAllRanges(); nativeRange = this.document.createRange(); nativeRange.setStart(startNode, startOffset); nativeRange.setEnd(endNode, endOffset); selection.addRange(nativeRange); if (!this.checkFocus()) { return this.doc.root.focus(); } } } else { selection.removeAllRanges(); return this.doc.root.blur(); } }; return Selection; })(); module.exports = Selection;