PageRenderTime 66ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/testing/selenium-core/lib/prototype.js

http://datanucleus-appengine.googlecode.com/
JavaScript | 2006 lines | 1775 code | 206 blank | 25 comment | 199 complexity | 94aac37d1eb793c17674e1963d3fca0d MD5 | raw file
Possible License(s): Apache-2.0
  1. /* Prototype JavaScript framework, version 1.5.0_rc0
  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_rc0',
  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 (var 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(function(script) { return eval(script) });
  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 (result == undefined || value >= result)
  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 (result == undefined || value < result)
  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. if (!Array.prototype._reverse)
  385. Array.prototype._reverse = Array.prototype.reverse;
  386. Object.extend(Array.prototype, {
  387. _each: function(iterator) {
  388. for (var i = 0; i < this.length; i++)
  389. iterator(this[i]);
  390. },
  391. clear: function() {
  392. this.length = 0;
  393. return this;
  394. },
  395. first: function() {
  396. return this[0];
  397. },
  398. last: function() {
  399. return this[this.length - 1];
  400. },
  401. compact: function() {
  402. return this.select(function(value) {
  403. return value != undefined || value != null;
  404. });
  405. },
  406. flatten: function() {
  407. return this.inject([], function(array, value) {
  408. return array.concat(value && value.constructor == Array ?
  409. value.flatten() : [value]);
  410. });
  411. },
  412. without: function() {
  413. var values = $A(arguments);
  414. return this.select(function(value) {
  415. return !values.include(value);
  416. });
  417. },
  418. indexOf: function(object) {
  419. for (var i = 0; i < this.length; i++)
  420. if (this[i] == object) return i;
  421. return -1;
  422. },
  423. reverse: function(inline) {
  424. return (inline !== false ? this : this.toArray())._reverse();
  425. },
  426. inspect: function() {
  427. return '[' + this.map(Object.inspect).join(', ') + ']';
  428. }
  429. });
  430. var Hash = {
  431. _each: function(iterator) {
  432. for (var key in this) {
  433. var value = this[key];
  434. if (typeof value == 'function') continue;
  435. var pair = [key, value];
  436. pair.key = key;
  437. pair.value = value;
  438. iterator(pair);
  439. }
  440. },
  441. keys: function() {
  442. return this.pluck('key');
  443. },
  444. values: function() {
  445. return this.pluck('value');
  446. },
  447. merge: function(hash) {
  448. return $H(hash).inject($H(this), function(mergedHash, pair) {
  449. mergedHash[pair.key] = pair.value;
  450. return mergedHash;
  451. });
  452. },
  453. toQueryString: function() {
  454. return this.map(function(pair) {
  455. return pair.map(encodeURIComponent).join('=');
  456. }).join('&');
  457. },
  458. inspect: function() {
  459. return '#<Hash:{' + this.map(function(pair) {
  460. return pair.map(Object.inspect).join(': ');
  461. }).join(', ') + '}>';
  462. }
  463. }
  464. function $H(object) {
  465. var hash = Object.extend({}, object || {});
  466. Object.extend(hash, Enumerable);
  467. Object.extend(hash, Hash);
  468. return hash;
  469. }
  470. ObjectRange = Class.create();
  471. Object.extend(ObjectRange.prototype, Enumerable);
  472. Object.extend(ObjectRange.prototype, {
  473. initialize: function(start, end, exclusive) {
  474. this.start = start;
  475. this.end = end;
  476. this.exclusive = exclusive;
  477. },
  478. _each: function(iterator) {
  479. var value = this.start;
  480. do {
  481. iterator(value);
  482. value = value.succ();
  483. } while (this.include(value));
  484. },
  485. include: function(value) {
  486. if (value < this.start)
  487. return false;
  488. if (this.exclusive)
  489. return value < this.end;
  490. return value <= this.end;
  491. }
  492. });
  493. var $R = function(start, end, exclusive) {
  494. return new ObjectRange(start, end, exclusive);
  495. }
  496. var Ajax = {
  497. getTransport: function() {
  498. return Try.these(
  499. function() {return new XMLHttpRequest()},
  500. function() {return new ActiveXObject('Msxml2.XMLHTTP')},
  501. function() {return new ActiveXObject('Microsoft.XMLHTTP')}
  502. ) || false;
  503. },
  504. activeRequestCount: 0
  505. }
  506. Ajax.Responders = {
  507. responders: [],
  508. _each: function(iterator) {
  509. this.responders._each(iterator);
  510. },
  511. register: function(responderToAdd) {
  512. if (!this.include(responderToAdd))
  513. this.responders.push(responderToAdd);
  514. },
  515. unregister: function(responderToRemove) {
  516. this.responders = this.responders.without(responderToRemove);
  517. },
  518. dispatch: function(callback, request, transport, json) {
  519. this.each(function(responder) {
  520. if (responder[callback] && typeof responder[callback] == 'function') {
  521. try {
  522. responder[callback].apply(responder, [request, transport, json]);
  523. } catch (e) {}
  524. }
  525. });
  526. }
  527. };
  528. Object.extend(Ajax.Responders, Enumerable);
  529. Ajax.Responders.register({
  530. onCreate: function() {
  531. Ajax.activeRequestCount++;
  532. },
  533. onComplete: function() {
  534. Ajax.activeRequestCount--;
  535. }
  536. });
  537. Ajax.Base = function() {};
  538. Ajax.Base.prototype = {
  539. setOptions: function(options) {
  540. this.options = {
  541. method: 'post',
  542. asynchronous: true,
  543. contentType: 'application/x-www-form-urlencoded',
  544. parameters: ''
  545. }
  546. Object.extend(this.options, options || {});
  547. },
  548. responseIsSuccess: function() {
  549. return this.transport.status == undefined
  550. || this.transport.status == 0
  551. || (this.transport.status >= 200 && this.transport.status < 300);
  552. },
  553. responseIsFailure: function() {
  554. return !this.responseIsSuccess();
  555. }
  556. }
  557. Ajax.Request = Class.create();
  558. Ajax.Request.Events =
  559. ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
  560. Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
  561. initialize: function(url, options) {
  562. this.transport = Ajax.getTransport();
  563. this.setOptions(options);
  564. this.request(url);
  565. },
  566. request: function(url) {
  567. var parameters = this.options.parameters || '';
  568. if (parameters.length > 0) parameters += '&_=';
  569. try {
  570. this.url = url;
  571. if (this.options.method == 'get' && parameters.length > 0)
  572. this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
  573. Ajax.Responders.dispatch('onCreate', this, this.transport);
  574. this.transport.open(this.options.method, this.url,
  575. this.options.asynchronous);
  576. if (this.options.asynchronous) {
  577. this.transport.onreadystatechange = this.onStateChange.bind(this);
  578. setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
  579. }
  580. this.setRequestHeaders();
  581. var body = this.options.postBody ? this.options.postBody : parameters;
  582. this.transport.send(this.options.method == 'post' ? body : null);
  583. } catch (e) {
  584. this.dispatchException(e);
  585. }
  586. },
  587. setRequestHeaders: function() {
  588. var requestHeaders =
  589. ['X-Requested-With', 'XMLHttpRequest',
  590. 'X-Prototype-Version', Prototype.Version,
  591. 'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
  592. if (this.options.method == 'post') {
  593. requestHeaders.push('Content-type', this.options.contentType);
  594. /* Force "Connection: close" for Mozilla browsers to work around
  595. * a bug where XMLHttpReqeuest sends an incorrect Content-length
  596. * header. See Mozilla Bugzilla #246651.
  597. */
  598. if (this.transport.overrideMimeType)
  599. requestHeaders.push('Connection', 'close');
  600. }
  601. if (this.options.requestHeaders)
  602. requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
  603. for (var i = 0; i < requestHeaders.length; i += 2)
  604. this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
  605. },
  606. onStateChange: function() {
  607. var readyState = this.transport.readyState;
  608. if (readyState != 1)
  609. this.respondToReadyState(this.transport.readyState);
  610. },
  611. header: function(name) {
  612. try {
  613. return this.transport.getResponseHeader(name);
  614. } catch (e) {}
  615. },
  616. evalJSON: function() {
  617. try {
  618. return eval('(' + this.header('X-JSON') + ')');
  619. } catch (e) {}
  620. },
  621. evalResponse: function() {
  622. try {
  623. return eval(this.transport.responseText);
  624. } catch (e) {
  625. this.dispatchException(e);
  626. }
  627. },
  628. respondToReadyState: function(readyState) {
  629. var event = Ajax.Request.Events[readyState];
  630. var transport = this.transport, json = this.evalJSON();
  631. if (event == 'Complete') {
  632. try {
  633. (this.options['on' + this.transport.status]
  634. || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
  635. || Prototype.emptyFunction)(transport, json);
  636. } catch (e) {
  637. this.dispatchException(e);
  638. }
  639. if ((this.header('Content-type') || '').match(/^text\/javascript/i))
  640. this.evalResponse();
  641. }
  642. try {
  643. (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
  644. Ajax.Responders.dispatch('on' + event, this, transport, json);
  645. } catch (e) {
  646. this.dispatchException(e);
  647. }
  648. /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
  649. if (event == 'Complete')
  650. this.transport.onreadystatechange = Prototype.emptyFunction;
  651. },
  652. dispatchException: function(exception) {
  653. (this.options.onException || Prototype.emptyFunction)(this, exception);
  654. Ajax.Responders.dispatch('onException', this, exception);
  655. }
  656. });
  657. Ajax.Updater = Class.create();
  658. Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
  659. initialize: function(container, url, options) {
  660. this.containers = {
  661. success: container.success ? $(container.success) : $(container),
  662. failure: container.failure ? $(container.failure) :
  663. (container.success ? null : $(container))
  664. }
  665. this.transport = Ajax.getTransport();
  666. this.setOptions(options);
  667. var onComplete = this.options.onComplete || Prototype.emptyFunction;
  668. this.options.onComplete = (function(transport, object) {
  669. this.updateContent();
  670. onComplete(transport, object);
  671. }).bind(this);
  672. this.request(url);
  673. },
  674. updateContent: function() {
  675. var receiver = this.responseIsSuccess() ?
  676. this.containers.success : this.containers.failure;
  677. var response = this.transport.responseText;
  678. if (!this.options.evalScripts)
  679. response = response.stripScripts();
  680. if (receiver) {
  681. if (this.options.insertion) {
  682. new this.options.insertion(receiver, response);
  683. } else {
  684. Element.update(receiver, response);
  685. }
  686. }
  687. if (this.responseIsSuccess()) {
  688. if (this.onComplete)
  689. setTimeout(this.onComplete.bind(this), 10);
  690. }
  691. }
  692. });
  693. Ajax.PeriodicalUpdater = Class.create();
  694. Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
  695. initialize: function(container, url, options) {
  696. this.setOptions(options);
  697. this.onComplete = this.options.onComplete;
  698. this.frequency = (this.options.frequency || 2);
  699. this.decay = (this.options.decay || 1);
  700. this.updater = {};
  701. this.container = container;
  702. this.url = url;
  703. this.start();
  704. },
  705. start: function() {
  706. this.options.onComplete = this.updateComplete.bind(this);
  707. this.onTimerEvent();
  708. },
  709. stop: function() {
  710. this.updater.onComplete = undefined;
  711. clearTimeout(this.timer);
  712. (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  713. },
  714. updateComplete: function(request) {
  715. if (this.options.decay) {
  716. this.decay = (request.responseText == this.lastText ?
  717. this.decay * this.options.decay : 1);
  718. this.lastText = request.responseText;
  719. }
  720. this.timer = setTimeout(this.onTimerEvent.bind(this),
  721. this.decay * this.frequency * 1000);
  722. },
  723. onTimerEvent: function() {
  724. this.updater = new Ajax.Updater(this.container, this.url, this.options);
  725. }
  726. });
  727. function $() {
  728. var results = [], element;
  729. for (var i = 0; i < arguments.length; i++) {
  730. element = arguments[i];
  731. if (typeof element == 'string')
  732. element = document.getElementById(element);
  733. results.push(Element.extend(element));
  734. }
  735. return results.length < 2 ? results[0] : results;
  736. }
  737. document.getElementsByClassName = function(className, parentElement) {
  738. var children = ($(parentElement) || document.body).getElementsByTagName('*');
  739. return $A(children).inject([], function(elements, child) {
  740. if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
  741. elements.push(Element.extend(child));
  742. return elements;
  743. });
  744. }
  745. /*--------------------------------------------------------------------------*/
  746. if (!window.Element)
  747. var Element = new Object();
  748. Element.extend = function(element) {
  749. if (!element) return;
  750. if (_nativeExtensions) return element;
  751. if (!element._extended && element.tagName && element != window) {
  752. var methods = Element.Methods, cache = Element.extend.cache;
  753. for (property in methods) {
  754. var value = methods[property];
  755. if (typeof value == 'function')
  756. element[property] = cache.findOrStore(value);
  757. }
  758. }
  759. element._extended = true;
  760. return element;
  761. }
  762. Element.extend.cache = {
  763. findOrStore: function(value) {
  764. return this[value] = this[value] || function() {
  765. return value.apply(null, [this].concat($A(arguments)));
  766. }
  767. }
  768. }
  769. Element.Methods = {
  770. visible: function(element) {
  771. return $(element).style.display != 'none';
  772. },
  773. toggle: function() {
  774. for (var i = 0; i < arguments.length; i++) {
  775. var element = $(arguments[i]);
  776. Element[Element.visible(element) ? 'hide' : 'show'](element);
  777. }
  778. },
  779. hide: function() {
  780. for (var i = 0; i < arguments.length; i++) {
  781. var element = $(arguments[i]);
  782. element.style.display = 'none';
  783. }
  784. },
  785. show: function() {
  786. for (var i = 0; i < arguments.length; i++) {
  787. var element = $(arguments[i]);
  788. element.style.display = '';
  789. }
  790. },
  791. remove: function(element) {
  792. element = $(element);
  793. element.parentNode.removeChild(element);
  794. },
  795. update: function(element, html) {
  796. $(element).innerHTML = html.stripScripts();
  797. setTimeout(function() {html.evalScripts()}, 10);
  798. },
  799. replace: function(element, html) {
  800. element = $(element);
  801. if (element.outerHTML) {
  802. element.outerHTML = html.stripScripts();
  803. } else {
  804. var range = element.ownerDocument.createRange();
  805. range.selectNodeContents(element);
  806. element.parentNode.replaceChild(
  807. range.createContextualFragment(html.stripScripts()), element);
  808. }
  809. setTimeout(function() {html.evalScripts()}, 10);
  810. },
  811. getHeight: function(element) {
  812. element = $(element);
  813. return element.offsetHeight;
  814. },
  815. classNames: function(element) {
  816. return new Element.ClassNames(element);
  817. },
  818. hasClassName: function(element, className) {
  819. if (!(element = $(element))) return;
  820. return Element.classNames(element).include(className);
  821. },
  822. addClassName: function(element, className) {
  823. if (!(element = $(element))) return;
  824. return Element.classNames(element).add(className);
  825. },
  826. removeClassName: function(element, className) {
  827. if (!(element = $(element))) return;
  828. return Element.classNames(element).remove(className);
  829. },
  830. // removes whitespace-only text node children
  831. cleanWhitespace: function(element) {
  832. element = $(element);
  833. for (var i = 0; i < element.childNodes.length; i++) {
  834. var node = element.childNodes[i];
  835. if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
  836. Element.remove(node);
  837. }
  838. },
  839. empty: function(element) {
  840. return $(element).innerHTML.match(/^\s*$/);
  841. },
  842. childOf: function(element, ancestor) {
  843. element = $(element), ancestor = $(ancestor);
  844. while (element = element.parentNode)
  845. if (element == ancestor) return true;
  846. return false;
  847. },
  848. scrollTo: function(element) {
  849. element = $(element);
  850. var x = element.x ? element.x : element.offsetLeft,
  851. y = element.y ? element.y : element.offsetTop;
  852. window.scrollTo(x, y);
  853. },
  854. getStyle: function(element, style) {
  855. element = $(element);
  856. var value = element.style[style.camelize()];
  857. if (!value) {
  858. if (document.defaultView && document.defaultView.getComputedStyle) {
  859. var css = document.defaultView.getComputedStyle(element, null);
  860. value = css ? css.getPropertyValue(style) : null;
  861. } else if (element.currentStyle) {
  862. value = element.currentStyle[style.camelize()];
  863. }
  864. }
  865. if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
  866. if (Element.getStyle(element, 'position') == 'static') value = 'auto';
  867. return value == 'auto' ? null : value;
  868. },
  869. setStyle: function(element, style) {
  870. element = $(element);
  871. for (var name in style)
  872. element.style[name.camelize()] = style[name];
  873. },
  874. getDimensions: function(element) {
  875. element = $(element);
  876. if (Element.getStyle(element, 'display') != 'none')
  877. return {width: element.offsetWidth, height: element.offsetHeight};
  878. // All *Width and *Height properties give 0 on elements with display none,
  879. // so enable the element temporarily
  880. var els = element.style;
  881. var originalVisibility = els.visibility;
  882. var originalPosition = els.position;
  883. els.visibility = 'hidden';
  884. els.position = 'absolute';
  885. els.display = '';
  886. var originalWidth = element.clientWidth;
  887. var originalHeight = element.clientHeight;
  888. els.display = 'none';
  889. els.position = originalPosition;
  890. els.visibility = originalVisibility;
  891. return {width: originalWidth, height: originalHeight};
  892. },
  893. makePositioned: function(element) {
  894. element = $(element);
  895. var pos = Element.getStyle(element, 'position');
  896. if (pos == 'static' || !pos) {
  897. element._madePositioned = true;
  898. element.style.position = 'relative';
  899. // Opera returns the offset relative to the positioning context, when an
  900. // element is position relative but top and left have not been defined
  901. if (window.opera) {
  902. element.style.top = 0;
  903. element.style.left = 0;
  904. }
  905. }
  906. },
  907. undoPositioned: function(element) {
  908. element = $(element);
  909. if (element._madePositioned) {
  910. element._madePositioned = undefined;
  911. element.style.position =
  912. element.style.top =
  913. element.style.left =
  914. element.style.bottom =
  915. element.style.right = '';
  916. }
  917. },
  918. makeClipping: function(element) {
  919. element = $(element);
  920. if (element._overflow) return;
  921. element._overflow = element.style.overflow;
  922. if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
  923. element.style.overflow = 'hidden';
  924. },
  925. undoClipping: function(element) {
  926. element = $(element);
  927. if (element._overflow) return;
  928. element.style.overflow = element._overflow;
  929. element._overflow = undefined;
  930. }
  931. }
  932. Object.extend(Element, Element.Methods);
  933. var _nativeExtensions = false;
  934. if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
  935. var HTMLElement = {}
  936. HTMLElement.prototype = document.createElement('div').__proto__;
  937. }
  938. Element.addMethods = function(methods) {
  939. Object.extend(Element.Methods, methods || {});
  940. if(typeof HTMLElement != 'undefined') {
  941. var methods = Element.Methods, cache = Element.extend.cache;
  942. for (property in methods) {
  943. var value = methods[property];
  944. if (typeof value == 'function')
  945. HTMLElement.prototype[property] = cache.findOrStore(value);
  946. }
  947. _nativeExtensions = true;
  948. }
  949. }
  950. Element.addMethods();
  951. var Toggle = new Object();
  952. Toggle.display = Element.toggle;
  953. /*--------------------------------------------------------------------------*/
  954. Abstract.Insertion = function(adjacency) {
  955. this.adjacency = adjacency;
  956. }
  957. Abstract.Insertion.prototype = {
  958. initialize: function(element, content) {
  959. this.element = $(element);
  960. this.content = content.stripScripts();
  961. if (this.adjacency && this.element.insertAdjacentHTML) {
  962. try {
  963. this.element.insertAdjacentHTML(this.adjacency, this.content);
  964. } catch (e) {
  965. var tagName = this.element.tagName.toLowerCase();
  966. if (tagName == 'tbody' || tagName == 'tr') {
  967. this.insertContent(this.contentFromAnonymousTable());
  968. } else {
  969. throw e;
  970. }
  971. }
  972. } else {
  973. this.range = this.element.ownerDocument.createRange();
  974. if (this.initializeRange) this.initializeRange();
  975. this.insertContent([this.range.createContextualFragment(this.content)]);
  976. }
  977. setTimeout(function() {content.evalScripts()}, 10);
  978. },
  979. contentFromAnonymousTable: function() {
  980. var div = document.createElement('div');
  981. div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
  982. return $A(div.childNodes[0].childNodes[0].childNodes);
  983. }
  984. }
  985. var Insertion = new Object();
  986. Insertion.Before = Class.create();
  987. Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
  988. initializeRange: function() {
  989. this.range.setStartBefore(this.element);
  990. },
  991. insertContent: function(fragments) {
  992. fragments.each((function(fragment) {
  993. this.element.parentNode.insertBefore(fragment, this.element);
  994. }).bind(this));
  995. }
  996. });
  997. Insertion.Top = Class.create();
  998. Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
  999. initializeRange: function() {
  1000. this.range.selectNodeContents(this.element);
  1001. this.range.collapse(true);
  1002. },
  1003. insertContent: function(fragments) {
  1004. fragments.reverse(false).each((function(fragment) {
  1005. this.element.insertBefore(fragment, this.element.firstChild);
  1006. }).bind(this));
  1007. }
  1008. });
  1009. Insertion.Bottom = Class.create();
  1010. Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
  1011. initializeRange: function() {
  1012. this.range.selectNodeContents(this.element);
  1013. this.range.collapse(this.element);
  1014. },
  1015. insertContent: function(fragments) {
  1016. fragments.each((function(fragment) {
  1017. this.element.appendChild(fragment);
  1018. }).bind(this));
  1019. }
  1020. });
  1021. Insertion.After = Class.create();
  1022. Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
  1023. initializeRange: function() {
  1024. this.range.setStartAfter(this.element);
  1025. },
  1026. insertContent: function(fragments) {
  1027. fragments.each((function(fragment) {
  1028. this.element.parentNode.insertBefore(fragment,
  1029. this.element.nextSibling);
  1030. }).bind(this));
  1031. }
  1032. });
  1033. /*--------------------------------------------------------------------------*/
  1034. Element.ClassNames = Class.create();
  1035. Element.ClassNames.prototype = {
  1036. initialize: function(element) {
  1037. this.element = $(element);
  1038. },
  1039. _each: function(iterator) {
  1040. this.element.className.split(/\s+/).select(function(name) {
  1041. return name.length > 0;
  1042. })._each(iterator);
  1043. },
  1044. set: function(className) {
  1045. this.element.className = className;
  1046. },
  1047. add: function(classNameToAdd) {
  1048. if (this.include(classNameToAdd)) return;
  1049. this.set(this.toArray().concat(classNameToAdd).join(' '));
  1050. },
  1051. remove: function(classNameToRemove) {
  1052. if (!this.include(classNameToRemove)) return;
  1053. this.set(this.select(function(className) {
  1054. return className != classNameToRemove;
  1055. }).join(' '));
  1056. },
  1057. toString: function() {
  1058. return this.toArray().join(' ');
  1059. }
  1060. }
  1061. Object.extend(Element.ClassNames.prototype, Enumerable);
  1062. var Selector = Class.create();
  1063. Selector.prototype = {
  1064. initialize: function(expression) {
  1065. this.params = {classNames: []};
  1066. this.expression = expression.toString().strip();
  1067. this.parseExpression();
  1068. this.compileMatcher();
  1069. },
  1070. parseExpression: function() {
  1071. function abort(message) { throw 'Parse error in selector: ' + message; }
  1072. if (this.expression == '') abort('empty expression');
  1073. var params = this.params, expr = this.expression, match, modifier, clause, rest;
  1074. while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
  1075. params.attributes = params.attributes || [];
  1076. params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
  1077. expr = match[1];
  1078. }
  1079. if (expr == '*') return this.params.wildcard = true;
  1080. while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
  1081. modifier = match[1], clause = match[2], rest = match[3];
  1082. switch (modifier) {
  1083. case '#': params.id = clause; break;
  1084. case '.': params.classNames.push(clause); break;
  1085. case '':
  1086. case undefined: params.tagName = clause.toUpperCase(); break;
  1087. default: abort(expr.inspect());
  1088. }
  1089. expr = rest;
  1090. }
  1091. if (expr.length > 0) abort(expr.inspect());
  1092. },
  1093. buildMatchExpression: function() {
  1094. var params = this.params, conditions = [], clause;
  1095. if (params.wildcard)
  1096. conditions.push('true');
  1097. if (clause = params.id)
  1098. conditions.push('element.id == ' + clause.inspect());
  1099. if (clause = params.tagName)
  1100. conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
  1101. if ((clause = params.classNames).length > 0)
  1102. for (var i = 0; i < clause.length; i++)
  1103. conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
  1104. if (clause = params.attributes) {
  1105. clause.each(function(attribute) {
  1106. var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
  1107. var splitValueBy = function(delimiter) {
  1108. return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
  1109. }
  1110. switch (attribute.operator) {
  1111. case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break;
  1112. case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
  1113. case '|=': conditions.push(
  1114. splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
  1115. ); break;
  1116. case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
  1117. case '':
  1118. case undefined: conditions.push(value + ' != null'); break;
  1119. default: throw 'Unknown operator ' + attribute.operator + ' in selector';
  1120. }
  1121. });
  1122. }
  1123. return conditions.join(' && ');
  1124. },
  1125. compileMatcher: function() {
  1126. this.match = new Function('element', 'if (!element.tagName) return false; \n' +
  1127. 'return ' + this.buildMatchExpression());
  1128. },
  1129. findElements: function(scope) {
  1130. var element;
  1131. if (element = $(this.params.id))
  1132. if (this.match(element))
  1133. if (!scope || Element.childOf(element, scope))
  1134. return [element];
  1135. scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
  1136. var results = [];
  1137. for (var i = 0; i < scope.length; i++)
  1138. if (this.match(element = scope[i]))
  1139. results.push(Element.extend(element));
  1140. return results;
  1141. },
  1142. toString: function() {
  1143. return this.expression;
  1144. }
  1145. }
  1146. function $$() {
  1147. return $A(arguments).map(function(expression) {
  1148. return expression.strip().split(/\s+/).inject([null], function(results, expr) {
  1149. var selector = new Selector(expr);
  1150. return results.map(selector.findElements.bind(selector)).flatten();
  1151. });
  1152. }).flatten();
  1153. }
  1154. var Field = {
  1155. clear: function() {
  1156. for (var i = 0; i < arguments.length; i++)
  1157. $(arguments[i]).value = '';
  1158. },
  1159. focus: function(element) {
  1160. $(element).focus();
  1161. },
  1162. present: function() {
  1163. for (var i = 0; i < arguments.length; i++)
  1164. if ($(arguments[i]).value == '') return false;
  1165. return true;
  1166. },
  1167. select: function(element) {
  1168. $(element).select();
  1169. },
  1170. activate: function(element) {
  1171. element = $(element);
  1172. element.focus();
  1173. if (element.select)
  1174. element.select();
  1175. }
  1176. }
  1177. /*--------------------------------------------------------------------------*/
  1178. var Form = {
  1179. serialize: function(form) {
  1180. var elements = Form.getElements($(form));
  1181. var queryComponents = new Array();
  1182. for (var i = 0; i < elements.length; i++) {
  1183. var queryComponent = Form.Element.serialize(elements[i]);
  1184. if (queryComponent)
  1185. queryComponents.push(queryComponent);
  1186. }
  1187. return queryComponents.join('&');
  1188. },
  1189. getElements: function(form) {
  1190. form = $(form);
  1191. var elements = new Array();
  1192. for (var tagName in Form.Element.Serializers) {
  1193. var tagElements = form.getElementsByTagName(tagName);
  1194. for (var j = 0; j < tagElements.length; j++)
  1195. elements.push(tagElements[j]);
  1196. }
  1197. return elements;
  1198. },
  1199. getInputs: function(form, typeName, name) {
  1200. form = $(form);
  1201. var inputs = form.getElementsByTagName('input');
  1202. if (!typeName && !name)
  1203. return inputs;
  1204. var matchingInputs = new Array();
  1205. for (var i = 0; i < inputs.length; i++) {
  1206. var input = inputs[i];
  1207. if ((typeName && input.type != typeName) ||
  1208. (name && input.name != name))
  1209. continue;
  1210. matchingInputs.push(input);
  1211. }
  1212. return matchingInputs;
  1213. },
  1214. disable: function(form) {
  1215. var elements = Form.getElements(form);
  1216. for (var i = 0; i < elements.length; i++) {
  1217. var element = elements[i];
  1218. element.blur();
  1219. element.disabled = 'true';
  1220. }
  1221. },
  1222. enable: function(form) {
  1223. var elements = Form.getElements(form);
  1224. for (var i = 0; i < elements.length; i++) {
  1225. var element = elements[i];
  1226. element.disabled = '';
  1227. }
  1228. },
  1229. findFirstElement: function(form) {
  1230. return Form.getElements(form).find(function(element) {
  1231. return element.type != 'hidden' && !element.disabled &&
  1232. ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
  1233. });
  1234. },
  1235. focusFirstElement: function(form) {
  1236. Field.activate(Form.findFirstElement(form));
  1237. },
  1238. reset: function(form) {
  1239. $(form).reset();
  1240. }
  1241. }
  1242. Form.Element = {
  1243. serialize: function(element) {
  1244. element = $(element);
  1245. var method = element.tagName.toLowerCase();
  1246. var parameter = Form.Element.Serializers[method](element);
  1247. if (parameter) {
  1248. var key = encodeURIComponent(parameter[0]);
  1249. if (key.length == 0) return;
  1250. if (parameter[1].constructor != Array)
  1251. parameter[1] = [parameter[1]];
  1252. return parameter[1].map(function(value) {
  1253. return key + '=' + encodeURIComponent(value);
  1254. }).join('&');
  1255. }
  1256. },
  1257. getValue: function(element) {
  1258. element = $(element);
  1259. var method = element.tagName.toLowerCase();
  1260. var parameter = Form.Element.Serializers[method](element);
  1261. if (parameter)
  1262. return parameter[1];
  1263. }
  1264. }
  1265. Form.Element.Serializers = {
  1266. input: function(element) {
  1267. switch (element.type.toLowerCase()) {
  1268. case 'submit':
  1269. case 'hidden':
  1270. case 'password':
  1271. case 'text':
  1272. return Form.Element.Serializers.textarea(element);
  1273. case 'checkbox':
  1274. case 'radio':
  1275. return Form.Element.Serializers.inputSelector(element);
  1276. }
  1277. return false;
  1278. },
  1279. inputSelector: function(element) {
  1280. if (element.checked)
  1281. return [element.name, element.value];
  1282. },
  1283. textarea: function(element) {
  1284. return [element.name, element.value];
  1285. },
  1286. select: function(element) {
  1287. return Form.Element.Serializers[element.type == 'select-one' ?
  1288. 'selectOne' : 'selectMany'](element);
  1289. },
  1290. selectOne: function(element) {
  1291. var value = '', opt, index = element.selectedIndex;
  1292. if (index >= 0) {
  1293. opt = element.options[index];
  1294. value = opt.value || opt.text;
  1295. }
  1296. return [element.name, value];
  1297. },
  1298. selectMany: function(element) {
  1299. var value = [];
  1300. for (var i = 0; i < element.length; i++) {
  1301. var opt = element.options[i];
  1302. if (opt.selected)
  1303. value.push(opt.value || opt.text);
  1304. }
  1305. return [element.name, value];
  1306. }
  1307. }
  1308. /*--------------------------------------------------------------------------*/
  1309. var $F = Form.Element.getValue;
  1310. /*--------------------------------------------------------------------------*/
  1311. Abstract.TimedObserver = function() {}
  1312. Abstract.TimedObserver.prototype = {
  1313. initialize: function(element, frequency, callback) {
  1314. this.frequency = frequency;
  1315. this.element = $(element);
  1316. this.callback = callback;
  1317. this.lastValue = this.getValue();
  1318. this.registerCallback();
  1319. },
  1320. registerCallback: function() {
  1321. setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  1322. },
  1323. onTimerEvent: function() {
  1324. var value = this.getValue();
  1325. if (this.lastValue != value) {
  1326. this.callback(this.element, value);
  1327. this.lastValue = value;
  1328. }
  1329. }
  1330. }
  1331. Form.Element.Observer = Class.create();
  1332. Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  1333. getValue: function() {
  1334. return Form.Element.getValue(this.element);
  1335. }
  1336. });
  1337. Form.Observer = Class.create();
  1338. Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  1339. getValue: function() {
  1340. return Form.serialize(this.element);
  1341. }
  1342. });
  1343. /*--------------------------------------------------------------------------*/
  1344. Abstract.EventObserver = function() {}
  1345. Abstract.EventObserver.prototype = {
  1346. initialize: function(element, callback) {
  1347. this.element = $(element);
  1348. this.callback = callback;
  1349. this.lastValue = this.getValue();
  1350. if (this.element.tagName.toLowerCase() == 'form')
  1351. this.registerFormCallbacks();
  1352. else
  1353. this.registerCallback(this.element);
  1354. },
  1355. onElementEvent: function() {
  1356. var value = this.getValue();
  1357. if (this.lastValue != value) {
  1358. this.callback(this.element, value);
  1359. this.lastValue = value;
  1360. }
  1361. },
  1362. registerFormCallbacks: function() {
  1363. var elements = Form.getElements(this.element);
  1364. for (var i = 0; i < elements.length; i++)
  1365. this.registerCallback(elements[i]);
  1366. },
  1367. registerCallback: function(element) {
  1368. if (element.type) {
  1369. switch (element.type.toLowerCase()) {
  1370. case 'checkbox':
  1371. case 'radio':
  1372. Event.observe(element, 'click', this.onElementEvent.bind(this));
  1373. break;
  1374. case 'password':
  1375. case 'text':
  1376. case 'textarea':
  1377. case 'select-one':
  1378. case 'select-multiple':
  1379. Event.observe(element, 'change', this.onElementEvent.bind(this));
  1380. break;
  1381. }
  1382. }
  1383. }
  1384. }
  1385. Form.Element.EventObserver = Class.create();
  1386. Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  1387. getValue: function() {
  1388. return Form.Element.getValue(this.element);
  1389. }
  1390. });
  1391. Form.EventObserver = Class.create();
  1392. Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  1393. getValue: function() {
  1394. return Form.serialize(this.element);
  1395. }
  1396. });
  1397. if (!window.Event) {
  1398. var Event = new Object();
  1399. }
  1400. Object.extend(Event, {
  1401. KEY_BACKSPACE: 8,
  1402. KEY_TAB: 9,
  1403. KEY_RETURN: 13,
  1404. KEY_ESC: 27,
  1405. KEY_LEFT: 37,
  1406. KEY_UP: 38,
  1407. KEY_RIGHT: 39,
  1408. KEY_DOWN: 40,
  1409. KEY_DELETE: 46,
  1410. element: function(event) {
  1411. return event.target || event.srcElement;
  1412. },
  1413. isLeftClick: function(event) {
  1414. return (((event.which) && (event.which == 1)) ||
  1415. ((event.button) && (event.button == 1)));
  1416. },
  1417. pointerX: function(event) {
  1418. return event.pageX || (event.clientX +
  1419. (document.documentElement.scrollLeft || document.body.scrollLeft));
  1420. },
  1421. pointerY: function(event) {
  1422. return event.pageY || (event.clientY +
  1423. (document.documentElement.scrollTop || document.body.scrollTop));
  1424. },
  1425. stop: function(event) {
  1426. if (event.preventDefault) {
  1427. event.preventDefault();
  1428. event.stopPropagation();
  1429. } else {
  1430. event.returnValue = false;
  1431. event.cancelBubble = true;
  1432. }
  1433. },
  1434. // find the first node with the given tagName, starting from the
  1435. // node the event was triggered on; traverses the DOM upwards
  1436. findElement: function(event, tagName) {
  1437. var element = Event.element(event);
  1438. while (element.parentNode && (!element.tagName ||
  1439. (element.tagName.toUpperCase() != tagName.toUpperCase())))
  1440. element = element.parentNode;
  1441. return element;
  1442. },
  1443. observers: false,
  1444. _observeAndCache: function(element, name, observer, useCapture) {
  1445. if (!this.observers) this.observers = [];
  1446. if (element.addEventListener) {
  1447. this.observers.push([element, name, observer, useCapture]);
  1448. element.addEventListener(name, observer, useCapture);
  1449. } else if (element.attachEvent) {
  1450. this.observers.push([element, name, observer, useCapture]);
  1451. element.attachEvent('on' + name, observer);
  1452. }
  1453. },
  1454. unloadCache: function() {
  1455. if (!Event.observers) return;
  1456. for (var i = 0; i < Event.observers.length; i++) {
  1457. Event.stopObserving.apply(this, Event.observers[i]);
  1458. Event.observers[i][0] = null;
  1459. }
  1460. Event.observers = false;
  1461. },
  1462. observe: function(element, name, observer, useCapture) {
  1463. var element = $(element);
  1464. useCapture = useCapture || false;
  1465. if (name == 'keypress' &&
  1466. (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
  1467. || element.attachEvent))
  1468. name = 'keydown';
  1469. this._observeAndCache(element, name, observer, useCapture);
  1470. },
  1471. stopObserving: function(element, name, observer, useCapture) {
  1472. var element = $(element);
  1473. useCapture = useCapture || false;
  1474. if (name == 'keypress' &&
  1475. (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
  1476. || element.detachEvent))
  1477. name = 'keydown';
  1478. if (element.removeEventListener) {
  1479. element.removeEventListener(name, observer, useCapture);
  1480. } else if (element.detachEvent) {
  1481. element.detachEvent('on' + name, observer);
  1482. }
  1483. }
  1484. });
  1485. /* prevent memory leaks in IE */
  1486. if (navigator.appVersion.match(/\bMSIE\b/))
  1487. Event.observe(window, 'unload', Event.unloadCache, false);
  1488. var Position = {
  1489. // set to true if needed, warning: firefox performance problems
  1490. // NOT neeeded for page scrolling, only if draggable contained in
  1491. // scrollable elements
  1492. includeScrollOffsets: false,
  1493. // must be called before calling withinIncludingScrolloffset, every time the
  1494. // page is scrolled
  1495. prepare: function() {
  1496. this.deltaX = window.pageXOffset
  1497. || document.documentElement.scrollLeft
  1498. || document.body.scrollLeft
  1499. || 0;
  1500. this.deltaY = window.pageYOffset
  1501. || document.documentElement.scrollTop
  1502. || document.body.scrollTop
  1503. || 0;
  1504. },
  1505. realOffset: function(element) {
  1506. var valueT = 0, valueL = 0;
  1507. do {
  1508. valueT += element.scrollTop || 0;
  1509. valueL += element.scrollLeft || 0;
  1510. element = element.parentNode;
  1511. } while (element);
  1512. return [valueL, valueT];
  1513. },
  1514. cumulativeOffset: function(element) {
  1515. var valueT = 0, valueL = 0;
  1516. do {
  1517. valueT += element.offsetTop || 0;
  1518. valueL += element.offsetLeft || 0;
  1519. element = element.offsetParent;
  1520. } while (element);
  1521. return [valueL, valueT];
  1522. },
  1523. positionedOffset: function(element) {
  1524. var valueT = 0, valueL = 0;
  1525. do {
  1526. valueT += element.offsetTop || 0;
  1527. valueL += element.offsetLeft || 0;
  1528. element = element.offsetParent;
  1529. if (element) {
  1530. p = Element.getStyle(element, 'position');
  1531. if (p == 'relative' || p == 'absolute') break;
  1532. }
  1533. } while (element);
  1534. return [valueL, valueT];
  1535. },
  1536. offsetParent: function(element) {
  1537. if (element.offsetParent) return element.offsetParent;
  1538. if (element == document.body) return element;
  1539. while ((element = element.parentNode) && element != document.body)
  1540. if (Element.getStyle(element, 'position') != 'static')
  1541. return element;
  1542. return document.body;
  1543. },
  1544. // caches x/y coordinate pair to use with overlap
  1545. within: function(element, x, y) {
  1546. if (this.includeScrollOffsets)
  1547. return this.withinIncludingScrolloffsets(element, x, y);
  1548. this.xcomp = x;
  1549. this.ycomp = y;
  1550. this.offset = this.cumulativeOffset(element);
  1551. return (y >= this.offset[1] &&
  1552. y < this.offset[1] + element.offsetHeight &&
  1553. x >= this.offset[0] &&
  1554. x < this.offset[0] + element.offsetWidth);
  1555. },
  1556. withinIncludingScrolloffsets: function(element, x, y) {
  1557. var offsetcache = this.realOffset(element);
  1558. this.xcomp = x + offsetcache[0] - this.deltaX;
  1559. this.ycomp = y + offsetcache[1] - this.deltaY;
  1560. this.offset = this.cumulativeOffset(element);
  1561. return (this.ycomp >= this.offset[1] &&
  1562. this.ycomp < this.offset[1] + element.offsetHeight &&
  1563. this.xcomp >= this.offset[0] &&
  1564. this.xcomp < this.offset[0] + element.offsetWidth);
  1565. },
  1566. // within must be called directly before
  1567. overlap: function(mode, element) {
  1568. if (!mode) return 0;
  1569. if (mode == 'vertical')
  1570. return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
  1571. element.offsetHeight;
  1572. if (mode == 'horizontal')
  1573. return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
  1574. element.offsetWidth;
  1575. },
  1576. clone: function(source, target) {
  1577. source = $(source);
  1578. target = $(target);
  1579. target.style.position = 'absolute';
  1580. var offsets = this.cumulativeOffset(source);
  1581. target.style.top = offsets[1] + 'px';
  1582. target.style.left = offsets[0] + 'px';
  1583. target.style.width = source.offsetWidth + 'px';
  1584. target.style.height = source.offsetHeight + 'px';
  1585. },
  1586. page: function(forElement) {
  1587. var valueT = 0, valueL = 0;
  1588. var element = forElement;
  1589. do {
  1590. valueT += element.offsetTop || 0;
  1591. valueL += element.offsetLeft || 0;
  1592. // Safari fix
  1593. if (element.offsetParent==document.body)
  1594. if (Element.getStyle(element,'position')=='absolute') break;
  1595. } while (element = element.offsetParent);
  1596. element = forElement;
  1597. do {
  1598. valueT -= element.scrollTop || 0;
  1599. valueL -= element.scrollLeft || 0;
  1600. } while (element = element.parentNode);
  1601. return [valueL, valueT];
  1602. },
  1603. clone: function(source, target) {
  1604. var options = Object.extend({
  1605. setLeft: true,
  1606. setTop: true,
  1607. setWidth: true,
  1608. setHeight: true,
  1609. offsetTop: 0,
  1610. offsetLeft: 0
  1611. }, arguments[2] || {})
  1612. // find page position of source
  1613. source = $(source);
  1614. var p = Position.page(source);
  1615. // find coordinate system to use
  1616. target = $(target);
  1617. var delta = [0, 0];
  1618. var parent = null;
  1619. // delta [0,0] will do fine with position: fixed elements,
  1620. // position:absolute needs offsetParent deltas
  1621. if (Element.getStyle(target,'position') == 'absolute') {
  1622. parent = Position.offsetParent(target);
  1623. delta = Position.page(parent);
  1624. }
  1625. // correct by body offsets (fixes Safari)
  1626. if (parent == document.body) {
  1627. delta[0] -= document.body.offsetLeft;
  1628. delta[1] -= document.body.offsetTop;
  1629. }
  1630. // set position
  1631. if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
  1632. if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
  1633. if(options.setWidth) target.style.width = source.offsetWidth + 'px';
  1634. if(options.setHeight) target.style.height = source.offsetHeight + 'px';
  1635. },
  1636. absolutize: function(element) {
  1637. element = $(element);
  1638. if (element.style.position == 'absolute') return;
  1639. Position.prepare();
  1640. var offsets = Position.positionedOffset(element);
  1641. var top = offsets[1];
  1642. var left = offsets[0];
  1643. var width = element.clientWidth;
  1644. var height = element.clientHeight;
  1645. element._originalLeft = left - parseFloat(element.style.left || 0);
  1646. element._originalTop = top - parseFloat(element.style.top || 0);
  1647. element._originalWidth = element.style.width;
  1648. element._originalHeight = element.style.height;
  1649. element.style.position = 'absolute';
  1650. element.style.top = top + 'px';;
  1651. element.style.left = left + 'px';;
  1652. element.style.width = width + 'px';;
  1653. element.style.height = height + 'px';;
  1654. },
  1655. relativize: function(element) {
  1656. element = $(element);
  1657. if (element.style.position == 'relative') return;
  1658. Position.prepare();
  1659. element.style.position = 'relative';
  1660. var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
  1661. var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
  1662. element.style.top = top + 'px';
  1663. element.style.left = left + 'px';
  1664. element.style.height = element._originalHeight;
  1665. element.style.width = element._originalWidth;
  1666. }
  1667. }
  1668. // Safari returns margins on body which is incorrect if the child is absolutely
  1669. // positioned. For performance reasons, redefine Position.cumulativeOffset for
  1670. // KHTML/WebKit only.
  1671. if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
  1672. Position.cumulativeOffset = function(element) {
  1673. var valueT = 0, valueL = 0;
  1674. do {
  1675. valueT += element.offsetTop || 0;
  1676. valueL += element.offsetLeft || 0;
  1677. if (element.offsetParent == document.body)
  1678. if (Element.getStyle(element, 'position') == 'absolute') break;
  1679. element = element.offsetParent;
  1680. } while (element);
  1681. return [valueL, valueT];
  1682. }
  1683. }