/**
* SignWriting Styled Viewer
*
* Copyright 2007-2013 Stephen E Slevinski Jr
* Steve (Slevin@signpuddle.net)
*
* This file is part of SWIS: the SignWriting Icon Server.
*
* SWIS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SWIS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SWIS. If not, see <http://www.gnu.org/licenses/>.
*
* END Copyright
*
* @copyright 2007-2013 Stephen E Slevinski Jr
* @author Steve (slevin@signpuddle.net)
* @license http://opensource.org/licenses/GPL-2.0 GPL
* @access public
* @version 1.0.0.rc.3
* @filesource
*
*/
/**
* define a function that uses regular expression to modify a node,
* then crawl the document object model for TEXT elements and apply the function
*/
(function(){ // Use this instead of .ready, so that styles can be applied before the body starts loading, if this is loaded from the head.
/*
* Miscellaneous bugs:
* IE8 needs mutation events work-around.
*/
var newStyle = "a:hover .signwritingtext { opacity: 0.8; } \
.signwritingtext{ \
cursor: vertical-text; \
background-position: 100% 0; \
background-repeat: no-repeat; \
background-origin: content-box; } \
.signwritingtext span{ display: table-cell; \
vertical-align: middle; \
font-size: 0%; \
color: transparent; \
text-align: center; \
height:inherit;} \
a .signwritingtext, label .signwritingtext, .ui-button .signwritingtext{ cursor: inherit; }"
///* writing modes
var tempDiv = document.createElement('div'), elemStyle = tempDiv.style,
listSLs = [ "ase", "rsl", "ise", "mfs", "mdl", "bfi", "fse" ],
// signLang = ( listSLs[ $.inArray( mw.config.get( "wgPageContentLanguage" ), listSLs ) ] || listSLs[ $.inArray( mw.config.get( "wgUserLanguage" ), listSLs ) ] ),
wmSupport = ( '-webkit-writing-mode' in elemStyle || '-moz-writing-mode' in elemStyle || '-ms-writing-mode' in elemStyle || 'writing-mode' in elemStyle );
// Temporary, until Bug 56920 is resolved.
var signLang = ( listSLs[ $.inArray( mw.config.get( "wgPageName" ).replace(/^(?:.+?:)?W[pbtqny]\/([a-z\-]{2,7})(?:\/.*)$/, "$1"), listSLs ) ] || listSLs[ $.inArray( mw.config.get( "wgUserLanguage" ), listSLs ) ] );
// tooltips css
newStyle +=
".swtooltip { position: absolute; min-width: 10px; min-height: 10px; pointer-events: none; padding: 4px; font-size: 70%; white-space: nowrap;" +
// Use native tooltip styling where possible. Currently only supported by FF.
// Chromium bug for -webkit-appearance: tooltip; is Issue 17371.
( "MozAppearance" in elemStyle ? "-moz-appearance: tooltip; " : "border-radius: 1px; background-color: #FFFFDD; border: 1px solid #AAAAAA; color: #333333; box-shadow: 0 0 3px rgba(0,0,0,.3); -webkit-box-shadow: 0 0 3px rgba(0,0,0,.3); opacity: 0.9; ") +
"z-index: 1101; display: none; }"; // TODO: build a workaround for pointer-events for nonsupporting browsers (IE10<).
if ( signLang && wmSupport ) {
elemStyle.cssText = "-ms-writing-mode: tb-lr;";
newStyle += "\
body { \
-webkit-writing-mode: vertical-lr; \
-moz-writing-mode: vertical-lr; \
-ms-writing-mode: tb-lr; \
writing-mode: vertical-lr; \
} \
.signwritingtext { \
display: inline-block;" +
// IE considers vertical-align: bottom; to be block-end instead of block-start like other browsers.
// Should really check for the actual performance. Maybe just checking if it accepts tb-lr won't cause problems?
( elemStyle.writingMode !== "tb-lr" ? "vertical-align: bottom;" : "vertical-align: top;" ) + "\
} \
#mw-content-text .signwritingtext { \
border-left: 1px solid #DDD; \
} \
#mw-content-text .mw-editsection .signwritingtext { \
border-left: 0; \
} \
#siteNotice { display: none } \/* temporary *\/ \
"
$( document ).ready( function(){
var msgs = ({
"ase" : "User:Yair rand/ASLMessages.js"
})[ signLang ];
msgs && importScript( msgs ); /* to be removed as soon as ASL interface is available */
importScript( "User:Yair rand/SWSelectBox.js" ); /* Should be merged here eventually. */
})
importStylesheet("User:Yair rand/rotatevector.css"); /* to be removed upon resolution of Bug 9436 */
}
//*/
mw.util.addCSS( newStyle );
var svgSupported = document.createElement('div');
svgSupported.innerHTML = '<svg/>';
svgSupported = (svgSupported.firstChild && svgSupported.firstChild.namespaceURI) == "http://www.w3.org/2000/svg" ? "svg" : "png";
var r = /(?:A(?:S[123][0-9a-f]{2}[0-5][0-9a-f])+)?([BLMR])[0-9]{3}x[0-9]{3}(?:S[123][0-9a-f]{2}[0-5][0-9a-f][0-9]{3}x[0-9]{3})*\s*|S38[7-9ab][0-5][0-9a-f][0-9]{3}x[0-9]{3}\s*/;
var swDiv = (function(){
var div = document.createElement( "div" );
div.className = "signwritingtext";
div.appendChild( document.createElement( "span" ) );
return div;
})();
window.signwriting_styled=(function (node) {
var u = 'http://swis.wmflabs.org/',
v = '1.0.0-rc.3',
s1, s2, d, p, o = { L : -1, R : 1 }, f,
r2 = /[0-9]{3}x[0-9]{3}/g;
function rgbToHex(rgb) {
if ( /^#[0-9a-fA-F]{6}$/.test( rgb ) ) {
return rgb.substr( 1 );
}
var rgbvals = /rgba?\(([^,]+),([^,]+),([^,\)]+)/i.exec(rgb);
if (!rgbvals) {
// ie8< don't like to give clean color values. not much can be done with color names other than check if "black",
// so other color names (other than white, which it defaults to) won't work. switch #XXX for #XXXXXX in JS:
return rgb === 'black' ? '000000' :
/#[0-9a-fA-F]{3}$/.test(rgb) ? rgb.substr(1).replace(/([0-9a-fA-F])/g,"$1$1") :
'ffffff'; // ie8< fix
}
var pad = function (value) {
return (value < 16 ? '0' + value.toString( 16 ) : value.toString( 16 ) );
};
return pad( 0 | rgbvals[1] ) + pad( 0 | rgbvals[2] ) + pad( 0 | rgbvals[3] );
}
f = function ( node, background ) {
for( var ex, value, color, size, nS; ex = r.exec( value = node.nodeValue ); ) {
color = color || rgbToHex(jQuery(node.parentNode).css('color'));
size = size || parseInt(jQuery(node.parentNode).css('font-size')) / 30 || 0.4;
var x, x1 = 500,
x2 = 500,
y, y1 = 500,
y2 = 500,
k, m = ex[0], index = ex.index, parent = node.parentNode;
k = ex[ 1 ];
m.replace(r2, function ($0) {
x = parseInt($0.slice(0, 3));
y = parseInt($0.slice(4, 7));
x1 = Math.min(x1, x);
x2 = Math.max(x2, x);
y1 = Math.min(y1, y);
y2 = Math.max(y2, y);
});
if ( k === undefined ) {
x2 = 1000 - x1;
y2 = 1000 - y1;
}
var div = swDiv.cloneNode( true ), span = div.firstChild, style = div.style;
style.marginRight = 10 * size + 'px';
style.width = ( ( x2 + ( ( o[k] || 0 ) * 75 ) - 390 ) * size ) + 'px';
style.height = ( y2 - y1 + 20 ) * size + 'px';
style.backgroundImage = 'url(\'' + u + 'glyphogram.php?font=' + svgSupported + '&text=' + m.replace( /\s+$/, '' ) + '&line=' + color + '&fill=' + rgbToHex( background() ) + '&size=' + size + '\')';
var pS = node.previousSibling;
if ( index > 0 ) {
node = node.splitText( index );
} else if ( k === undefined && pS ) {
var nowrap = document.createElement( "span" );
nowrap.style.whiteSpace = "nowrap";
nowrap.appendChild( pS );
nowrap.appendChild( div );
div = nowrap;
}
if ( value.length > index + m.length ) {
nS = node.splitText( m.length );
span.appendChild( node );
parent.insertBefore( div, node = nS );
} else {
nS = node.nextSibling;
span.appendChild( node );
if ( nS ) {
parent.insertBefore( div, nS );
} else {
parent.appendChild( div );
}
return;
}
}
};
function fswReplace(node, background) {
if (node.nodeType === 3) {
f( node, background );
} else {
var nodes = node.nodeName !== 'TEXTAREA' && node.nodeType !== 8 && node.childNodes;
if (nodes) {
var _bg, _background = function(){
if( _bg ) {
return _bg;
} else {
_bg = $( node ).css('background-color');
return _bg = ( _bg && _bg !== 'rgba(0, 0, 0, 0)' && _bg !== 'transparent' ) ? _bg : background();
}
}
for( var i = nodes.length; i--; ){ fswReplace(nodes[i], _background); }
}
}
};
if (!node || !node.nodeType) node = document.body;
function findBackground( node ) {
for( var background, parent = node; ( background = $( parent ).css('background-color') ) && ( background.toString() === 'rgba(0, 0, 0, 0)' || background.toString() === 'transparent' ); ) {
parent = parent.parentNode;
}
return background || '#ffffff';
}
fswReplace(node, function(){return findBackground( node ); } );
// MUTATION OBSERVERS
// These things moniter every dom addition and class/style change in the document.
// If there's no dynamic changes in the doc, don't use them, as they have serious performance issues.
// MOs are supported by IE11+, FF14+, Chrome, Safari 6+, Opera 15+, and a bunch of mobile stuffs.
// DOM events are supported by IE9/10, and are used as a backup system.
///*
if( !window.swMO && signLang ) { // don't do it more than once, don't do it when not on an ASL page to conserve performance.
(function(){
var MO = window.MutationObserver || window.WebKitMutationObserver,
rgb = rgbToHex,
rLine = /&line=[0-9A-Fa-f]+/, rFill = /&fill=[0-9A-Fa-f]+/;
window.swMO = true;
if( MO ) {
(new MO(function(mutations) {
// var z = new Date;
var mutations_ = [], uniqueElems = [];
mutations.forEach(function( mutation ) { // first, clear out the mutations with duplicate elements
if( uniqueElems.indexOf( mutation.target ) === -1 ) {
mutations_.push( mutation );
uniqueElems.push( mutation.target );
}
});
mutations_.forEach(function(mutation) { // too much duplication below. todo: fix.
if( mutation.attributeName === "style" && $( mutation.target ).is( ":animated" ) ) {
return;
}
$( mutation.target ).find( ".signwritingtext" ).css( "background-image", function( a, b ){
var j = b.replace( rLine, "&line=" + rgb( $( this ).css("color") ) ); //there's probably a faster way to do this...
if( j !== b ){ // don't change unless it actually needs changing
return j.replace( rFill, "&fill=" + rgb( findBackground( this ) ) );
}
});
});
// console.log( "TIME:", new Date - z );
}).observe( document.body, { subtree: true, attributes: true, attributeFilter: ["class", "style"] }));
} else {
document.body.addEventListener && (function(){
var elemsToDo = [], timer = false;
document.body.addEventListener( "DOMAttrModified", function( e ) { // IE10<
if( e.attrName === "class" || e.attrName === "style" ) {
var target = e.target;
if( elemsToDo.indexOf( target ) === -1 && elemsToDo.push( target ) && timer === false ) {
timer = setTimeout( function() {
timer = false;
elemsToDo.forEach( function( elem ){
if( $( elem ).is( ":animated" ) ) {
return;
}
$( elem ).find( ".signwritingtext" ).css( "background-image", function( a, b ){
var j = b.replace( rLine, "&line=" + rgb( $( this ).css("color") ) );
if( j !== b ){ // don't change unless it actually needs changing
return j.replace( rFill, "&fill=" + rgb( findBackground( this ) ) );
}
});
});
elemsToDo.length = 0; // empty the array
}, 32 );
}
}
});
})();
// Technically there are things other than class and style that
// can alter CSS, but it would be too heavy to moniter all that.
}
})();
(function(){ // now for characterData and childList monitering. TODO: merge the observers? (maybe not, actually...)
var MO = window.MutationObserver || window.WebKitMutationObserver, swcn = "signwritingtext",
MOopts = { subtree: true, characterData: true, childList: true }, tMO, body = document.body,
elemsToDo = [], allElems = [];
function findstuff( addedNode ){
var aP;
if( addedNode.nodeType === 3 ) {
( aP = addedNode.parentNode ) && elemsToDo.indexOf( aP ) === -1 && body.contains( aP ) &&
r.test( addedNode.nodeValue ) === true && // is pre-regexping helpful?
elemsToDo.push( aP );
} else if( addedNode.nodeType === 1 && allElems.indexOf( addedNode ) === -1 ) {
allElems.push( addedNode );
[].forEach.call( addedNode.childNodes, findstuff );
}
}
function processElem( elem ) {
if( elem.parentNode && elem.parentNode.className !== "signwritingtext" && elem.parentNode.className !== "swtooltip" ) {
// BUG: if mutated elem contains both sw as text and already-
// processed signwriting, the sw node gets re-styled, resulting
// in an extra layer of signwriting block inside the existing one.
signwriting_styled( elem );
}
};
if( MO ) {
tMO = new MO(function(mutations) {
mutations.forEach(function( mutation ) {
if( mutation.type === "characterData" ) {
findstuff( mutation.target );
} else {
[].forEach.call( mutation.addedNodes, findstuff );
}
});
elemsToDo.length && tMO.disconnect();
elemsToDo.forEach( processElem );
elemsToDo.length && tMO.observe( body, MOopts );
allElems.length = elemsToDo.length = 0;
});
tMO.observe( body, MOopts );
} else {
var timer = false, ignoreMutations = false; // too much duplication. todo: fix.
// todo: IE onDOMCharacterDataModified
body.addEventListener && body.addEventListener('DOMNodeInserted', function( e ){
if( ignoreMutations === false ) {
findstuff( e.target );
if( timer === false ) {
timer = setTimeout( function() {
ignoreMutations = true;
elemsToDo.forEach( processElem );
timer = ignoreMutations = false;
allElems.length = elemsToDo.length = 0;
}, 32 );
}
}
});
}
})();
// TEXT HIGHLIGHTING
// still a bit buggy.
window.getSelection && (function() { // if getSelection isn't available, there's nothing we can do.
var Highlight, HighlightText, $elem = $( tempDiv ).appendTo("body"), tempHighlightText = $elem.css( "color" );
elemStyle.color = "HighlightText";
elemStyle.backgroundColor = "Highlight";
HighlightText = $elem.css( "color" ); // Find the default highlighted text color (typically white).
Highlight = $elem.css( "background-color" );
if( HighlightText !== tempHighlightText || Highlight !== $elem.css( "background-color", "" ).css( "background-color") ) { // did Highlight or HighlightText work?
mw.util.addCSS( ".signwritingtext ::selection, .signwritingtext::selection{ background-color: transparent; }"); // don't double-highlight.
Highlight = rgbToHex( Highlight ); // ... and the highlighted background color.
HighlightText = rgbToHex( HighlightText );
// var $bb = $("<div>").appendTo("body").css({"position":"absolute","top":0,"left":0});
var lineAndFill = "&line=" + HighlightText + "&fill=" + Highlight,
rLineAndFill = /&line=[0-9a-fA-F]+&fill=[0-9a-fA-f]+/,
undoThese = [], highlightedElems = [], $body = $( document.body ),
prevEditedAnchor, prevEditedFocus, prevFocusOffset;
function fixSelection( e ){
if( e.which === 0 ) { // No mouse button pressed.
return;
}
e.type !== "mousemove" && e.type !== "keydown" && e.type !== "keyup" &&
$body[ e.type === "mousedown" ? "on" : "off" ]( "mousemove", fixSelection ); // track mousemove only when the mouse is down.
var q = window.getSelection(), // note: range is not supported by some browsers
rangeSupport, MSselection, MSrange;
/*
if( !( rangeSupport = q.type !== undefined ) ) { // option one: use Range
if( !( MSselection = document.selection ) ) { // option two: MS TextRange
return; // option three: give up, because nothing's going to work.
}
}
*/
if( ( ( rangeSupport = q.type !== undefined ) ?
q.type === "Range" :
( ( MSselection = document.selection ) && MSselection.type === "Text" ) ) &&
q.isCollapsed === false &&
q.focusNode && q.anchorNode
) {
if( q.focusNode === prevEditedFocus && q.anchorNode === prevEditedAnchor && q.focusOffset === prevFocusOffset ) { // anchor/focus haven't moved. nothing needs doing.
return;
}
var comparePosition = q.anchorNode.compareDocumentPosition( q.focusNode );
if( ( comparePosition & 6 ) === 0 ) {
if( q.anchorNode.parentNode.parentNode.className !== "signwritingtext" ) {
return;
}
comparePosition = q.focusOffset < q.anchorOffset ? 2 : 4;
}
var range = q.getRangeAt( 0 ),
startElem = $( ( comparePosition & 4 ) ? q.anchorNode : q.focusNode ).closest( ".signwritingtext>span" )[ 0 ],
endElem = $( ( comparePosition & 4 ) ? q.focusNode : q.anchorNode ).closest( ".signwritingtext>span" )[ 0 ];
if( ( startElem || ( startElem = endElem ) ) && ( endElem || ( endElem = startElem ) ) ) {
if( rangeSupport ) {
if( comparePosition & 2 ) { // backwards selection
range.setEndAfter( endElem ); // needs a bit of fiddling to keep the selection backwards-facing.
range.collapse( false ); // collapse to end
q.removeAllRanges();
q.addRange( range ); // make the selection a single point at the end, then extend it backward.
q.extend( startElem.parentNode, 0 );
} else if ( comparePosition & 4 ) { // forwards selection
range.setStartBefore( startElem ); // keep the selection including both the start and end nodes.
range.setEndAfter( endElem );
q.addRange( range );
}
} else { // IE
// this should probably use a different variable.
range = document.body.createTextRange(); // keep the selection including the start node. moving the end messes up IE.
range.moveToElementText( startElem );
MSrange = MSselection.createRange();
MSrange.setEndPoint( "StartToStart", range );
}
// $bb.text( q ); // for testing
startElem = startElem.parentNode; endElem = endElem.parentNode;
range = q.getRangeAt( 0 ); // unnecessary?
prevFocusOffset = q.focusOffset;
// check anchor and focus again after changes
if( prevEditedFocus === q.focusNode && prevEditedAnchor === q.anchorNode ) { // same anchor/focus. No highlighting changes.
return;
}
var compareFocus = prevEditedFocus && prevEditedFocus.compareDocumentPosition( q.focusNode );
// is the cDP actually quicker than just checking everything each time?
var compareEditedFocusToAnchor = ( ( compareFocus | comparePosition ) & 6 ) === 6 ?
prevEditedFocus && prevEditedFocus.compareDocumentPosition( q.anchorNode )
:
compareFocus;
var selectionContains = q.containsNode ?
function( elem ) { return q.containsNode( elem ) || elem === startElem || elem === endElem; } :
function( elem ) { // IE
var dup = MSrange.duplicate();
dup.moveToElementText( elem );
return MSrange.inRange( dup ) || elem === startElem;
};
if( highlightedElems.length !== 0 && compareEditedFocusToAnchor === compareFocus ) { // find elems to unhighlight
// console.log( "Find unhighlightable..." );
for( var i = highlightedElems.length, elem; i--; ) {
elem = highlightedElems[ i ];
if( !selectionContains( elem ) ) {
undoThese.splice( i, 1 )[ 0 ]();
highlightedElems.splice( i, 1 );
}
}
}
prevEditedFocus = q.focusNode; prevEditedAnchor = q.anchorNode;
// check if the focus moved in selection's direction (so that it's possible for new elems to be highlighted)...
if( compareFocus === comparePosition || ( compareFocus & 6 ) === 0 ) {
var cAC = range.commonAncestorContainer;
// Find highlightable...
( cAC.nodeType === 3 ? $( startElem ) : $( cAC ).find(".signwritingtext") ).each(function(){ // find potential sw divs
if( highlightedElems.indexOf( this ) === -1 && selectionContains( this ) ) {
var $elem = $( this );
$elem.css( "background-color", "#" + Highlight ).css( "background-image", function(a, original) {
var rep = original.replace( rLineAndFill, lineAndFill );
if( rep !== original ) {
highlightedElems.push( this );
undoThese.push( function(){
// BUG: if style change caused background to change, the undo will reverse the new change
$elem.css( { "background-image" : original, "background-color" : "" } );
});
return rep;
}
})
}
});
}
}
} else { // unhighlight everything
for( ; undoThese.length; ) {
undoThese.shift()();
}
highlightedElems.length = 0;
prevEditedAnchor = prevEditedFocus = undefined;
}
}
$body
.on( "keyup", fixSelection )
.on( "mousedown mouseup keydown", function( e ) { // any other events that can change the selection?
setTimeout( function(){
fixSelection( e );
}, 1 );
});
}
$elem.remove();
})();
wmSupport && (function(){ // Fix webkit anchor-handling bug.
// First, check whether the bug is present. Put together an overflowed div and put it in the body.
var $child, $parent = $( "<div>" ).css( { display: 'inline-block', width: 100, height: 100, overflow: "scroll" } )
.append( $child = $( "<div>" ).css( { margin: 200, height: 200, width: 200 } ) ).appendTo( "body" ),
parent = $parent[ 0 ];
if ( parent.scrollByLines ) { // atm the only browsers to support this are also the only ones that need this check, so...
parent.scrollByLines(1); // which way is "down" in scrolling? try to scroll toward the "bottom" by one line.
if ( parent.scrollTop > parent.scrollLeft ) { // which direction did it scroll? the correct behaviour is rightward.
function anchorFix( $target ) {
$target.length && setTimeout( function(){ // use setTimeout to make sure link-following is done first.
var $headerParent = $target.parent( "h2" );
if ( $headerParent.length ) {
$target = $headerParent;
}
window.scrollTo( $target.offset().left, 0 ); // ... and fix the scroll position.
}, 1);
}
$("[href^=\"#\"]:not([href=\"#\"])").on("click", function(){
var $target = $( this.getAttribute( "href" ) ); // I know this looks like an XSS vector, but it's not.
anchorFix( $target );
});
if ( location.hash ) { // in case the page has a hash at the start
anchorFix( $( location.hash ) ); // seems to sometimes be a few pixels off. dunno why.
}
}
}
$parent.remove();
})();
}
//*/
});
// attach the function to crawl to the DOM to .ready because the page might not be loaded yet
$( document ).ready(function(){signwriting_styled();});
$( document ).ready(function( $ ){ // tooltips
var swt = $("<div>").addClass( "swtooltip" ).appendTo( document.body ),
timer,
rcache = {},
x, y;
$( "body" ).on( "mouseover", "[title]", function(e) { // would manually checking [title] be faster?
var title = this.title, rc = rcache[ title ], onmm = function(e){ x = e.pageX; y = e.pageY; };
if ( rc !== false && ( rc === undefined ? ( rcache[ title ] = r.test( title ) ) : true ) ) {
x = e.pageX; y = e.pageY;
var t = this;
timer && clearTimeout( timer );
if( !rc ) {
rc = rcache[ title ] = $( "<span>" ).text( title );
swt.empty().append( rc );
signwriting_styled( rc[ 0 ] ); // technically unnecessary with the mutationobservers running, but I suspect it might give a slight speed boost
} else {
swt.empty().append( rc ); // todo: remove duplication
}
timer = setTimeout( function(){
swt.css( { top: y + 20, left: x + 1 } ).fadeIn( 50 );
var $w = $( window ),
oTop = swt[ 0 ].offsetHeight, oLeft = swt[ 0 ].offsetWidth,
wTop = $w.scrollTop() + $w.height(), wLeft = $w.scrollLeft() + $w.width(); // Bug: IE flips width and height values.
if( x + oLeft > wLeft ) {
swt.css( { left : wLeft - oLeft } );
}
if( y + oTop > wTop ) {
swt.css( { top : wTop - oTop } );
}
}, 350);
$( t ).data("swtitle", t.title ).removeAttr( "title" ) // make sure the native tooltip doesn't go off
.on( "mousemove", onmm ) // update mouse positions
.one( "mouseleave", function() {
$( t ).attr( "title", $( t ).data( "swtitle" ) ).off( "mousemove", onmm );
timer && clearTimeout( timer );
swt.fadeOut( 0 );
})
}
} );
});
})();