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 again

wbowling / adium (fork of adium / adium)

Fork of Adium for patches/improvements

Clone this repository (size: 338.7 MB): HTTPS / SSH
hg clone https://bitbucket.org/wbowling/adium
hg clone ssh://hg@bitbucket.org/wbowling/adium

adium / 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>