PageRenderTime 65ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/thirdparty/prototype/prototype-safe.js

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