1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
|
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Definition of the browser range interface.
*
* DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead.
*
* @author robbyw@google.com (Robby Walker)
*/
goog.provide('goog.dom.browserrange.AbstractRange');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.dom');
goog.require('goog.dom.NodeType');
goog.require('goog.dom.RangeEndpoint');
goog.require('goog.dom.TagName');
goog.require('goog.dom.TextRangeIterator');
goog.require('goog.iter');
goog.require('goog.math.Coordinate');
goog.require('goog.string');
goog.require('goog.string.StringBuffer');
goog.require('goog.userAgent');
/**
* The constructor for abstract ranges. Don't call this from subclasses.
* @constructor
*/
goog.dom.browserrange.AbstractRange = function() {};
/**
* @return {goog.dom.browserrange.AbstractRange} A clone of this range.
*/
goog.dom.browserrange.AbstractRange.prototype.clone = goog.abstractMethod;
/**
* Returns the browser native implementation of the range. Please refrain from
* using this function - if you find you need the range please add wrappers for
* the functionality you need rather than just using the native range.
* @return {Range|TextRange} The browser native range object.
*/
goog.dom.browserrange.AbstractRange.prototype.getBrowserRange =
goog.abstractMethod;
/**
* Returns the deepest node in the tree that contains the entire range.
* @return {Node} The deepest node that contains the entire range.
*/
goog.dom.browserrange.AbstractRange.prototype.getContainer =
goog.abstractMethod;
/**
* Returns the node the range starts in.
* @return {Node} The element or text node the range starts in.
*/
goog.dom.browserrange.AbstractRange.prototype.getStartNode =
goog.abstractMethod;
/**
* Returns the offset into the node the range starts in.
* @return {number} The offset into the node the range starts in. For text
* nodes, this is an offset into the node value. For elements, this is
* an offset into the childNodes array.
*/
goog.dom.browserrange.AbstractRange.prototype.getStartOffset =
goog.abstractMethod;
/**
* @return {goog.math.Coordinate} The coordinate of the selection start node
* and offset.
*/
goog.dom.browserrange.AbstractRange.prototype.getStartPosition = function() {
return this.getPosition_(true);
};
/**
* Returns the node the range ends in.
* @return {Node} The element or text node the range ends in.
*/
goog.dom.browserrange.AbstractRange.prototype.getEndNode = goog.abstractMethod;
/**
* Returns the offset into the node the range ends in.
* @return {number} The offset into the node the range ends in. For text
* nodes, this is an offset into the node value. For elements, this is
* an offset into the childNodes array.
*/
goog.dom.browserrange.AbstractRange.prototype.getEndOffset =
goog.abstractMethod;
/**
* @return {goog.math.Coordinate} The coordinate of the selection end node
* and offset.
*/
goog.dom.browserrange.AbstractRange.prototype.getEndPosition = function() {
return this.getPosition_(false);
};
/**
* @param {boolean} start Whether to get the position of the start or end.
* @return {goog.math.Coordinate} The coordinate of the selection point.
* @private
*/
goog.dom.browserrange.AbstractRange.prototype.getPosition_ = function(start) {
goog.asserts.assert(
this.range_.getClientRects,
'Getting selection coordinates is not supported.');
var rects = this.range_.getClientRects();
if (rects.length) {
var r = start ? rects[0] : goog.array.peek(rects);
return new goog.math.Coordinate(
start ? r.left : r.right, start ? r.top : r.bottom);
}
return null;
};
/**
* Compares one endpoint of this range with the endpoint of another browser
* native range object.
* @param {Range|TextRange} range The browser native range to compare against.
* @param {goog.dom.RangeEndpoint} thisEndpoint The endpoint of this range
* to compare with.
* @param {goog.dom.RangeEndpoint} otherEndpoint The endpoint of the other
* range to compare with.
* @return {number} 0 if the endpoints are equal, negative if this range
* endpoint comes before the other range endpoint, and positive otherwise.
*/
goog.dom.browserrange.AbstractRange.prototype.compareBrowserRangeEndpoints =
goog.abstractMethod;
/**
* Tests if this range contains the given range.
* @param {goog.dom.browserrange.AbstractRange} abstractRange The range to test.
* @param {boolean=} opt_allowPartial If not set or false, the range must be
* entirely contained in the selection for this function to return true.
* @return {boolean} Whether this range contains the given range.
*/
goog.dom.browserrange.AbstractRange.prototype.containsRange = function(
abstractRange, opt_allowPartial) {
// IE sometimes misreports the boundaries for collapsed ranges. So if the
// other range is collapsed, make sure the whole range is contained. This is
// logically equivalent, and works around IE's bug.
var checkPartial = opt_allowPartial && !abstractRange.isCollapsed();
var range = abstractRange.getBrowserRange();
var start = goog.dom.RangeEndpoint.START, end = goog.dom.RangeEndpoint.END;
try {
if (checkPartial) {
// There are two ways to not overlap. Being before, and being after.
// Before is represented by this.end before range.start: comparison < 0.
// After is represented by this.start after range.end: comparison > 0.
// The below is the negation of not overlapping.
return this.compareBrowserRangeEndpoints(range, end, start) >= 0 &&
this.compareBrowserRangeEndpoints(range, start, end) <= 0;
} else {
// Return true if this range bounds the parameter range from both sides.
return this.compareBrowserRangeEndpoints(range, end, end) >= 0 &&
this.compareBrowserRangeEndpoints(range, start, start) <= 0;
}
} catch (e) {
if (!goog.userAgent.IE) {
throw e;
}
// IE sometimes throws exceptions when one range is invalid, i.e. points
// to a node that has been removed from the document. Return false in this
// case.
return false;
}
};
/**
* Tests if this range contains the given node.
* @param {Node} node The node to test.
* @param {boolean=} opt_allowPartial If not set or false, the node must be
* entirely contained in the selection for this function to return true.
* @return {boolean} Whether this range contains the given node.
* @suppress {missingRequire} Cannot depend on goog.dom.browserrange because it
* creates a circular dependency.
*/
goog.dom.browserrange.AbstractRange.prototype.containsNode = function(
node, opt_allowPartial) {
/** @suppress {missingRequire} Circular dep with browserrange */
return this.containsRange(
goog.dom.browserrange.createRangeFromNodeContents(node),
opt_allowPartial);
};
/**
* Tests if the selection is collapsed - i.e. is just a caret.
* @return {boolean} Whether the range is collapsed.
*/
goog.dom.browserrange.AbstractRange.prototype.isCollapsed = goog.abstractMethod;
/**
* @return {string} The text content of the range.
*/
goog.dom.browserrange.AbstractRange.prototype.getText = goog.abstractMethod;
/**
* Returns the HTML fragment this range selects. This is slow on all browsers.
* @return {string} HTML fragment of the range, does not include context
* containing elements.
*/
goog.dom.browserrange.AbstractRange.prototype.getHtmlFragment = function() {
var output = new goog.string.StringBuffer();
goog.iter.forEach(this, function(node, ignore, it) {
if (node.nodeType == goog.dom.NodeType.TEXT) {
output.append(
goog.string.htmlEscape(
node.nodeValue.substring(
it.getStartTextOffset(), it.getEndTextOffset())));
} else if (node.nodeType == goog.dom.NodeType.ELEMENT) {
if (it.isEndTag()) {
if (goog.dom.canHaveChildren(node)) {
output.append('</' + node.tagName + '>');
}
} else {
var shallow = node.cloneNode(false);
var html = goog.dom.getOuterHtml(shallow);
if (goog.userAgent.IE && node.tagName == goog.dom.TagName.LI) {
// For an LI, IE just returns "<li>" with no closing tag
output.append(html);
} else {
var index = html.lastIndexOf('<');
output.append(index ? html.substr(0, index) : html);
}
}
}
}, this);
return output.toString();
};
/**
* Returns valid HTML for this range. This is fast on IE, and semi-fast on
* other browsers.
* @return {string} Valid HTML of the range, including context containing
* elements.
*/
goog.dom.browserrange.AbstractRange.prototype.getValidHtml =
goog.abstractMethod;
/**
* Returns a RangeIterator over the contents of the range. Regardless of the
* direction of the range, the iterator will move in document order.
* @param {boolean=} opt_keys Unused for this iterator.
* @return {!goog.dom.RangeIterator} An iterator over tags in the range.
*/
goog.dom.browserrange.AbstractRange.prototype.__iterator__ = function(
opt_keys) {
return new goog.dom.TextRangeIterator(
this.getStartNode(), this.getStartOffset(), this.getEndNode(),
this.getEndOffset());
};
// SELECTION MODIFICATION
/**
* Set this range as the selection in its window.
* @param {boolean=} opt_reverse Whether to select the range in reverse,
* if possible.
*/
goog.dom.browserrange.AbstractRange.prototype.select = goog.abstractMethod;
/**
* Removes the contents of the range from the document. As a side effect, the
* selection will be collapsed. The behavior of content removal is normalized
* across browsers. For instance, IE sometimes creates extra text nodes that
* a W3C browser does not. That behavior is corrected for.
*/
goog.dom.browserrange.AbstractRange.prototype.removeContents =
goog.abstractMethod;
/**
* Surrounds the text range with the specified element (on Mozilla) or with a
* clone of the specified element (on IE). Returns a reference to the
* surrounding element if the operation was successful; returns null if the
* operation failed.
* @param {Element} element The element with which the selection is to be
* surrounded.
* @return {Element} The surrounding element (same as the argument on Mozilla,
* but not on IE), or null if unsuccessful.
*/
goog.dom.browserrange.AbstractRange.prototype.surroundContents =
goog.abstractMethod;
/**
* Inserts a node before (or after) the range. The range may be disrupted
* beyond recovery because of the way this splits nodes.
* @param {Node} node The node to insert.
* @param {boolean} before True to insert before, false to insert after.
* @return {Node} The node added to the document. This may be different
* than the node parameter because on IE we have to clone it.
*/
goog.dom.browserrange.AbstractRange.prototype.insertNode = goog.abstractMethod;
/**
* Surrounds this range with the two given nodes. The range may be disrupted
* beyond recovery because of the way this splits nodes.
* @param {Element} startNode The node to insert at the start.
* @param {Element} endNode The node to insert at the end.
*/
goog.dom.browserrange.AbstractRange.prototype.surroundWithNodes =
goog.abstractMethod;
/**
* Collapses the range to one of its boundary points.
* @param {boolean} toStart Whether to collapse to the start of the range.
*/
goog.dom.browserrange.AbstractRange.prototype.collapse = goog.abstractMethod;
|