Updated Occitan translation
[nijm-empathy.git] / data / Template.html
blob295ba8b844f4c522ff55b8db6f1dce0f9d9af104
1 <html>
2 <head>
3 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
4 <base href="%@">
5 <script type="text/javascript" defer="defer">
6 // NOTE:
7 // Any percent signs in this file must be escaped!
8 // Use two escape signs (%%) to display it, this is passed through a format call!
10 function appendHTML(html) {
11 var node = document.getElementById("Chat");
12 var range = document.createRange();
13 range.selectNode(node);
14 var documentFragment = range.createContextualFragment(html);
15 node.appendChild(documentFragment);
18 // a coalesced HTML object buffers and outputs DOM objects en masse.
19 // saves A LOT of CSS recalculation time when loading many messages.
20 // (ex. a long twitter timeline)
21 function CoalescedHTML() {
22 var self = this;
23 this.fragment = document.createDocumentFragment();
24 this.timeoutID = 0;
25 this.coalesceRounds = 0;
26 this.isCoalescing = false;
27 this.isConsecutive = undefined;
28 this.shouldScroll = undefined;
30 var appendElement = function (elem) {
31 document.getElementById("Chat").appendChild(elem);
34 function outputHTML() {
35 var insert = document.getElementById("insert");
36 if(!!insert && self.isConsecutive) {
37 insert.parentNode.replaceChild(self.fragment, insert);
38 } else {
39 if(insert)
40 insert.parentNode.removeChild(insert);
41 // insert the documentFragment into the live DOM
42 appendElement(self.fragment);
44 alignChat(self.shouldScroll);
46 // reset state to empty/non-coalescing
47 self.shouldScroll = undefined;
48 self.isConsecutive = undefined;
49 self.isCoalescing = false;
50 self.coalesceRounds = 0;
53 // creates and returns a new documentFragment, containing all content nodes
54 // which can be inserted as a single node.
55 function createHTMLNode(html) {
56 var range = document.createRange();
57 range.selectNode(document.getElementById("Chat"));
58 return range.createContextualFragment(html);
61 // removes first insert node from the internal fragment.
62 function rmInsertNode() {
63 var insert = self.fragment.querySelector("#insert");
64 if(insert)
65 insert.parentNode.removeChild(insert);
68 function setShouldScroll(flag) {
69 if(flag && undefined === self.shouldScroll)
70 self.shouldScroll = flag;
73 // hook in a custom method to append new data
74 // to the chat.
75 this.setAppendElementMethod = function (func) {
76 if(typeof func === 'function')
77 appendElement = func;
80 // (re)start the coalescing timer.
81 // we wait 25ms for a new message to come in.
82 // If we get one, restart the timer and wait another 10ms.
83 // If not, run outputHTML()
84 // We do this a maximum of 400 times, for 10s max that can be spent
85 // coalescing input, since this will block display.
86 this.coalesce = function() {
87 window.clearTimeout(self.timeoutID);
88 self.timeoutID = window.setTimeout(outputHTML, 25);
89 self.isCoalescing = true;
90 self.coalesceRounds += 1;
91 if(400 < self.coalesceRounds)
92 self.cancel();
95 // if we need to append content into an insertion div,
96 // we need to clear the buffer and cancel the timeout.
97 this.cancel = function() {
98 if(self.isCoalescing) {
99 window.clearTimeout(self.timeoutID);
100 outputHTML();
105 // coalased analogs to the global functions
107 this.append = function(html, shouldScroll) {
108 // if we started this fragment with a consecuative message,
109 // cancel and output before we continue
110 if(self.isConsecutive) {
111 self.cancel();
113 self.isConsecutive = false;
114 rmInsertNode();
115 var node = createHTMLNode(html);
116 self.fragment.appendChild(node);
118 node = null;
120 setShouldScroll(shouldScroll);
121 self.coalesce();
124 this.appendNext = function(html, shouldScroll) {
125 if(undefined === self.isConsecutive)
126 self.isConsecutive = true;
127 var node = createHTMLNode(html);
128 var insert = self.fragment.querySelector("#insert");
129 if(insert) {
130 insert.parentNode.replaceChild(node, insert);
131 } else {
132 self.fragment.appendChild(node);
134 node = null;
135 setShouldScroll(shouldScroll);
136 self.coalesce();
139 this.replaceLast = function (html, shouldScroll) {
140 rmInsertNode();
141 var node = createHTMLNode(html);
142 var lastMessage = self.fragment.lastChild;
143 lastMessage.parentNode.replaceChild(node, lastMessage);
144 node = null;
145 setShouldScroll(shouldScroll);
148 var coalescedHTML;
150 //Appending new content to the message view
151 function appendMessage(html) {
152 var shouldScroll;
154 // Only call nearBottom() if should scroll is undefined.
155 if(undefined === coalescedHTML.shouldScroll) {
156 shouldScroll = nearBottom();
157 } else {
158 shouldScroll = coalescedHTML.shouldScroll;
160 appendMessageNoScroll(html, shouldScroll);
163 function appendMessageNoScroll(html, shouldScroll) {
164 shouldScroll = shouldScroll || false;
165 // always try to coalesce new, non-griuped, messages
166 coalescedHTML.append(html, shouldScroll)
169 function appendNextMessage(html){
170 var shouldScroll;
171 if(undefined === coalescedHTML.shouldScroll) {
172 shouldScroll = nearBottom();
173 } else {
174 shouldScroll = coalescedHTML.shouldScroll;
176 appendNextMessageNoScroll(html, shouldScroll);
179 function appendNextMessageNoScroll(html, shouldScroll){
180 shouldScroll = shouldScroll || false;
181 // only group next messages if we're already coalescing input
182 coalescedHTML.appendNext(html, shouldScroll);
185 function replaceLastMessage(html){
186 var shouldScroll;
187 // only replace messages if we're already coalescing
188 if(coalescedHTML.isCoalescing){
189 if(undefined === coalescedHTML.shouldScroll) {
190 shouldScroll = nearBottom();
191 } else {
192 shouldScroll = coalescedHTML.shouldScroll;
194 coalescedHTML.replaceLast(html, shouldScroll);
195 } else {
196 shouldScroll = nearBottom();
197 //Retrieve the current insertion point, then remove it
198 //This requires that there have been an insertion point... is there a better way to retrieve the last element? -evands
199 var insert = document.getElementById("insert");
200 if(insert){
201 var parentNode = insert.parentNode;
202 parentNode.removeChild(insert);
203 var lastMessage = document.getElementById("Chat").lastChild;
204 document.getElementById("Chat").removeChild(lastMessage);
207 //Now append the message itself
208 appendHTML(html);
210 alignChat(shouldScroll);
214 //Auto-scroll to bottom. Use nearBottom to determine if a scrollToBottom is desired.
215 function nearBottom() {
216 return ( document.body.scrollTop >= ( document.body.offsetHeight - ( window.innerHeight * 1.2 ) ) );
218 function scrollToBottom() {
219 document.body.scrollTop = document.body.offsetHeight;
222 //Dynamically exchange the active stylesheet
223 function setStylesheet( id, url ) {
224 var code = "<style id=\"" + id + "\" type=\"text/css\" media=\"screen,print\">";
225 if( url.length )
226 code += "@import url( \"" + url + "\" );";
227 code += "</style>";
228 var range = document.createRange();
229 var head = document.getElementsByTagName( "head" ).item(0);
230 range.selectNode( head );
231 var documentFragment = range.createContextualFragment( code );
232 head.removeChild( document.getElementById( id ) );
233 head.appendChild( documentFragment );
236 /* Converts emoticon images to textual emoticons; all emoticons in message if alt is held */
237 document.onclick = function imageCheck() {
238 var node = event.target;
239 if (node.tagName.toLowerCase() != 'img')
240 return;
242 imageSwap(node, false);
245 /* Converts textual emoticons to images if textToImagesFlag is true, otherwise vice versa */
246 function imageSwap(node, textToImagesFlag) {
247 var shouldScroll = nearBottom();
249 var images = [node];
250 if (event.altKey) {
251 while (node.id != "Chat" && node.parentNode.id != "Chat")
252 node = node.parentNode;
253 images = node.querySelectorAll(textToImagesFlag ? "a" : "img");
256 for (var i = 0; i < images.length; i++) {
257 textToImagesFlag ? textToImage(images[i]) : imageToText(images[i]);
260 alignChat(shouldScroll);
263 function textToImage(node) {
264 if (!node.getAttribute("isEmoticon"))
265 return;
266 //Swap the image/text
267 var img = document.createElement('img');
268 img.setAttribute('src', node.getAttribute('src'));
269 img.setAttribute('alt', node.firstChild.nodeValue);
270 img.className = node.className;
271 node.parentNode.replaceChild(img, node);
274 function imageToText(node)
276 if (client.zoomImage(node) || !node.alt)
277 return;
278 var a = document.createElement('a');
279 a.setAttribute('onclick', 'imageSwap(this, true)');
280 a.setAttribute('src', node.getAttribute('src'));
281 a.setAttribute('isEmoticon', true);
282 a.className = node.className;
283 var text = document.createTextNode(node.alt);
284 a.appendChild(text);
285 node.parentNode.replaceChild(a, node);
288 //Align our chat to the bottom of the window. If true is passed, view will also be scrolled down
289 function alignChat(shouldScroll) {
290 var windowHeight = window.innerHeight;
292 if (windowHeight > 0) {
293 var contentElement = document.getElementById('Chat');
294 var contentHeight = contentElement.offsetHeight;
295 if (windowHeight - contentHeight > 0) {
296 contentElement.style.position = 'relative';
297 contentElement.style.top = (windowHeight - contentHeight) + 'px';
298 } else {
299 contentElement.style.position = 'static';
303 if (shouldScroll) scrollToBottom();
306 window.onresize = function windowDidResize(){
307 alignChat(true/*nearBottom()*/); //nearBottom buggy with inactive tabs
310 function initStyle() {
311 alignChat(true);
312 if(!coalescedHTML)
313 coalescedHTML = new CoalescedHTML();
315 </script>
317 <style type="text/css">
318 .actionMessageUserName { display:none; }
319 .actionMessageBody:before { content:"*"; }
320 .actionMessageBody:after { content:"*"; }
321 * { word-wrap:break-word; text-rendering: optimizelegibility; }
322 img.scaledToFitImage { height: auto; max-width: 100%%; }
323 </style>
325 <!-- This style is shared by all variants. !-->
326 <style id="baseStyle" type="text/css" media="screen,print">
328 </style>
330 <!-- Although we call this mainStyle for legacy reasons, it's actually the variant style !-->
331 <style id="mainStyle" type="text/css" media="screen,print">
332 @import url( "%@" );
333 </style>
335 </head>
336 <body onload="initStyle();" style="==bodyBackground==">
338 <div id="Chat">
339 </div>
341 </body>
342 </html>