You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
728 lines
14 KiB
728 lines
14 KiB
/*
|
|
# Movable Type (r) Open Source (C) 2003-2010 Six Apart, Ltd.
|
|
# This program is distributed under the terms of the
|
|
# GNU General Public License, version 2.
|
|
#
|
|
# $Id: tc.js 3455 2009-02-23 02:29:31Z auno $
|
|
*/
|
|
|
|
|
|
/*
|
|
--------------------------------------------------------------------------------
|
|
TC
|
|
core utility class
|
|
--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/* dummy constructor */
|
|
|
|
TC = function()
|
|
{
|
|
}
|
|
|
|
|
|
/* static variables */
|
|
|
|
TC.matchShortWords = /\b(\S*)\b/gi;
|
|
TC.matchLeadingSpace = /^\s+/;
|
|
TC.matchTrailingSpace = /\s+$/;
|
|
TC.matchSpace = /\s+/g;
|
|
|
|
|
|
/* utility methods */
|
|
|
|
TC.defined = function( x )
|
|
{
|
|
try
|
|
{
|
|
if( typeof x != "undefined" )
|
|
return true;
|
|
}
|
|
catch( e ) {}
|
|
return false;
|
|
}
|
|
|
|
|
|
TC.inspect = function( x )
|
|
{
|
|
var t = "";
|
|
for( var i in x )
|
|
t += i + " = " + x[ i ] + "<br />";
|
|
return t;
|
|
}
|
|
|
|
|
|
TC.elementOrId = function( element )
|
|
{
|
|
if( !element )
|
|
return null;
|
|
if( typeof element == "string" )
|
|
element = document.getElementById( element );
|
|
return element;
|
|
}
|
|
|
|
|
|
TC.applyFunction = function( elements, func )
|
|
{
|
|
if( !elements )
|
|
return;
|
|
for( var i in elements )
|
|
{
|
|
var element = elements[ i ];
|
|
if( !element )
|
|
continue;
|
|
func( element );
|
|
}
|
|
}
|
|
|
|
|
|
TC.attachWindowEvent = function( eventName, func )
|
|
{
|
|
var onEventName = 'on' + eventName;
|
|
var old = window[onEventName];
|
|
if( typeof old != 'function' )
|
|
window[onEventName] = func;
|
|
else
|
|
{
|
|
window[onEventName] = function( evt )
|
|
{
|
|
old( evt );
|
|
return func( evt );
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
TC.attachLoadEvent = function( func )
|
|
{
|
|
TC.attachWindowEvent('load', func);
|
|
}
|
|
|
|
|
|
TC.attachBeforeUnloadEvent = function( func )
|
|
{
|
|
var old = window.onbeforeunload;
|
|
if( typeof old != 'function' )
|
|
window.onbeforeunload = func;
|
|
else
|
|
{
|
|
window.onbeforeunload = function( evt )
|
|
{
|
|
old( evt );
|
|
return func( evt );
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
TC.attachDocumentEvent = function( element, eventName, func, recurse )
|
|
{
|
|
if( !element || !element.document )
|
|
return;
|
|
var doc = element.document;
|
|
TC.attachEvent( doc, eventName, func );
|
|
if( !recurse )
|
|
return;
|
|
|
|
// get frames
|
|
var elements = [];
|
|
if( doc.frames )
|
|
{
|
|
for( var i in doc.frames );
|
|
elements[ elements.length ] = doc.frames[ i ];
|
|
}
|
|
|
|
// get iframes
|
|
var iframes = doc.getElementsByTagName( "iframe" ) || [];
|
|
for( var i in iframes )
|
|
elements[ elements.length ] = iframes[ i ];
|
|
|
|
// attach event handler
|
|
for( var i in elements )
|
|
{
|
|
if( !elements[ i ] || !elements[ i ].contentWindow )
|
|
continue;
|
|
TC.attachDocumentEvent( elements[ i ].contentWindow, eventName, func, recurse );
|
|
}
|
|
}
|
|
|
|
|
|
TC.attachEvent = function( element, eventName, func )
|
|
{
|
|
element = TC.elementOrId( element );
|
|
if( !element )
|
|
return;
|
|
if( element.addEventListener )
|
|
element.addEventListener( eventName, func, true );
|
|
else if( element.attachEvent )
|
|
element.attachEvent( "on" + eventName, func );
|
|
else
|
|
element[ "on" + eventName ] = func;
|
|
}
|
|
|
|
|
|
TC.detachEvent = function( element, eventName, func )
|
|
{
|
|
element = TC.elementOrId( element );
|
|
if( !element )
|
|
return;
|
|
if( element.removeEventListener )
|
|
element.removeEventListener( eventName, func, true );
|
|
else if( element.detachEvent )
|
|
element.detachEvent( "on" + eventName, func );
|
|
else
|
|
element[ "on" + eventName ] = null;
|
|
}
|
|
|
|
|
|
TC.stopEvent = function( evt )
|
|
{
|
|
evt = evt || event;
|
|
|
|
// moz
|
|
if( evt.preventDefault )
|
|
evt.preventDefault();
|
|
if( evt.stopPropagation )
|
|
evt.stopPropagation();
|
|
|
|
// ie
|
|
if( TC.defined( evt.returnValue ) )
|
|
{
|
|
evt.cancelBubble = true;
|
|
evt.returnValue = false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
TC.allowTabs = function( element )
|
|
{
|
|
element = TC.elementOrId( element );
|
|
TC.attachEvent( element, "keypress", TC.allowTabs.keyPress );
|
|
TC.attachEvent( element, "keydown", TC.allowTabs.keyDown );
|
|
}
|
|
|
|
|
|
TC.allowTabs.keyPress = function( evt )
|
|
{
|
|
evt = evt || event;
|
|
var element = evt.target || evt.srcElement;
|
|
if( evt.keyCode == 9 )
|
|
{
|
|
return TC.stopEvent( evt );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
TC.allowTabs.keyDown = function( evt )
|
|
{
|
|
evt = evt || event;
|
|
var element = evt.target || evt.srcElement;
|
|
if( evt.keyCode == 9 )
|
|
{
|
|
TC.setSelectionValue( element, "\t" );
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
TC.allowHover = function( element )
|
|
{
|
|
element = TC.elementOrId( element );
|
|
TC.attachEvent( element, "mouseover", TC.allowHover.mouseOver );
|
|
TC.attachEvent( element, "mouseout", TC.allowHover.mouseOut );
|
|
}
|
|
|
|
|
|
TC.allowHover.mouseOver = function( evt )
|
|
{
|
|
evt = evt || event;
|
|
var element = evt.target || evt.srcElement;
|
|
TC.addClassName( element, "hover" );
|
|
}
|
|
|
|
|
|
TC.allowHover.mouseOut = function( evt )
|
|
{
|
|
evt = evt || event;
|
|
var element = evt.target || evt.srcElement;
|
|
TC.removeClassName( element, "hover" );
|
|
}
|
|
|
|
|
|
/* text and selection related methods */
|
|
|
|
TC.getSelection = function( element )
|
|
{
|
|
var doc = TC.getOwnerDocument( element );
|
|
var win = TC.getOwnerWindow( doc );
|
|
if( win.getSelection )
|
|
return win.getSelection();
|
|
else if( doc.getSelection )
|
|
return doc.getSelection();
|
|
else if( doc.selection )
|
|
return doc.selection;
|
|
return null;
|
|
}
|
|
|
|
TC.getCaretPosition = function( element )
|
|
{
|
|
var doc = TC.getOwnerDocument( element );
|
|
if (doc.selection) {
|
|
var range = doc.selection.createRange();
|
|
var isCollapsed = range.compareEndPoints("StartToEnd", range) == 0;
|
|
if (!isCollapsed)
|
|
range.collapse(true);
|
|
var b = range.getBookmark();
|
|
return b.charCodeAt(2) - 2;
|
|
} else if (element.selectionStart != 'undefined') {
|
|
return element.selectionStart;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
TC.setCaretPosition = function( element, pos )
|
|
{
|
|
if (element.createTextRange) {
|
|
var range = element.createTextRange();
|
|
range.collapse(true);
|
|
range.moveStart("character", pos);
|
|
range.select();
|
|
return true;
|
|
} else {
|
|
element.selectionStart = pos;
|
|
element.selectionEnd = pos;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
TC.createRange = function( selection, element )
|
|
{
|
|
var doc = TC.getOwnerDocument( element );
|
|
if( selection && selection.getRangeAt )
|
|
return selection.getRangeAt( 0 );
|
|
else if( selection && selection.createRange )
|
|
return selection.createRange();
|
|
else if( doc.createRange )
|
|
return doc.createRange();
|
|
return null;
|
|
}
|
|
|
|
|
|
TC.extractElementText = function( element )
|
|
{
|
|
element = TC.elementOrId( element );
|
|
if( !element || element.nodeType == 8 ) // ignore html comments
|
|
return '';
|
|
var tagName = element.tagName ? element.tagName.toLowerCase() : '';
|
|
if( tagName == 'input' || tagName == 'textarea' )
|
|
return '';
|
|
|
|
var text = element.nodeValue != null ? element.nodeValue : '';
|
|
for( var i = 0; i < element.childNodes.length; i++ )
|
|
text += TC.extractElementText( element.childNodes[ i ] );
|
|
|
|
return text;
|
|
}
|
|
|
|
|
|
TC.setSelectionValue = function( element, value )
|
|
{
|
|
element = TC.elementOrId( element );
|
|
var scrollFromBottom = element.scrollHeight - element.scrollTop;
|
|
|
|
// msie
|
|
if( document.selection )
|
|
{
|
|
element.focus();
|
|
document.selection.createRange().text = value;
|
|
}
|
|
|
|
// mozilla
|
|
else
|
|
{
|
|
var length = element.textLength;
|
|
var start = element.selectionStart;
|
|
var end = element.selectionEnd;
|
|
element.value = element.value.substring( 0, start ) +
|
|
value + element.value.substring( end, length );
|
|
element.caretPos = start + length;
|
|
element.selectionStart = start + value.length;
|
|
element.selectionEnd = start + value.length;
|
|
}
|
|
|
|
element.scrollTop = element.scrollHeight - scrollFromBottom;
|
|
}
|
|
|
|
|
|
TC.normalizeWords = function( text )
|
|
{
|
|
text = text.toLowerCase();
|
|
|
|
var originalWords = text.match( matchShortWords );
|
|
var exists = new Array();
|
|
var words = new Array();
|
|
|
|
for( i = 0; i < originalWords.length; i++ )
|
|
{
|
|
if( exists[ originalWords[ i ] ] )
|
|
continue;
|
|
exists[ originalWords[ i ] ] = 1;
|
|
words[ words.length ] = originalWords[ i ];
|
|
}
|
|
|
|
words.sort();
|
|
return words;
|
|
}
|
|
|
|
|
|
/* dom methods */
|
|
|
|
TC.getOwnerDocument = function( element )
|
|
{
|
|
if( !element )
|
|
return document;
|
|
if( element.ownerDocument )
|
|
return element.ownerDocument;
|
|
if( element.getElementById )
|
|
return element
|
|
return document;
|
|
}
|
|
|
|
|
|
TC.getOwnerWindow = function( element )
|
|
{
|
|
if( !element )
|
|
return window;
|
|
|
|
// msie
|
|
if( element.parentWindow )
|
|
return element.parentWindow;
|
|
|
|
// mozilla
|
|
var doc = TC.getOwnerDocument( element );
|
|
if( doc && doc.defaultView )
|
|
return doc.defaultView;
|
|
|
|
return window;
|
|
}
|
|
|
|
|
|
TC.getElementsByTagAndClassName = function( tagName, className, root )
|
|
{
|
|
root = TC.elementOrId( root );
|
|
if( !root )
|
|
root = document;
|
|
var allElements = root.getElementsByTagName( tagName );
|
|
var elements = [];
|
|
for( var i = 0; i < allElements.length; i++ )
|
|
{
|
|
var element = allElements[ i ];
|
|
if( !element )
|
|
continue;
|
|
if( TC.hasClassName( element, className ) )
|
|
elements[ elements.length ] = element;
|
|
}
|
|
return elements;
|
|
}
|
|
|
|
|
|
TC.getElementsByClassName = function( className, root )
|
|
{
|
|
return TC.getElementsByTagAndClassName( "*", className, root );
|
|
}
|
|
|
|
|
|
TC.getParentByTagName = function( element, tagName )
|
|
{
|
|
tagName = tagName.toLowerCase();
|
|
var parent = element.parentNode;
|
|
while( parent )
|
|
{
|
|
if( parent.tagName && parent.tagName.toLowerCase() == tagName )
|
|
return parent;
|
|
parent = parent.parentNode;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
TC.inlineDisplays =
|
|
{
|
|
"inline" : 1,
|
|
"inline-block" : 1
|
|
}
|
|
|
|
TC.isInlineNode = function( node )
|
|
{
|
|
/* text nodes are inline */
|
|
if( node.nodeType == 3 )
|
|
return true;
|
|
|
|
/* document nodes are non-inline */
|
|
if( node.nodeType == 9 )
|
|
return false;
|
|
|
|
/* all non-element nodes are inline */
|
|
if( node.nodeType != 1 )
|
|
return true;
|
|
|
|
/* br elements are not inline */
|
|
if( node.tagName && node.tagName.toLowerCase() == "br" )
|
|
return false;
|
|
|
|
/* examine the style property of the inline node */
|
|
var d = TC.getStyle( node, "display" );
|
|
if( TC.inlineDisplays[ d ] )
|
|
return true;
|
|
|
|
/* assume non-inline */
|
|
return false;
|
|
}
|
|
|
|
|
|
TC.getPreviousTextNode = function( node, inline )
|
|
{
|
|
var up = false;
|
|
while( node )
|
|
{
|
|
if( !up && node.lastChild )
|
|
{
|
|
node = node.lastChild; // down
|
|
up = false;
|
|
}
|
|
else if( node.previousSibling )
|
|
{
|
|
node = node.previousSibling; // left
|
|
up = false;
|
|
}
|
|
else if( node.parentNode )
|
|
{
|
|
node = node.parentNode; // up
|
|
up = true;
|
|
}
|
|
else
|
|
return null; // borked
|
|
|
|
if( node.nodeType == 3 )
|
|
return node;
|
|
if( inline && !TC.isInlineNode( node ) )
|
|
return null;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
TC.getNextTextNode = function( node, inline )
|
|
{
|
|
var up = false;
|
|
while( node )
|
|
{
|
|
if( !up && node.firstChild )
|
|
{
|
|
node = node.firstChild; // down
|
|
up = false;
|
|
}
|
|
else if( node.nextSibling )
|
|
{
|
|
node = node.nextSibling; // right
|
|
up = false;
|
|
}
|
|
else if( node.parentNode )
|
|
{
|
|
node = node.parentNode; // up
|
|
up = true;
|
|
}
|
|
else
|
|
return null; // borked
|
|
|
|
if( node.nodeType == 3 )
|
|
return node;
|
|
if( inline && !TC.isInlineNode( node ) )
|
|
return null;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
TC.setAttributes = function( element, attr )
|
|
{
|
|
element = TC.elementOrId( element );
|
|
if( !element || !attr )
|
|
return;
|
|
for( var a in attr )
|
|
element.setAttribute( a, attr[ a ] );
|
|
}
|
|
|
|
|
|
// this and the following classname functions honor w3c case-sensitive classnames
|
|
TC.hasClassName = function( element, className )
|
|
{
|
|
if( !element || !element.className || !className )
|
|
return false;
|
|
var classNames = element.className.split( TC.matchSpace );
|
|
for( var i = 0; i < classNames.length; i++ )
|
|
{
|
|
if( classNames[ i ] == className )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
TC.getClassNames = function( e ) {
|
|
if( !e || !e.className )
|
|
return [];
|
|
return e.className.split( /\s+/g );
|
|
}
|
|
|
|
TC.addClassName = function( e, cn ) {
|
|
if( !e || !cn )
|
|
return false;
|
|
var cs = TC.getClassNames( e );
|
|
for( var i = 0; i < cs.length; i++ ) {
|
|
if( cs[ i ] == cn )
|
|
return true;
|
|
}
|
|
cs.push( cn );
|
|
e.className = cs.join( " " );
|
|
return false;
|
|
}
|
|
|
|
TC.removeClassName = function( e, cn ) {
|
|
var r = false;
|
|
if( !e || !e.className || !cn )
|
|
return r;
|
|
var cs = (e.className && e.className.length)
|
|
? e.className.split( /\s+/g )
|
|
: [];
|
|
var ncs = [];
|
|
/* support regex */
|
|
if( cn instanceof RegExp ) {
|
|
for( var i = 0; i < cs.length; i++ ) {
|
|
if ( cn.test( cs[ i ] ) ) {
|
|
r = true;
|
|
continue;
|
|
}
|
|
ncs.push( cs[ i ] );
|
|
}
|
|
} else {
|
|
for( var i = 0; i < cs.length; i++ ) {
|
|
if( cs[ i ] == cn ) {
|
|
r = true;
|
|
continue;
|
|
}
|
|
ncs.push( cs[ i ] );
|
|
}
|
|
}
|
|
if( r )
|
|
e.className = ncs.join( " " );
|
|
return r;
|
|
}
|
|
|
|
TC.getComputedStyle = function( e )
|
|
{
|
|
if( e.currentStyle )
|
|
return e.currentStyle;
|
|
var style = {};
|
|
var owner = TC.getOwnerDocument( e );
|
|
if( owner && owner.defaultView && owner.defaultView.getComputedStyle ) {
|
|
try {
|
|
style = owner.defaultView.getComputedStyle( e, null );
|
|
} catch( e ) {}
|
|
}
|
|
return style;
|
|
}
|
|
|
|
TC.getStyle = function( element, property )
|
|
{
|
|
element = TC.elementOrId( element );
|
|
var style;
|
|
if( window.getComputedStyle )
|
|
style = window.getComputedStyle( element, null ).getPropertyValue( property );
|
|
else if( element.currentStyle )
|
|
style = element.currentStyle[ property ];
|
|
return style;
|
|
}
|
|
|
|
|
|
TC.setStyle = function( element, style )
|
|
{
|
|
element = TC.elementOrId( element );
|
|
if( !element || !element.style || !style )
|
|
return;
|
|
for( var s in style )
|
|
element.style[ s ] = style[ s ];
|
|
}
|
|
|
|
|
|
TC.getAbsolutePosition = function( element )
|
|
{
|
|
element = TC.elementOrId( element );
|
|
var pos = { "left" : 0, "top" : 0 };
|
|
if( !element )
|
|
return pos;
|
|
while( element )
|
|
{
|
|
pos.left += element.offsetLeft;
|
|
pos.top += element.offsetTop;
|
|
element = element.offsetParent;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
|
|
TC.getAbsoluteCursorPosition = function( evt )
|
|
{
|
|
evt = evt || event;
|
|
|
|
// get basic position
|
|
var pos =
|
|
{
|
|
x: evt.clientX,
|
|
y: evt.clientY
|
|
};
|
|
|
|
// ie
|
|
if( document.documentElement && TC.defined( document.documentElement.scrollLeft ) )
|
|
{
|
|
pos.x += document.documentElement.scrollLeft;
|
|
pos.y += document.documentElement.scrollTop;
|
|
}
|
|
|
|
// safari
|
|
else if( TC.defined( window.scrollX ) )
|
|
{
|
|
pos.x += window.scrollX;
|
|
pos.y += window.scrollX;
|
|
}
|
|
|
|
// opera
|
|
else if( document.body && TC.defined( document.body.scrollLeft ) )
|
|
{
|
|
pos.x += document.body.scrollLeft;
|
|
pos.y += document.body.scrollTop;
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
/* array methods */
|
|
|
|
TC.scramble = function( array )
|
|
{
|
|
var length = array.length;
|
|
for( var i = 0; i < length; i++ )
|
|
{
|
|
var a = Math.floor( Math.random() * length );
|
|
var b = Math.floor( Math.random() * length );
|
|
var temp = array[ a ];
|
|
array[ a ] = array[ b ];
|
|
array[ b ] = temp;
|
|
}
|
|
}
|