PageRenderTime 50ms CodeModel.GetById 2ms app.highlight 41ms RepoModel.GetById 1ms app.codeStats 1ms

/library/DOM.py

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