/library/DOM.py

http://pyjamas.googlecode.com/ · Python · 812 lines · 634 code · 64 blank · 114 comment · 9 complexity · 5f775bea07a67ecacd9fd950d1287638 MD5 · raw file

  1. # Copyright 2006 James Tauber and contributors
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from __pyjamas__ import JS
  15. sCaptureElem = None
  16. sEventPreviewStack = []
  17. def init():
  18. JS("""
  19. // Set up capture event dispatchers.
  20. $wnd.__dispatchCapturedMouseEvent = function(evt) {
  21. if ($wnd.__dispatchCapturedEvent(evt)) {
  22. var cap = $wnd.__captureElem;
  23. if (cap && cap.__listener) {
  24. DOM_dispatchEvent(evt, cap, cap.__listener);
  25. evt.stopPropagation();
  26. }
  27. }
  28. };
  29. $wnd.__dispatchCapturedEvent = function(evt) {
  30. if (!DOM_previewEvent(evt)) {
  31. evt.stopPropagation();
  32. evt.preventDefault();
  33. return false;
  34. }
  35. return true;
  36. };
  37. $wnd.addEventListener(
  38. 'mouseout',
  39. function(evt){
  40. var cap = $wnd.__captureElem;
  41. if (cap) {
  42. if (!evt.relatedTarget) {
  43. // When the mouse leaves the window during capture, release capture
  44. // and synthesize an 'onlosecapture' event.
  45. $wnd.__captureElem = null;
  46. if (cap.__listener) {
  47. var lcEvent = $doc.createEvent('UIEvent');
  48. lcEvent.initUIEvent('losecapture', false, false, $wnd, 0);
  49. DOM_dispatchEvent(lcEvent, cap, cap.__listener);
  50. }
  51. }
  52. }
  53. },
  54. true
  55. );
  56. $wnd.addEventListener('click', $wnd.__dispatchCapturedMouseEvent, true);
  57. $wnd.addEventListener('dblclick', $wnd.__dispatchCapturedMouseEvent, true);
  58. $wnd.addEventListener('mousedown', $wnd.__dispatchCapturedMouseEvent, true);
  59. $wnd.addEventListener('mouseup', $wnd.__dispatchCapturedMouseEvent, true);
  60. $wnd.addEventListener('mousemove', $wnd.__dispatchCapturedMouseEvent, true);
  61. $wnd.addEventListener('keydown', $wnd.__dispatchCapturedEvent, true);
  62. $wnd.addEventListener('keyup', $wnd.__dispatchCapturedEvent, true);
  63. $wnd.addEventListener('keypress', $wnd.__dispatchCapturedEvent, true);
  64. $wnd.__dispatchEvent = function(evt) {
  65. var listener, curElem = this;
  66. while (curElem && !(listener = curElem.__listener)) {
  67. curElem = curElem.parentNode;
  68. }
  69. if (curElem && curElem.nodeType != 1) {
  70. curElem = null;
  71. }
  72. if (listener) {
  73. DOM_dispatchEvent(evt, curElem, listener);
  74. }
  75. };
  76. $wnd.__captureElem = null;
  77. """)
  78. init()
  79. def addEventPreview(preview):
  80. global sEventPreviewStack
  81. sEventPreviewStack.append(preview)
  82. def appendChild(parent, child):
  83. JS("""
  84. parent.appendChild(child);
  85. """)
  86. def compare(elem1, elem2):
  87. JS("""
  88. return (elem1 == elem2);
  89. """)
  90. def createAnchor():
  91. return createElement("A")
  92. def createButton():
  93. return createElement("button")
  94. def createCol():
  95. return createElement("col")
  96. def createDiv():
  97. return createElement("div")
  98. def createElement(tag):
  99. JS("""
  100. return $doc.createElement(tag);
  101. """)
  102. def createFieldSet():
  103. return createElement("fieldset")
  104. def createForm():
  105. return createElement("form")
  106. def createIFrame():
  107. return createElement("iframe")
  108. def createImg():
  109. return createElement("img")
  110. def createInputCheck():
  111. return createInputElement("checkbox")
  112. def createInputElement(elementType):
  113. JS("""
  114. var e = $doc.createElement("INPUT");
  115. e.type = elementType;
  116. return e;
  117. """)
  118. def createInputPassword():
  119. return createInputElement("password")
  120. def createInputRadio(group):
  121. JS("""
  122. var elem = $doc.createElement("INPUT");
  123. elem.type = 'radio';
  124. elem.name = group;
  125. return elem;
  126. """)
  127. def createInputText():
  128. return createInputElement("text")
  129. def createLabel():
  130. return createElement("label")
  131. def createLegend():
  132. return createElement("legend")
  133. def createOptions():
  134. return createElement("options")
  135. def createSelect():
  136. return createElement("select")
  137. def createSpan():
  138. return createElement("span")
  139. def createTable():
  140. return createElement("table")
  141. def createTBody():
  142. return createElement("tbody")
  143. def createTD():
  144. return createElement("td")
  145. def createTextArea():
  146. return createElement("textarea")
  147. def createTH():
  148. return createElement("th")
  149. def createTR():
  150. return createElement("tr")
  151. def eventCancelBubble(evt, cancel):
  152. evt.cancelBubble = cancel
  153. def eventGetAltKey(evt):
  154. JS("""
  155. return evt.altKey;
  156. """)
  157. def eventGetButton(evt):
  158. JS("""
  159. return evt.button;
  160. """)
  161. def eventGetClientX(evt):
  162. JS("""
  163. return evt.clientX;
  164. """)
  165. def eventGetClientY(evt):
  166. JS("""
  167. return evt.clientY;
  168. """)
  169. def eventGetCtrlKey(evt):
  170. JS("""
  171. return evt.ctrlKey;
  172. """)
  173. def eventGetFromElement(evt):
  174. JS("""
  175. return evt.fromElement ? evt.fromElement : null;
  176. """)
  177. def eventGetKeyCode(evt):
  178. JS("""
  179. return evt.which ? evt.which : evt.keyCode;
  180. """)
  181. def eventGetRepeat(evt):
  182. JS("""
  183. return evt.repeat;
  184. """)
  185. def eventGetScreenX(evt):
  186. JS("""
  187. return evt.screenX;
  188. """)
  189. def eventGetScreenY(evt):
  190. JS("""
  191. return evt.screenY;
  192. """)
  193. def eventGetShiftKey(evt):
  194. JS("""
  195. return evt.shiftKey;
  196. """)
  197. def eventGetTarget(event):
  198. JS("""
  199. return event.target ? event.target : null;
  200. """)
  201. def eventGetToElement(evt):
  202. JS("""
  203. return evt.relatedTarget ? evt.relatedTarget : null;
  204. """)
  205. def eventGetType(event):
  206. JS("""
  207. return event.type;
  208. """)
  209. def eventGetTypeInt(event):
  210. JS("""
  211. switch (event.type) {
  212. case "blur": return 0x01000;
  213. case "change": return 0x00400;
  214. case "click": return 0x00001;
  215. case "dblclick": return 0x00002;
  216. case "focus": return 0x00800;
  217. case "keydown": return 0x00080;
  218. case "keypress": return 0x00100;
  219. case "keyup": return 0x00200;
  220. case "load": return 0x08000;
  221. case "losecapture": return 0x02000;
  222. case "mousedown": return 0x00004;
  223. case "mousemove": return 0x00040;
  224. case "mouseout": return 0x00020;
  225. case "mouseover": return 0x00010;
  226. case "mouseup": return 0x00008;
  227. case "scroll": return 0x04000;
  228. case "error": return 0x10000;
  229. }
  230. """)
  231. def eventGetTypeString(event):
  232. return eventGetType(event)
  233. def eventPreventDefault(evt):
  234. evt.preventDefault()
  235. def eventSetKeyCode(evt, key):
  236. JS("""
  237. evt.keyCode = key;
  238. """)
  239. def eventToString(evt):
  240. JS("""
  241. return evt.toString();
  242. """)
  243. def iframeGetSrc(elem):
  244. JS("""
  245. return elem.src;
  246. """)
  247. def getAbsoluteLeft(elem):
  248. JS("""
  249. var left = 0;
  250. while (elem) {
  251. left += elem.offsetLeft - elem.scrollLeft;
  252. elem = elem.offsetParent;
  253. }
  254. return left + $doc.body.scrollLeft;
  255. """)
  256. def getAbsoluteTop(elem):
  257. JS("""
  258. var top = 0;
  259. while (elem) {
  260. top += elem.offsetTop - elem.scrollTop;
  261. elem = elem.offsetParent;
  262. }
  263. return top + $doc.body.scrollTop;
  264. """)
  265. def getAttribute(elem, attr):
  266. JS("""
  267. var ret = elem[attr];
  268. return (ret == null) ? null : String(ret);
  269. """)
  270. def getBooleanAttribute(elem, attr):
  271. JS("""
  272. return !!elem[attr];
  273. """)
  274. def getCaptureElement():
  275. global sCaptureElem
  276. return sCaptureElem
  277. def getChild(elem, index):
  278. """
  279. Get a child of the DOM element by specifying an index.
  280. """
  281. JS("""
  282. var count = 0, child = elem.firstChild;
  283. while (child) {
  284. var next = child.nextSibling;
  285. if (child.nodeType == 1) {
  286. if (index == count)
  287. return child;
  288. ++count;
  289. }
  290. child = next;
  291. }
  292. return null;
  293. """)
  294. def getChildCount(elem):
  295. """
  296. Calculate the number of children the given element has. This loops
  297. over all the children of that element and counts them.
  298. """
  299. JS("""
  300. var count = 0, child = elem.firstChild;
  301. while (child) {
  302. if (child.nodeType == 1)
  303. ++count;
  304. child = child.nextSibling;
  305. }
  306. return count;
  307. """)
  308. def getChildIndex(parent, toFind):
  309. """
  310. Return the index of the given child in the given parent.
  311. This performs a linear search.
  312. """
  313. JS("""
  314. var count = 0, child = parent.firstChild;
  315. while (child) {
  316. if (child == toFind)
  317. return count;
  318. if (child.nodeType == 1)
  319. ++count;
  320. child = child.nextSibling;
  321. }
  322. return -1;
  323. """)
  324. def getElementById(id):
  325. """
  326. Return the element in the document's DOM tree with the given id.
  327. """
  328. JS("""
  329. var elem = $doc.getElementById(id);
  330. return elem ? elem : null;
  331. """)
  332. def getEventListener(element):
  333. """
  334. See setEventListener for more information.
  335. """
  336. JS("""
  337. return element.__listener;
  338. """)
  339. def getEventsSunk(element):
  340. """
  341. Return which events are currently "sunk" for a given DOM node. See
  342. sinkEvents() for more information.
  343. """
  344. JS("""
  345. return element.__eventBits ? element.__eventBits : 0;
  346. """)
  347. def getFirstChild(elem):
  348. JS("""
  349. var child = elem.firstChild;
  350. while (child && child.nodeType != 1)
  351. child = child.nextSibling;
  352. return child ? child : null;
  353. """)
  354. def getInnerHTML(element):
  355. JS("""
  356. var ret = element.innerHTML;
  357. return (ret == null) ? null : ret;
  358. """)
  359. def getInnerText(element):
  360. JS("""
  361. // To mimic IE's 'innerText' property in the W3C DOM, we need to recursively
  362. // concatenate all child text nodes (depth first).
  363. var text = '', child = element.firstChild;
  364. while (child) {
  365. if (child.nodeType == 1){ // 1 == Element node
  366. text += DOM_getInnerText(child);
  367. } else if (child.nodeValue) {
  368. text += child.nodeValue;
  369. }
  370. child = child.nextSibling;
  371. }
  372. return text;
  373. """)
  374. def getIntAttribute(elem, attr):
  375. JS("""
  376. var i = parseInt(elem[attr]);
  377. if (!i) {
  378. return 0;
  379. }
  380. return i;
  381. """)
  382. def getIntStyleAttribute(elem, attr):
  383. JS("""
  384. var i = parseInt(elem.style[attr]);
  385. if (!i) {
  386. return 0;
  387. }
  388. return i;
  389. """)
  390. def getNextSibling(elem):
  391. JS("""
  392. var sib = elem.nextSibling;
  393. while (sib && sib.nodeType != 1)
  394. sib = sib.nextSibling;
  395. return sib ? sib : null;
  396. """)
  397. def getParent(elem):
  398. JS("""
  399. var parent = elem.parentNode;
  400. if(parent == null) {
  401. return null;
  402. }
  403. if (parent.nodeType != 1)
  404. parent = null;
  405. return parent ? parent : null;
  406. """)
  407. def getStyleAttribute(elem, attr):
  408. JS("""
  409. var ret = elem.style[attr];
  410. return (ret == null) ? null : ret;
  411. """)
  412. def insertChild(parent, toAdd, index):
  413. JS("""
  414. var count = 0, child = parent.firstChild, before = null;
  415. while (child) {
  416. if (child.nodeType == 1) {
  417. if (count == index) {
  418. before = child;
  419. break;
  420. }
  421. ++count;
  422. }
  423. child = child.nextSibling;
  424. }
  425. parent.insertBefore(toAdd, before);
  426. """)
  427. def iterChildren(elem):
  428. """
  429. Returns an iterator over all the children of the given
  430. DOM node.
  431. """
  432. JS("""
  433. var parent = elem;
  434. var child = elem.firstChild;
  435. var lastChild = null;
  436. return {
  437. 'next': function() {
  438. if (child == null) {
  439. throw StopIteration;
  440. }
  441. lastChild = child;
  442. child = DOM_getNextSibling(child);
  443. return lastChild;
  444. },
  445. 'remove': function() {
  446. parent.removeChild(lastChild);
  447. },
  448. __iter__: function() {
  449. return this;
  450. }
  451. };
  452. """)
  453. def walkChildren(elem):
  454. """
  455. Walk an entire subtree of the DOM. This returns an
  456. iterator/iterable which performs a pre-order traversal
  457. of all the children of the given element.
  458. """
  459. JS("""
  460. var parent = elem;
  461. var child = DOM_getFirstChild(elem);
  462. var lastChild = null;
  463. var stack = [];
  464. var parentStack = [];
  465. return {
  466. 'next': function() {
  467. if (child == null) {
  468. throw StopIteration;
  469. }
  470. lastChild = child;
  471. var firstChild = DOM_getFirstChild(child);
  472. var nextSibling = DOM_getNextSibling(child);
  473. if(firstChild != null) {
  474. if(nextSibling != null) {
  475. stack.push(nextSibling);
  476. parentStack.push(parent);
  477. }
  478. parent = child;
  479. child = firstChild;
  480. } else if(nextSibling != null) {
  481. child = nextSibling;
  482. } else if(stack.length > 0) {
  483. child = stack.pop();
  484. parent = parentStack.pop();
  485. } else {
  486. child = null;
  487. }
  488. return lastChild;
  489. },
  490. 'remove': function() {
  491. parent.removeChild(lastChild);
  492. },
  493. __iter__: function() {
  494. return this;
  495. }
  496. };
  497. """)
  498. def isOrHasChild(parent, child):
  499. JS("""
  500. while (child) {
  501. if (parent == child)
  502. return true;
  503. child = child.parentNode;
  504. if (child.nodeType != 1)
  505. child = null;
  506. }
  507. return false;
  508. """)
  509. def releaseCapture(elem):
  510. JS("""
  511. if ((DOM_sCaptureElem != null) && DOM_compare(elem, DOM_sCaptureElem))
  512. DOM_sCaptureElem = null;
  513. if (elem == $wnd.__captureElem)
  514. $wnd.__captureElem = null;
  515. """)
  516. def removeChild(parent, child):
  517. JS("""
  518. parent.removeChild(child);
  519. """)
  520. def replaceChild(parent, newChild, oldChild):
  521. JS("""
  522. parent.replaceChild(newChild, oldChild);
  523. """)
  524. def removeEventPreview(preview):
  525. global sEventPreviewStack
  526. sEventPreviewStack.remove(preview)
  527. def scrollIntoView(elem):
  528. JS("""
  529. var left = elem.offsetLeft, top = elem.offsetTop;
  530. var width = elem.offsetWidth, height = elem.offsetHeight;
  531. if (elem.parentNode != elem.offsetParent) {
  532. left -= elem.parentNode.offsetLeft;
  533. top -= elem.parentNode.offsetTop;
  534. }
  535. var cur = elem.parentNode;
  536. while (cur && (cur.nodeType == 1)) {
  537. if ((cur.style.overflow == 'auto') || (cur.style.overflow == 'scroll')) {
  538. if (left < cur.scrollLeft) {
  539. cur.scrollLeft = left;
  540. }
  541. if (left + width > cur.scrollLeft + cur.clientWidth) {
  542. cur.scrollLeft = (left + width) - cur.clientWidth;
  543. }
  544. if (top < cur.scrollTop) {
  545. cur.scrollTop = top;
  546. }
  547. if (top + height > cur.scrollTop + cur.clientHeight) {
  548. cur.scrollTop = (top + height) - cur.clientHeight;
  549. }
  550. }
  551. var offsetLeft = cur.offsetLeft, offsetTop = cur.offsetTop;
  552. if (cur.parentNode != cur.offsetParent) {
  553. offsetLeft -= cur.parentNode.offsetLeft;
  554. offsetTop -= cur.parentNode.offsetTop;
  555. }
  556. left += offsetLeft - cur.scrollLeft;
  557. top += offsetTop - cur.scrollTop;
  558. cur = cur.parentNode;
  559. }
  560. """)
  561. def removeAttribute(element, attribute):
  562. JS("""
  563. delete element[attribute];
  564. """)
  565. def setAttribute(element, attribute, value):
  566. JS("""
  567. element[attribute] = value;
  568. """)
  569. def setBooleanAttribute(elem, attr, value):
  570. JS("""
  571. elem[attr] = value;
  572. """)
  573. def setCapture(elem):
  574. JS("""
  575. DOM_sCaptureElem = elem;
  576. $wnd.__captureElem = elem;
  577. """)
  578. def setEventListener(element, listener):
  579. """
  580. Register an object to receive event notifications for the given
  581. element. The listener's onBrowserEvent() method will be called
  582. when a captured event occurs. To set which events are captured,
  583. use sinkEvents().
  584. """
  585. JS("""
  586. element.__listener = listener;
  587. """)
  588. def setInnerHTML(element, html):
  589. JS("""
  590. if (!html) {
  591. html = "";
  592. }
  593. element.innerHTML = html;
  594. """)
  595. def setInnerText(elem, text):
  596. JS("""
  597. // Remove all children first.
  598. while (elem.firstChild) {
  599. elem.removeChild(elem.firstChild);
  600. }
  601. // Add a new text node.
  602. elem.appendChild($doc.createTextNode(text));
  603. """)
  604. def setIntAttribute(elem, attr, value):
  605. JS("""
  606. elem[attr] = value;
  607. """)
  608. def setIntStyleAttribute(elem, attr, value):
  609. JS("""
  610. elem.style[attr] = value;
  611. """)
  612. def setOptionText(select, text, index):
  613. JS("""
  614. var option = select.options[index];
  615. option.text = text;
  616. """)
  617. def setStyleAttribute(element, name, value):
  618. JS("""
  619. element.style[name] = value;
  620. """)
  621. def sinkEvents(element, bits):
  622. """
  623. Set which events should be captured on a given element and passed to the
  624. registered listener. To set the listener, use setEventListener().
  625. @param bits: A combination of bits; see ui.Event for bit values
  626. """
  627. JS("""
  628. element.__eventBits = bits;
  629. element.onclick = (bits & 0x00001) ? $wnd.__dispatchEvent : null;
  630. element.ondblclick = (bits & 0x00002) ? $wnd.__dispatchEvent : null;
  631. element.onmousedown = (bits & 0x00004) ? $wnd.__dispatchEvent : null;
  632. element.onmouseup = (bits & 0x00008) ? $wnd.__dispatchEvent : null;
  633. element.onmouseover = (bits & 0x00010) ? $wnd.__dispatchEvent : null;
  634. element.onmouseout = (bits & 0x00020) ? $wnd.__dispatchEvent : null;
  635. element.onmousemove = (bits & 0x00040) ? $wnd.__dispatchEvent : null;
  636. element.onkeydown = (bits & 0x00080) ? $wnd.__dispatchEvent : null;
  637. element.onkeypress = (bits & 0x00100) ? $wnd.__dispatchEvent : null;
  638. element.onkeyup = (bits & 0x00200) ? $wnd.__dispatchEvent : null;
  639. element.onchange = (bits & 0x00400) ? $wnd.__dispatchEvent : null;
  640. element.onfocus = (bits & 0x00800) ? $wnd.__dispatchEvent : null;
  641. element.onblur = (bits & 0x01000) ? $wnd.__dispatchEvent : null;
  642. element.onlosecapture = (bits & 0x02000) ? $wnd.__dispatchEvent : null;
  643. element.onscroll = (bits & 0x04000) ? $wnd.__dispatchEvent : null;
  644. element.onload = (bits & 0x08000) ? $wnd.__dispatchEvent : null;
  645. element.onerror = (bits & 0x10000) ? $wnd.__dispatchEvent : null;
  646. """)
  647. def toString(elem):
  648. JS("""
  649. var temp = elem.cloneNode(true);
  650. var tempDiv = $doc.createElement("DIV");
  651. tempDiv.appendChild(temp);
  652. outer = tempDiv.innerHTML;
  653. temp.innerHTML = "";
  654. return outer;
  655. """)
  656. # TODO: missing dispatchEventAndCatch
  657. def dispatchEvent(event, element, listener):
  658. dispatchEventImpl(event, element, listener)
  659. def previewEvent(evt):
  660. global sEventPreviewStack
  661. ret = True
  662. if len(sEventPreviewStack) > 0:
  663. preview = sEventPreviewStack[len(sEventPreviewStack) - 1]
  664. ret = preview.onEventPreview(evt)
  665. if not ret:
  666. eventCancelBubble(evt, True)
  667. eventPreventDefault(evt)
  668. return ret
  669. # TODO
  670. def dispatchEventAndCatch(evt, elem, listener, handler):
  671. pass
  672. def dispatchEventImpl(event, element, listener):
  673. global sCaptureElem
  674. if element == sCaptureElem:
  675. if eventGetType(event) == "losecapture":
  676. sCaptureElem = None
  677. listener.onBrowserEvent(event)
  678. def insertListItem(select, item, value, index):
  679. option = createElement("OPTION")
  680. setInnerText(option, item)
  681. if value != None:
  682. setAttribute(option, "value", value)
  683. if index == -1:
  684. appendChild(select, option)
  685. else:
  686. insertChild(select, option, index)