William Bowling is sharing code with you
Bitbucket is a code hosting site. Unlimited public and private repositories. Free for small teams.
Don't show this againadium / Plugins / WebKit Message View / Template.html
- commit
- f7650158eeb5
- parent
- 7a2935ce4146
- branch
- default
Set the max width to 100%, don't scale all images up to 100%.
1 |
e22ad6bc8b46
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> |
2 |
e22ad6bc8b46
|
<html> |
3 |
e22ad6bc8b46
|
<head> |
4 |
e22ad6bc8b46
|
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> |
5 |
e22ad6bc8b46
|
<base href="%@"> |
6 |
1b797e2e9b8e
|
<script type="text/javascript" defer="defer"> |
7 |
73cd8a02f298
|
// NOTE: |
8 |
73cd8a02f298
|
// Any percent signs in this file must be escaped! |
9 |
73cd8a02f298
|
// Use two escape signs (%%) to display it, this is passed through a format call! |
10 |
a4caa7c2401d
|
|
11 |
a4caa7c2401d
|
function appendHTML(html) { |
12 |
a4caa7c2401d
|
var node = document.getElementById("Chat"); |
13 |
a4caa7c2401d
|
var range = document.createRange(); |
14 |
a4caa7c2401d
|
range.selectNode(node); |
15 |
a4caa7c2401d
|
var documentFragment = range.createContextualFragment(html); |
16 |
a4caa7c2401d
|
node.appendChild(documentFragment); |
17 |
a4caa7c2401d
|
} |
18 |
58dcead8d4c0
|
|
19 |
58dcead8d4c0
|
// a coalesced HTML object buffers and outputs DOM objects en masse. |
20 |
58dcead8d4c0
|
// saves A LOT of CSS recalculation time when loading many messages. |
21 |
58dcead8d4c0
|
// (ex. a long twitter timeline) |
22 |
58dcead8d4c0
|
function CoalescedHTML() { |
23 |
58dcead8d4c0
|
var self = this; |
24 |
81552ae2f111
|
this.fragment = document.createDocumentFragment(); |
25 |
58dcead8d4c0
|
this.timeoutID = 0; |
26 |
6c65e4eeb0db
|
this.coalesceRounds = 0; |
27 |
58dcead8d4c0
|
this.isCoalescing = false; |
28 |
9642be635e28
|
this.isConsecutive = undefined; |
29 |
15f5873def89
|
this.shouldScroll = undefined; |
30 |
d8970611ed55
|
|
31 |
58dcead8d4c0
|
function outputHTML() { |
32 |
58dcead8d4c0
|
var insert = document.getElementById("insert"); |
33 |
80187ccccb36
|
if(!!insert && self.isConsecutive) { |
34 |
3627dfff7c3f
|
insert.parentNode.replaceChild(self.fragment, insert); |
35 |
3627dfff7c3f
|
} else { |
36 |
3627dfff7c3f
|
if(insert) |
37 |
3627dfff7c3f
|
insert.parentNode.removeChild(insert); |
38 |
3627dfff7c3f
|
// insert the documentFragment into the live DOM |
39 |
3627dfff7c3f
|
document.getElementById("Chat").appendChild(self.fragment); |
40 |
3627dfff7c3f
|
} |
41 |
58dcead8d4c0
|
alignChat(self.shouldScroll); |
42 |
58dcead8d4c0
|
|
43 |
97a8a57fcef4
|
// reset state to empty/non-coalescing |
44 |
15f5873def89
|
self.shouldScroll = undefined; |
45 |
9642be635e28
|
self.isConsecutive = undefined; |
46 |
58dcead8d4c0
|
self.isCoalescing = false; |
47 |
6c65e4eeb0db
|
self.coalesceRounds = 0; |
48 |
58dcead8d4c0
|
} |
49 |
58dcead8d4c0
|
|
50 |
ea5902712400
|
// creates and returns a new documentFragment, containing all content nodes |
51 |
ea5902712400
|
// which can be inserted as a single node. |
52 |
eedc8ed773b1
|
function createHTMLNode(html) { |
53 |
eadbff5014e6
|
var range = document.createRange(); |
54 |
eadbff5014e6
|
range.selectNode(document.getElementById("Chat")); |
55 |
eadbff5014e6
|
return range.createContextualFragment(html); |
56 |
38433bea7f5e
|
} |
57 |
38433bea7f5e
|
|
58 |
0c12378de034
|
// removes first insert node from the internal fragment. |
59 |
0c12378de034
|
function rmInsertNode() { |
60 |
0c12378de034
|
var insert = self.fragment.querySelector("#insert"); |
61 |
0c12378de034
|
if(insert) |
62 |
0c12378de034
|
insert.parentNode.removeChild(insert); |
63 |
0c12378de034
|
} |
64 |
0c12378de034
|
|
65 |
ea5902712400
|
// (re)start the coalescing timer. |
66 |
687f12da3240
|
// we wait 25ms for a new message to come in. |
67 |
ea5902712400
|
// If we get one, restart the timer and wait another 10ms. |
68 |
ea5902712400
|
// If not, run outputHTML() |
69 |
687f12da3240
|
// We do this a maximum of 400 times, for 10s max that can be spent |
70 |
ea5902712400
|
// coalescing input, since this will block display. |
71 |
58dcead8d4c0
|
this.coalesce = function() { |
72 |
6c65e4eeb0db
|
window.clearTimeout(self.timeoutID); |
73 |
687f12da3240
|
self.timeoutID = window.setTimeout(outputHTML, 25); |
74 |
6c65e4eeb0db
|
self.isCoalescing = true; |
75 |
5fd4260e1b0d
|
self.coalesceRounds += 1; |
76 |
687f12da3240
|
if(400 < self.coalesceRounds) |
77 |
6c65e4eeb0db
|
self.cancel(); |
78 |
58dcead8d4c0
|
} |
79 |
58dcead8d4c0
|
|
80 |
58dcead8d4c0
|
// if we need to append content into an insertion div, |
81 |
58dcead8d4c0
|
// we need to clear the buffer and cancel the timeout. |
82 |
58dcead8d4c0
|
this.cancel = function() { |
83 |
58dcead8d4c0
|
if(self.isCoalescing) { |
84 |
58dcead8d4c0
|
window.clearTimeout(self.timeoutID); |
85 |
58dcead8d4c0
|
outputHTML(); |
86 |
58dcead8d4c0
|
} |
87 |
58dcead8d4c0
|
} |
88 |
58dcead8d4c0
|
|
89 |
ea5902712400
|
|
90 |
ea5902712400
|
// coalased analogs to the global functions |
91 |
ea5902712400
|
|
92 |
58dcead8d4c0
|
this.append = function(html, shouldScroll) { |
93 |
9642be635e28
|
// if we started this fragment with a consecuative message, |
94 |
9642be635e28
|
// cancel and output before we continue |
95 |
80187ccccb36
|
if(self.isConsecutive) { |
96 |
9642be635e28
|
self.cancel(); |
97 |
9642be635e28
|
} |
98 |
9642be635e28
|
self.isConsecutive = false; |
99 |
0c12378de034
|
rmInsertNode(); |
100 |
5fd4260e1b0d
|
var node = createHTMLNode(html); |
101 |
5fd4260e1b0d
|
self.fragment.appendChild(node); |
102 |
1baad7856ddd
|
|
103 |
1baad7856ddd
|
node = null; |
104 |
38433bea7f5e
|
|
105 |
38433bea7f5e
|
if(shouldScroll) self.shouldScroll = shouldScroll; |
106 |
38433bea7f5e
|
self.coalesce(); |
107 |
38433bea7f5e
|
} |
108 |
38433bea7f5e
|
|
109 |
38433bea7f5e
|
this.appendNext = function(html, shouldScroll) { |
110 |
80187ccccb36
|
if(undefined === self.isConsecutive) |
111 |
80187ccccb36
|
self.isConsecutive = true; |
112 |
80187ccccb36
|
var node = createHTMLNode(html); |
113 |
5fd4260e1b0d
|
var insert = self.fragment.querySelector("#insert"); |
114 |
38433bea7f5e
|
if(insert) { |
115 |
70e57c04f788
|
insert.parentNode.replaceChild(node, insert); |
116 |
38433bea7f5e
|
} else { |
117 |
80187ccccb36
|
self.fragment.appendChild(node); |
118 |
58dcead8d4c0
|
} |
119 |
80187ccccb36
|
node = null; |
120 |
38433bea7f5e
|
if(shouldScroll) |
121 |
38433bea7f5e
|
self.shouldScroll = shouldScroll; |
122 |
58dcead8d4c0
|
self.coalesce(); |
123 |
58dcead8d4c0
|
} |
124 |
b4cc4b3566d3
|
|
125 |
b4cc4b3566d3
|
this.replaceLast = function (html, shouldScroll) { |
126 |
0c12378de034
|
rmInsertNode(); |
127 |
b4cc4b3566d3
|
var node = createHTMLNode(html); |
128 |
b4cc4b3566d3
|
var lastMessage = self.fragment.lastChild; |
129 |
b4cc4b3566d3
|
lastMessage.parentNode.replaceChild(node, lastMessage); |
130 |
1baad7856ddd
|
node = null; |
131 |
b4cc4b3566d3
|
if(shouldScroll) |
132 |
b4cc4b3566d3
|
self.shouldScroll = shouldScroll; |
133 |
b4cc4b3566d3
|
} |
134 |
58dcead8d4c0
|
} |
135 |
58dcead8d4c0
|
var coalescedHTML; |
136 |
0f4a11302be7
|
|
137 |
e22ad6bc8b46
|
//Appending new content to the message view |
138 |
e22ad6bc8b46
|
function appendMessage(html) { |
139 |
15f5873def89
|
var shouldScroll; |
140 |
15f5873def89
|
|
141 |
15f5873def89
|
// Only call nearBottom() if should scroll is undefined. |
142 |
15f5873def89
|
if(undefined === coalescedHTML.shouldScroll) { |
143 |
15f5873def89
|
shouldScroll = nearBottom(); |
144 |
15f5873def89
|
} else { |
145 |
15f5873def89
|
shouldScroll = coalescedHTML.shouldScroll; |
146 |
15f5873def89
|
} |
147 |
58dcead8d4c0
|
appendMessageNoScroll(html, shouldScroll); |
148 |
a4caa7c2401d
|
} |
149 |
a4caa7c2401d
|
|
150 |
58dcead8d4c0
|
function appendMessageNoScroll(html, shouldScroll) { |
151 |
58dcead8d4c0
|
shouldScroll = shouldScroll || false; |
152 |
ea5902712400
|
// always try to coalesce new, non-griuped, messages |
153 |
58dcead8d4c0
|
coalescedHTML.append(html, shouldScroll) |
154 |
a4caa7c2401d
|
} |
155 |
a4caa7c2401d
|
|
156 |
a4caa7c2401d
|
function appendNextMessage(html){ |
157 |
15f5873def89
|
var shouldScroll; |
158 |
15f5873def89
|
if(undefined === coalescedHTML.shouldScroll) { |
159 |
15f5873def89
|
shouldScroll = nearBottom(); |
160 |
15f5873def89
|
} else { |
161 |
15f5873def89
|
shouldScroll = coalescedHTML.shouldScroll; |
162 |
15f5873def89
|
} |
163 |
38433bea7f5e
|
appendNextMessageNoScroll(html, shouldScroll); |
164 |
e22ad6bc8b46
|
} |
165 |
a4caa7c2401d
|
|
166 |
38433bea7f5e
|
function appendNextMessageNoScroll(html, shouldScroll){ |
167 |
38433bea7f5e
|
shouldScroll = shouldScroll || false; |
168 |
ea5902712400
|
// only group next messages if we're already coalescing input |
169 |
9642be635e28
|
coalescedHTML.appendNext(html, shouldScroll); |
170 |
e22ad6bc8b46
|
} |
171 |
7d7e889ad3af
|
|
172 |
e22ad6bc8b46
|
function replaceLastMessage(html){ |
173 |
15f5873def89
|
var shouldScroll; |
174 |
ea5902712400
|
// only replace messages if we're already coalescing |
175 |
b4cc4b3566d3
|
if(coalescedHTML.isCoalescing){ |
176 |
15f5873def89
|
if(undefined === coalescedHTML.shouldScroll) { |
177 |
15f5873def89
|
shouldScroll = nearBottom(); |
178 |
15f5873def89
|
} else { |
179 |
15f5873def89
|
shouldScroll = coalescedHTML.shouldScroll; |
180 |
15f5873def89
|
} |
181 |
b4cc4b3566d3
|
coalescedHTML.replaceLast(html, shouldScroll); |
182 |
b4cc4b3566d3
|
} else { |
183 |
15f5873def89
|
shouldScroll = nearBottom(); |
184 |
b4cc4b3566d3
|
//Retrieve the current insertion point, then remove it |
185 |
b4cc4b3566d3
|
//This requires that there have been an insertion point... is there a better way to retrieve the last element? -evands |
186 |
b4cc4b3566d3
|
var insert = document.getElementById("insert"); |
187 |
b4cc4b3566d3
|
if(insert){ |
188 |
b4cc4b3566d3
|
var parentNode = insert.parentNode; |
189 |
b4cc4b3566d3
|
parentNode.removeChild(insert); |
190 |
b4cc4b3566d3
|
var lastMessage = document.getElementById("Chat").lastChild; |
191 |
b4cc4b3566d3
|
document.getElementById("Chat").removeChild(lastMessage); |
192 |
b4cc4b3566d3
|
} |
193 |
e22ad6bc8b46
|
|
194 |
b4cc4b3566d3
|
//Now append the message itself |
195 |
b4cc4b3566d3
|
appendHTML(html); |
196 |
b4cc4b3566d3
|
|
197 |
b4cc4b3566d3
|
alignChat(shouldScroll); |
198 |
be67d382603f
|
} |
199 |
e22ad6bc8b46
|
} |
200 |
7d7e889ad3af
|
|
201 |
e22ad6bc8b46
|
//Auto-scroll to bottom. Use nearBottom to determine if a scrollToBottom is desired. |
202 |
e22ad6bc8b46
|
function nearBottom() { |
203 |
e22ad6bc8b46
|
return ( document.body.scrollTop >= ( document.body.offsetHeight - ( window.innerHeight * 1.2 ) ) ); |
204 |
e22ad6bc8b46
|
} |
205 |
e22ad6bc8b46
|
function scrollToBottom() { |
206 |
e22ad6bc8b46
|
document.body.scrollTop = document.body.offsetHeight; |
207 |
e22ad6bc8b46
|
} |
208 |
e22ad6bc8b46
|
|
209 |
e22ad6bc8b46
|
//Dynamically exchange the active stylesheet |
210 |
e22ad6bc8b46
|
function setStylesheet( id, url ) { |
211 |
e22ad6bc8b46
|
var code = "<style id=\"" + id + "\" type=\"text/css\" media=\"screen,print\">"; |
212 |
e22ad6bc8b46
|
if( url.length ) |
213 |
e22ad6bc8b46
|
code += "@import url( \"" + url + "\" );"; |
214 |
e22ad6bc8b46
|
code += "</style>"; |
215 |
e22ad6bc8b46
|
var range = document.createRange(); |
216 |
e22ad6bc8b46
|
var head = document.getElementsByTagName( "head" ).item(0); |
217 |
e22ad6bc8b46
|
range.selectNode( head ); |
218 |
e22ad6bc8b46
|
var documentFragment = range.createContextualFragment( code ); |
219 |
e22ad6bc8b46
|
head.removeChild( document.getElementById( id ) ); |
220 |
e22ad6bc8b46
|
head.appendChild( documentFragment ); |
221 |
e22ad6bc8b46
|
} |
222 |
0f4a11302be7
|
|
223 |
26d53e9f30c5
|
/* Converts emoticon images to textual emoticons; all emoticons in message if alt is held */ |
224 |
26d53e9f30c5
|
document.onclick = function imageCheck() { |
225 |
a4caa7c2401d
|
var node = event.target; |
226 |
a4caa7c2401d
|
if (node.tagName.toLowerCase() != 'img') |
227 |
a4caa7c2401d
|
return; |
228 |
a4caa7c2401d
|
|
229 |
a4caa7c2401d
|
imageSwap(node, false); |
230 |
a4caa7c2401d
|
} |
231 |
a4caa7c2401d
|
|
232 |
26d53e9f30c5
|
/* Converts textual emoticons to images if textToImagesFlag is true, otherwise vice versa */ |
233 |
a4caa7c2401d
|
function imageSwap(node, textToImagesFlag) { |
234 |
0f4a11302be7
|
var shouldScroll = nearBottom(); |
235 |
a4caa7c2401d
|
|
236 |
a4caa7c2401d
|
var images = [node]; |
237 |
a4caa7c2401d
|
if (event.altKey) { |
238 |
a4caa7c2401d
|
while (node.id != "Chat" && node.parentNode.id != "Chat") |
239 |
a4caa7c2401d
|
node = node.parentNode; |
240 |
a4caa7c2401d
|
images = node.querySelectorAll(textToImagesFlag ? "a" : "img"); |
241 |
83ef5f5390a3
|
} |
242 |
a4caa7c2401d
|
|
243 |
a4caa7c2401d
|
for (var i = 0; i < images.length; i++) { |
244 |
a4caa7c2401d
|
textToImagesFlag ? textToImage(images[i]) : imageToText(images[i]); |
245 |
e22ad6bc8b46
|
} |
246 |
a4caa7c2401d
|
|
247 |
0f4a11302be7
|
alignChat(shouldScroll); |
248 |
e22ad6bc8b46
|
} |
249 |
e22ad6bc8b46
|
|
250 |
a4caa7c2401d
|
function textToImage(node) { |
251 |
a4caa7c2401d
|
if (!node.getAttribute("isEmoticon")) |
252 |
a4caa7c2401d
|
return; |
253 |
a4caa7c2401d
|
//Swap the image/text |
254 |
a4caa7c2401d
|
var img = document.createElement('img'); |
255 |
a4caa7c2401d
|
img.setAttribute('src', node.getAttribute('src')); |
256 |
a4caa7c2401d
|
img.setAttribute('alt', node.firstChild.nodeValue); |
257 |
a4caa7c2401d
|
img.className = node.className; |
258 |
a4caa7c2401d
|
node.parentNode.replaceChild(img, node); |
259 |
a4caa7c2401d
|
} |
260 |
a4caa7c2401d
|
|
261 |
83ef5f5390a3
|
function imageToText(node) |
262 |
83ef5f5390a3
|
{ |
263 |
a4caa7c2401d
|
if (client.zoomImage(node) || !node.alt) |
264 |
a4caa7c2401d
|
return; |
265 |
83ef5f5390a3
|
var a = document.createElement('a'); |
266 |
a4caa7c2401d
|
a.setAttribute('onclick', 'imageSwap(this, true)'); |
267 |
83ef5f5390a3
|
a.setAttribute('src', node.getAttribute('src')); |
268 |
83ef5f5390a3
|
a.setAttribute('isEmoticon', true); |
269 |
83ef5f5390a3
|
a.className = node.className; |
270 |
83ef5f5390a3
|
var text = document.createTextNode(node.alt); |
271 |
83ef5f5390a3
|
a.appendChild(text); |
272 |
83ef5f5390a3
|
node.parentNode.replaceChild(a, node); |
273 |
83ef5f5390a3
|
} |
274 |
83ef5f5390a3
|
|
275 |
e22ad6bc8b46
|
//Align our chat to the bottom of the window. If true is passed, view will also be scrolled down |
276 |
e22ad6bc8b46
|
function alignChat(shouldScroll) { |
277 |
e22ad6bc8b46
|
var windowHeight = window.innerHeight; |
278 |
0f4a11302be7
|
|
279 |
e22ad6bc8b46
|
if (windowHeight > 0) { |
280 |
e22ad6bc8b46
|
var contentElement = document.getElementById('Chat'); |
281 |
e22ad6bc8b46
|
var contentHeight = contentElement.offsetHeight; |
282 |
e22ad6bc8b46
|
if (windowHeight - contentHeight > 0) { |
283 |
e22ad6bc8b46
|
contentElement.style.position = 'relative'; |
284 |
e22ad6bc8b46
|
contentElement.style.top = (windowHeight - contentHeight) + 'px'; |
285 |
e22ad6bc8b46
|
} else { |
286 |
e22ad6bc8b46
|
contentElement.style.position = 'static'; |
287 |
e22ad6bc8b46
|
} |
288 |
e22ad6bc8b46
|
} |
289 |
0f4a11302be7
|
|
290 |
e22ad6bc8b46
|
if (shouldScroll) scrollToBottom(); |
291 |
e22ad6bc8b46
|
} |
292 |
0f4a11302be7
|
|
293 |
26d53e9f30c5
|
window.onresize = function windowDidResize(){ |
294 |
e22ad6bc8b46
|
alignChat(true/*nearBottom()*/); //nearBottom buggy with inactive tabs |
295 |
e22ad6bc8b46
|
} |
296 |
58dcead8d4c0
|
|
297 |
58dcead8d4c0
|
function adiumOnLoad() { |
298 |
58dcead8d4c0
|
alignChat(true); |
299 |
58dcead8d4c0
|
coalescedHTML = new CoalescedHTML(); |
300 |
58dcead8d4c0
|
} |
301 |
e22ad6bc8b46
|
</script> |
302 |
0f4a11302be7
|
|
303 |
e22ad6bc8b46
|
<style type="text/css"> |
304 |
e22ad6bc8b46
|
.actionMessageUserName { display:none; } |
305 |
e22ad6bc8b46
|
.actionMessageBody:before { content:"*"; } |
306 |
e22ad6bc8b46
|
.actionMessageBody:after { content:"*"; } |
307 |
73cd8a02f298
|
hr#focus { border: 0; border-bottom: 1px solid red; width: 25%%; margin: 0 auto 0 auto; } |
308 |
73cd8a02f298
|
* { word-wrap:break-word; } |
309 |
f7650158eeb5
|
img.scaledToFitImage { height: auto; max-width: 100%%; } |
310 |
e22ad6bc8b46
|
</style> |
311 |
0f4a11302be7
|
|
312 |
e22ad6bc8b46
|
<!-- This style is shared by all variants. !--> |
313 |
0f4a11302be7
|
<style id="baseStyle" type="text/css" media="screen,print"> |
314 |
e22ad6bc8b46
|
%@ |
315 |
e22ad6bc8b46
|
</style> |
316 |
0f4a11302be7
|
|
317 |
e22ad6bc8b46
|
<!-- Although we call this mainStyle for legacy reasons, it's actually the variant style !--> |
318 |
0f4a11302be7
|
<style id="mainStyle" type="text/css" media="screen,print"> |
319 |
e22ad6bc8b46
|
@import url( "%@" ); |
320 |
e22ad6bc8b46
|
</style> |
321 |
e22ad6bc8b46
|
|
322 |
e22ad6bc8b46
|
</head> |
323 |
58dcead8d4c0
|
<body onload="adiumOnLoad();" style="==bodyBackground=="> |
324 |
e22ad6bc8b46
|
%@ |
325 |
e22ad6bc8b46
|
<div id="Chat"> |
326 |
e22ad6bc8b46
|
</div> |
327 |
e22ad6bc8b46
|
%@ |
328 |
e22ad6bc8b46
|
</body> |
329 |
e22ad6bc8b46
|
</html> |