| 1495 | | |
|---|
| 1496 | | [transformedMessage escapeCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\\\"'"]]; |
|---|
| 1497 | | [transformedMessage replaceOccurrencesOfString:@"\n" withString:@"\\n" options:NSLiteralSearch range:NSMakeRange( 0, [transformedMessage length] )]; |
|---|
| 1498 | | [transformedMessage replaceOccurrencesOfString:@" " withString:@" " options:NSLiteralSearch range:NSMakeRange( 0, [transformedMessage length] )]; |
|---|
| 1499 | | if( subsequent ) [display stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"scrollBackLimit = %d; appendConsecutiveMessage( \"%@\" );", scrollbackLimit, transformedMessage]]; |
|---|
| 1500 | | else [display stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"scrollBackLimit = %d; appendMessage( \"%@\" );", scrollbackLimit, transformedMessage]]; |
|---|
| 1501 | | |
|---|
| 1502 | | if( [_currentMessage isHighlighted] && [scroller isKindOfClass:[JVMarkedScroller class]] ) { |
|---|
| 1503 | | unsigned int loc = [[display stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"locationOfMessage( \"%d\" );", ( _messageId - 1 )]] intValue]; |
|---|
| 1504 | | if( loc ) [(JVMarkedScroller *)scroller addMarkAt:loc]; |
|---|
| | 1475 | [self appendMessage:transformedMessage subsequent:subsequent]; |
|---|
| | 1476 | |
|---|
| | 1477 | if( [_currentMessage isHighlighted] ) { |
|---|
| | 1478 | NSScroller *scroller = [[[[[display mainFrame] frameView] documentView] enclosingScrollView] verticalScroller]; |
|---|
| | 1479 | if( [scroller isKindOfClass:[JVMarkedScroller class]] ) { |
|---|
| | 1480 | unsigned int loc = [self locationOfMessage:( _messageId - 1 )]; |
|---|
| | 1481 | if( loc ) [(JVMarkedScroller *)scroller addMarkAt:loc]; |
|---|
| | 1482 | } |
|---|
| | 1501 | } |
|---|
| | 1502 | |
|---|
| | 1503 | - (int) locationOfMessage:(unsigned int) identifier { |
|---|
| | 1504 | if( [[display mainFrame] respondsToSelector:@selector( DOMDocument )] ) { |
|---|
| | 1505 | #ifdef _WEB_SCRIPT_OBJECT_H_ |
|---|
| | 1506 | DOMElement *element = [[[display mainFrame] DOMDocument] getElementById:[NSString stringWithFormat:@"%d", identifier]]; |
|---|
| | 1507 | return [[element valueForKey:@"offsetTop"] intValue]; |
|---|
| | 1508 | #endif |
|---|
| | 1509 | } else { // old JavaScript method |
|---|
| | 1510 | return [[display stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"locationOfMessage( \"%d\" );", identifier]] intValue]; |
|---|
| | 1511 | } |
|---|
| | 1512 | } |
|---|
| | 1513 | |
|---|
| | 1514 | - (int) locationOfElementByIndex:(unsigned int) index { |
|---|
| | 1515 | if( [[display mainFrame] respondsToSelector:@selector( DOMDocument )] ) { |
|---|
| | 1516 | #ifdef _WEB_SCRIPT_OBJECT_H_ |
|---|
| | 1517 | DOMHTMLElement *body = [(DOMHTMLDocument *)[[display mainFrame] DOMDocument] body]; |
|---|
| | 1518 | if( index < [[body children] length] ) return [[[[body children] item:index] valueForKey:@"offsetTop"] intValue]; |
|---|
| | 1519 | else return 0; |
|---|
| | 1520 | #endif |
|---|
| | 1521 | } else { // old JavaScript method |
|---|
| | 1522 | return [[display stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"locationOfElementByIndex( %d );", index]] intValue]; |
|---|
| | 1523 | } |
|---|
| | 1524 | } |
|---|
| | 1525 | |
|---|
| | 1526 | - (void) scrollToBottom { |
|---|
| | 1527 | if( [[display mainFrame] respondsToSelector:@selector( DOMDocument )] ) { |
|---|
| | 1528 | #ifdef _WEB_SCRIPT_OBJECT_H_ |
|---|
| | 1529 | DOMHTMLElement *body = [(DOMHTMLDocument *)[[display mainFrame] DOMDocument] body]; |
|---|
| | 1530 | [body setValue:[body valueForKey:@"offsetHeight"] forKey:@"scrollTop"]; |
|---|
| | 1531 | #endif |
|---|
| | 1532 | } else { |
|---|
| | 1533 | [display stringByEvaluatingJavaScriptFromString:@"scrollToBottom();"]; |
|---|
| | 1534 | } |
|---|
| | 1535 | } |
|---|
| | 1536 | |
|---|
| | 1537 | - (void) appendMessage:(NSString *) html subsequent:(BOOL) subsequent { |
|---|
| | 1538 | unsigned int messageCount = 0; |
|---|
| | 1539 | unsigned int scrollbackLimit = [[NSUserDefaults standardUserDefaults] integerForKey:@"JVChatScrollbackLimit"]; |
|---|
| | 1540 | NSScroller *scroller = [[[[[display mainFrame] frameView] documentView] enclosingScrollView] verticalScroller]; |
|---|
| | 1541 | |
|---|
| | 1542 | if( [[display mainFrame] respondsToSelector:@selector( DOMDocument )] ) { |
|---|
| | 1543 | #ifdef _WEB_SCRIPT_OBJECT_H_ |
|---|
| | 1544 | messageCount = [[[(DOMHTMLDocument *)[[display mainFrame] DOMDocument] body] children] length]; |
|---|
| | 1545 | #endif |
|---|
| | 1546 | } else messageCount = [[display stringByEvaluatingJavaScriptFromString:@"scrollBackMessageCount();"] intValue]; |
|---|
| | 1547 | |
|---|
| | 1548 | if( ( messageCount + 1 ) > scrollbackLimit ) { |
|---|
| | 1549 | int loc = [self locationOfElementByIndex:( ( messageCount + 1 ) - scrollbackLimit )]; |
|---|
| | 1550 | if( loc > 0 && [scroller isKindOfClass:[JVMarkedScroller class]] ) |
|---|
| | 1551 | [(JVMarkedScroller *)scroller shiftMarksAndShadedAreasBy:( loc * -1 )]; |
|---|
| | 1552 | } |
|---|
| | 1553 | |
|---|
| | 1554 | if( [[display mainFrame] respondsToSelector:@selector( DOMDocument )] ) { |
|---|
| | 1555 | #ifdef _WEB_SCRIPT_OBJECT_H_ |
|---|
| | 1556 | DOMHTMLElement *element = (DOMHTMLElement *)[[[display mainFrame] DOMDocument] createElement:@"span"]; |
|---|
| | 1557 | DOMHTMLElement *replaceElement = (DOMHTMLElement *)[[[display mainFrame] DOMDocument] getElementById:@"consecutiveInsert"]; |
|---|
| | 1558 | if( ! replaceElement ) subsequent = NO; |
|---|
| | 1559 | |
|---|
| | 1560 | NSMutableString *transformedMessage = [html mutableCopy]; |
|---|
| | 1561 | [transformedMessage replaceOccurrencesOfString:@" " withString:@" " options:NSLiteralSearch range:NSMakeRange( 0, [transformedMessage length] )]; |
|---|
| | 1562 | [transformedMessage replaceOccurrencesOfString:@"<?message type=\"subsequent\"?>" withString:@"" options:NSLiteralSearch range:NSMakeRange( 0, [transformedMessage length] )]; |
|---|
| | 1563 | |
|---|
| | 1564 | // parses the message so we can get the DOM tree |
|---|
| | 1565 | [element setInnerHTML:transformedMessage]; |
|---|
| | 1566 | |
|---|
| | 1567 | [transformedMessage release]; |
|---|
| | 1568 | transformedMessage = nil; |
|---|
| | 1569 | |
|---|
| | 1570 | // check if we are near the bottom of the chat area, and if we should scroll down later |
|---|
| | 1571 | NSNumber *scrollNeeded = [[[display mainFrame] DOMDocument] evaluateWebScript:@"( document.body.scrollTop >= ( document.body.offsetHeight - ( window.innerHeight * 1.1 ) ) )"]; |
|---|
| | 1572 | DOMHTMLElement *body = [(DOMHTMLDocument *)[[display mainFrame] DOMDocument] body]; |
|---|
| | 1573 | |
|---|
| | 1574 | unsigned int i = 0; |
|---|
| | 1575 | if( ! subsequent ) { // append message normally |
|---|
| | 1576 | [[replaceElement parentNode] removeChild:replaceElement]; |
|---|
| | 1577 | while( [[element children] length] ) // append all children |
|---|
| | 1578 | [body appendChild:[element firstChild]]; |
|---|
| | 1579 | } else if( [[element children] length] >= 1 ) { // append as a subsequent message |
|---|
| | 1580 | DOMNode *parent = [replaceElement parentNode]; |
|---|
| | 1581 | DOMNode *nextSib = [replaceElement nextSibling]; |
|---|
| | 1582 | [parent replaceChild:[element firstChild] :replaceElement]; // replaces the consecutiveInsert node |
|---|
| | 1583 | while( [[element children] length] ) { // append all remaining children (in reverse order) |
|---|
| | 1584 | if( nextSib ) [parent insertBefore:[element firstChild] :nextSib]; |
|---|
| | 1585 | else [parent appendChild:[element firstChild]]; |
|---|
| | 1586 | } |
|---|
| | 1587 | } |
|---|
| | 1588 | |
|---|
| | 1589 | // enforce the scrollback limit |
|---|
| | 1590 | if( scrollbackLimit > 0 && [[body children] length] > scrollbackLimit ) |
|---|
| | 1591 | for( i = 0; [[body children] length] > scrollbackLimit && i < ( [[body children] length] - scrollbackLimit ); i++ ) |
|---|
| | 1592 | [body removeChild:[[body children] item:0]]; |
|---|
| | 1593 | |
|---|
| | 1594 | // scroll down if we need to |
|---|
| | 1595 | if( [scrollNeeded boolValue] ) [self scrollToBottom]; |
|---|
| | 1596 | #endif |
|---|
| | 1597 | } else { // old JavaScript method |
|---|
| | 1598 | NSMutableString *transformedMessage = [html mutableCopy]; |
|---|
| | 1599 | [transformedMessage escapeCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\\\"'"]]; |
|---|
| | 1600 | [transformedMessage replaceOccurrencesOfString:@"\n" withString:@"\\n" options:NSLiteralSearch range:NSMakeRange( 0, [transformedMessage length] )]; |
|---|
| | 1601 | [transformedMessage replaceOccurrencesOfString:@" " withString:@" " options:NSLiteralSearch range:NSMakeRange( 0, [transformedMessage length] )]; |
|---|
| | 1602 | [transformedMessage replaceOccurrencesOfString:@"<?message type=\"subsequent\"?>" withString:@"" options:NSLiteralSearch range:NSMakeRange( 0, [transformedMessage length] )]; |
|---|
| | 1603 | if( subsequent ) [display stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"scrollBackLimit = %d; appendConsecutiveMessage( \"%@\" );", scrollbackLimit, transformedMessage]]; |
|---|
| | 1604 | else [display stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"scrollBackLimit = %d; appendMessage( \"%@\" );", scrollbackLimit, transformedMessage]]; |
|---|
| | 1605 | [transformedMessage release]; |
|---|
| | 1606 | } |
|---|