1 |
dpavlin |
134 |
/* |
2 |
|
|
|
3 |
|
|
Created By: Corey Johnson |
4 |
|
|
E-mail: probablyCorey@gmail.com |
5 |
|
|
|
6 |
|
|
Requires: Prototype Javascript library (http://prototype.conio.net/) |
7 |
|
|
|
8 |
|
|
Use it all you want. Just remember to give me some credit :) |
9 |
|
|
|
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
// ------------ |
13 |
|
|
// Custom Event |
14 |
|
|
// ------------ |
15 |
|
|
|
16 |
|
|
CustomEvent = Class.create() |
17 |
|
|
CustomEvent.prototype = { |
18 |
|
|
initialize : function() { |
19 |
|
|
this.listeners = [] |
20 |
|
|
}, |
21 |
|
|
|
22 |
|
|
addListener : function(method) { |
23 |
|
|
this.listeners.push(method) |
24 |
|
|
}, |
25 |
|
|
|
26 |
|
|
removeListener : function(method) { |
27 |
|
|
var foundIndexes = this._findListenerIndexes(method) |
28 |
|
|
|
29 |
|
|
for(var i = 0; i < foundIndexes.length; i++) { |
30 |
|
|
this.listeners.splice(foundIndexes[i], 1) |
31 |
|
|
} |
32 |
|
|
}, |
33 |
|
|
|
34 |
|
|
dispatch : function(handler) { |
35 |
|
|
for(var i = 0; i < this.listeners.length; i++) { |
36 |
|
|
try { |
37 |
|
|
this.listeners[i](handler) |
38 |
|
|
} |
39 |
|
|
catch (e) { |
40 |
|
|
alert("Could not run the listener " + this.listeners[i] + ". " + e) |
41 |
|
|
} |
42 |
|
|
} |
43 |
|
|
}, |
44 |
|
|
|
45 |
|
|
// Private Methods |
46 |
|
|
// --------------- |
47 |
|
|
_findListenerIndexes : function(method) { |
48 |
|
|
var indexes = [] |
49 |
|
|
for(var i = 0; i < this.listeners.length; i++) { |
50 |
|
|
if (this.listeners[i] == method) { |
51 |
|
|
indexes.push(i) |
52 |
|
|
} |
53 |
|
|
} |
54 |
|
|
|
55 |
|
|
return indexes |
56 |
|
|
} |
57 |
|
|
} |
58 |
|
|
|
59 |
|
|
// ------ |
60 |
|
|
// Cookie |
61 |
|
|
// ------ |
62 |
|
|
|
63 |
|
|
var Cookie = { |
64 |
|
|
set : function(name, value, expirationInDays, path) { |
65 |
|
|
var cookie = escape(name) + "=" + escape(value) |
66 |
|
|
|
67 |
|
|
if (expirationInDays) { |
68 |
|
|
var date = new Date() |
69 |
|
|
date.setDate(date.getDate() + expirationInDays) |
70 |
|
|
cookie += "; expires=" + date.toGMTString() |
71 |
|
|
} |
72 |
|
|
|
73 |
|
|
if (path) { |
74 |
|
|
cookie += ";path=" + path |
75 |
|
|
} |
76 |
|
|
|
77 |
|
|
document.cookie = cookie |
78 |
|
|
|
79 |
|
|
if (value && (expirationInDays == undefined || expirationInDays > 0) && !this.get(name)) { |
80 |
|
|
Logger.error("Cookie (" + name + ") was not set correctly... The value was " + value.toString().length + " charachters long (This may be over the cookie limit)"); |
81 |
|
|
} |
82 |
|
|
}, |
83 |
|
|
|
84 |
|
|
get : function(name) { |
85 |
|
|
var pattern = "(^|;)\\s*" + escape(name) + "=([^;]+)" |
86 |
|
|
|
87 |
|
|
var m = document.cookie.match(pattern) |
88 |
|
|
if (m && m[2]) { |
89 |
|
|
return unescape(m[2]) |
90 |
|
|
} |
91 |
|
|
else return null |
92 |
|
|
}, |
93 |
|
|
|
94 |
|
|
getAll : function() { |
95 |
|
|
var cookies = document.cookie.split(';') |
96 |
|
|
var cookieArray = [] |
97 |
|
|
|
98 |
|
|
for (var i = 0; i < cookies.length; i++) { |
99 |
|
|
try { |
100 |
|
|
var name = unescape(cookies[i].match(/^\s*([^=]+)/m)[1]) |
101 |
|
|
var value = unescape(cookies[i].match(/=(.*$)/m)[1]) |
102 |
|
|
} |
103 |
|
|
catch (e) { |
104 |
|
|
continue |
105 |
|
|
} |
106 |
|
|
|
107 |
|
|
cookieArray.push({name : name, value : value}) |
108 |
|
|
|
109 |
|
|
if (cookieArray[name] != undefined) { |
110 |
|
|
Logger.waring("Trying to retrieve cookie named(" + name + "). There appears to be another property with this name though."); |
111 |
|
|
} |
112 |
|
|
|
113 |
|
|
cookieArray[name] = value |
114 |
|
|
} |
115 |
|
|
|
116 |
|
|
return cookieArray |
117 |
|
|
}, |
118 |
|
|
|
119 |
|
|
clear : function(name) { |
120 |
|
|
this.set(name, "", -1) |
121 |
|
|
}, |
122 |
|
|
|
123 |
|
|
clearAll : function() { |
124 |
|
|
var cookies = this.getAll() |
125 |
|
|
|
126 |
|
|
for(var i = 0; i < cookies.length; i++) { |
127 |
|
|
this.clear(cookies[i].name) |
128 |
|
|
} |
129 |
|
|
|
130 |
|
|
} |
131 |
|
|
} |
132 |
|
|
|
133 |
|
|
// ------ |
134 |
|
|
// Logger |
135 |
|
|
// ----- |
136 |
|
|
|
137 |
|
|
Logger = { |
138 |
|
|
logEntries : [], |
139 |
|
|
|
140 |
|
|
onupdate : new CustomEvent(), |
141 |
|
|
onclear : new CustomEvent(), |
142 |
|
|
|
143 |
|
|
|
144 |
|
|
// Logger output |
145 |
|
|
log : function(message, tag) { |
146 |
|
|
var logEntry = new LogEntry(message, tag || "info") |
147 |
|
|
this.logEntries.push(logEntry) |
148 |
|
|
this.onupdate.dispatch(logEntry) |
149 |
|
|
}, |
150 |
|
|
|
151 |
|
|
info : function(message) { |
152 |
|
|
this.log(message, 'info') |
153 |
|
|
}, |
154 |
|
|
|
155 |
|
|
debug : function(message) { |
156 |
|
|
this.log(message, 'debug') |
157 |
|
|
}, |
158 |
|
|
|
159 |
|
|
warn : function(message) { |
160 |
|
|
this.log(message, 'warning') |
161 |
|
|
}, |
162 |
|
|
|
163 |
|
|
error : function(message, error) { |
164 |
|
|
this.log(message + ": \n" + error, 'error') |
165 |
|
|
}, |
166 |
|
|
|
167 |
|
|
clear : function () { |
168 |
|
|
this.logEntries = [] |
169 |
|
|
this.onclear.dispatch() |
170 |
|
|
} |
171 |
|
|
} |
172 |
|
|
|
173 |
|
|
LogEntry = Class.create() |
174 |
|
|
LogEntry.prototype = { |
175 |
|
|
initialize : function(message, tag) { |
176 |
|
|
this.message = message |
177 |
|
|
this.tag = tag |
178 |
|
|
} |
179 |
|
|
} |
180 |
|
|
|
181 |
|
|
LogConsole = Class.create() |
182 |
|
|
LogConsole.prototype = { |
183 |
|
|
|
184 |
|
|
// Properties |
185 |
|
|
// ---------- |
186 |
|
|
commandHistory : [], |
187 |
|
|
commandIndex : 0, |
188 |
|
|
|
189 |
|
|
// Methods |
190 |
|
|
// ------- |
191 |
|
|
|
192 |
|
|
initialize : function() { |
193 |
|
|
this.outputCount = 0 |
194 |
|
|
this.tagPattern = Cookie.get('tagPattern') || ".*" |
195 |
|
|
|
196 |
|
|
// I hate writing javascript in HTML... but what's a better alternative |
197 |
|
|
this.logElement = document.createElement('div') |
198 |
|
|
document.body.appendChild(this.logElement) |
199 |
|
|
Element.hide(this.logElement) |
200 |
|
|
|
201 |
|
|
this.logElement.style.position = "absolute" |
202 |
|
|
this.logElement.style.left = '0px' |
203 |
|
|
this.logElement.style.width = '100%' |
204 |
|
|
|
205 |
|
|
this.logElement.style.textAlign = "left" |
206 |
|
|
this.logElement.style.fontFamily = "lucida console" |
207 |
|
|
this.logElement.style.fontSize = "100%" |
208 |
|
|
this.logElement.style.backgroundColor = 'darkgray' |
209 |
|
|
this.logElement.style.opacity = 0.9 |
210 |
|
|
this.logElement.style.zIndex = 2000 |
211 |
|
|
|
212 |
|
|
// Add toolbarElement |
213 |
|
|
this.toolbarElement = document.createElement('div') |
214 |
|
|
this.logElement.appendChild(this.toolbarElement) |
215 |
|
|
this.toolbarElement.style.padding = "0 0 0 2px" |
216 |
|
|
|
217 |
|
|
// Add buttons |
218 |
|
|
this.buttonsContainerElement = document.createElement('span') |
219 |
|
|
this.toolbarElement.appendChild(this.buttonsContainerElement) |
220 |
|
|
|
221 |
|
|
this.buttonsContainerElement.innerHTML += '<button onclick="logConsole.toggle()" style="float:right;color:black">close</button>' |
222 |
|
|
this.buttonsContainerElement.innerHTML += '<button onclick="Logger.clear()" style="float:right;color:black">clear</button>' |
223 |
|
|
|
224 |
|
|
|
225 |
|
|
//Add Tag Filter |
226 |
|
|
this.tagFilterContainerElement = document.createElement('span') |
227 |
|
|
this.toolbarElement.appendChild(this.tagFilterContainerElement) |
228 |
|
|
this.tagFilterContainerElement.style.cssFloat = 'left' |
229 |
|
|
this.tagFilterContainerElement.appendChild(document.createTextNode("Log Filter")) |
230 |
|
|
|
231 |
|
|
this.tagFilterElement = document.createElement('input') |
232 |
|
|
this.tagFilterContainerElement.appendChild(this.tagFilterElement) |
233 |
|
|
this.tagFilterElement.style.width = '200px' |
234 |
|
|
this.tagFilterElement.value = this.tagPattern |
235 |
|
|
this.tagFilterElement.setAttribute('autocomplete', 'off') // So Firefox doesn't flip out |
236 |
|
|
|
237 |
|
|
Event.observe(this.tagFilterElement, 'keyup', this.updateTags.bind(this)) |
238 |
|
|
Event.observe(this.tagFilterElement, 'click', function() {this.tagFilterElement.select()}.bind(this)) |
239 |
|
|
|
240 |
|
|
// Add outputElement |
241 |
|
|
this.outputElement = document.createElement('div') |
242 |
|
|
this.logElement.appendChild(this.outputElement) |
243 |
|
|
this.outputElement.style.overflow = "auto" |
244 |
|
|
this.outputElement.style.clear = "both" |
245 |
|
|
this.outputElement.style.height = "200px" |
246 |
|
|
this.outputElement.style.backgroundColor = 'black' |
247 |
|
|
|
248 |
|
|
this.inputContainerElement = document.createElement('div') |
249 |
|
|
this.inputContainerElement.style.width = "100%" |
250 |
|
|
this.logElement.appendChild(this.inputContainerElement) |
251 |
|
|
|
252 |
|
|
this.inputElement = document.createElement('input') |
253 |
|
|
this.inputContainerElement.appendChild(this.inputElement) |
254 |
|
|
this.inputElement.style.width = '100%' |
255 |
|
|
this.inputElement.style.borderWidth = '0px' // Inputs with 100% width always seem to be too large (I HATE THEM) they only work if the border, margin and padding are 0 |
256 |
|
|
this.inputElement.style.margin = '0px' |
257 |
|
|
this.inputElement.style.padding = '0px' |
258 |
|
|
this.inputElement.value = 'Type command here' |
259 |
|
|
this.inputElement.setAttribute('autocomplete', 'off') // So Firefox doesn't flip out |
260 |
|
|
|
261 |
|
|
Event.observe(this.inputElement, 'keyup', this.handleInput.bind(this)) |
262 |
|
|
Event.observe(this.inputElement, 'click', function() {this.inputElement.select()}.bind(this)) |
263 |
|
|
|
264 |
|
|
window.setInterval(this.repositionWindow.bind(this), 500) |
265 |
|
|
this.repositionWindow() |
266 |
|
|
|
267 |
|
|
// Listen to the logger.... |
268 |
|
|
Logger.onupdate.addListener(this.logUpdate.bind(this)) |
269 |
|
|
Logger.onclear.addListener(this.clear.bind(this)) |
270 |
|
|
|
271 |
|
|
// Preload log element with the log entries that have been entered |
272 |
|
|
for (var i = 0; i < Logger.logEntries.length; i++) { |
273 |
|
|
this.logUpdate(Logger.logEntries[i]) |
274 |
|
|
} |
275 |
|
|
|
276 |
|
|
// Feed all errors into the logger (For some unknown reason I can only get this to work |
277 |
|
|
// with an inline event declaration) |
278 |
|
|
Event.observe(window, 'error', function(msg, url, lineNumber) {Logger.error("Error in (" + (url || location) + ") on line "+lineNumber+"", msg)}) |
279 |
|
|
|
280 |
|
|
// Allow acess key link |
281 |
|
|
var accessElement = document.createElement('span') |
282 |
|
|
accessElement.innerHTML = '<button style="position:absolute;top:-100px" onclick="javascript:logConsole.toggle()" accesskey="d"></button>' |
283 |
|
|
document.body.appendChild(accessElement) |
284 |
|
|
|
285 |
|
|
if (Cookie.get('ConsoleVisible') == 'true') { |
286 |
|
|
this.toggle() |
287 |
|
|
} |
288 |
|
|
}, |
289 |
|
|
|
290 |
|
|
toggle : function() { |
291 |
|
|
if (this.logElement.style.display == 'none') { |
292 |
|
|
this.show() |
293 |
|
|
} |
294 |
|
|
else { |
295 |
|
|
this.hide() |
296 |
|
|
} |
297 |
|
|
}, |
298 |
|
|
|
299 |
|
|
show : function() { |
300 |
|
|
Element.show(this.logElement) |
301 |
|
|
this.outputElement.scrollTop = this.outputElement.scrollHeight // Scroll to bottom when toggled |
302 |
|
|
Cookie.set('ConsoleVisible', 'true') |
303 |
|
|
this.inputElement.select() |
304 |
|
|
}, |
305 |
|
|
|
306 |
|
|
hide : function() { |
307 |
|
|
Element.hide(this.logElement) |
308 |
|
|
Cookie.set('ConsoleVisible', 'false') |
309 |
|
|
}, |
310 |
|
|
|
311 |
|
|
output : function(message, style) { |
312 |
|
|
// If we are at the bottom of the window, then keep scrolling with the output |
313 |
|
|
var shouldScroll = (this.outputElement.scrollTop + (2 * this.outputElement.clientHeight)) >= this.outputElement.scrollHeight |
314 |
|
|
|
315 |
|
|
this.outputCount++ |
316 |
|
|
style = (style ? style += ';' : '') |
317 |
|
|
style += 'padding:1px;margin:0 0 5px 0' |
318 |
|
|
|
319 |
|
|
if (this.outputCount % 2 == 0) style += ";background-color:#101010" |
320 |
|
|
|
321 |
|
|
message = message || "undefined" |
322 |
|
|
message = message.toString().escapeHTML() |
323 |
|
|
|
324 |
|
|
this.outputElement.innerHTML += "<pre style='" + style + "'>" + message + "</pre>" |
325 |
|
|
|
326 |
|
|
if (shouldScroll) { |
327 |
|
|
this.outputElement.scrollTop = this.outputElement.scrollHeight |
328 |
|
|
} |
329 |
|
|
}, |
330 |
|
|
|
331 |
|
|
updateTags : function() { |
332 |
|
|
var pattern = this.tagFilterElement.value |
333 |
|
|
|
334 |
|
|
if (this.tagPattern == pattern) return |
335 |
|
|
|
336 |
|
|
try { |
337 |
|
|
new RegExp(pattern) |
338 |
|
|
} |
339 |
|
|
catch (e) { |
340 |
|
|
return |
341 |
|
|
} |
342 |
|
|
|
343 |
|
|
this.tagPattern = pattern |
344 |
|
|
Cookie.set('tagPattern', this.tagPattern) |
345 |
|
|
|
346 |
|
|
this.outputElement.innerHTML = "" |
347 |
|
|
|
348 |
|
|
// Go through each log entry again |
349 |
|
|
this.outputCount = 0; |
350 |
|
|
for (var i = 0; i < Logger.logEntries.length; i++) { |
351 |
|
|
this.logUpdate(Logger.logEntries[i]) |
352 |
|
|
} |
353 |
|
|
}, |
354 |
|
|
|
355 |
|
|
repositionWindow : function() { |
356 |
|
|
var offset = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop |
357 |
|
|
var pageHeight = self.innerHeight || document.documentElement.clientHeight || document.body.clientHeight |
358 |
|
|
this.logElement.style.top = (offset + pageHeight - Element.getHeight(this.logElement)) + "px" |
359 |
|
|
}, |
360 |
|
|
|
361 |
|
|
// Event Handlers |
362 |
|
|
// -------------- |
363 |
|
|
|
364 |
|
|
logUpdate : function(logEntry) { |
365 |
|
|
if (logEntry.tag.search(new RegExp(this.tagPattern, 'igm')) == -1) return |
366 |
|
|
var style = '' |
367 |
|
|
if (logEntry.tag.search(/error/) != -1) style += 'color:red' |
368 |
|
|
else if (logEntry.tag.search(/warning/) != -1) style += 'color:orange' |
369 |
|
|
else if (logEntry.tag.search(/debug/) != -1) style += 'color:green' |
370 |
|
|
else if (logEntry.tag.search(/info/) != -1) style += 'color:white' |
371 |
|
|
else style += 'color:yellow' |
372 |
|
|
|
373 |
|
|
this.output(logEntry.message, style) |
374 |
|
|
}, |
375 |
|
|
|
376 |
|
|
clear : function(e) { |
377 |
|
|
this.outputElement.innerHTML = "" |
378 |
|
|
}, |
379 |
|
|
|
380 |
|
|
handleInput : function(e) { |
381 |
|
|
if (e.keyCode == Event.KEY_RETURN ) { |
382 |
|
|
var command = this.inputElement.value |
383 |
|
|
|
384 |
|
|
switch(command) { |
385 |
|
|
case "clear": |
386 |
|
|
Logger.clear() |
387 |
|
|
break |
388 |
|
|
|
389 |
|
|
default: |
390 |
|
|
var consoleOutput = "" |
391 |
|
|
|
392 |
|
|
try { |
393 |
|
|
consoleOutput = eval(this.inputElement.value) |
394 |
|
|
} |
395 |
|
|
catch (e) { |
396 |
|
|
Logger.error("Problem parsing input <" + command + ">", e) |
397 |
|
|
break |
398 |
|
|
} |
399 |
|
|
|
400 |
|
|
Logger.log(consoleOutput) |
401 |
|
|
break |
402 |
|
|
} |
403 |
|
|
|
404 |
|
|
if (this.inputElement.value != "" && this.inputElement.value != this.commandHistory[0]) { |
405 |
|
|
this.commandHistory.unshift(this.inputElement.value) |
406 |
|
|
} |
407 |
|
|
|
408 |
|
|
this.commandIndex = 0 |
409 |
|
|
this.inputElement.value = "" |
410 |
|
|
} |
411 |
|
|
else if (e.keyCode == Event.KEY_UP && this.commandHistory.length > 0) { |
412 |
|
|
this.inputElement.value = this.commandHistory[this.commandIndex] |
413 |
|
|
|
414 |
|
|
if (this.commandIndex < this.commandHistory.length - 1) { |
415 |
|
|
this.commandIndex += 1 |
416 |
|
|
} |
417 |
|
|
} |
418 |
|
|
else if (e.keyCode == Event.KEY_DOWN && this.commandHistory.length > 0) { |
419 |
|
|
if (this.commandIndex > 0) { |
420 |
|
|
this.commandIndex -= 1 |
421 |
|
|
} |
422 |
|
|
|
423 |
|
|
this.inputElement.value = this.commandHistory[this.commandIndex] |
424 |
|
|
} |
425 |
|
|
else { |
426 |
|
|
this.commandIndex = 0 |
427 |
|
|
} |
428 |
|
|
} |
429 |
|
|
} |
430 |
|
|
|
431 |
|
|
// Load the Console when the window loads |
432 |
|
|
Event.observe(window, "load", function() {logConsole = new LogConsole()}) |
433 |
|
|
|
434 |
|
|
// ------------------------- |
435 |
|
|
// Helper Functions And Junk |
436 |
|
|
// ------------------------- |
437 |
|
|
function inspect(element, hideProperties, hideMethods) { |
438 |
|
|
var properties = [] |
439 |
|
|
var methods = [] |
440 |
|
|
|
441 |
|
|
element = $(element) |
442 |
|
|
|
443 |
|
|
for(var internal in element) { |
444 |
|
|
try { |
445 |
|
|
if (element[internal] instanceof Function) { |
446 |
|
|
if (!hideMethods) methods.push(internal + ":\t" + element[internal] ) |
447 |
|
|
} |
448 |
|
|
else { |
449 |
|
|
if (!hideProperties) properties.push(internal + ":\t" + element[internal] ) |
450 |
|
|
} |
451 |
|
|
} |
452 |
|
|
catch (e) { |
453 |
|
|
Logger.error("Excetion thrown while inspecting object.", e) |
454 |
|
|
} |
455 |
|
|
} |
456 |
|
|
|
457 |
|
|
properties.sort() |
458 |
|
|
methods.sort() |
459 |
|
|
|
460 |
|
|
var internals = properties.concat(methods) |
461 |
|
|
var output = "" |
462 |
|
|
for (var i = 0; i < internals.length; i++) { |
463 |
|
|
output += (internals[i] + "\n") |
464 |
|
|
} |
465 |
|
|
|
466 |
|
|
return output |
467 |
|
|
} |
468 |
|
|
|
469 |
|
|
Array.prototype.contains = function(object) { |
470 |
|
|
for(var i = 0; i < this.length; i++) { |
471 |
|
|
if (object == this[i]) return true |
472 |
|
|
} |
473 |
|
|
|
474 |
|
|
return false |
475 |
|
|
} |
476 |
|
|
|
477 |
|
|
// Helper Alias for simple logging |
478 |
|
|
var puts = function() {return Logger.log(arguments[0], arguments[1])} |