pidgin: 2cf562c8: Update Template.html from Adium's hg rep...
qulogic at pidgin.im
qulogic at pidgin.im
Wed Jan 4 02:08:22 EST 2012
----------------------------------------------------------------------
Revision: 2cf562c8c0960df4851d120d135b9add9df8d3b0
Parent: c42a020b96e47b7e4b3e9e355aa5e652d8ab4de7
Author: qulogic at pidgin.im
Date: 01/03/12 19:44:36
Branch: im.pidgin.pidgin
URL: http://d.pidgin.im/viewmtn/revision/info/2cf562c8c0960df4851d120d135b9add9df8d3b0
Changelog:
Update Template.html from Adium's hg repo.
Changes against parent c42a020b96e47b7e4b3e9e355aa5e652d8ab4de7
patched pidgin/themes/Template.html
-------------- next part --------------
============================================================
--- pidgin/themes/Template.html fa4eb32aeafcc92c5d3ba217a3c44c3674a1d88e
+++ pidgin/themes/Template.html 3da2bbf3eeae509fb346acf7f4c2c1ac87d8522f
@@ -3,66 +3,215 @@
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<base href="%@">
- <script type="text/ecmascript" defer="defer">
-
+ <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!
+
+ 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;
+
+ setShouldScroll(shouldScroll);
+ self.coalesce();
+ }
+
+ 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);
+ }
+ }
+ var coalescedHTML;
+
//Appending new content to the message view
function appendMessage(html) {
- shouldScroll = nearBottom();
-
- //Remove any existing insertion point
- insert = document.getElementById("insert");
- if(insert) insert.parentNode.removeChild(insert);
+ var shouldScroll;
- //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);
-
- alignChat(shouldScroll);
+ // Only call nearBottom() if should scroll is undefined.
+ if(undefined === coalescedHTML.shouldScroll) {
+ shouldScroll = nearBottom();
+ } else {
+ shouldScroll = coalescedHTML.shouldScroll;
+ }
+ appendMessageNoScroll(html, shouldScroll);
}
- function appendMessageNoScroll(html) {
- //Remove any existing insertion point
- insert = document.getElementById("insert");
- if(insert) insert.parentNode.removeChild(insert);
- //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);
+ function appendMessageNoScroll(html, shouldScroll) {
+ shouldScroll = shouldScroll || false;
+ // always try to coalesce new, non-griuped, messages
+ coalescedHTML.append(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);
+ function appendNextMessage(html){
+ var shouldScroll;
+ if(undefined === coalescedHTML.shouldScroll) {
+ shouldScroll = nearBottom();
+ } else {
+ shouldScroll = coalescedHTML.shouldScroll;
+ }
+ appendNextMessageNoScroll(html, shouldScroll);
+ }
- //swap
- insert.parentNode.replaceChild(newNode,insert);
-
- alignChat(shouldScroll);
+ function appendNextMessageNoScroll(html, shouldScroll){
+ shouldScroll = shouldScroll || false;
+ // only group next messages if we're already coalescing input
+ coalescedHTML.appendNext(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 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 ) ) );
@@ -73,89 +222,119 @@
//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 );
}
-
- //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 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);
+ }
+
+ /* 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;
- if (windowHeight - contentHeight > 0) {
+ var heightDifference = (windowHeight - contentElement.offsetHeight);
+ if (heightDifference > 0) {
contentElement.style.position = 'relative';
- contentElement.style.top = (windowHeight - contentHeight) + 'px';
+ contentElement.style.top = heightDifference + 'px';
} else {
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:before { content:"*"; }
+ .actionMessageUserName { display:none; }
+ .actionMessageBody:before { content:"*"; }
.actionMessageBody:after { content:"*"; }
- *{ word-wrap:break-word; }
- img.scaledToFitImage { height:auto; width:100%; }
+ * { 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">
+ <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>
More information about the Commits
mailing list