%PDF- %PDF-
| Direktori : /www/varak.net/wiki.varak.net/extensions/VisualEditor/lib/ve/lib/rangefix/ |
| Current File : /www/varak.net/wiki.varak.net/extensions/VisualEditor/lib/ve/lib/rangefix/rangefix.js |
/*!
* RangeFix v0.2.0
* https://github.com/edg2s/rangefix
*
* Copyright 2014-16 Ed Sanders.
* Released under the MIT license
*/
( function () {
var broken,
rangeFix = {};
/**
* Check if bugs are present in the native functions
*
* For getClientRects, constructs two lines of text and
* creates a range between them. Broken browsers will
* return three rectangles instead of two.
*
* For getBoundingClientRect, create a collapsed range
* and check if the resulting rect has non-zero offsets.
*
* getBoundingClientRect is also considered broken if
* getClientRects is broken.
*
* For the IE zoom bug, just check the version number as
* we can't detect the bug if the zoom level is currently 100%.
*
* @private
* @return {Object} Object containing boolean properties 'getClientRects',
* 'getBoundingClientRect' and 'ieZoom' indicating bugs are present
* in these functions/browsers.
*/
function isBroken() {
var boundingRect, p1, p2, t1, t2, range, jscriptVersion;
if ( broken === undefined ) {
p1 = document.createElement( 'p' );
p2 = document.createElement( 'p' );
t1 = document.createTextNode( 'aa' );
t2 = document.createTextNode( 'aa' );
range = document.createRange();
broken = {};
p1.appendChild( t1 );
p2.appendChild( t2 );
document.body.appendChild( p1 );
document.body.appendChild( p2 );
range.setStart( t1, 1 );
range.setEnd( t2, 1 );
broken.getClientRects = broken.getBoundingClientRect = range.getClientRects().length > 2;
if ( !broken.getBoundingClientRect ) {
// Safari doesn't return a valid bounding rect for collapsed ranges
range.setEnd( t1, 1 );
boundingRect = range.getBoundingClientRect();
broken.getBoundingClientRect = boundingRect.top === 0 && boundingRect.left === 0;
}
document.body.removeChild( p1 );
document.body.removeChild( p2 );
// Detect IE<=10
/*jshint evil:true */
jscriptVersion = window.ActiveXObject && new Function( '/*@cc_on return @_jscript_version; @*/' )();
/*jshint evil:false */
broken.ieZoom = jscriptVersion && jscriptVersion <= 10;
}
return broken;
}
/**
* Compensate for the current zoom level in IE<=10
*
* getClientRects returns values in real pixels in these browsers,
* so using them in your CSS will result in them getting scaled again.
*
* @private
* @param {ClientRectList|ClientRect[]|ClientRect|Object|null} rectOrRects Rect or list of rects to fix
* @return {ClientRectList|ClientRect[]|ClientRect|Object|null} Fixed rect or list of rects
*/
function zoomFix( rectOrRects ) {
var zoom;
if ( !rectOrRects ) {
return rectOrRects;
}
// Optimisation when zoom level is 1: return original object
if ( screen.deviceXDPI === screen.logicalXDPI ) {
return rectOrRects;
}
// Rect list: map this function to each rect
if ( 'length' in rectOrRects ) {
return Array.prototype.map.call( rectOrRects, zoomFix );
}
// Single rect: Adjust by zoom factor
zoom = screen.deviceXDPI / screen.logicalXDPI;
return {
top: rectOrRects.top / zoom,
bottom: rectOrRects.bottom / zoom,
left: rectOrRects.left / zoom,
right: rectOrRects.right / zoom,
width: rectOrRects.width / zoom,
height: rectOrRects.height / zoom
};
}
/**
* Get client rectangles from a range
*
* @param {Range} range Range
* @return {ClientRectList|ClientRect[]} ClientRectList or list of ClientRect objects describing range
*/
rangeFix.getClientRects = function ( range ) {
var rects, endContainer, endOffset, partialRange,
broken = isBroken();
if ( broken.ieZoom ) {
return zoomFix( range.getClientRects() );
} else if ( !broken.getClientRects ) {
return range.getClientRects();
}
// Chrome gets the end container rects wrong when spanning
// nodes so we need to traverse up the tree from the endContainer until
// we reach the common ancestor, then we can add on from start to where
// we got up to
// https://code.google.com/p/chromium/issues/detail?id=324437
rects = [];
endContainer = range.endContainer;
endOffset = range.endOffset;
partialRange = document.createRange();
while ( endContainer !== range.commonAncestorContainer ) {
partialRange.setStart( endContainer, 0 );
partialRange.setEnd( endContainer, endOffset );
Array.prototype.push.apply( rects, partialRange.getClientRects() );
endOffset = Array.prototype.indexOf.call( endContainer.parentNode.childNodes, endContainer );
endContainer = endContainer.parentNode;
}
// Once we've reached the common ancestor, add on the range from the
// original start position to where we ended up.
partialRange = range.cloneRange();
partialRange.setEnd( endContainer, endOffset );
Array.prototype.push.apply( rects, partialRange.getClientRects() );
return rects;
};
/**
* Get bounding rectangle from a range
*
* @param {Range} range Range
* @return {ClientRect|Object|null} ClientRect or ClientRect-like object describing
* bounding rectangle, or null if not computable
*/
rangeFix.getBoundingClientRect = function ( range ) {
var i, l, boundingRect, rect, nativeBoundingRect, broken,
rects = this.getClientRects( range );
// If there are no rects return null, otherwise we'll fall through to
// getBoundingClientRect, which in Chrome and Firefox becomes [0,0,0,0].
if ( rects.length === 0 ) {
return null;
}
nativeBoundingRect = range.getBoundingClientRect();
broken = isBroken();
if ( broken.ieZoom ) {
return zoomFix( nativeBoundingRect );
} else if ( !broken.getBoundingClientRect ) {
return nativeBoundingRect;
}
// When nativeRange is a collapsed cursor at the end of a line or
// the start of a line, the bounding rect is [0,0,0,0] in Chrome.
// getClientRects returns two rects, one correct, and one at the
// end of the next line / start of the previous line. We can't tell
// here which one to use so just pick the first. This matches
// Firefox's behaviour, which tells you the cursor is at the end
// of the previous line when it is at the start of the line.
// See https://code.google.com/p/chromium/issues/detail?id=426017
if ( nativeBoundingRect.width === 0 && nativeBoundingRect.height === 0 ) {
return rects[ 0 ];
}
for ( i = 0, l = rects.length; i < l; i++ ) {
rect = rects[ i ];
if ( !boundingRect ) {
boundingRect = {
left: rect.left,
top: rect.top,
right: rect.right,
bottom: rect.bottom
};
} else {
boundingRect.left = Math.min( boundingRect.left, rect.left );
boundingRect.top = Math.min( boundingRect.top, rect.top );
boundingRect.right = Math.max( boundingRect.right, rect.right );
boundingRect.bottom = Math.max( boundingRect.bottom, rect.bottom );
}
}
if ( boundingRect ) {
boundingRect.width = boundingRect.right - boundingRect.left;
boundingRect.height = boundingRect.bottom - boundingRect.top;
}
return boundingRect;
};
// Expose
window.RangeFix = rangeFix;
} )();