]>
Commit | Line | Data |
---|---|---|
1 | /* XXX: this message is ultra-lame */ | |
2 | var _assert = function (expr, value) { | |
3 | if (!expr) { | |
4 | var message = "_assert(" + value + ")"; | |
5 | console.log(message); | |
6 | throw message; | |
7 | } | |
8 | } | |
9 | ||
10 | // Compatibility {{{ | |
11 | if (typeof Array.prototype.push != "function") | |
12 | Array.prototype.push = function (value) { | |
13 | this[this.length] = value; | |
14 | }; | |
15 | // }}} | |
16 | ||
17 | var $ = function (arg, doc) { | |
18 | if (this.magic_ != $.prototype.magic_) | |
19 | return new $(arg); | |
20 | ||
21 | if (arg == null) | |
22 | arg = []; | |
23 | ||
24 | var type = $.type(arg); | |
25 | ||
26 | if (type == "function") | |
27 | $.ready(arg); | |
28 | else if (type == "string") { | |
29 | if (typeof doc == 'undefined') | |
30 | doc = document; | |
31 | if (arg.charAt(0) == '#') { | |
32 | /* XXX: this is somewhat incorrect-a-porter */ | |
33 | var element = doc.getElementById(arg.substring(1)); | |
34 | return $(element == null ? [] : [element]); | |
35 | } else if (arg.charAt(0) == '.') | |
36 | return $(doc.getElementsByClassName(arg.substring(1))); | |
37 | else | |
38 | return $([doc]).descendants(arg); | |
39 | } else if (typeof arg.length != 'undefined') { | |
40 | _assert(typeof doc == 'undefined', "non-query with document to $"); | |
41 | this.set(arg); | |
42 | return this; | |
43 | } else _assert(false, "unknown argument to $: " + typeof arg); | |
44 | }; | |
45 | ||
46 | $.xml = function (value) { | |
47 | return value | |
48 | .replace(/&/, "&") | |
49 | .replace(/</, "<") | |
50 | .replace(/>/, ">") | |
51 | .replace(/"/, """) | |
52 | .replace(/'/, "'") | |
53 | ; | |
54 | } | |
55 | ||
56 | $.type = function (value) { | |
57 | var type = typeof value; | |
58 | ||
59 | if ((type == "function" || type == "object") && value.toString != null) { | |
60 | var string = value.toString(); | |
61 | if (string.substring(0, 8) == "[object ") | |
62 | return string.substring(8, string.length - 1); | |
63 | } | |
64 | ||
65 | return type; | |
66 | }; | |
67 | ||
68 | (function () { | |
69 | var ready_ = null; | |
70 | ||
71 | $.ready = function (_function) { | |
72 | if (ready_ == null) { | |
73 | ready_ = []; | |
74 | ||
75 | document.addEventListener("DOMContentLoaded", function () { | |
76 | for (var i = 0; i != ready_.length; ++i) | |
77 | ready_[i](); | |
78 | }, false); | |
79 | } | |
80 | ||
81 | ready_.push(_function); | |
82 | }; | |
83 | })(); | |
84 | ||
85 | /* XXX: verify arg3 overflow */ | |
86 | $.each = function (values, _function, arg0, arg1, arg2) { | |
87 | for (var i = 0, e = values.length; i != e; ++i) | |
88 | _function(values[i], arg0, arg1, arg2); | |
89 | }; | |
90 | ||
91 | /* XXX: verify arg3 overflow */ | |
92 | $.map = function (values, _function, arg0, arg1, arg2) { | |
93 | var mapped = []; | |
94 | for (var i = 0, e = values.length; i != e; ++i) | |
95 | mapped.push(_function(values[i], arg0, arg1, arg2)); | |
96 | return mapped; | |
97 | }; | |
98 | ||
99 | $.array = function (values) { | |
100 | if (values.constructor == Array) | |
101 | return values; | |
102 | _assert(typeof values.length != 'undefined', "$.array on underlying non-array"); | |
103 | var array = []; | |
104 | for (var i = 0; i != values.length; ++i) | |
105 | array.push(values[i]); | |
106 | return array; | |
107 | }; | |
108 | ||
109 | $.document = function (node) { | |
110 | for (;;) { | |
111 | var parent = node.parentNode; | |
112 | if (parent == null) | |
113 | return node; | |
114 | node = parent; | |
115 | } | |
116 | }; | |
117 | ||
118 | $.reclass = function (_class) { | |
119 | return new RegExp('(\\s|^)' + _class + '(\\s|$)'); | |
120 | }; | |
121 | ||
122 | $.prototype = { | |
123 | magic_: 2041085062, | |
124 | ||
125 | add: function (nodes) { | |
126 | Array.prototype.push.apply(this, $.array(nodes)); | |
127 | }, | |
128 | ||
129 | at: function (name, value) { | |
130 | if (typeof value == 'undefined') | |
131 | return $.map(this, function (node) { | |
132 | return node.getAttribute(name); | |
133 | }); | |
134 | else if (value == null) | |
135 | $.each(this, function (node) { | |
136 | node.removeAttribute(); | |
137 | }); | |
138 | else | |
139 | $.each(this, function (node) { | |
140 | node.setAttribute(name, value); | |
141 | }); | |
142 | }, | |
143 | ||
144 | set: function (nodes) { | |
145 | this.length = 0; | |
146 | this.add(nodes); | |
147 | }, | |
148 | ||
149 | /* XXX: verify arg3 overflow */ | |
150 | each: function (_function, arg0, arg1, arg2) { | |
151 | $.each(this, function (node) { | |
152 | _function($([node]), arg0, arg1, arg2); | |
153 | }); | |
154 | }, | |
155 | ||
156 | css: function (name, value) { | |
157 | $.each(this, function (node) { | |
158 | node.style[name] = value; | |
159 | }); | |
160 | }, | |
161 | ||
162 | addClass: function (_class) { | |
163 | $.each(this, function (node) { | |
164 | if (!$([node]).hasClass(_class)[0]) | |
165 | node.className += " " + _class; | |
166 | }); | |
167 | }, | |
168 | ||
169 | blur: function () { | |
170 | $.each(this, function (node) { | |
171 | node.blur(); | |
172 | }); | |
173 | }, | |
174 | ||
175 | focus: function () { | |
176 | $.each(this, function (node) { | |
177 | node.focus(); | |
178 | }); | |
179 | }, | |
180 | ||
181 | removeClass: function (_class) { | |
182 | $.each(this, function (node) { | |
183 | node.className = node.className.replace($.reclass(_class), ' '); | |
184 | }); | |
185 | }, | |
186 | ||
187 | hasClass: function (_class) { | |
188 | return $.map(this, function (node) { | |
189 | return node.className.match($.reclass(_class)); | |
190 | }); | |
191 | }, | |
192 | ||
193 | append: function (children) { | |
194 | if ($.type(children) == "string") | |
195 | $.each(this, function (node) { | |
196 | var doc = $.document(node); | |
197 | ||
198 | // XXX: implement wrapper system | |
199 | var div = doc.createElement("div"); | |
200 | div.innerHTML = children; | |
201 | ||
202 | while (div.childNodes.length != 0) { | |
203 | var child = div.childNodes[0]; | |
204 | node.appendChild(child); | |
205 | } | |
206 | }); | |
207 | else | |
208 | $.each(this, function (node) { | |
209 | $.each(children, function (child) { | |
210 | node.appendChild(child); | |
211 | }); | |
212 | }); | |
213 | }, | |
214 | ||
215 | xpath: function (expression) { | |
216 | var value = $([]); | |
217 | ||
218 | $.each(this, function (node) { | |
219 | var doc = $.document(node); | |
220 | var results = doc.evaluate(expression, node, null, XPathResult.ANY_TYPE, null); | |
221 | var result; | |
222 | while (result = results.iterateNext()) | |
223 | value.add([result]); | |
224 | }); | |
225 | ||
226 | return value; | |
227 | }, | |
228 | ||
229 | clone: function (deep) { | |
230 | return $($.map(this, function (node) { | |
231 | return node.cloneNode(deep); | |
232 | })); | |
233 | }, | |
234 | ||
235 | descendants: function (expression) { | |
236 | var descendants = $([]); | |
237 | ||
238 | $.each(this, function (node) { | |
239 | var nodes = node.getElementsByTagName(expression); | |
240 | descendants.add(nodes); | |
241 | }); | |
242 | ||
243 | return descendants; | |
244 | }, | |
245 | ||
246 | remove: function () { | |
247 | $.each(this, function (node) { | |
248 | node.parentNode.removeChild(node); | |
249 | }); | |
250 | } | |
251 | }; | |
252 | ||
253 | $.scroll = function (x, y) { | |
254 | window.scrollTo(x, y); | |
255 | }; | |
256 | ||
257 | // XXX: document.all? | |
258 | $.all = function (doc) { | |
259 | if (typeof doc == 'undefined') | |
260 | doc = document; | |
261 | return $(doc.getElementsByTagName("*")); | |
262 | }; | |
263 | ||
264 | $.inject = function (a, b) { | |
265 | if ($.type(a) == "string") { | |
266 | $.prototype[a] = function (value) { | |
267 | if (typeof value == 'undefined') | |
268 | return $.map(this, function (node) { | |
269 | return b.get(node); | |
270 | }); | |
271 | else | |
272 | $.each(this, function (node, value) { | |
273 | b.set(node, value); | |
274 | }, value); | |
275 | }; | |
276 | } else for (var name in a) | |
277 | $.inject(name, a[name]); | |
278 | }; | |
279 | ||
280 | $.inject({ | |
281 | _default: { | |
282 | get: function (node) { | |
283 | return node.style.defaultValue; | |
284 | }, | |
285 | set: function (node, value) { | |
286 | node.style.defaultValue = value; | |
287 | } | |
288 | }, | |
289 | ||
290 | height: { | |
291 | get: function (node) { | |
292 | return node.height; | |
293 | }, | |
294 | set: function (node, value) { | |
295 | node.height = value; | |
296 | } | |
297 | }, | |
298 | ||
299 | html: { | |
300 | get: function (node) { | |
301 | return node.innerHTML; | |
302 | }, | |
303 | set: function (node, value) { | |
304 | node.innerHTML = value; | |
305 | } | |
306 | }, | |
307 | ||
308 | href: { | |
309 | get: function (node) { | |
310 | return node.href; | |
311 | }, | |
312 | set: function (node, value) { | |
313 | node.href = value; | |
314 | } | |
315 | }, | |
316 | ||
317 | name: { | |
318 | get: function (node) { | |
319 | return node.name; | |
320 | }, | |
321 | set: function (node, value) { | |
322 | node.name = value; | |
323 | } | |
324 | }, | |
325 | ||
326 | parent: { | |
327 | get: function (node) { | |
328 | return node.parentNode; | |
329 | } | |
330 | }, | |
331 | ||
332 | src: { | |
333 | get: function (node) { | |
334 | return node.src; | |
335 | }, | |
336 | set: function (node, value) { | |
337 | node.src = value; | |
338 | } | |
339 | }, | |
340 | ||
341 | type: { | |
342 | get: function (node) { | |
343 | return node.localName; | |
344 | } | |
345 | }, | |
346 | ||
347 | value: { | |
348 | get: function (node) { | |
349 | return node.value; | |
350 | }, | |
351 | set: function (node, value) { | |
352 | // XXX: do I really need this? | |
353 | if (true || node.localName != "select") | |
354 | node.value = value; | |
355 | else { | |
356 | var options = node.options; | |
357 | for (var i = 0, e = options.length; i != e; ++i) | |
358 | if (options[i].value == value) { | |
359 | if (node.selectedIndex != i) | |
360 | node.selectedIndex = i; | |
361 | break; | |
362 | } | |
363 | } | |
364 | } | |
365 | }, | |
366 | ||
367 | width: { | |
368 | get: function (node) { | |
369 | return node.offsetWidth; | |
370 | } | |
371 | } | |
372 | }); | |
373 | ||
374 | // Query String Parsing {{{ | |
375 | $.query = function () { | |
376 | var args = {}; | |
377 | ||
378 | var search = location.search; | |
379 | if (search != null) { | |
380 | _assert(search[0] == "?", "query string without ?"); | |
381 | ||
382 | var values = search.substring(1).split("&"); | |
383 | for (var index in values) { | |
384 | var value = values[index] | |
385 | var equal = value.indexOf("="); | |
386 | var name; | |
387 | ||
388 | if (equal == -1) { | |
389 | name = value; | |
390 | value = null; | |
391 | } else { | |
392 | name = value.substring(0, equal); | |
393 | value = value.substring(equal + 1); | |
394 | value = decodeURIComponent(value); | |
395 | } | |
396 | ||
397 | name = decodeURIComponent(name); | |
398 | if (typeof args[name] == "undefined") | |
399 | args[name] = []; | |
400 | if (value != null) | |
401 | args[name].push(value); | |
402 | } | |
403 | } | |
404 | ||
405 | return args; | |
406 | }; | |
407 | // }}} | |
408 | // Event Registration {{{ | |
409 | // XXX: unable to remove registration | |
410 | $.prototype.event = function (event, _function) { | |
411 | $.each(this, function (node) { | |
412 | // XXX: smooth over this pointer ugliness | |
413 | if (node.addEventListener) | |
414 | node.addEventListener(event, _function, false); | |
415 | else if (node.attachEvent) | |
416 | node.attachEvent("on" + event, _function); | |
417 | else | |
418 | // XXX: multiple registration SNAFU | |
419 | node["on" + event] = _function; | |
420 | }); | |
421 | }; | |
422 | ||
423 | $.each([ | |
424 | "click", "load", "submit" | |
425 | ], function (event) { | |
426 | $.prototype[event] = function (_function) { | |
427 | if (typeof _function == 'undefined') | |
428 | _assert(false, "undefined function to $.[event]"); | |
429 | else | |
430 | this.event(event, _function); | |
431 | }; | |
432 | }); | |
433 | // }}} | |
434 | // Timed Animation {{{ | |
435 | $.interpolate = function (duration, event) { | |
436 | var start = new Date(); | |
437 | ||
438 | var next = function () { | |
439 | setTimeout(update, 0); | |
440 | }; | |
441 | ||
442 | var update = function () { | |
443 | var time = new Date() - start; | |
444 | ||
445 | if (time >= duration) | |
446 | event(1); | |
447 | else { | |
448 | event(time / duration); | |
449 | next(); | |
450 | } | |
451 | }; | |
452 | ||
453 | next(); | |
454 | }; | |
455 | // }}} | |
456 | // AJAX Requests {{{ | |
457 | // XXX: abstract and implement other cases | |
458 | $.xhr = function (url, method, headers, data, events) { | |
459 | var xhr = new XMLHttpRequest(); | |
460 | xhr.open(method, url, true); | |
461 | ||
462 | for (var name in headers) | |
463 | xhr.setRequestHeader(name.replace(/_/, "-"), headers[name]); | |
464 | ||
465 | if (events == null) | |
466 | events = {}; | |
467 | ||
468 | xhr.onreadystatechange = function () { | |
469 | if (xhr.readyState == 4) { | |
470 | var status = xhr.status; | |
471 | var text = xhr.responseText; | |
472 | if (events.response != null) | |
473 | events.response(status, text); | |
474 | if (status == 200) { | |
475 | if (events.success != null) | |
476 | events.success(text); | |
477 | } else { | |
478 | if (events.failure != null) | |
479 | events.failure(status); | |
480 | } | |
481 | } | |
482 | }; | |
483 | ||
484 | xhr.send(data); | |
485 | }; | |
486 | ||
487 | $.call = function (url, post, onsuccess) { | |
488 | var events = {}; | |
489 | ||
490 | if (onsuccess != null) | |
491 | events.complete = function (text) { | |
492 | onsuccess(eval(text)); | |
493 | }; | |
494 | ||
495 | if (post == null) | |
496 | $.xhr(url, "POST", null, null, events); | |
497 | else | |
498 | $.xhr(url, "POST", { | |
499 | Content_Type: "application/json" | |
500 | }, $.json(post), events); | |
501 | }; | |
502 | // }}} | |
503 | // WWW Form URL Encoder {{{ | |
504 | $.form = function (parameters) { | |
505 | var data = ""; | |
506 | ||
507 | var ampersand = false; | |
508 | for (var name in parameters) { | |
509 | if (!ampersand) | |
510 | ampersand = true; | |
511 | else | |
512 | data += "&"; | |
513 | ||
514 | var value = parameters[name]; | |
515 | ||
516 | data += escape(name); | |
517 | data += "="; | |
518 | data += escape(value); | |
519 | } | |
520 | ||
521 | return data; | |
522 | }; | |
523 | // }}} | |
524 | // JSON Serializer {{{ | |
525 | $.json = function (value) { | |
526 | if (value == null) | |
527 | return "null"; | |
528 | ||
529 | var type = $.type(value); | |
530 | ||
531 | if (type == "number") | |
532 | return value; | |
533 | else if (type == "string") | |
534 | return "\"" + value | |
535 | .replace(/\\/, "\\\\") | |
536 | .replace(/\t/, "\\t") | |
537 | .replace(/\r/, "\\r") | |
538 | .replace(/\n/, "\\n") | |
539 | .replace(/"/, "\\\"") | |
540 | + "\""; | |
541 | else if (value.constructor == Array) { | |
542 | var json = "["; | |
543 | var comma = false; | |
544 | ||
545 | for (var i = 0; i != value.length; ++i) { | |
546 | if (!comma) | |
547 | comma = true; | |
548 | else | |
549 | json += ","; | |
550 | ||
551 | json += $.json(value[i]); | |
552 | } | |
553 | ||
554 | return json + "]"; | |
555 | } else if ( | |
556 | value.constructor == Object && | |
557 | value.toString() == "[object Object]" | |
558 | ) { | |
559 | var json = "{"; | |
560 | var comma = false; | |
561 | ||
562 | for (var name in value) { | |
563 | if (!comma) | |
564 | comma = true; | |
565 | else | |
566 | json += ","; | |
567 | ||
568 | json += name + ":" + $.json(value[name]); | |
569 | } | |
570 | return json + "}"; | |
571 | } else { | |
572 | return value; | |
573 | } | |
574 | }; | |
575 | // }}} |