2016-05-01 11:20:39 +07:00
var Spade = function Spade ( ) {
this . stack = [ ] ;
}
Spade . prototype = {
track : function ( _elem ) {
this . target = _elem ;
var spade = this ;
var el = document . createElement ( "div" ) ;
keyHook = null ;
if ( _elem . textInput && _elem . textInput . getElement ) {
keyHook = _elem . textInput . getElement ( ) ;
} else {
keyHook = _elem ;
}
keyHook . addEventListener ( "keydown" , function ( _event ) { spade . createEvent ( spade . target ) } ) ;
//Maybe this is needed depending on Firefox/other browsers? Duplicate non-diff events get compiled down.
keyHook . addEventListener ( "keyup" , function ( _event ) { spade . createEvent ( spade . target ) } ) ;
_elem . addEventListener ( "mouseup" , function ( _event ) { spade . createEvent ( spade . target ) } ) ;
} ,
createEvent : function ( _target ) {
if ( _target . getValue ) {
this . stack . push ( {
"startPos" : _target . selection . getCursor ( ) ,
"endPos" : _target . selection . getSelectionAnchor ( ) ,
"content" : _target . getValue ( ) ,
"timestamp" : ( new Date ( ) ) . getTime ( )
} ) ;
} else {
this . stack . push ( {
"startPos" : _target . selectionStart ,
"endPos" : _target . selectionEnd ,
"content" : _target . value ,
"timestamp" : ( new Date ( ) ) . getTime ( )
} ) ;
}
} ,
compile : function ( ) {
var compiledStack = [ ] ;
if ( this . stack . length > 0 ) {
var startTime = this . stack [ 0 ] . timestamp ;
var sum = 0 ;
var sum2 = 0 ;
for ( var i = 0 ; i < this . stack . length ; i ++ ) {
var c = this . stack [ i ] ;
var adjustedTimestamp = c . timestamp - startTime ;
var tString = "" ; //The changed string.
var fIndex = null ; //The first index of changes.
var eIndex = null ; //The last index of changes.
var dCount = 0 ; //Amount of character changes.
if ( i >= 1 ) {
var p = this . stack [ i - 1 ] ;
var isOkay = false ;
for ( var key in p ) {
if ( key != "timestamp" ) {
if ( typeof p [ key ] === "string" ) {
if ( p [ key ] !== c [ key ] ) {
isOkay = true ;
}
} else {
for ( var key2 in p [ key ] ) {
if ( c [ key ] [ key2 ] !== undefined ) {
if ( p [ key ] [ key2 ] !== c [ key ] [ key2 ] ) {
isOkay = true ;
}
} else {
console . warn ( "Warning: c[key][key2] doesn't exist, but p[key][key2] does." ) ;
isOkay = true ;
}
}
}
}
}
if ( ! isOkay ) {
sum2 ++ ;
continue ;
}
sum ++ ;
if ( p . content != c . content ) {
//Check from the start to the end, which characters are different.
for ( var j = 0 ; j < Math . max ( p . content . length , c . content . length ) ; j ++ ) {
if ( p . content . charAt ( j ) === c . content . charAt ( j ) ) {
if ( fIndex != null ) {
tString += c . content . charAt ( j ) ;
dCount ++ ;
}
} else {
tString += c . content . charAt ( j ) ;
if ( fIndex === null ) {
fIndex = j ;
}
dCount ++ ;
}
}
//Check from the end to the start, which characters are different.
for ( var j = 0 ; j < Math . min ( p . content . length , c . content . length ) - fIndex ; j ++ ) {
if ( p . content . charAt ( p . content . length - 1 - j ) !== c . content . charAt ( c . content . length - 1 - j ) ) {
if ( eIndex == null ) {
eIndex = j ;
break ;
}
}
}
//This accounts for the fact when changing from "aa" to "aaa" (for example).
if ( eIndex === null ) {
eIndex = Math . min ( p . content . length , c . content . length ) - fIndex ;
}
tString = tString . substring ( 0 , tString . length - eIndex ) ;
}
} else {
tString = c . content ;
fIndex = 0 ;
eIndex = tString . length ;
}
compiledStack . push ( {
"timestamp" : adjustedTimestamp ,
"difContent" : tString ,
"difFIndex" : fIndex ,
"difEIndex" : eIndex ,
"selFIndex" : c . startPos ,
"selEIndex" : c . endPos
} ) ;
}
} else {
//Just return the empty array.
}
return compiledStack ;
} ,
play : function ( _stack , _elem ) {
if ( _stack . length === 0 ) {
console . warn ( "SPADE: No events to play." )
return
}
if ( _elem . setValue ) {
_elem . setValue ( _stack [ 0 ] . difContent ) ;
} else {
_elem . value = _stack [ 0 ] . difContent
}
_stack = _stack . slice ( ) ;
_stack . shift ( ) ;
var curTime , dTime ;
var elapsedTime = 0 ;
var prevTime = ( new Date ( ) ) . getTime ( ) ;
var playbackInterval = setInterval ( function ( ) {
curTime = ( new Date ( ) ) . getTime ( ) ;
dTime = curTime - prevTime ;
dTime *= 1 ; //Multiply for faster/slower playback speeds.
elapsedTime += dTime ;
var tArray = _stack . filter ( function ( _event ) {
return ( ( _event . timestamp ) >= ( elapsedTime - dTime ) ) && ( ( _event . timestamp ) < ( elapsedTime ) ) ;
} ) ;
for ( var i = 0 ; i < tArray . length ; i ++ ) {
var tEvent = tArray [ i ] ;
var oVal = null ;
if ( _elem . getValue ) {
oVal = _elem . getValue ( ) ;
} else {
oVal = _elem . value ;
}
if ( tEvent . difFIndex !== null && tEvent . difEIndex !== null ) {
if ( _elem . setValue ) {
_elem . setValue ( oVal . substring ( 0 , tEvent . difFIndex ) + tEvent . difContent + oVal . substring ( oVal . length - tEvent . difEIndex , oVal . length ) ) ;
} else {
_elem . value = oVal . substring ( 0 , tEvent . difFIndex ) + tEvent . difContent + oVal . substring ( oVal . length - tEvent . difEIndex , oVal . length )
}
}
if ( _elem . selection && _elem . selection . moveCursorToPosition ) {
//Maybe this will work someday
_elem . selection . moveCursorToPosition ( tEvent . selFIndex ) ;
_elem . selection . setSelectionAnchor ( tEvent . selEIndex . row , tEvent . selEIndex . column ) ;
_elem . selection . selectTo ( tEvent . selFIndex . row , tEvent . selFIndex . column ) ;
} else {
//Likewise
_elem . focus ( ) ;
_elem . setSelectionRange ( tEvent . selFIndex , tEvent . selEIndex ) ;
}
}
if ( _stack [ _stack . length - 1 ] === undefined || elapsedTime > _stack [ _stack . length - 1 ] . timestamp ) {
clearInterval ( playbackInterval ) ;
}
prevTime = curTime ;
} , 10 ) ;
} ,
debugPlay : function ( _stack ) {
var area = document . createElement ( 'textarea' ) ;
area . zIndex = 9999 ;
area . style . width = "512px" ;
area . style . height = "512px" ;
area . style . position = "absolute" ;
area . style . left = "100px" ;
area . style . top = "100px" ;
document . body . appendChild ( area ) ;
this . play ( _stack , area ) ;
} ,
condense : function ( _stack ) {
var compressedArray = [ ] ;
for ( var i = 0 ; i < _stack . length ; i ++ ) {
var u = _stack [ i ] ;
compressedArray . push ( [
u . timestamp ,
u . difContent ,
u . difFIndex ,
u . difEIndex ,
u . selFIndex . row ,
u . selFIndex . column ,
u . selEIndex . row ,
u . selEIndex . column
] ) ;
}
return compressedArray ;
} ,
expand : function ( _array ) {
var uncompressedArray = [ ] ;
for ( var i = 0 ; i < _array . length ; i ++ ) {
var c = _array [ i ] ;
uncompressedArray . push ( {
"timestamp" : c [ 0 ] ,
"difContent" : c [ 1 ] ,
"difFIndex" : c [ 2 ] ,
"difEIndex" : c [ 3 ] ,
"selFIndex" : {
"row" : c [ 4 ] ,
"column" : c [ 5 ]
} ,
"selEIndex" : {
"row" : c [ 6 ] ,
"column" : c [ 7 ]
} ,
} ) ;
}
return uncompressedArray ;
}
}