PageRenderTime 57ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/thirdparty/prototype/prototype.js

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