/soc/2015/igor.gajowiak/chatlog: 975639850201: Refactor Template...
Igor Gajowiak <[email protected]>
| Newsgroups | gmane.comp.gnome.gaim.cvs |
|---|---|
| Message-ID | <[email protected]> |
Changeset: 9756398502012874940852ddb5bd78b89968969d Author: Igor Gajowiak <[email protected]> Date: 2015-06-06 22:39 +0200 Branch: default URL: https://hg.pidgin.im/soc/2015/igor.gajowiak/chatlog/rev/975639850201 Description: Refactor Template.html diffstat: pidgin/themes/Contents/Resources/Incoming/Content.html | 1 - pidgin/themes/Template.html | 410 +++++----------- 2 files changed, 125 insertions(+), 286 deletions(-) diffs (truncated from 476 to 300 lines): diff --git a/pidgin/themes/Contents/Resources/Incoming/Content.html b/pidgin/themes/Contents/Resources/Incoming/Content.html --- a/pidgin/themes/Contents/Resources/Incoming/Content.html +++ b/pidgin/themes/Contents/Resources/Incoming/Content.html @@ -5,4 +5,3 @@ </span> <span class="x-message">%message%</span> </div> -<div id="insert"></div> diff --git a/pidgin/themes/Template.html b/pidgin/themes/Template.html --- a/pidgin/themes/Template.html +++ b/pidgin/themes/Template.html @@ -3,293 +3,152 @@ <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <base href="%@"> + <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! - var PURPLE_IMAGE_STORE_PROTOCOL = 'purple-image:'; + function DeferredInvoker(timeout, maxStackSize) { + var self = this; + this.timerID = 0; + this.stackSize = 0; - function appendHTML(html) { - var node = document.getElementById("Chat"); - var range = document.createRange(); - range.selectNode(node); - var documentFragment = range.createContextualFragment(html); - node.appendChild(documentFragment); - } + this.invokeDeferred = function (func) { + window.clearTimeout(self.timerID); + ++self.stackSize; - // 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) { - var 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, shouldScroll) { - shouldScroll = shouldScroll || false; - // always try to coalesce new, non-griuped, messages - coalescedHTML.append(html, shouldScroll) - } - - 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); + if (self.stackSize >= maxStackSize) { + func(); + self.stackSize = 0; + return; } - //Now append the message itself - appendHTML(html); - - alignChat(shouldScroll); + self.timerID = window.setTimeout( + function () { func(); self.stackSize = 0; }, + timeout); } } - var SCROLLMODE_UNKNOWN = 0; - var SCROLLMODE_WEBKIT1 = 1; - var SCROLLMODE_WEBKIT2 = 2; - var scroll_mode = SCROLLMODE_UNKNOWN; + function Scroller() { + var self = this; + var SCROLLMODE_UNKNOWN = 0; + var SCROLLMODE_WEBKIT1 = 1; + var SCROLLMODE_WEBKIT2 = 2; - function detectWebkitScrolling() { - if (scroll_mode != SCROLLMODE_UNKNOWN) - return scroll_mode; - if (document.body.scrollTop > 0) - scroll_mode = SCROLLMODE_WEBKIT1; - if (document.documentElement.scrollTop > 0) - scroll_mode = SCROLLMODE_WEBKIT2; - return scroll_mode; - } - - var stickyscroll_just_scrolled = false; - var stickyscroll_just_scrolled_more = false; - var stickyscroll_to_bottom = true; - var stickyscroll_to_bottom_new = true; - - function windowDidScroll(ev) { - if (stickyscroll_just_scrolled) { - stickyscroll_just_scrolled_more = true; - return; + function detectWebkitScrollingMode() { + if (document.body.scrollTop > 0) + scroll_mode = SCROLLMODE_WEBKIT1; + if (document.documentElement.scrollTop > 0) + scroll_mode = SCROLLMODE_WEBKIT2; + return SCROLLMODE_UNKNOWN; } - stickyscroll_just_scrolled = true; - var update_to_bottom = function() { - stickyscroll_to_bottom = stickyscroll_to_bottom_new; + this.nearBottom = undefined; + this.scrollToBottom = undefined; - var mode = detectWebkitScrolling(); - if (mode == SCROLLMODE_UNKNOWN || mode == SCROLLMODE_WEBKIT1) { - stickyscroll_to_bottom_new = ( document.body.scrollTop >= + // Initialize nearBottom and scrollToBottom + var mode = detectWebkitScrollingMode(); + if (mode == SCROLLMODE_UNKNOWN || mode == SCROLLMODE_WEBKIT1) { + this.nearBottom = function () { + return ( document.body.scrollTop >= ( document.body.offsetHeight - (window.innerHeight + 20) ) ); - } else { /* SCROLLMODE_WEBKIT2 */ - stickyscroll_to_bottom_new = ( document.documentElement.scrollTop >= + } + this.scrollToBottom = function() { + document.body.scrollTop = document.body.offsetHeight; + }; + }