]> git.0d.be Git - empathy.git/commitdiff
Update Template.html from newest adium
authorXavier Claessens <xclaesse@gmail.com>
Mon, 2 May 2011 13:10:39 +0000 (15:10 +0200)
committerXavier Claessens <xclaesse@gmail.com>
Mon, 2 May 2011 13:13:09 +0000 (15:13 +0200)
data/Template.html

index 708e85bdb75e527b2358b5539aa0a7898175e29d..79224b8de5b41c36c7815ce70faa86e07ce96d01 100644 (file)
 <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <base href="%@">
-       <script type="text/ecmascript" defer="defer">
-       
-               //Appending new content to the message view
-               function appendMessage(html) {
-                       shouldScroll = nearBottom();
+       <script type="text/javascript" defer="defer">
+               // NOTE:
+               // Any percent signs in this file must be escaped!
+               // Use two escape signs (%%) to display it, this is passed through a format call!
                
-                       //Remove any existing insertion point
-                       insert = document.getElementById("insert");
-                       if(insert) insert.parentNode.removeChild(insert);
+               function appendHTML(html) {
+                       var node = document.getElementById("Chat");
+                       var range = document.createRange();
+                       range.selectNode(node);
+                       var documentFragment = range.createContextualFragment(html);
+                       node.appendChild(documentFragment);
+               }
+               
+               // a coalesced HTML object buffers and outputs DOM objects en masse.
+               // saves A LOT of CSS recalculation time when loading many messages.
+               // (ex. a long twitter timeline)
+               function CoalescedHTML() {
+                       var self = this;
+                       this.fragment = document.createDocumentFragment();
+                       this.timeoutID = 0;
+                       this.coalesceRounds = 0;
+                       this.isCoalescing = false;
+                       this.isConsecutive = undefined;
+                       this.shouldScroll = undefined;
+                       
+                       var appendElement = function (elem) {
+                               document.getElementById("Chat").appendChild(elem);
+                       };
+                       
+                       function outputHTML() {
+                               var insert = document.getElementById("insert");
+                               if(!!insert && self.isConsecutive) {
+                                       insert.parentNode.replaceChild(self.fragment, insert);
+                               } else {
+                                       if(insert)
+                                               insert.parentNode.removeChild(insert);
+                                       // insert the documentFragment into the live DOM
+                                       appendElement(self.fragment);
+                               }
+                               alignChat(self.shouldScroll);
+                               
+                               // reset state to empty/non-coalescing
+                               self.shouldScroll = undefined;
+                               self.isConsecutive = undefined;
+                               self.isCoalescing = false;
+                               self.coalesceRounds = 0;
+                       }
+                       
+                       // creates and returns a new documentFragment, containing all content nodes
+                       // which can be inserted as a single node.
+                       function createHTMLNode(html) {
+                               var range = document.createRange();
+                               range.selectNode(document.getElementById("Chat"));
+                               return range.createContextualFragment(html);
+                       }
+                       
+                       // removes first insert node from the internal fragment.
+                       function rmInsertNode() {
+                               var insert = self.fragment.querySelector("#insert");
+                               if(insert)
+                                       insert.parentNode.removeChild(insert);
+                       }
+                       
+                       function setShouldScroll(flag) {
+                               if(flag && undefined === self.shouldScroll)
+                                       self.shouldScroll = flag;
+                       }
+                       
+                       // hook in a custom method to append new data
+                       // to the chat.
+                       this.setAppendElementMethod = function (func) {
+                               if(typeof func === 'function')
+                                       appendElement = func;
+                       }
+                                               
+                       // (re)start the coalescing timer.
+                       //   we wait 25ms for a new message to come in.
+                       //   If we get one, restart the timer and wait another 10ms.
+                       //   If not, run outputHTML()
+                       //  We do this a maximum of 400 times, for 10s max that can be spent
+                       //  coalescing input, since this will block display.
+                       this.coalesce = function() {
+                               window.clearTimeout(self.timeoutID);
+                               self.timeoutID = window.setTimeout(outputHTML, 25);
+                               self.isCoalescing = true;
+                               self.coalesceRounds += 1;
+                               if(400 < self.coalesceRounds)
+                                       self.cancel();
+                       }
+                       
+                       // if we need to append content into an insertion div,
+                       // we need to clear the buffer and cancel the timeout.
+                       this.cancel = function() {
+                               if(self.isCoalescing) {
+                                       window.clearTimeout(self.timeoutID);
+                                       outputHTML();
+                               }
+                       }
+                       
+                       
+                       // coalased analogs to the global functions
+                       
+                       this.append = function(html, shouldScroll) {
+                               // if we started this fragment with a consecuative message,
+                               // cancel and output before we continue
+                               if(self.isConsecutive) {
+                                       self.cancel();
+                               }
+                               self.isConsecutive = false;
+                               rmInsertNode();
+                               var node = createHTMLNode(html);
+                               self.fragment.appendChild(node);
+                               
+                               node = null;
 
-                       //Append the new message to the bottom of our chat block
-                       chat = document.getElementById("Chat");
-                       range = document.createRange();
-                       range.selectNode(chat);
-                       documentFragment = range.createContextualFragment(html);
-                       chat.appendChild(documentFragment);
+                               setShouldScroll(shouldScroll);
+                               self.coalesce();
+                       }
                        
-                       alignChat(shouldScroll);
+                       this.appendNext = function(html, shouldScroll) {
+                               if(undefined === self.isConsecutive)
+                                       self.isConsecutive = true;
+                               var node = createHTMLNode(html);
+                               var insert = self.fragment.querySelector("#insert");
+                               if(insert) {
+                                       insert.parentNode.replaceChild(node, insert);
+                               } else {
+                                       self.fragment.appendChild(node);
+                               }
+                               node = null;
+                               setShouldScroll(shouldScroll);
+                               self.coalesce();
+                       }
+                       
+                       this.replaceLast = function (html, shouldScroll) {
+                               rmInsertNode();
+                               var node = createHTMLNode(html);
+                               var lastMessage = self.fragment.lastChild;
+                               lastMessage.parentNode.replaceChild(node, lastMessage);
+                               node = null;
+                               setShouldScroll(shouldScroll);
+                       }
                }
-               function appendMessageNoScroll(html) {
-                       //Remove any existing insertion point
-                       insert = document.getElementById("insert");
-                       if(insert) insert.parentNode.removeChild(insert);
+               var coalescedHTML;
 
-                       //Append the new message to the bottom of our chat block
-                       chat = document.getElementById("Chat");
-                       range = document.createRange();
-                       range.selectNode(chat);
-                       documentFragment = range.createContextualFragment(html);
-                       chat.appendChild(documentFragment);
+               //Appending new content to the message view
+               function appendMessage(html) {
+                       var shouldScroll;
+                       
+                       // Only call nearBottom() if should scroll is undefined.
+                       if(undefined === coalescedHTML.shouldScroll) {
+                               shouldScroll = nearBottom();
+                       } else {
+                               shouldScroll = coalescedHTML.shouldScroll;
+                       }
+                       appendMessageNoScroll(html, shouldScroll);
                }
-               function appendNextMessage(html){
-                       shouldScroll = nearBottom();
-
-                       //Locate the insertion point
-                       insert = document.getElementById("insert");
                
-                       //make new node
-                       range = document.createRange();
-                       range.selectNode(insert.parentNode);
-                       newNode = range.createContextualFragment(html);
-
-                       //swap
-                       insert.parentNode.replaceChild(newNode,insert);
-                       
-                       alignChat(shouldScroll);
+               function appendMessageNoScroll(html, shouldScroll) {                    
+                       shouldScroll = shouldScroll || false;
+                       // always try to coalesce new, non-griuped, messages
+                       coalescedHTML.append(html, shouldScroll)
                }
-               function appendNextMessageNoScroll(html){
-                       //Locate the insertion point
-                       insert = document.getElementById("insert");
                
-                       //make new node
-                       range = document.createRange();
-                       range.selectNode(insert.parentNode);
-                       newNode = range.createContextualFragment(html);
-
-                       //swap
-                       insert.parentNode.replaceChild(newNode,insert);
+               function appendNextMessage(html){
+                       var shouldScroll;
+                       if(undefined === coalescedHTML.shouldScroll) {
+                               shouldScroll = nearBottom();
+                       } else {
+                               shouldScroll = coalescedHTML.shouldScroll;
+                       }
+                       appendNextMessageNoScroll(html, shouldScroll);
                }
                
+               function appendNextMessageNoScroll(html, shouldScroll){
+                       shouldScroll = shouldScroll || false;
+                       // only group next messages if we're already coalescing input
+                       coalescedHTML.appendNext(html, shouldScroll);
+               }
+
+               function replaceLastMessage(html){
+                       var shouldScroll;
+                       // only replace messages if we're already coalescing
+                       if(coalescedHTML.isCoalescing){
+                               if(undefined === coalescedHTML.shouldScroll) {
+                                       shouldScroll = nearBottom();
+                               } else {
+                                       shouldScroll = coalescedHTML.shouldScroll;
+                               }
+                               coalescedHTML.replaceLast(html, shouldScroll);
+                       } else {
+                               shouldScroll = nearBottom();
+                               //Retrieve the current insertion point, then remove it
+                               //This requires that there have been an insertion point... is there a better way to retrieve the last element? -evands
+                               var insert = document.getElementById("insert");
+                               if(insert){
+                                       var parentNode = insert.parentNode;
+                                       parentNode.removeChild(insert);
+                                       var lastMessage = document.getElementById("Chat").lastChild;
+                                       document.getElementById("Chat").removeChild(lastMessage);
+                               }
+
+                               //Now append the message itself
+                               appendHTML(html);
+
+                               alignChat(shouldScroll);
+                       }
+               }
+
                //Auto-scroll to bottom.  Use nearBottom to determine if a scrollToBottom is desired.
                function nearBottom() {
                        return ( document.body.scrollTop >= ( document.body.offsetHeight - ( window.innerHeight * 1.2 ) ) );
 
                //Dynamically exchange the active stylesheet
                function setStylesheet( id, url ) {
-                       code = "<style id=\"" + id + "\" type=\"text/css\" media=\"screen,print\">";
-                       if( url.length ) code += "@import url( \"" + url + "\" );";
+                       var code = "<style id=\"" + id + "\" type=\"text/css\" media=\"screen,print\">";
+                       if( url.length ) 
+                               code += "@import url( \"" + url + "\" );";
                        code += "</style>";
-                       range = document.createRange();
-                       head = document.getElementsByTagName( "head" ).item(0);
+                       var range = document.createRange();
+                       var head = document.getElementsByTagName( "head" ).item(0);
                        range.selectNode( head );
-                       documentFragment = range.createContextualFragment( code );
+                       var documentFragment = range.createContextualFragment( code );
                        head.removeChild( document.getElementById( id ) );
                        head.appendChild( documentFragment );
                }
+
+               /* Converts emoticon images to textual emoticons; all emoticons in message if alt is held */
+               document.onclick = function imageCheck() {
+                       var node = event.target;
+                       if (node.tagName.toLowerCase() != 'img')
+                               return;
+                               
+                       imageSwap(node, false);
+               }
                
-               //Swap an image with its alt-tag text on click, or expand/unexpand an attached image
-               document.onclick = imageCheck;
-               function imageCheck() {         
-                       node = event.target;
-                       if(node.tagName == 'IMG' && !client.zoomImage(node) && node.alt) {
-                               a = document.createElement('a');
-                               a.setAttribute('onclick', 'imageSwap(this)');
-                               a.setAttribute('src', node.getAttribute('src'));
-                               a.className = node.className;
-                               text = document.createTextNode(node.alt);
-                               a.appendChild(text);
-                               node.parentNode.replaceChild(a, node);
+               /* Converts textual emoticons to images if textToImagesFlag is true, otherwise vice versa */
+               function imageSwap(node, textToImagesFlag) {
+                       var shouldScroll = nearBottom();
+                       
+                       var images = [node];
+                       if (event.altKey) {
+                               while (node.id != "Chat" && node.parentNode.id != "Chat")
+                                       node = node.parentNode;
+                               images = node.querySelectorAll(textToImagesFlag ? "a" : "img");
+                       }
+                       
+                       for (var i = 0; i < images.length; i++) {
+                               textToImagesFlag ? textToImage(images[i]) : imageToText(images[i]);
                        }
+                       
+                       alignChat(shouldScroll);
                }
 
-               function imageSwap(node) {
-                       shouldScroll = nearBottom();
-
+               function textToImage(node) {
+                       if (!node.getAttribute("isEmoticon"))
+                               return;
                        //Swap the image/text
-                       img = document.createElement('img');
+                       var img = document.createElement('img');
                        img.setAttribute('src', node.getAttribute('src'));
                        img.setAttribute('alt', node.firstChild.nodeValue);
                        img.className = node.className;
                        node.parentNode.replaceChild(img, node);
-                       
-                       alignChat(shouldScroll);
+               }
+               
+               function imageToText(node)
+               {
+                       if (client.zoomImage(node) || !node.alt)
+                               return;
+                       var a = document.createElement('a');
+                       a.setAttribute('onclick', 'imageSwap(this, true)');
+                       a.setAttribute('src', node.getAttribute('src'));
+                       a.setAttribute('isEmoticon', true);
+                       a.className = node.className;
+                       var text = document.createTextNode(node.alt);
+                       a.appendChild(text);
+                       node.parentNode.replaceChild(a, node);
                }
                
                //Align our chat to the bottom of the window.  If true is passed, view will also be scrolled down
                function alignChat(shouldScroll) {
                        var windowHeight = window.innerHeight;
-                       
+
                        if (windowHeight > 0) {
                                var contentElement = document.getElementById('Chat');
                                var contentHeight = contentElement.offsetHeight;
                                        contentElement.style.position = 'static';
                                }
                        }
-                       
+
                        if (shouldScroll) scrollToBottom();
                }
-               
-               function windowDidResize(){
+
+               window.onresize = function windowDidResize(){
                        alignChat(true/*nearBottom()*/); //nearBottom buggy with inactive tabs
                }
                
-               window.onresize = windowDidResize;
+               function initStyle() {
+                       alignChat(true);
+                       if(!coalescedHTML)
+                               coalescedHTML = new CoalescedHTML();
+               }
        </script>
-       
+
+       <style type="text/css">
+               .actionMessageUserName { display:none; }
+               .actionMessageBody:before { content:"*"; }
+               .actionMessageBody:after { content:"*"; }
+               * { word-wrap:break-word; text-rendering: optimizelegibility; }
+               img.scaledToFitImage { height: auto; max-width: 100%%; }
+       </style>
+
        <!-- This style is shared by all variants. !-->
-       <style id="baseStyle" type="text/css" media="screen,print">     
-               %@  
-               *{ word-wrap:break-word; }
-               img.scaledToFitImage { height:auto; width:100%; }
+       <style id="baseStyle" type="text/css" media="screen,print">
+               %@
        </style>
-       
+
        <!-- Although we call this mainStyle for legacy reasons, it's actually the variant style !-->
-       <style id="mainStyle" type="text/css" media="screen,print">     
+       <style id="mainStyle" type="text/css" media="screen,print">
                @import url( "%@" );
        </style>
 
 </head>
-<body onload="alignChat(true);" style="==bodyBackground==">
+<body onload="initStyle();" style="==bodyBackground==">
 %@
 <div id="Chat">
 </div>