A tumblelog CMS built on AJAX, PHP and MySQL.

prototype.js 53KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985
  1. /* Prototype JavaScript framework, version 1.5.0_pre1
  2. * (c) 2005 Sam Stephenson <sam@conio.net>
  3. *
  4. * Prototype is freely distributable under the terms of an MIT-style license.
  5. * For details, see the Prototype web site: http://prototype.conio.net/
  6. *
  7. /*--------------------------------------------------------------------------*/
  8. var Prototype = {
  9. Version: '1.5.0_pre1',
  10. ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
  11. emptyFunction: function() {},
  12. K: function(x) {return x}
  13. }
  14. var Class = {
  15. create: function() {
  16. return function() {
  17. this.initialize.apply(this, arguments);
  18. }
  19. }
  20. }
  21. var Abstract = new Object();
  22. Object.extend = function(destination, source) {
  23. for (property in source) {
  24. destination[property] = source[property];
  25. }
  26. return destination;
  27. }
  28. Object.inspect = function(object) {
  29. try {
  30. if (object == undefined) return 'undefined';
  31. if (object == null) return 'null';
  32. return object.inspect ? object.inspect() : object.toString();
  33. } catch (e) {
  34. if (e instanceof RangeError) return '...';
  35. throw e;
  36. }
  37. }
  38. Function.prototype.bind = function() {
  39. var __method = this, args = $A(arguments), object = args.shift();
  40. return function() {
  41. return __method.apply(object, args.concat($A(arguments)));
  42. }
  43. }
  44. Function.prototype.bindAsEventListener = function(object) {
  45. var __method = this;
  46. return function(event) {
  47. return __method.call(object, event || window.event);
  48. }
  49. }
  50. Object.extend(Number.prototype, {
  51. toColorPart: function() {
  52. var digits = this.toString(16);
  53. if (this < 16) return '0' + digits;
  54. return digits;
  55. },
  56. succ: function() {
  57. return this + 1;
  58. },
  59. times: function(iterator) {
  60. $R(0, this, true).each(iterator);
  61. return this;
  62. }
  63. });
  64. var Try = {
  65. these: function() {
  66. var returnValue;
  67. for (var i = 0; i < arguments.length; i++) {
  68. var lambda = arguments[i];
  69. try {
  70. returnValue = lambda();
  71. break;
  72. } catch (e) {}
  73. }
  74. return returnValue;
  75. }
  76. }
  77. /*--------------------------------------------------------------------------*/
  78. var PeriodicalExecuter = Class.create();
  79. PeriodicalExecuter.prototype = {
  80. initialize: function(callback, frequency) {
  81. this.callback = callback;
  82. this.frequency = frequency;
  83. this.currentlyExecuting = false;
  84. this.registerCallback();
  85. },
  86. registerCallback: function() {
  87. setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  88. },
  89. onTimerEvent: function() {
  90. if (!this.currentlyExecuting) {
  91. try {
  92. this.currentlyExecuting = true;
  93. this.callback();
  94. } finally {
  95. this.currentlyExecuting = false;
  96. }
  97. }
  98. }
  99. }
  100. Object.extend(String.prototype, {
  101. gsub: function(pattern, replacement) {
  102. var result = '', source = this, match;
  103. replacement = arguments.callee.prepareReplacement(replacement);
  104. while (source.length > 0) {
  105. if (match = source.match(pattern)) {
  106. result += source.slice(0, match.index);
  107. result += (replacement(match) || '').toString();
  108. source = source.slice(match.index + match[0].length);
  109. } else {
  110. result += source, source = '';
  111. }
  112. }
  113. return result;
  114. },
  115. sub: function(pattern, replacement, count) {
  116. replacement = this.gsub.prepareReplacement(replacement);
  117. count = count === undefined ? 1 : count;
  118. return this.gsub(pattern, function(match) {
  119. if (--count < 0) return match[0];
  120. return replacement(match);
  121. });
  122. },
  123. scan: function(pattern, iterator) {
  124. this.gsub(pattern, iterator);
  125. return this;
  126. },
  127. truncate: function(length, truncation) {
  128. length = length || 30;
  129. truncation = truncation === undefined ? '...' : truncation;
  130. return this.length > length ?
  131. this.slice(0, length - truncation.length) + truncation : this;
  132. },
  133. strip: function() {
  134. return this.replace(/^\s+/, '').replace(/\s+$/, '');
  135. },
  136. stripTags: function() {
  137. return this.replace(/<\/?[^>]+>/gi, '');
  138. },
  139. stripScripts: function() {
  140. return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  141. },
  142. extractScripts: function() {
  143. var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
  144. var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
  145. return (this.match(matchAll) || []).map(function(scriptTag) {
  146. return (scriptTag.match(matchOne) || ['', ''])[1];
  147. });
  148. },
  149. evalScripts: function() {
  150. return this.extractScripts().map(eval);
  151. },
  152. escapeHTML: function() {
  153. var div = document.createElement('div');
  154. var text = document.createTextNode(this);
  155. div.appendChild(text);
  156. return div.innerHTML;
  157. },
  158. unescapeHTML: function() {
  159. var div = document.createElement('div');
  160. div.innerHTML = this.stripTags();
  161. return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
  162. },
  163. toQueryParams: function() {
  164. var pairs = this.match(/^\??(.*)$/)[1].split('&');
  165. return pairs.inject({}, function(params, pairString) {
  166. var pair = pairString.split('=');
  167. params[pair[0]] = pair[1];
  168. return params;
  169. });
  170. },
  171. toArray: function() {
  172. return this.split('');
  173. },
  174. camelize: function() {
  175. var oStringList = this.split('-');
  176. if (oStringList.length == 1) return oStringList[0];
  177. var camelizedString = this.indexOf('-') == 0
  178. ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
  179. : oStringList[0];
  180. for (var i = 1, len = oStringList.length; i < len; i++) {
  181. var s = oStringList[i];
  182. camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
  183. }
  184. return camelizedString;
  185. },
  186. inspect: function() {
  187. return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'";
  188. }
  189. });
  190. String.prototype.gsub.prepareReplacement = function(replacement) {
  191. if (typeof replacement == 'function') return replacement;
  192. var template = new Template(replacement);
  193. return function(match) { return template.evaluate(match) };
  194. }
  195. String.prototype.parseQuery = String.prototype.toQueryParams;
  196. var Template = Class.create();
  197. Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
  198. Template.prototype = {
  199. initialize: function(template, pattern) {
  200. this.template = template.toString();
  201. this.pattern = pattern || Template.Pattern;
  202. },
  203. evaluate: function(object) {
  204. return this.template.gsub(this.pattern, function(match) {
  205. var before = match[1];
  206. if (before == '\\') return match[2];
  207. return before + (object[match[3]] || '').toString();
  208. });
  209. }
  210. }
  211. var $break = new Object();
  212. var $continue = new Object();
  213. var Enumerable = {
  214. each: function(iterator) {
  215. var index = 0;
  216. try {
  217. this._each(function(value) {
  218. try {
  219. iterator(value, index++);
  220. } catch (e) {
  221. if (e != $continue) throw e;
  222. }
  223. });
  224. } catch (e) {
  225. if (e != $break) throw e;
  226. }
  227. },
  228. all: function(iterator) {
  229. var result = true;
  230. this.each(function(value, index) {
  231. result = result && !!(iterator || Prototype.K)(value, index);
  232. if (!result) throw $break;
  233. });
  234. return result;
  235. },
  236. any: function(iterator) {
  237. var result = true;
  238. this.each(function(value, index) {
  239. if (result = !!(iterator || Prototype.K)(value, index))
  240. throw $break;
  241. });
  242. return result;
  243. },
  244. collect: function(iterator) {
  245. var results = [];
  246. this.each(function(value, index) {
  247. results.push(iterator(value, index));
  248. });
  249. return results;
  250. },
  251. detect: function (iterator) {
  252. var result;
  253. this.each(function(value, index) {
  254. if (iterator(value, index)) {
  255. result = value;
  256. throw $break;
  257. }
  258. });
  259. return result;
  260. },
  261. findAll: function(iterator) {
  262. var results = [];
  263. this.each(function(value, index) {
  264. if (iterator(value, index))
  265. results.push(value);
  266. });
  267. return results;
  268. },
  269. grep: function(pattern, iterator) {
  270. var results = [];
  271. this.each(function(value, index) {
  272. var stringValue = value.toString();
  273. if (stringValue.match(pattern))
  274. results.push((iterator || Prototype.K)(value, index));
  275. })
  276. return results;
  277. },
  278. include: function(object) {
  279. var found = false;
  280. this.each(function(value) {
  281. if (value == object) {
  282. found = true;
  283. throw $break;
  284. }
  285. });
  286. return found;
  287. },
  288. inject: function(memo, iterator) {
  289. this.each(function(value, index) {
  290. memo = iterator(memo, value, index);
  291. });
  292. return memo;
  293. },
  294. invoke: function(method) {
  295. var args = $A(arguments).slice(1);
  296. return this.collect(function(value) {
  297. return value[method].apply(value, args);
  298. });
  299. },
  300. max: function(iterator) {
  301. var result;
  302. this.each(function(value, index) {
  303. value = (iterator || Prototype.K)(value, index);
  304. if (value >= (result || value))
  305. result = value;
  306. });
  307. return result;
  308. },
  309. min: function(iterator) {
  310. var result;
  311. this.each(function(value, index) {
  312. value = (iterator || Prototype.K)(value, index);
  313. if (value <= (result || value))
  314. result = value;
  315. });
  316. return result;
  317. },
  318. partition: function(iterator) {
  319. var trues = [], falses = [];
  320. this.each(function(value, index) {
  321. ((iterator || Prototype.K)(value, index) ?
  322. trues : falses).push(value);
  323. });
  324. return [trues, falses];
  325. },
  326. pluck: function(property) {
  327. var results = [];
  328. this.each(function(value, index) {
  329. results.push(value[property]);
  330. });
  331. return results;
  332. },
  333. reject: function(iterator) {
  334. var results = [];
  335. this.each(function(value, index) {
  336. if (!iterator(value, index))
  337. results.push(value);
  338. });
  339. return results;
  340. },
  341. sortBy: function(iterator) {
  342. return this.collect(function(value, index) {
  343. return {value: value, criteria: iterator(value, index)};
  344. }).sort(function(left, right) {
  345. var a = left.criteria, b = right.criteria;
  346. return a < b ? -1 : a > b ? 1 : 0;
  347. }).pluck('value');
  348. },
  349. toArray: function() {
  350. return this.collect(Prototype.K);
  351. },
  352. zip: function() {
  353. var iterator = Prototype.K, args = $A(arguments);
  354. if (typeof args.last() == 'function')
  355. iterator = args.pop();
  356. var collections = [this].concat(args).map($A);
  357. return this.map(function(value, index) {
  358. return iterator(collections.pluck(index));
  359. });
  360. },
  361. inspect: function() {
  362. return '#<Enumerable:' + this.toArray().inspect() + '>';
  363. }
  364. }
  365. Object.extend(Enumerable, {
  366. map: Enumerable.collect,
  367. find: Enumerable.detect,
  368. select: Enumerable.findAll,
  369. member: Enumerable.include,
  370. entries: Enumerable.toArray
  371. });
  372. var $A = Array.from = function(iterable) {
  373. if (!iterable) return [];
  374. if (iterable.toArray) {
  375. return iterable.toArray();
  376. } else {
  377. var results = [];
  378. for (var i = 0; i < iterable.length; i++)
  379. results.push(iterable[i]);
  380. return results;
  381. }
  382. }
  383. Object.extend(Array.prototype, Enumerable);
  384. Array.prototype._reverse = Array.prototype.reverse;
  385. Object.extend(Array.prototype, {
  386. _each: function(iterator) {
  387. for (var i = 0; i < this.length; i++)
  388. iterator(this[i]);
  389. },
  390. clear: function() {
  391. this.length = 0;
  392. return this;
  393. },
  394. first: function() {
  395. return this[0];
  396. },
  397. last: function() {
  398. return this[this.length - 1];
  399. },
  400. compact: function() {
  401. return this.select(function(value) {
  402. return value != undefined || value != null;
  403. });
  404. },
  405. flatten: function() {
  406. return this.inject([], function(array, value) {
  407. return array.concat(value.constructor == Array ?
  408. value.flatten() : [value]);
  409. });
  410. },
  411. without: function() {
  412. var values = $A(arguments);
  413. return this.select(function(value) {
  414. return !values.include(value);
  415. });
  416. },
  417. indexOf: function(object) {
  418. for (var i = 0; i < this.length; i++)
  419. if (this[i] == object) return i;
  420. return -1;
  421. },
  422. reverse: function(inline) {
  423. return (inline !== false ? this : this.toArray())._reverse();
  424. },
  425. shift: function() {
  426. var result = this[0];
  427. for (var i = 0; i < this.length - 1; i++)
  428. this[i] = this[i + 1];
  429. this.length--;
  430. return result;
  431. },
  432. inspect: function() {
  433. return '[' + this.map(Object.inspect).join(', ') + ']';
  434. }
  435. });
  436. var Hash = {
  437. _each: function(iterator) {
  438. for (key in this) {
  439. var value = this[key];
  440. if (typeof value == 'function') continue;
  441. var pair = [key, value];
  442. pair.key = key;
  443. pair.value = value;
  444. iterator(pair);
  445. }
  446. },
  447. keys: function() {
  448. return this.pluck('key');
  449. },
  450. values: function() {
  451. return this.pluck('value');
  452. },
  453. merge: function(hash) {
  454. return $H(hash).inject($H(this), function(mergedHash, pair) {
  455. mergedHash[pair.key] = pair.value;
  456. return mergedHash;
  457. });
  458. },
  459. toQueryString: function() {
  460. return this.map(function(pair) {
  461. return pair.map(encodeURIComponent).join('=');
  462. }).join('&');
  463. },
  464. inspect: function() {
  465. return '#<Hash:{' + this.map(function(pair) {
  466. return pair.map(Object.inspect).join(': ');
  467. }).join(', ') + '}>';
  468. }
  469. }
  470. function $H(object) {
  471. var hash = Object.extend({}, object || {});
  472. Object.extend(hash, Enumerable);
  473. Object.extend(hash, Hash);
  474. return hash;
  475. }
  476. ObjectRange = Class.create();
  477. Object.extend(ObjectRange.prototype, Enumerable);
  478. Object.extend(ObjectRange.prototype, {
  479. initialize: function(start, end, exclusive) {
  480. this.start = start;
  481. this.end = end;
  482. this.exclusive = exclusive;
  483. },
  484. _each: function(iterator) {
  485. var value = this.start;
  486. do {
  487. iterator(value);
  488. value = value.succ();
  489. } while (this.include(value));
  490. },
  491. include: function(value) {
  492. if (value < this.start)
  493. return false;
  494. if (this.exclusive)
  495. return value < this.end;
  496. return value <= this.end;
  497. }
  498. });
  499. var $R = function(start, end, exclusive) {
  500. return new ObjectRange(start, end, exclusive);
  501. }
  502. var Ajax = {
  503. getTransport: function() {
  504. return Try.these(
  505. function() {return new ActiveXObject('Msxml2.XMLHTTP')},
  506. function() {return new ActiveXObject('Microsoft.XMLHTTP')},
  507. function() {return new XMLHttpRequest()}
  508. ) || false;
  509. },
  510. activeRequestCount: 0
  511. }
  512. Ajax.Responders = {
  513. responders: [],
  514. _each: function(iterator) {
  515. this.responders._each(iterator);
  516. },
  517. register: function(responderToAdd) {
  518. if (!this.include(responderToAdd))
  519. this.responders.push(responderToAdd);
  520. },
  521. unregister: function(responderToRemove) {
  522. this.responders = this.responders.without(responderToRemove);
  523. },
  524. dispatch: function(callback, request, transport, json) {
  525. this.each(function(responder) {
  526. if (responder[callback] && typeof responder[callback] == 'function') {
  527. try {
  528. responder[callback].apply(responder, [request, transport, json]);
  529. } catch (e) {}
  530. }
  531. });
  532. }
  533. };
  534. Object.extend(Ajax.Responders, Enumerable);
  535. Ajax.Responders.register({
  536. onCreate: function() {
  537. Ajax.activeRequestCount++;
  538. },
  539. onComplete: function() {
  540. Ajax.activeRequestCount--;
  541. }
  542. });
  543. Ajax.Base = function() {};
  544. Ajax.Base.prototype = {
  545. setOptions: function(options) {
  546. this.options = {
  547. method: 'post',
  548. asynchronous: true,
  549. parameters: ''
  550. }
  551. Object.extend(this.options, options || {});
  552. },
  553. responseIsSuccess: function() {
  554. return this.transport.status == undefined
  555. || this.transport.status == 0
  556. || (this.transport.status >= 200 && this.transport.status < 300);
  557. },
  558. responseIsFailure: function() {
  559. return !this.responseIsSuccess();
  560. }
  561. }
  562. Ajax.Request = Class.create();
  563. Ajax.Request.Events =
  564. ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
  565. Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
  566. initialize: function(url, options) {
  567. this.transport = Ajax.getTransport();
  568. this.setOptions(options);
  569. this.request(url);
  570. },
  571. request: function(url) {
  572. var parameters = this.options.parameters || '';
  573. if (parameters.length > 0) parameters += '&_=';
  574. try {
  575. this.url = url;
  576. if (this.options.method == 'get' && parameters.length > 0)
  577. this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
  578. Ajax.Responders.dispatch('onCreate', this, this.transport);
  579. this.transport.open(this.options.method, this.url,
  580. this.options.asynchronous);
  581. if (this.options.asynchronous) {
  582. this.transport.onreadystatechange = this.onStateChange.bind(this);
  583. setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
  584. }
  585. this.setRequestHeaders();
  586. var body = this.options.postBody ? this.options.postBody : parameters;
  587. this.transport.send(this.options.method == 'post' ? body : null);
  588. } catch (e) {
  589. this.dispatchException(e);
  590. }
  591. },
  592. setRequestHeaders: function() {
  593. var requestHeaders =
  594. ['X-Requested-With', 'XMLHttpRequest',
  595. 'X-Prototype-Version', Prototype.Version,
  596. 'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
  597. if (this.options.method == 'post') {
  598. requestHeaders.push('Content-type',
  599. 'application/x-www-form-urlencoded');
  600. /* Force "Connection: close" for Mozilla browsers to work around
  601. * a bug where XMLHttpReqeuest sends an incorrect Content-length
  602. * header. See Mozilla Bugzilla #246651.
  603. */
  604. if (this.transport.overrideMimeType)
  605. requestHeaders.push('Connection', 'close');
  606. }
  607. if (this.options.requestHeaders)
  608. requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
  609. for (var i = 0; i < requestHeaders.length; i += 2)
  610. this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
  611. },
  612. onStateChange: function() {
  613. var readyState = this.transport.readyState;
  614. if (readyState != 1)
  615. this.respondToReadyState(this.transport.readyState);
  616. },
  617. header: function(name) {
  618. try {
  619. return this.transport.getResponseHeader(name);
  620. } catch (e) {}
  621. },
  622. evalJSON: function() {
  623. try {
  624. return eval(this.header('X-JSON'));
  625. } catch (e) {}
  626. },
  627. evalResponse: function() {
  628. try {
  629. return eval(this.transport.responseText);
  630. } catch (e) {
  631. this.dispatchException(e);
  632. }
  633. },
  634. respondToReadyState: function(readyState) {
  635. var event = Ajax.Request.Events[readyState];
  636. var transport = this.transport, json = this.evalJSON();
  637. if (event == 'Complete') {
  638. try {
  639. (this.options['on' + this.transport.status]
  640. || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
  641. || Prototype.emptyFunction)(transport, json);
  642. } catch (e) {
  643. this.dispatchException(e);
  644. }
  645. if ((this.header('Content-type') || '').match(/^text\/javascript/i))
  646. this.evalResponse();
  647. }
  648. try {
  649. (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
  650. Ajax.Responders.dispatch('on' + event, this, transport, json);
  651. } catch (e) {
  652. this.dispatchException(e);
  653. }
  654. /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
  655. if (event == 'Complete')
  656. this.transport.onreadystatechange = Prototype.emptyFunction;
  657. },
  658. dispatchException: function(exception) {
  659. (this.options.onException || Prototype.emptyFunction)(this, exception);
  660. Ajax.Responders.dispatch('onException', this, exception);
  661. }
  662. });
  663. Ajax.Updater = Class.create();
  664. Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
  665. initialize: function(container, url, options) {
  666. this.containers = {
  667. success: container.success ? $(container.success) : $(container),
  668. failure: container.failure ? $(container.failure) :
  669. (container.success ? null : $(container))
  670. }
  671. this.transport = Ajax.getTransport();
  672. this.setOptions(options);
  673. var onComplete = this.options.onComplete || Prototype.emptyFunction;
  674. this.options.onComplete = (function(transport, object) {
  675. this.updateContent();
  676. onComplete(transport, object);
  677. }).bind(this);
  678. this.request(url);
  679. },
  680. updateContent: function() {
  681. var receiver = this.responseIsSuccess() ?
  682. this.containers.success : this.containers.failure;
  683. var response = this.transport.responseText;
  684. if (!this.options.evalScripts)
  685. response = response.stripScripts();
  686. if (receiver) {
  687. if (this.options.insertion) {
  688. new this.options.insertion(receiver, response);
  689. } else {
  690. Element.update(receiver, response);
  691. }
  692. }
  693. if (this.responseIsSuccess()) {
  694. if (this.onComplete)
  695. setTimeout(this.onComplete.bind(this), 10);
  696. }
  697. }
  698. });
  699. Ajax.PeriodicalUpdater = Class.create();
  700. Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
  701. initialize: function(container, url, options) {
  702. this.setOptions(options);
  703. this.onComplete = this.options.onComplete;
  704. this.frequency = (this.options.frequency || 2);
  705. this.decay = (this.options.decay || 1);
  706. this.updater = {};
  707. this.container = container;
  708. this.url = url;
  709. this.start();
  710. },
  711. start: function() {
  712. this.options.onComplete = this.updateComplete.bind(this);
  713. this.onTimerEvent();
  714. },
  715. stop: function() {
  716. this.updater.onComplete = undefined;
  717. clearTimeout(this.timer);
  718. (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  719. },
  720. updateComplete: function(request) {
  721. if (this.options.decay) {
  722. this.decay = (request.responseText == this.lastText ?
  723. this.decay * this.options.decay : 1);
  724. this.lastText = request.responseText;
  725. }
  726. this.timer = setTimeout(this.onTimerEvent.bind(this),
  727. this.decay * this.frequency * 1000);
  728. },
  729. onTimerEvent: function() {
  730. this.updater = new Ajax.Updater(this.container, this.url, this.options);
  731. }
  732. });
  733. function $() {
  734. var results = [], element;
  735. for (var i = 0; i < arguments.length; i++) {
  736. element = arguments[i];
  737. if (typeof element == 'string')
  738. element = document.getElementById(element);
  739. results.push(Element.extend(element));
  740. }
  741. return results.length < 2 ? results[0] : results;
  742. }
  743. document.getElementsByClassName = function(className, parentElement) {
  744. var children = ($(parentElement) || document.body).getElementsByTagName('*');
  745. return $A(children).inject([], function(elements, child) {
  746. if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
  747. elements.push(Element.extend(child));
  748. return elements;
  749. });
  750. }
  751. /*--------------------------------------------------------------------------*/
  752. if (!window.Element)
  753. var Element = new Object();
  754. Element.extend = function(element) {
  755. if (!element) return;
  756. if (!element._extended && element.tagName && element != window) {
  757. var methods = Element.Methods;
  758. for (property in methods) {
  759. var value = methods[property];
  760. if (typeof value == 'function')
  761. element[property] = value.bind(null, element);
  762. }
  763. }
  764. element._extended = true;
  765. return element;
  766. }
  767. Element.Methods = {
  768. visible: function(element) {
  769. return $(element).style.display != 'none';
  770. },
  771. toggle: function() {
  772. for (var i = 0; i < arguments.length; i++) {
  773. var element = $(arguments[i]);
  774. Element[Element.visible(element) ? 'hide' : 'show'](element);
  775. }
  776. },
  777. hide: function() {
  778. for (var i = 0; i < arguments.length; i++) {
  779. var element = $(arguments[i]);
  780. element.style.display = 'none';
  781. }
  782. },
  783. show: function() {
  784. for (var i = 0; i < arguments.length; i++) {
  785. var element = $(arguments[i]);
  786. element.style.display = '';
  787. }
  788. },
  789. remove: function(element) {
  790. element = $(element);
  791. element.parentNode.removeChild(element);
  792. },
  793. update: function(element, html) {
  794. $(element).innerHTML = html.stripScripts();
  795. setTimeout(function() {html.evalScripts()}, 10);
  796. },
  797. replace: function(element, html) {
  798. element = $(element);
  799. if (element.outerHTML) {
  800. element.outerHTML = html.stripScripts();
  801. } else {
  802. var range = element.ownerDocument.createRange();
  803. range.selectNodeContents(element);
  804. element.parentNode.replaceChild(
  805. range.createContextualFragment(html.stripScripts()), element);
  806. }
  807. setTimeout(function() {html.evalScripts()}, 10);
  808. },
  809. getHeight: function(element) {
  810. element = $(element);
  811. return element.offsetHeight;
  812. },
  813. classNames: function(element) {
  814. return new Element.ClassNames(element);
  815. },
  816. hasClassName: function(element, className) {
  817. if (!(element = $(element))) return;
  818. return Element.classNames(element).include(className);
  819. },
  820. addClassName: function(element, className) {
  821. if (!(element = $(element))) return;
  822. return Element.classNames(element).add(className);
  823. },
  824. removeClassName: function(element, className) {
  825. if (!(element = $(element))) return;
  826. return Element.classNames(element).remove(className);
  827. },
  828. // removes whitespace-only text node children
  829. cleanWhitespace: function(element) {
  830. element = $(element);
  831. for (var i = 0; i < element.childNodes.length; i++) {
  832. var node = element.childNodes[i];
  833. if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
  834. Element.remove(node);
  835. }
  836. },
  837. empty: function(element) {
  838. return $(element).innerHTML.match(/^\s*$/);
  839. },
  840. childOf: function(element, ancestor) {
  841. element = $(element), ancestor = $(ancestor);
  842. while (element = element.parentNode)
  843. if (element == ancestor) return true;
  844. return false;
  845. },
  846. scrollTo: function(element) {
  847. element = $(element);
  848. var x = element.x ? element.x : element.offsetLeft,
  849. y = element.y ? element.y : element.offsetTop;
  850. window.scrollTo(x, y);
  851. },
  852. getStyle: function(element, style) {
  853. element = $(element);
  854. var value = element.style[style.camelize()];
  855. if (!value) {
  856. if (document.defaultView && document.defaultView.getComputedStyle) {
  857. var css = document.defaultView.getComputedStyle(element, null);
  858. value = css ? css.getPropertyValue(style) : null;
  859. } else if (element.currentStyle) {
  860. value = element.currentStyle[style.camelize()];
  861. }
  862. }
  863. if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
  864. if (Element.getStyle(element, 'position') == 'static') value = 'auto';
  865. return value == 'auto' ? null : value;
  866. },
  867. setStyle: function(element, style) {
  868. element = $(element);
  869. for (name in style)
  870. element.style[name.camelize()] = style[name];
  871. },
  872. getDimensions: function(element) {
  873. element = $(element);
  874. if (Element.getStyle(element, 'display') != 'none')
  875. return {width: element.offsetWidth, height: element.offsetHeight};
  876. // All *Width and *Height properties give 0 on elements with display none,
  877. // so enable the element temporarily
  878. var els = element.style;
  879. var originalVisibility = els.visibility;
  880. var originalPosition = els.position;
  881. els.visibility = 'hidden';
  882. els.position = 'absolute';
  883. els.display = '';
  884. var originalWidth = element.clientWidth;
  885. var originalHeight = element.clientHeight;
  886. els.display = 'none';
  887. els.position = originalPosition;
  888. els.visibility = originalVisibility;
  889. return {width: originalWidth, height: originalHeight};
  890. },
  891. makePositioned: function(element) {
  892. element = $(element);
  893. var pos = Element.getStyle(element, 'position');
  894. if (pos == 'static' || !pos) {
  895. element._madePositioned = true;
  896. element.style.position = 'relative';
  897. // Opera returns the offset relative to the positioning context, when an
  898. // element is position relative but top and left have not been defined
  899. if (window.opera) {
  900. element.style.top = 0;
  901. element.style.left = 0;
  902. }
  903. }
  904. },
  905. undoPositioned: function(element) {
  906. element = $(element);
  907. if (element._madePositioned) {
  908. element._madePositioned = undefined;
  909. element.style.position =
  910. element.style.top =
  911. element.style.left =
  912. element.style.bottom =
  913. element.style.right = '';
  914. }
  915. },
  916. makeClipping: function(element) {
  917. element = $(element);
  918. if (element._overflow) return;
  919. element._overflow = element.style.overflow;
  920. if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
  921. element.style.overflow = 'hidden';
  922. },
  923. undoClipping: function(element) {
  924. element = $(element);
  925. if (element._overflow) return;
  926. element.style.overflow = element._overflow;
  927. element._overflow = undefined;
  928. }
  929. }
  930. Object.extend(Element, Element.Methods);
  931. var Toggle = new Object();
  932. Toggle.display = Element.toggle;
  933. /*--------------------------------------------------------------------------*/
  934. Abstract.Insertion = function(adjacency) {
  935. this.adjacency = adjacency;
  936. }
  937. Abstract.Insertion.prototype = {
  938. initialize: function(element, content) {
  939. this.element = $(element);
  940. this.content = content.stripScripts();
  941. if (this.adjacency && this.element.insertAdjacentHTML) {
  942. try {
  943. this.element.insertAdjacentHTML(this.adjacency, this.content);
  944. } catch (e) {
  945. if (this.element.tagName.toLowerCase() == 'tbody') {
  946. this.insertContent(this.contentFromAnonymousTable());
  947. } else {
  948. throw e;
  949. }
  950. }
  951. } else {
  952. this.range = this.element.ownerDocument.createRange();
  953. if (this.initializeRange) this.initializeRange();
  954. this.insertContent([this.range.createContextualFragment(this.content)]);
  955. }
  956. setTimeout(function() {content.evalScripts()}, 10);
  957. },
  958. contentFromAnonymousTable: function() {
  959. var div = document.createElement('div');
  960. div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
  961. return $A(div.childNodes[0].childNodes[0].childNodes);
  962. }
  963. }
  964. var Insertion = new Object();
  965. Insertion.Before = Class.create();
  966. Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
  967. initializeRange: function() {
  968. this.range.setStartBefore(this.element);
  969. },
  970. insertContent: function(fragments) {
  971. fragments.each((function(fragment) {
  972. this.element.parentNode.insertBefore(fragment, this.element);
  973. }).bind(this));
  974. }
  975. });
  976. Insertion.Top = Class.create();
  977. Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
  978. initializeRange: function() {
  979. this.range.selectNodeContents(this.element);
  980. this.range.collapse(true);
  981. },
  982. insertContent: function(fragments) {
  983. fragments.reverse(false).each((function(fragment) {
  984. this.element.insertBefore(fragment, this.element.firstChild);
  985. }).bind(this));
  986. }
  987. });
  988. Insertion.Bottom = Class.create();
  989. Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
  990. initializeRange: function() {
  991. this.range.selectNodeContents(this.element);
  992. this.range.collapse(this.element);
  993. },
  994. insertContent: function(fragments) {
  995. fragments.each((function(fragment) {
  996. this.element.appendChild(fragment);
  997. }).bind(this));
  998. }
  999. });
  1000. Insertion.After = Class.create();
  1001. Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
  1002. initializeRange: function() {
  1003. this.range.setStartAfter(this.element);
  1004. },
  1005. insertContent: function(fragments) {
  1006. fragments.each((function(fragment) {
  1007. this.element.parentNode.insertBefore(fragment,
  1008. this.element.nextSibling);
  1009. }).bind(this));
  1010. }
  1011. });
  1012. /*--------------------------------------------------------------------------*/
  1013. Element.ClassNames = Class.create();
  1014. Element.ClassNames.prototype = {
  1015. initialize: function(element) {
  1016. this.element = $(element);
  1017. },
  1018. _each: function(iterator) {
  1019. this.element.className.split(/\s+/).select(function(name) {
  1020. return name.length > 0;
  1021. })._each(iterator);
  1022. },
  1023. set: function(className) {
  1024. this.element.className = className;
  1025. },
  1026. add: function(classNameToAdd) {
  1027. if (this.include(classNameToAdd)) return;
  1028. this.set(this.toArray().concat(classNameToAdd).join(' '));
  1029. },
  1030. remove: function(classNameToRemove) {
  1031. if (!this.include(classNameToRemove)) return;
  1032. this.set(this.select(function(className) {
  1033. return className != classNameToRemove;
  1034. }).join(' '));
  1035. },
  1036. toString: function() {
  1037. return this.toArray().join(' ');
  1038. }
  1039. }
  1040. Object.extend(Element.ClassNames.prototype, Enumerable);
  1041. var Selector = Class.create();
  1042. Selector.prototype = {
  1043. initialize: function(expression) {
  1044. this.params = {classNames: []};
  1045. this.expression = expression.toString().strip();
  1046. this.parseExpression();
  1047. this.compileMatcher();
  1048. },
  1049. parseExpression: function() {
  1050. function abort(message) { throw 'Parse error in selector: ' + message; }
  1051. if (this.expression == '') abort('empty expression');
  1052. var params = this.params, expr = this.expression, match, modifier, clause, rest;
  1053. while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
  1054. params.attributes = params.attributes || [];
  1055. params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
  1056. expr = match[1];
  1057. }
  1058. if (expr == '*') return this.params.wildcard = true;
  1059. while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
  1060. modifier = match[1], clause = match[2], rest = match[3];
  1061. switch (modifier) {
  1062. case '#': params.id = clause; break;
  1063. case '.': params.classNames.push(clause); break;
  1064. case '':
  1065. case undefined: params.tagName = clause.toUpperCase(); break;
  1066. default: abort(expr.inspect());
  1067. }
  1068. expr = rest;
  1069. }
  1070. if (expr.length > 0) abort(expr.inspect());
  1071. },
  1072. buildMatchExpression: function() {
  1073. var params = this.params, conditions = [], clause;
  1074. if (params.wildcard)
  1075. conditions.push('true');
  1076. if (clause = params.id)
  1077. conditions.push('element.id == ' + clause.inspect());
  1078. if (clause = params.tagName)
  1079. conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
  1080. if ((clause = params.classNames).length > 0)
  1081. for (var i = 0; i < clause.length; i++)
  1082. conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
  1083. if (clause = params.attributes) {
  1084. clause.each(function(attribute) {
  1085. var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
  1086. var splitValueBy = function(delimiter) {
  1087. return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
  1088. }
  1089. switch (attribute.operator) {
  1090. case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break;
  1091. case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
  1092. case '|=': conditions.push(
  1093. splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
  1094. ); break;
  1095. case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
  1096. case '':
  1097. case undefined: conditions.push(value + ' != null'); break;
  1098. default: throw 'Unknown operator ' + attribute.operator + ' in selector';
  1099. }
  1100. });
  1101. }
  1102. return conditions.join(' && ');
  1103. },
  1104. compileMatcher: function() {
  1105. this.match = new Function('element', 'if (!element.tagName) return false; \
  1106. return ' + this.buildMatchExpression());
  1107. },
  1108. findElements: function(scope) {
  1109. var element;
  1110. if (element = $(this.params.id))
  1111. if (this.match(element))
  1112. if (!scope || Element.childOf(element, scope))
  1113. return [element];
  1114. scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
  1115. var results = [];
  1116. for (var i = 0; i < scope.length; i++)
  1117. if (this.match(element = scope[i]))
  1118. results.push(Element.extend(element));
  1119. return results;
  1120. },
  1121. toString: function() {
  1122. return this.expression;
  1123. }
  1124. }
  1125. function $$() {
  1126. return $A(arguments).map(function(expression) {
  1127. return expression.strip().split(/\s+/).inject([null], function(results, expr) {
  1128. var selector = new Selector(expr);
  1129. return results.map(selector.findElements.bind(selector)).flatten();
  1130. });
  1131. }).flatten();
  1132. }
  1133. var Field = {
  1134. clear: function() {
  1135. for (var i = 0; i < arguments.length; i++)
  1136. $(arguments[i]).value = '';
  1137. },
  1138. focus: function(element) {
  1139. $(element).focus();
  1140. },
  1141. present: function() {
  1142. for (var i = 0; i < arguments.length; i++)
  1143. if ($(arguments[i]).value == '') return false;
  1144. return true;
  1145. },
  1146. select: function(element) {
  1147. $(element).select();
  1148. },
  1149. activate: function(element) {
  1150. element = $(element);
  1151. element.focus();
  1152. if (element.select)
  1153. element.select();
  1154. }
  1155. }
  1156. /*--------------------------------------------------------------------------*/
  1157. var Form = {
  1158. serialize: function(form) {
  1159. var elements = Form.getElements($(form));
  1160. var queryComponents = new Array();
  1161. for (var i = 0; i < elements.length; i++) {
  1162. var queryComponent = Form.Element.serialize(elements[i]);
  1163. if (queryComponent)
  1164. queryComponents.push(queryComponent);
  1165. }
  1166. return queryComponents.join('&');
  1167. },
  1168. getElements: function(form) {
  1169. form = $(form);
  1170. var elements = new Array();
  1171. for (tagName in Form.Element.Serializers) {
  1172. var tagElements = form.getElementsByTagName(tagName);
  1173. for (var j = 0; j < tagElements.length; j++)
  1174. elements.push(tagElements[j]);
  1175. }
  1176. return elements;
  1177. },
  1178. getInputs: function(form, typeName, name) {
  1179. form = $(form);
  1180. var inputs = form.getElementsByTagName('input');
  1181. if (!typeName && !name)
  1182. return inputs;
  1183. var matchingInputs = new Array();
  1184. for (var i = 0; i < inputs.length; i++) {
  1185. var input = inputs[i];
  1186. if ((typeName && input.type != typeName) ||
  1187. (name && input.name != name))
  1188. continue;
  1189. matchingInputs.push(input);
  1190. }
  1191. return matchingInputs;
  1192. },
  1193. disable: function(form) {
  1194. var elements = Form.getElements(form);
  1195. for (var i = 0; i < elements.length; i++) {
  1196. var element = elements[i];
  1197. element.blur();
  1198. element.disabled = 'true';
  1199. }
  1200. },
  1201. enable: function(form) {
  1202. var elements = Form.getElements(form);
  1203. for (var i = 0; i < elements.length; i++) {
  1204. var element = elements[i];
  1205. element.disabled = '';
  1206. }
  1207. },
  1208. findFirstElement: function(form) {
  1209. return Form.getElements(form).find(function(element) {
  1210. return element.type != 'hidden' && !element.disabled &&
  1211. ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
  1212. });
  1213. },
  1214. focusFirstElement: function(form) {
  1215. Field.activate(Form.findFirstElement(form));
  1216. },
  1217. reset: function(form) {
  1218. $(form).reset();
  1219. }
  1220. }
  1221. Form.Element = {
  1222. serialize: function(element) {
  1223. element = $(element);
  1224. var method = element.tagName.toLowerCase();
  1225. var parameter = Form.Element.Serializers[method](element);
  1226. if (parameter) {
  1227. var key = encodeURIComponent(parameter[0]);
  1228. if (key.length == 0) return;
  1229. if (parameter[1].constructor != Array)
  1230. parameter[1] = [parameter[1]];
  1231. return parameter[1].map(function(value) {
  1232. return key + '=' + encodeURIComponent(value);
  1233. }).join('&');
  1234. }
  1235. },
  1236. getValue: function(element) {
  1237. element = $(element);
  1238. var method = element.tagName.toLowerCase();
  1239. var parameter = Form.Element.Serializers[method](element);
  1240. if (parameter)
  1241. return parameter[1];
  1242. }
  1243. }
  1244. Form.Element.Serializers = {
  1245. input: function(element) {
  1246. switch (element.type.toLowerCase()) {
  1247. case 'submit':
  1248. case 'hidden':
  1249. case 'password':
  1250. case 'text':
  1251. return Form.Element.Serializers.textarea(element);
  1252. case 'checkbox':
  1253. case 'radio':
  1254. return Form.Element.Serializers.inputSelector(element);
  1255. }
  1256. return false;
  1257. },
  1258. inputSelector: function(element) {
  1259. if (element.checked)
  1260. return [element.name, element.value];
  1261. },
  1262. textarea: function(element) {
  1263. return [element.name, element.value];
  1264. },
  1265. select: function(element) {
  1266. return Form.Element.Serializers[element.type == 'select-one' ?
  1267. 'selectOne' : 'selectMany'](element);
  1268. },
  1269. selectOne: function(element) {
  1270. var value = '', opt, index = element.selectedIndex;
  1271. if (index >= 0) {
  1272. opt = element.options[index];
  1273. value = opt.value;
  1274. if (!value && !('value' in opt))
  1275. value = opt.text;
  1276. }
  1277. return [element.name, value];
  1278. },
  1279. selectMany: function(element) {
  1280. var value = new Array();
  1281. for (var i = 0; i < element.length; i++) {
  1282. var opt = element.options[i];
  1283. if (opt.selected) {
  1284. var optValue = opt.value;
  1285. if (!optValue && !('value' in opt))
  1286. optValue = opt.text;
  1287. value.push(optValue);
  1288. }
  1289. }
  1290. return [element.name, value];
  1291. }
  1292. }
  1293. /*--------------------------------------------------------------------------*/
  1294. var $F = Form.Element.getValue;
  1295. /*--------------------------------------------------------------------------*/
  1296. Abstract.TimedObserver = function() {}
  1297. Abstract.TimedObserver.prototype = {
  1298. initialize: function(element, frequency, callback) {
  1299. this.frequency = frequency;
  1300. this.element = $(element);
  1301. this.callback = callback;
  1302. this.lastValue = this.getValue();
  1303. this.registerCallback();
  1304. },
  1305. registerCallback: function() {
  1306. setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  1307. },
  1308. onTimerEvent: function() {
  1309. var value = this.getValue();
  1310. if (this.lastValue != value) {
  1311. this.callback(this.element, value);
  1312. this.lastValue = value;
  1313. }
  1314. }
  1315. }
  1316. Form.Element.Observer = Class.create();
  1317. Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  1318. getValue: function() {
  1319. return Form.Element.getValue(this.element);
  1320. }
  1321. });
  1322. Form.Observer = Class.create();
  1323. Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  1324. getValue: function() {
  1325. return Form.serialize(this.element);
  1326. }
  1327. });
  1328. /*--------------------------------------------------------------------------*/
  1329. Abstract.EventObserver = function() {}
  1330. Abstract.EventObserver.prototype = {
  1331. initialize: function(element, callback) {
  1332. this.element = $(element);
  1333. this.callback = callback;
  1334. this.lastValue = this.getValue();
  1335. if (this.element.tagName.toLowerCase() == 'form')
  1336. this.registerFormCallbacks();
  1337. else
  1338. this.registerCallback(this.element);
  1339. },
  1340. onElementEvent: function() {
  1341. var value = this.getValue();
  1342. if (this.lastValue != value) {
  1343. this.callback(this.element, value);
  1344. this.lastValue = value;
  1345. }
  1346. },
  1347. registerFormCallbacks: function() {
  1348. var elements = Form.getElements(this.element);
  1349. for (var i = 0; i < elements.length; i++)
  1350. this.registerCallback(elements[i]);
  1351. },
  1352. registerCallback: function(element) {
  1353. if (element.type) {
  1354. switch (element.type.toLowerCase()) {
  1355. case 'checkbox':
  1356. case 'radio':
  1357. Event.observe(element, 'click', this.onElementEvent.bind(this));
  1358. break;
  1359. case 'password':
  1360. case 'text':
  1361. case 'textarea':
  1362. case 'select-one':
  1363. case 'select-multiple':
  1364. Event.observe(element, 'change', this.onElementEvent.bind(this));
  1365. break;
  1366. }
  1367. }
  1368. }
  1369. }
  1370. Form.Element.EventObserver = Class.create();
  1371. Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  1372. getValue: function() {
  1373. return Form.Element.getValue(this.element);
  1374. }
  1375. });
  1376. Form.EventObserver = Class.create();
  1377. Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  1378. getValue: function() {
  1379. return Form.serialize(this.element);
  1380. }
  1381. });
  1382. if (!window.Event) {
  1383. var Event = new Object();
  1384. }
  1385. Object.extend(Event, {
  1386. KEY_BACKSPACE: 8,
  1387. KEY_TAB: 9,
  1388. KEY_RETURN: 13,
  1389. KEY_ESC: 27,
  1390. KEY_LEFT: 37,
  1391. KEY_UP: 38,
  1392. KEY_RIGHT: 39,
  1393. KEY_DOWN: 40,
  1394. KEY_DELETE: 46,
  1395. element: function(event) {
  1396. return event.target || event.srcElement;
  1397. },
  1398. isLeftClick: function(event) {
  1399. return (((event.which) && (event.which == 1)) ||
  1400. ((event.button) && (event.button == 1)));
  1401. },
  1402. pointerX: function(event) {
  1403. return event.pageX || (event.clientX +
  1404. (document.documentElement.scrollLeft || document.body.scrollLeft));
  1405. },
  1406. pointerY: function(event) {
  1407. return event.pageY || (event.clientY +
  1408. (document.documentElement.scrollTop || document.body.scrollTop));
  1409. },
  1410. stop: function(event) {
  1411. if (event.preventDefault) {
  1412. event.preventDefault();
  1413. event.stopPropagation();
  1414. } else {
  1415. event.returnValue = false;
  1416. event.cancelBubble = true;
  1417. }
  1418. },
  1419. // find the first node with the given tagName, starting from the
  1420. // node the event was triggered on; traverses the DOM upwards
  1421. findElement: function(event, tagName) {
  1422. var element = Event.element(event);
  1423. while (element.parentNode && (!element.tagName ||
  1424. (element.tagName.toUpperCase() != tagName.toUpperCase())))
  1425. element = element.parentNode;
  1426. return element;
  1427. },
  1428. observers: false,
  1429. _observeAndCache: function(element, name, observer, useCapture) {
  1430. if (!this.observers) this.observers = [];
  1431. if (element.addEventListener) {
  1432. this.observers.push([element, name, observer, useCapture]);
  1433. element.addEventListener(name, observer, useCapture);
  1434. } else if (element.attachEvent) {
  1435. this.observers.push([element, name, observer, useCapture]);
  1436. element.attachEvent('on' + name, observer);
  1437. }
  1438. },
  1439. unloadCache: function() {
  1440. if (!Event.observers) return;
  1441. for (var i = 0; i < Event.observers.length; i++) {
  1442. Event.stopObserving.apply(this, Event.observers[i]);
  1443. Event.observers[i][0] = null;
  1444. }
  1445. Event.observers = false;
  1446. },
  1447. observe: function(element, name, observer, useCapture) {
  1448. var element = $(element);
  1449. useCapture = useCapture || false;
  1450. if (name == 'keypress' &&
  1451. (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
  1452. || element.attachEvent))
  1453. name = 'keydown';
  1454. this._observeAndCache(element, name, observer, useCapture);
  1455. },
  1456. stopObserving: function(element, name, observer, useCapture) {
  1457. var element = $(element);
  1458. useCapture = useCapture || false;
  1459. if (name == 'keypress' &&
  1460. (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
  1461. || element.detachEvent))
  1462. name = 'keydown';
  1463. if (element.removeEventListener) {
  1464. element.removeEventListener(name, observer, useCapture);
  1465. } else if (element.detachEvent) {
  1466. element.detachEvent('on' + name, observer);
  1467. }
  1468. }
  1469. });
  1470. /* prevent memory leaks in IE */
  1471. Event.observe(window, 'unload', Event.unloadCache, false);
  1472. var Position = {
  1473. // set to true if needed, warning: firefox performance problems
  1474. // NOT neeeded for page scrolling, only if draggable contained in
  1475. // scrollable elements
  1476. includeScrollOffsets: false,
  1477. // must be called before calling withinIncludingScrolloffset, every time the
  1478. // page is scrolled
  1479. prepare: function() {
  1480. this.deltaX = window.pageXOffset
  1481. || document.documentElement.scrollLeft
  1482. || document.body.scrollLeft
  1483. || 0;
  1484. this.deltaY = window.pageYOffset
  1485. || document.documentElement.scrollTop
  1486. || document.body.scrollTop
  1487. || 0;
  1488. },
  1489. realOffset: function(element) {
  1490. var valueT = 0, valueL = 0;
  1491. do {
  1492. valueT += element.scrollTop || 0;
  1493. valueL += element.scrollLeft || 0;
  1494. element = element.parentNode;
  1495. } while (element);
  1496. return [valueL, valueT];
  1497. },
  1498. cumulativeOffset: function(element) {
  1499. var valueT = 0, valueL = 0;
  1500. do {
  1501. valueT += element.offsetTop || 0;
  1502. valueL += element.offsetLeft || 0;
  1503. element = element.offsetParent;
  1504. } while (element);
  1505. return [valueL, valueT];
  1506. },
  1507. positionedOffset: function(element) {
  1508. var valueT = 0, valueL = 0;
  1509. do {
  1510. valueT += element.offsetTop || 0;
  1511. valueL += element.offsetLeft || 0;
  1512. element = element.offsetParent;
  1513. if (element) {
  1514. p = Element.getStyle(element, 'position');
  1515. if (p == 'relative' || p == 'absolute') break;
  1516. }
  1517. } while (element);
  1518. return [valueL, valueT];
  1519. },
  1520. offsetParent: function(element) {
  1521. if (element.offsetParent) return element.offsetParent;
  1522. if (element == document.body) return element;
  1523. while ((element = element.parentNode) && element != document.body)
  1524. if (Element.getStyle(element, 'position') != 'static')
  1525. return element;
  1526. return document.body;
  1527. },
  1528. // caches x/y coordinate pair to use with overlap
  1529. within: function(element, x, y) {
  1530. if (this.includeScrollOffsets)
  1531. return this.withinIncludingScrolloffsets(element, x, y);
  1532. this.xcomp = x;
  1533. this.ycomp = y;
  1534. this.offset = this.cumulativeOffset(element);
  1535. return (y >= this.offset[1] &&
  1536. y < this.offset[1] + element.offsetHeight &&
  1537. x >= this.offset[0] &&
  1538. x < this.offset[0] + element.offsetWidth);
  1539. },
  1540. withinIncludingScrolloffsets: function(element, x, y) {
  1541. var offsetcache = this.realOffset(element);
  1542. this.xcomp = x + offsetcache[0] - this.deltaX;
  1543. this.ycomp = y + offsetcache[1] - this.deltaY;
  1544. this.offset = this.cumulativeOffset(element);
  1545. return (this.ycomp >= this.offset[1] &&
  1546. this.ycomp < this.offset[1] + element.offsetHeight &&
  1547. this.xcomp >= this.offset[0] &&
  1548. this.xcomp < this.offset[0] + element.offsetWidth);
  1549. },
  1550. // within must be called directly before
  1551. overlap: function(mode, element) {
  1552. if (!mode) return 0;
  1553. if (mode == 'vertical')
  1554. return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
  1555. element.offsetHeight;
  1556. if (mode == 'horizontal')
  1557. return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
  1558. element.offsetWidth;
  1559. },
  1560. clone: function(source, target) {
  1561. source = $(source);
  1562. target = $(target);
  1563. target.style.position = 'absolute';
  1564. var offsets = this.cumulativeOffset(source);
  1565. target.style.top = offsets[1] + 'px';
  1566. target.style.left = offsets[0] + 'px';
  1567. target.style.width = source.offsetWidth + 'px';
  1568. target.style.height = source.offsetHeight + 'px';
  1569. },
  1570. page: function(forElement) {
  1571. var valueT = 0, valueL = 0;
  1572. var element = forElement;
  1573. do {
  1574. valueT += element.offsetTop || 0;
  1575. valueL += element.offsetLeft || 0;
  1576. // Safari fix
  1577. if (element.offsetParent==document.body)
  1578. if (Element.getStyle(element,'position')=='absolute') break;
  1579. } while (element = element.offsetParent);
  1580. element = forElement;
  1581. do {
  1582. valueT -= element.scrollTop || 0;
  1583. valueL -= element.scrollLeft || 0;
  1584. } while (element = element.parentNode);
  1585. return [valueL, valueT];
  1586. },
  1587. clone: function(source, target) {
  1588. var options = Object.extend({
  1589. setLeft: true,
  1590. setTop: true,
  1591. setWidth: true,
  1592. setHeight: true,
  1593. offsetTop: 0,
  1594. offsetLeft: 0
  1595. }, arguments[2] || {})
  1596. // find page position of source
  1597. source = $(source);
  1598. var p = Position.page(source);
  1599. // find coordinate system to use
  1600. target = $(target);
  1601. var delta = [0, 0];
  1602. var parent = null;
  1603. // delta [0,0] will do fine with position: fixed elements,
  1604. // position:absolute needs offsetParent deltas
  1605. if (Element.getStyle(target,'position') == 'absolute') {
  1606. parent = Position.offsetParent(target);
  1607. delta = Position.page(parent);
  1608. }
  1609. // correct by body offsets (fixes Safari)
  1610. if (parent == document.body) {
  1611. delta[0] -= document.body.offsetLeft;
  1612. delta[1] -= document.body.offsetTop;
  1613. }
  1614. // set position
  1615. if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
  1616. if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
  1617. if(options.setWidth) target.style.width = source.offsetWidth + 'px';
  1618. if(options.setHeight) target.style.height = source.offsetHeight + 'px';
  1619. },
  1620. absolutize: function(element) {
  1621. element = $(element);
  1622. if (element.style.position == 'absolute') return;
  1623. Position.prepare();
  1624. var offsets = Position.positionedOffset(element);
  1625. var top = offsets[1];
  1626. var left = offsets[0];
  1627. var width = element.clientWidth;
  1628. var height = element.clientHeight;
  1629. element._originalLeft = left - parseFloat(element.style.left || 0);
  1630. element._originalTop = top - parseFloat(element.style.top || 0);
  1631. element._originalWidth = element.style.width;
  1632. element._originalHeight = element.style.height;
  1633. element.style.position = 'absolute';
  1634. element.style.top = top + 'px';;
  1635. element.style.left = left + 'px';;
  1636. element.style.width = width + 'px';;
  1637. element.style.height = height + 'px';;
  1638. },
  1639. relativize: function(element) {
  1640. element = $(element);
  1641. if (element.style.position == 'relative') return;
  1642. Position.prepare();
  1643. element.style.position = 'relative';
  1644. var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
  1645. var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
  1646. element.style.top = top + 'px';
  1647. element.style.left = left + 'px';
  1648. element.style.height = element._originalHeight;
  1649. element.style.width = element._originalWidth;
  1650. }
  1651. }
  1652. // Safari returns margins on body which is incorrect if the child is absolutely
  1653. // positioned. For performance reasons, redefine Position.cumulativeOffset for
  1654. // KHTML/WebKit only.
  1655. if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
  1656. Position.cumulativeOffset = function(element) {
  1657. var valueT = 0, valueL = 0;
  1658. do {
  1659. valueT += element.offsetTop || 0;
  1660. valueL += element.offsetLeft || 0;
  1661. if (element.offsetParent == document.body)
  1662. if (Element.getStyle(element, 'position') == 'absolute') break;
  1663. element = element.offsetParent;
  1664. } while (element);
  1665. return [valueL, valueT];
  1666. }
  1667. }