package textbender.a.b.rhinohideDemo._; // Copyright 2007, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE. import java.awt.BorderLayout; import java.awt.EventQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.*; import javax.swing.*; import javax.swing.border.EmptyBorder; import org.w3c.dom.*; import org.w3c.dom.events.*; import textbender.g.lang.*; import textbender.g.util.logging.LoggerX; import textbender.o.Browser; import textbender.o.rhinohide.*; import textbender.o.rhinohide._.*; import textbender.o.rhinohide.events.RelaySIP; /** Demo of Rhinohide, Events Level 2. * * @see <a href='http://reluk.ca/var/cache/rhinohide-demo/textbender/a/b/rhinohideDemo/Events_2_Demo.xht' * >http://reluk.ca/var/cache/rhinohide-demo/textbender/a/b/rhinohideDemo/Events_2_Demo.xht</a> */ public @ThreadRestricted("AWT event dispatch") final class Events_2_Demo extends JApplet implements Runnable { // - A p p l e t ---------------------------------------------------------------------- private final AtomicBoolean isStartedA = new AtomicBoolean(); public @ThreadSafe void start() { if( isStartedA.getAndSet( true )) return; // start once only LoggerX.i(getClass()).info( "starting" ); EventQueue.invokeLater( Events_2_Demo.this ); // in AWT event dispatch thread } private final AtomicBoolean isDestroyedA = new AtomicBoolean(); public @ThreadSafe void destroy() { if( isDestroyedA.getAndSet( true )) { assert false; return; } LoggerX.i(getClass()).info( "destroying" ); if( window != null ) window.release(); } // - R u n n a b l e ------------------------------------------------------------------ /** Runs this test applet. */ public void run() { reportList.addElement( "starting..." ); ThreadSafe.U.disableChecking(); // I lack permission, I'm an unsigned applet { // Construct GUI. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - JList reportListV = new JList( reportList ); reportListV.setBorder( new EmptyBorder( /*top*/2, /*left*/2, /*bottom*/2, /*right*/0 )); JScrollPane scrollPane = new JScrollPane( reportListV ); scrollPane.setBorder( null ); getContentPane().add( scrollPane, BorderLayout.CENTER ); } try { window = RhiWindow.createWindow( Events_2_Demo.this ); // Query DOM implementation. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - final Document document = window.getDocument(); reportList.addElement ( "browser supports Events Level 2... " + // document.isSupported( "Events", "2.0" ) //////// this is better, because isSupported() assumes level 2 document.getImplementation().hasFeature( "Events", "2.0" ) ); // Listen for events. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ((EventTarget)document).addEventListener // no need to remove at page exit, only because in-memory caching is disabled in our page, per http://reluk.ca/project/textbender/o/browser.xht#In-Memory-Caching-and-JSObject ( "mouseover", new NodeIndicator(), /*use capture*/false ); // - - - reportList.addElement( "successfully started" ); } catch( StunnedRhinoException xSR ) { LoggerX.i(getClass()).info( Browser.pageExitSupressionMessage( xSR )); } // in case the page exits, from beneath us catch( Exception x ) // all, checked or not { LoggerX.i(getClass()).log( LoggerX.WARNING, /*message*/"", x ); } } //// P r i v a t e /////////////////////////////////////////////////////////////////////// @ThreadSafe static void addHighlight( Element element ) { String previousClasses = element.getAttributeNS( null, "class" ); element.setAttributeNS( null, "class", "highlight-me " + previousClasses ); } /** Matches "highlight-me", with or without trailing spaces. */ private static final Pattern highlightRemovalPattern = Pattern.compile( "\\bhighlight-me *" ); // private volatile Text targetIndicationText; // final after init // // // @ThreadSafe static void removeHighlight( Element element ) { Matcher m = highlightRemovalPattern.matcher( element.getAttributeNS( null, "class" )); String cleanClasses = m.replaceAll( "" ); // i.e. remove element.setAttributeNS( null, "class", cleanClasses ); } @ThreadRestricted( "AWT event dispatch" ) private final DefaultListModel reportList = new DefaultListModel(); private volatile RhiWindow window; // final after init // ==================================================================================== private final class NodeIndicator extends RelaySIP { private Element highlightedElement; private final Text targetIndicationText = (Text) (window.getDocument().getElementById("target-indication-element")).getFirstChild(); public @ThreadSafe void handleEvent( Event e ) { if( isDestroyedA.get() ) return; // ignore stragglers // System.out.println( "T22E event: " + _e ); final MouseEvent eM = (MouseEvent)e; final EventTarget target = eM.getTarget(); targetIndicationText.setData( target.toString() ); if( !( target instanceof Element )) return; Element targetE = (Element)target; synchronized( this ) // sync for atomic act/record { if( highlightedElement != null ) { removeHighlight( highlightedElement ); highlightedElement = null; } String tagName = targetE.getTagName(); if( tagName.equals("html") || tagName.equals("body") ) return; // don't blind the user // belongs in the style sheet, FIX as an exercise... addHighlight( targetE ); highlightedElement = targetE; } } } }