1 |
andrew.betts |
6 |
// Set domain at highest level |
2 |
|
|
var domainparts = document.domain.split("."); |
3 |
|
|
document.domain = domainparts[domainparts.length-2]+"."+domainparts[domainparts.length-1]; |
4 |
|
|
|
5 |
|
|
Function.prototype.bind = function(obj) { |
6 |
|
|
var method = this, |
7 |
|
|
temp = function() { |
8 |
|
|
return method.apply(obj, arguments); |
9 |
|
|
}; |
10 |
|
|
return temp; |
11 |
|
|
} |
12 |
|
|
Function.prototype.andThen=function(g) { |
13 |
|
|
var f=this; |
14 |
|
|
var a=this.arguments |
15 |
|
|
return function(args) { |
16 |
|
|
f(a);g(args); |
17 |
|
|
} |
18 |
|
|
}; |
19 |
andrew.betts |
8 |
function addUnLoadEvent(func) { |
20 |
|
|
var oldonunload = window.onunload; |
21 |
|
|
if (typeof window.onunload != 'function') { |
22 |
|
|
window.onunload = func; |
23 |
|
|
} else { |
24 |
|
|
window.onunload = function() { |
25 |
|
|
if (oldonunload) { |
26 |
|
|
oldonunload(); |
27 |
|
|
} |
28 |
|
|
func(); |
29 |
|
|
} |
30 |
|
|
} |
31 |
|
|
} |
32 |
|
|
addUnLoadEvent(meteordestroy); |
33 |
|
|
function meteordestroy() { |
34 |
|
|
var x = Meteor.instances.length; |
35 |
|
|
for(var i=0; i<x; i++) { |
36 |
|
|
if (typeof(Meteor.instances[i].transferDoc)=="object") { |
37 |
|
|
Meteor.instances[i].transferDoc.open(); |
38 |
|
|
Meteor.instances[i].transferDoc.close(); |
39 |
|
|
delete Meteor.instances[i].transferDoc; |
40 |
|
|
} |
41 |
|
|
if (document.getElementById("meteorframe_"+Meteor.instances[i].instID)) { |
42 |
|
|
document.body.removeChild(document.getElementById("meteorframe_"+Meteor.instances[i].instID)); |
43 |
|
|
} |
44 |
|
|
delete Meteor.instances[i]; |
45 |
|
|
} |
46 |
|
|
} |
47 |
andrew.betts |
6 |
|
48 |
|
|
function Meteor(instID) { |
49 |
|
|
|
50 |
|
|
this.lastmsgreceived = -1; |
51 |
|
|
this.transferDoc = false; |
52 |
|
|
this.pingtimer = false; |
53 |
|
|
this.updatepollfreqtimer = false; |
54 |
|
|
this.lastrequest = 0; |
55 |
|
|
this.recvtimes = new Array(); |
56 |
|
|
this.MHostId = false; |
57 |
|
|
this.callback_process = function() {}; |
58 |
|
|
this.callback_reset = function() {}; |
59 |
|
|
this.callback_eof = function() {}; |
60 |
|
|
this.callback_changemode = function() {}; |
61 |
andrew.betts |
7 |
this.callback_statuschanged = function() {}; |
62 |
andrew.betts |
6 |
this.persist = true; |
63 |
andrew.betts |
7 |
this.frameloadtimer = false; |
64 |
|
|
this.frameurl = false; |
65 |
andrew.betts |
6 |
|
66 |
|
|
// Documented public properties |
67 |
|
|
this.channel = false; |
68 |
|
|
this.subdomain = "data"; |
69 |
|
|
this.dynamicpageaddress = "push"; |
70 |
|
|
this.backtrack = 0; |
71 |
|
|
this.smartpoll = true; |
72 |
|
|
this.pollfreq = 2000; |
73 |
|
|
this.minpollfreq = 2000; |
74 |
|
|
this.mode = "stream"; |
75 |
|
|
this.polltimeout=30000; |
76 |
|
|
this.maxmessages=0; |
77 |
|
|
this.pingtimeout = 10000; |
78 |
andrew.betts |
7 |
this.status = 0; |
79 |
andrew.betts |
6 |
|
80 |
andrew.betts |
7 |
/* Statuses: 0 = Uninitialised, |
81 |
|
|
1 = Loading stream, |
82 |
|
|
2 = Loading controller frame, |
83 |
|
|
3 = Controller frame timeout, retrying every 5 seconds |
84 |
|
|
4 = Controller frame loaded and ready |
85 |
|
|
5 = Receiving data |
86 |
|
|
*/ |
87 |
|
|
|
88 |
andrew.betts |
6 |
// Set or retrieve host id. Cookie takes this form: |
89 |
|
|
// MeteorID=123:6356353/124:098320454; |
90 |
|
|
var MeteIds = Meteor.readCookie("MeteorID"); |
91 |
|
|
var regex1 = new RegExp("^([0-9\:\/M]+\/)*"+instID+"\:([^\/]+)(\/[0-9\:\/M]+)*$"); |
92 |
|
|
var regex2 = new RegExp("^([0-9\:\/M]+\/)*M\:([^\/]+)(\/[0-9\:\/M]+)*$"); |
93 |
|
|
if (typeof(instID) == "Number" && regex1.exec(MeteIds)) { |
94 |
|
|
this.MHostId = ma[2]; |
95 |
|
|
} else if (typeof(instID) == "Number") { |
96 |
|
|
this.MHostId = Math.floor(Math.random()*1000000); |
97 |
|
|
var newcookie = (MeteIds)?MeteIds+"/":""; |
98 |
|
|
newcookie += instID+":"+this.MHostId; |
99 |
|
|
Meteor.createCookie("MeteorID", newcookie); |
100 |
|
|
} else if (ma = regex2.exec(MeteIds)) { |
101 |
|
|
this.MHostId = ma[2]; |
102 |
|
|
} else { |
103 |
|
|
this.MHostId = Math.floor(Math.random()*1000000); |
104 |
|
|
var newcookie = (MeteIds)?MeteIds+"/":""; |
105 |
|
|
newcookie += "M:"+this.MHostId; |
106 |
|
|
Meteor.createCookie("MeteorID", newcookie); |
107 |
|
|
} |
108 |
|
|
this.instID = (typeof(instID) != "undefined") ? instID : 0; |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
Meteor.instances = new Array(); |
112 |
|
|
Meteor.servertimeoffset = 0; |
113 |
|
|
|
114 |
|
|
Meteor.create = function(instID) { |
115 |
|
|
if (!instID) instID = 0; |
116 |
|
|
Meteor.instances[instID] = new Meteor(instID); |
117 |
|
|
return Meteor.instances[instID]; |
118 |
|
|
} |
119 |
|
|
|
120 |
|
|
Meteor.register = function(ifr) { |
121 |
|
|
instid = new String(ifr.window.frameElement.id); |
122 |
|
|
instid = instid.replace("meteorframe_", ""); |
123 |
|
|
ifr.p = this.instances[instid].process.bind(this.instances[instid]); |
124 |
|
|
ifr.r = this.instances[instid].reset.bind(this.instances[instid]); |
125 |
|
|
ifr.eof = this.instances[instid].eof.bind(this.instances[instid]); |
126 |
|
|
ifr.get = this.instances[instid].get.bind(this.instances[instid]); |
127 |
|
|
ifr.increasepolldelay = this.instances[instid].increasepolldelay.bind(this.instances[instid]); |
128 |
andrew.betts |
7 |
clearTimeout(this.instances[instid].frameloadtimer); |
129 |
|
|
this.instances[instid].setstatus(4); |
130 |
andrew.betts |
6 |
} |
131 |
|
|
|
132 |
|
|
Meteor.setServerTime = function(timestamp) { |
133 |
|
|
var now = new Date(); |
134 |
|
|
var clienttime = (now.getTime() / 1000); |
135 |
|
|
Meteor.servertimeoffset = timestamp - clienttime; |
136 |
|
|
} |
137 |
|
|
|
138 |
|
|
Meteor.prototype.start = function() { |
139 |
|
|
this.persist = (this.maxmessages)?1:0; |
140 |
|
|
this.smartpoll = (this.smartpoll)?1:0; |
141 |
|
|
this.mode = (this.mode=="stream")?"stream":"poll"; |
142 |
|
|
if (!this.subdomain || !this.channel) throw "Channel or Meteor subdomain host not specified"; |
143 |
|
|
var now = new Date(); |
144 |
|
|
var t = now.getTime(); |
145 |
|
|
if (typeof(this.transferDoc)=="object") { |
146 |
|
|
this.transferDoc.open(); |
147 |
|
|
this.transferDoc.close(); |
148 |
|
|
delete this.transferDoc; |
149 |
|
|
} |
150 |
|
|
if (document.getElementById("meteorframe_"+this.instID)) { |
151 |
|
|
document.body.removeChild(document.getElementById("meteorframe_"+this.instID)); |
152 |
|
|
} |
153 |
|
|
if (this.mode=="stream") { |
154 |
|
|
if (document.all) { |
155 |
andrew.betts |
7 |
this.setstatus(1); |
156 |
andrew.betts |
6 |
this.transferDoc = new ActiveXObject("htmlfile"); |
157 |
|
|
this.transferDoc.open(); |
158 |
|
|
this.transferDoc.write("<html>"); |
159 |
|
|
this.transferDoc.write("<script>document.domain=\""+(document.domain)+"\";</"+"script>"); |
160 |
|
|
this.transferDoc.write("</html>"); |
161 |
|
|
var selfref = this; |
162 |
|
|
this.transferDoc.parentWindow.Meteor = Meteor; |
163 |
|
|
this.transferDoc.close(); |
164 |
|
|
var ifrDiv = this.transferDoc.createElement("div"); |
165 |
|
|
this.transferDoc.appendChild(ifrDiv); |
166 |
|
|
var url = "http://"+this.subdomain+"."+location.hostname+"/"+this.dynamicpageaddress+"?channel="+this.channel+"&id="+this.MHostId; |
167 |
|
|
if (this.lastmsgreceived >= 0) { |
168 |
|
|
url += "&restartfrom="+this.lastmsgreceived; |
169 |
|
|
} else if (this.backtrack > 0) { |
170 |
|
|
url += "&backtrack="+this.backtrack; |
171 |
|
|
} else if (this.backtrack < 0 || isNaN(this.backtrack)) { |
172 |
|
|
url += "&restartfrom="; |
173 |
|
|
} |
174 |
|
|
ifrDiv.innerHTML = "<iframe id=\"meteorframe_"+this.instID+"\" src=\""+url+"&nocache="+t+"\" style=\"display: none;\"></iframe>"; |
175 |
|
|
} else { |
176 |
|
|
var ifr = document.createElement("IFRAME"); |
177 |
|
|
ifr.style.width = "10px"; |
178 |
|
|
ifr.style.height = "10px"; |
179 |
|
|
ifr.style.border = "none"; |
180 |
|
|
ifr.style.position = "absolute"; |
181 |
|
|
ifr.style.top = "-10px"; |
182 |
|
|
ifr.style.marginTop = "-10px"; |
183 |
|
|
ifr.style.zIndex = "-20"; |
184 |
|
|
ifr.id = "meteorframe_"+this.instID; |
185 |
|
|
document.body.appendChild(ifr); |
186 |
andrew.betts |
7 |
this.frameurl = "http://"+this.subdomain+"."+location.hostname+"/stream.html"; |
187 |
|
|
this.frameload(); |
188 |
andrew.betts |
6 |
} |
189 |
|
|
var f = this.pollmode.bind(this); |
190 |
|
|
clearTimeout(this.pingtimer); |
191 |
|
|
this.pingtimer = setTimeout(f, this.pingtimeout); |
192 |
|
|
|
193 |
|
|
} else { |
194 |
|
|
var ifr = document.createElement("IFRAME"); |
195 |
|
|
ifr.style.width = "10px"; |
196 |
|
|
ifr.style.height = "10px"; |
197 |
|
|
ifr.style.border = "none"; |
198 |
|
|
if (document.all) { |
199 |
|
|
ifr.style.display = "none"; |
200 |
|
|
} else { |
201 |
|
|
ifr.style.position = "absolute"; |
202 |
|
|
ifr.style.marginTop = "-10px"; |
203 |
|
|
ifr.style.zIndex = "-20"; |
204 |
|
|
} |
205 |
|
|
ifr.id = "meteorframe_"+this.instID; |
206 |
|
|
document.body.appendChild(ifr); |
207 |
andrew.betts |
7 |
this.frameurl = "http://"+this.subdomain+"."+location.hostname+"/poll.html"; |
208 |
|
|
this.frameload(); |
209 |
andrew.betts |
6 |
this.recvtimes[0] = t; |
210 |
|
|
if (this.updatepollfreqtimer) clearTimeout(this.updatepollfreqtimer); |
211 |
|
|
this.updatepollfreqtimer = setInterval(this.updatepollfreq.bind(this), 2500); |
212 |
|
|
} |
213 |
|
|
this.lastrequest = t; |
214 |
|
|
} |
215 |
|
|
|
216 |
|
|
Meteor.prototype.pollmode = function() { |
217 |
|
|
this.mode="poll"; |
218 |
|
|
this.start(); |
219 |
|
|
this.callback_changemode("poll"); |
220 |
andrew.betts |
7 |
this.lastpingtime = false; |
221 |
andrew.betts |
6 |
} |
222 |
|
|
|
223 |
|
|
Meteor.prototype.process = function(id, data) { |
224 |
|
|
if (id > this.lastmsgreceived) { |
225 |
|
|
this.callback_process(data); |
226 |
|
|
if (id != -1) this.lastmsgreceived = id; |
227 |
|
|
if (this.mode=="poll") { |
228 |
|
|
var now = new Date(); |
229 |
|
|
var t = now.getTime(); |
230 |
|
|
this.recvtimes[this.recvtimes.length] = t; |
231 |
|
|
while (this.recvtimes.length > 5) this.recvtimes.shift(); |
232 |
|
|
} |
233 |
|
|
} else if (id == -1) { |
234 |
|
|
this.ping(); |
235 |
|
|
} |
236 |
andrew.betts |
7 |
this.setstatus(5); |
237 |
andrew.betts |
6 |
} |
238 |
|
|
|
239 |
|
|
Meteor.prototype.ping = function() { |
240 |
|
|
if (this.mode=="stream" && this.pingtimer) { |
241 |
|
|
clearTimeout(this.pingtimer); |
242 |
|
|
var f = this.pollmode.bind(this); |
243 |
|
|
this.pingtimer = setTimeout(f, this.pingtimeout); |
244 |
|
|
var now = new Date(); |
245 |
|
|
this.lastpingtime = now.getTime(); |
246 |
|
|
} |
247 |
andrew.betts |
7 |
this.setstatus(5); |
248 |
andrew.betts |
6 |
} |
249 |
|
|
|
250 |
|
|
Meteor.prototype.reset = function() { |
251 |
|
|
var now = new Date(); |
252 |
|
|
var t = now.getTime(); |
253 |
|
|
var x = this.pollfreq - (t-this.lastrequest); |
254 |
|
|
if (x < 10) x = 10; |
255 |
|
|
this.ping(); |
256 |
|
|
this.callback_reset(); |
257 |
|
|
setTimeout(this.start.bind(this), x); |
258 |
|
|
} |
259 |
|
|
|
260 |
|
|
Meteor.prototype.eof = function() { |
261 |
|
|
this.callback_eof(); |
262 |
|
|
} |
263 |
|
|
|
264 |
|
|
Meteor.prototype.get = function(varname) { |
265 |
|
|
eval("var a = this."+varname+";"); |
266 |
|
|
if (typeof(a) == "undefined") throw "Cannot get value of "+varname; |
267 |
|
|
return a; |
268 |
|
|
} |
269 |
|
|
|
270 |
|
|
Meteor.prototype.increasepolldelay = function() { |
271 |
|
|
this.pollfreq *= 2; |
272 |
|
|
} |
273 |
|
|
|
274 |
|
|
Meteor.prototype.updatepollfreq = function() { |
275 |
|
|
if (this.smartpoll) { |
276 |
|
|
var now = new Date(); |
277 |
|
|
var t = now.getTime(); |
278 |
|
|
var avg = 0; |
279 |
|
|
for (var i=1; i<this.recvtimes.length; i++) { |
280 |
|
|
var x = (this.recvtimes[i]-this.recvtimes[i-1]); |
281 |
|
|
avg += (x>60000)? 60000 : x; |
282 |
|
|
} |
283 |
|
|
x = (t-this.recvtimes[this.recvtimes.length-1]); |
284 |
|
|
avg += (x>180000)? 180000 : x; |
285 |
|
|
avg /= this.recvtimes.length; |
286 |
|
|
if ((avg/3) < this.pollfreq && (avg/3) >= this.minpollfreq) this.pollfreq = Math.ceil(this.pollfreq*0.9); |
287 |
|
|
if ((avg/3) > this.pollfreq) this.pollfreq = Math.floor(this.pollfreq*1.05); |
288 |
|
|
} |
289 |
|
|
} |
290 |
|
|
|
291 |
|
|
Meteor.prototype.registerEventCallback = function(evt, funcRef) { |
292 |
|
|
if (evt=="process") { |
293 |
|
|
this.callback_process = (this.callback_process).andThen(funcRef); |
294 |
|
|
} else if (evt=="reset") { |
295 |
|
|
this.callback_reset = (this.callback_reset).andThen(funcRef); |
296 |
|
|
} else if (evt=="eof") { |
297 |
|
|
this.callback_eof = (this.callback_eof).andThen(funcRef); |
298 |
|
|
} else if (evt=="changemode") { |
299 |
|
|
this.callback_changemode = (this.callback_changemode).andThen(funcRef); |
300 |
andrew.betts |
7 |
} else if (evt=="changestatus") { |
301 |
|
|
this.callback_statuschanged = (this.callback_statuschanged).andThen(funcRef); |
302 |
andrew.betts |
6 |
} |
303 |
|
|
} |
304 |
|
|
|
305 |
andrew.betts |
7 |
Meteor.prototype.frameload = function() { |
306 |
|
|
this.setstatus(2); |
307 |
|
|
if (document.getElementById("meteorframe_"+this.instID)) { |
308 |
|
|
var f = this.frameloadtimeout.bind(this); |
309 |
|
|
this.frameloadtimer = setTimeout(f, 5000); |
310 |
|
|
document.getElementById("meteorframe_"+this.instID).src = "about:blank"; |
311 |
|
|
setTimeout(this.doloadurl.bind(this), 100); |
312 |
|
|
} |
313 |
|
|
} |
314 |
|
|
Meteor.prototype.doloadurl = function() { |
315 |
|
|
var now = new Date(); |
316 |
|
|
var t = now.getTime(); |
317 |
|
|
document.getElementById("meteorframe_"+this.instID).src = this.frameurl+"?nocache="+t; |
318 |
|
|
} |
319 |
|
|
Meteor.prototype.frameloadtimeout = function() { |
320 |
|
|
if (this.frameloadtimer) clearTimeout(this.frameloadtimer); |
321 |
|
|
this.setstatus(3); |
322 |
|
|
this.frameload(); |
323 |
|
|
} |
324 |
|
|
Meteor.prototype.setstatus = function(newstatus) { |
325 |
|
|
if (this.status != newstatus) { |
326 |
|
|
this.status = newstatus; |
327 |
|
|
this.callback_statuschanged(newstatus); |
328 |
|
|
} |
329 |
|
|
} |
330 |
andrew.betts |
6 |
|
331 |
|
|
Meteor.createCookie = function(name,value,days) { |
332 |
|
|
if (days) { |
333 |
|
|
var date = new Date(); |
334 |
|
|
date.setTime(date.getTime()+(days*24*60*60*1000)); |
335 |
|
|
var expires = "; expires="+date.toGMTString(); |
336 |
|
|
} |
337 |
|
|
else var expires = ""; |
338 |
|
|
document.cookie = name+"="+value+expires+"; path=/"; |
339 |
|
|
} |
340 |
|
|
|
341 |
|
|
Meteor.readCookie = function(name) { |
342 |
|
|
var nameEQ = name + "="; |
343 |
|
|
var ca = document.cookie.split(';'); |
344 |
|
|
for(var i=0;i < ca.length;i++) { |
345 |
|
|
var c = ca[i]; |
346 |
|
|
while (c.charAt(0)==' ') c = c.substring(1,c.length); |
347 |
|
|
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); |
348 |
|
|
} |
349 |
|
|
return null; |
350 |
|
|
} |
351 |
|
|
|
352 |
|
|
Meteor.eraseCookie = function(name) { |
353 |
|
|
createCookie(name,"",-1); |
354 |
andrew.betts |
3 |
} |