PageRenderTime 620ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 1ms

/js/prototype.js

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