Using the HTML+TIME Document Object Model (DOM) | Internet Development Index |
With HTML+TIME (Timed Interactive Multimedia Extensions), you can use persistent Extensible Markup Language (XML) elements and attributes to add timing to your HTML page. This means you don't have to know how to program with scripting languages to make your pages more dynamic. However, for people who already know how to use scripting languages, HTML+TIME supports a complete object model that extends the existing Dynamic HTML (DHTML) Object Model. Thus, you can use HTML+TIME properties, methods, and events to add even more interactive features to your Web pages. All HTML+TIME objects are accessible through script at run time. This article introduces you to the HTML+TIME objects, methods, events, and properties available to your Web pages.
The following topics are discussed in this document.
- Prerequisites
- Accessing Elements as Objects
- HTML+TIME Scripting Components
- Methods
- Properties
- Events
- Collections
- playList Collection
- Where to Go from Here
- Related Topics
Prerequisites
This article assumes you know how to use Introduction to DHTML Behaviors, specifically, the time2 behavior of HTML+TIME. This article does not go into great detail on how to add a behavior to your page nor does it cover how to declare a namespace and use custom tags as required by the time2 behavior. These topics are covered in the HTML+TIME Overview and Spice Up Your Web Pages with HTML+TIME. You should be familiar with the Microsoft?JScript?programming language and have a basic working knowledge of DHTML.
Accessing Elements as Objects
Just like using the Introduction to Dynamic HTML Document Object Model (DOM), you can expose HTML+TIME objects to script by setting the ID attribute.
<t:media ID="oSoundFile" ... />
Once you've set the ID attribute, you can access the element through script, just like any other HTML object.
oSoundFile.beginElement();
Just like other HTML objects, time objects expose a number of methods, properties, events, and collections that allow your script to manipulate the objects.
HTML+TIME Scripting Components
Because HTML+TIME can be applied to regular HTML elements, you still have access to all of the DHTML methods, properties, events, and collections of an element from script. You also have access to the components specific to HTML+TIME. For example, if you apply the time behavior to a span element, you can manipulate the span using standard DHTML applicable to span elements and all HTML+TIME components available. The following example shows how a SPAN element with the time behavior applied to it uses the conventional DHTML property innerText to change the text of the span.
<HTML xmlns:t= "urn:schemas-microsoft-com:time" xmlns:control> <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> @media all { control\:slider {behavior:url(slider.htc);} } .time{behavior:url(#default#time2)} </STYLE> <SCRIPT> var nCounter = 1; // Although the SPAN has the time behavior applied to it, all of the standard // properties, methods, events etc. of the SPAN are available to script, // such as the innerText property used to change the text inside the SPAN every // second. function fnChangeSpan() { switch (nCounter) { case 1: oSpan.innerText = "one"; break; case 2: oSpan.innerText = "two"; break; case 3: oSpan.innerText = "three"; break; case 4: oSpan.innerText = "four"; break; case 5: oSpan.innerText = "five"; break; case 6: nCounter = 0; break; } nCounter++; } </SCRIPT> </HEAD> <BODY> <!-- The HTML+TIME "onrepeat" event is used by the SPAN to fire the "fnChangeSpan()" function every second. This event is an example of an event only available to time elements.--> <SPAN id="oSpan" CLASS="time" DUR="1" REPEATCOUNT="indefinite" onrepeat="fnChangeSpan()"></SPAN> </BODY> </HTML>
Methods
HTML+TIME exposes many unique methods. Although there are a variety of these methods, all of them allow you to control the time line or playback of time elements in some way. Some of these methods are exposed by almost all HTML+TIME objects, while other methods are only exposed by select time objects. Among the most commonly used methods are the beginElement and endElement methods that allow you to begin and end time elements through script. The following is a simple example that uses the beginElement and endElement methods to allow the user to begin and end media on the time line with the push of a button. In addition, the pauseElement and resumeElement methods are used to allow the media to pause and resume.
... <t:MEDIA id="oMedia" timeAction="visibility" src="countlead.wmv" begin="indefinite"/> <BUTTON onclick="oMedia.beginElement();">Start</BUTTON> <BUTTON onclick="oMedia.endElement();">Stop</BUTTON> <BUTTON onclick="oMedia.pauseElement();">Pause</BUTTON> <BUTTON onclick="oMedia.resumeElement();">Resume</BUTTON> ...
Another example of a method that allows significant control over the time line of an object is the seekTo method. The following example shows how to use the seekTo method, one of several methods that enable seeking, to jump to any time in the time line of a time element.
<HTML xmlns:t= "urn:schemas-microsoft-com:time" xmlns:control> <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> <!-- The slider htc file must be at the given path in order for the slider to work. Note that the slider only provides a user interface component; it is not specifically needed for seeking. --> @media all { control\:slider {behavior:url(slider.htc);} } .time{behavior:url(#default#time2)} </STYLE> </HEAD> <BODY> <t:video style="width:175px; height:150px;" id="oMedia" src="movie.avi" /> <!-- The "seekTo" method uses the value of the slide bar as the time in seconds to seek to in the active time of the media. --> <!-- The media element is checked to see if it is active using the "isActive" property, which is exposed by the HTML+TIME object "currTimeState". This is necessary because if the media is not active and the "seekTo" method is fired on the media, an error occurs. The currTimeState object is explored later in this article. --> <control:slider ID="oSlider" TICKINTERVAL="1" TICKNUMBER="19" onchange="oMedia.seekTo(1, oSlider.value);"> </control:slider> </BODY> </HTML>
Many methods are only exposed on specific time objects. The following sample shows how to use the prevElement and nextElement methods, which are only exposed by the seq object, to move from one slide to the next.
... <t:SEQ ID="oSeq" REPEATCOUNT="indefinite"> <t:PAR> <DIV CLASS="time" DUR="5" TIMEACTION="display">First</DIV> <IMG CLASS="time" DUR="5" TIMEACTION="display" SRC="\workshop\samples\author\behaviors\media\newyork.jpg" STYLE="height=150; width=300" /> </t:PAR> <t:PAR> <DIV CLASS="time" DUR="5" TIMEACTION="display">Second</DIV> <IMG CLASS="time" DUR="5" TIMEACTION="display" SRC="\workshop\samples\author\behaviors\media\moscow.jpg" STYLE="height=150; width=300" /> </t:PAR> <t:PAR> <DIV CLASS="time" DUR="5" TIMEACTION="display">Third</DIV> <IMG CLASS="time" DUR="5" TIMEACTION="display" SRC="\workshop\samples\author\behaviors\media\london.jpg" STYLE="height=150; width=300" /> </t:PAR> <t:PAR> <DIV CLASS="time" DUR="5" TIMEACTION="display">Fourth</DIV> <IMG CLASS="time" DUR="5" TIMEACTION="display" SRC="\workshop\samples\author\behaviors\media\hongkong.jpg" STYLE="height=150; width=300" /> </t:PAR> </t:SEQ> <BUTTON onclick="oSeq.prevElement()">Previous Slide</BUTTON> <BUTTON onclick="oSeq.nextElement()">Next Slide</BUTTON> ...
Properties
Since HTML+TIME is dynamic by nature, why would you want to access HTML+TIME properties through script? Aren't they supposed to change by themselves over time? Isn't that what HTML+TIME enables?
Well, yes, but perhaps you'd want to change these properties in response to user-generated events. For instance, you could change the speed of an animation by allowing the user to adjust its dur property. If you were creating a portal page, you could allow the user to choose between different themes; this can result in different animations or media, like sound effects, to be loaded on the page. The following sample shows how to change the background animation of a slide show by changing the to and from properties of an animation object.
<HTML XMLNS:t ="urn:schemas-microsoft-com:time"> <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> .time {behavior: url(#default#time2);} </STYLE> <SCRIPT> var sNewColor = "#99CCFF"; function fnChangeColor() { // The last color animated "to" is now what is animated "from". oAnimate.from = oAnimate.to; // The "fnSetTo" function assigns a random color to "sNewColor" variable sNewColor = fnSetTo(); // The "to" property is assigned a new value. oAnimate.to = sNewColor; oAnimate.beginElement(); } // This function is just used to return a random color. function fnSetTo() { // Create a random value between 1 and 4 var randomNumber = (Math.random() * 4); randomNumber = Math.ceil(randomNumber); switch(randomNumber) { case 1: sNewColor = "#666666" break; case 2: sNewColor = "#00CC33" break; case 3: sNewColor = "#FF0033" break; case 4: sNewColor = "#FFCC00" break; } return sNewColor; } </SCRIPT> </HEAD> <BODY> <!-- This element is used to animate the color change. --> <t:ANIMATECOLOR ID="oAnimate" TARGETELEMENT="oContainerDiv" BEGIN="indefinite" ATTRIBUTENAME="backgroundColor" TO="#FFCC00" START="2" DUR="1" FILL="hold"/> <DIV ID="oContainerDiv" STYLE="background-color:#99CCFF;width:400px;height:200px;"> <DIV STYLE="text-align:center"> <t:SEQ ID="oSeq" REPEATCOUNT="indefinite"> <!-- The "onbegin" HTML+TIME event is used to initiate the color animation when any "t:PAR" element begins on the time line. We will discuss HTML+TIME events in more detail later in this article. --> <t:PAR onbegin="fnChangeColor();"> <DIV CLASS="time" DUR="5" TIMEACTION="display">One</DIV> <IMG CLASS="time" DUR="5" TIMEACTION="display" SRC="\workshop\samples\author\behaviors\media\newyork.jpg" STYLE="height=150; width=300" /> </t:PAR> <t:PAR onbegin="fnChangeColor();"> <DIV CLASS="time" DUR="5" TIMEACTION="display">Two</DIV> <IMG CLASS="time" DUR="5" TIMEACTION="display" SRC="\workshop\samples\author\behaviors\media\moscow.jpg" STYLE="height=150; width=300" /> </t:PAR> <t:PAR onbegin="fnChangeColor();"> <DIV CLASS="time" DUR="5" TIMEACTION="display">Three</DIV> <IMG CLASS="time" DUR="5" TIMEACTION="display" SRC="\workshop\samples\author\behaviors\media\london.jpg" STYLE="height=150; width=300" /> </t:PAR> <t:PAR onbegin="fnChangeColor();"> <DIV CLASS="time" DUR="5" TIMEACTION="display">Four</DIV> <IMG CLASS="time" DUR="5" TIMEACTION="display" SRC="\workshop\samples\author\behaviors\media\hongkong.jpg" STYLE="height=150; width=300" /> </t:PAR> </t:SEQ> <BR> <BUTTON onclick="oSeq.prevElement()">Previous Slide</BUTTON> <BUTTON onclick="oSeq.nextElement()">Next Slide</BUTTON> </DIV> </DIV> </BODY> </HTML>
In the previous example, the to property was changed on an element and then the element was restarted on the time line. Another option, manipulating properties while media is playing, should be done with caution. Changing properties on time elements while they are playing media or animating objects can have unpredictable or undefined results. When you want to change a property on such an element, the preferred process is to stop the time object, change the property, and then restart the element with the beginElement method. For example, to change the path attribute on an animate object, you could do the following:
<SCRIPT> animateObject.endElement(); animateObject.path="M 0 0 L 100 100"; animateObject.beginElement(); </SCRIPT>
That said, there are some properties that are well suited for manipulation on active elements, such as speed, mute, and volume. The following example shows how to change the volume or mute media while it is playing using the volume and mute properties.
<HTML XMLNS:t="urn:schemas-microsoft-com:time"> <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> .time{ behavior: url(#default#time2);} </STYLE> <SCRIPT LANGUAGE="JScript"> var nCurVolume = 100; function fnVolAdjust(sDirection) { if(sDirection == "up" && nCurVolume < 100) { nCurVolume = nCurVolume + 20; } else if(sDirection == "down" && nCurVolume > 0) { nCurVolume = nCurVolume - 20; } oMedia.volume = nCurVolume; } </SCRIPT> </HEAD> <BODY > <t:MEDIA id="oMedia" timeAction="visibility" src="shuttle3.wmv"/> <b>Volume:</b> <BUTTON onclick="fnVolAdjust('down');"> Down </BUTTON> <BUTTON onclick="fnVolAdjust('up');"> Up </BUTTON> <b>Mute:</b> on <INPUT TYPE="radio" NAME="mute" onclick="oMedia.mute = true;"/> off <INPUT TYPE="radio" NAME="mute" CHECKED onclick="oMedia.mute = false;"/> </BODY> </HTML>
Events
HTML+TIME events are similar to DHTML Events except that they only apply to elements with the time2 behavior applied to them. These events typically are used to detect events that occur on a time element's time line. The following example shows how to use several different HTML+TIME events to determine which media control buttons should be active in response to whether the media is active or paused.
<HTML XMLNS:t="urn:schemas-microsoft-com:time"> <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> .time{ behavior: url(#default#time2);} </STYLE> </HEAD> <BODY > <t:MEDIA id="oMedia" timeAction="visibility" src="countlead.wmv" begin="indefinite" onend="btnStart.disabled=false; btnStop.disabled=true; btnPause.disabled=true; btnResume.disabled=true;" onbegin="btnStart.disabled=true; btnStop.disabled=false; btnPause.disabled=false; btnResume.disabled=true;" onpause="btnPause.disabled=true; btnResume.disabled=false;" onresume="btnPause.disabled=false; btnResume.disabled=true;" /> <BUTTON ID="btnStart" onclick="oMedia.beginElement();"> Start </BUTTON> <BUTTON DISABLED="true" ID="btnStop" onclick="oMedia.endElement();"> Stop </BUTTON> <BUTTON DISABLED="true" ID="btnPause" onclick="oMedia.pauseElement();"> Pause </BUTTON> <BUTTON DISABLED="true" ID="btnResume" onclick="oMedia.resumeElement();"> Resume </BUTTON> </BODY> </HTML>
Repeat and Looping Events
One particularly useful event is the onrepeat event. Loops are quite common in scripting. Now with the onrepeat event, you can create loops that depend on time. You can attach an event handler to this event that is fired at regular intervals. There are a number of examples in this article that do just that. The following example shows how to use the onrepeat event to create a simple timer.
<B>Media Timer:</B> <!-- This SPAN repeats itself on its time line once every second (dur="1"). Every time it repeats, the "onrepeat" event is fired and updates its inner text with the current active time of the media. The "currTimeState" object is used to retrieve information about time elements at run time. This object is discussed in more detail later in this article. --> <SPAN id="Timer" class="time" dur="1" repeatCount="indefinite" fill="hold" onrepeat="innerText=oMedia.currTimeState.activeTime;">0</SPAN> <t:MEDIA id="oMedia" timeAction="visibility" src="countlead.wmv"/>
You could have a time element that has no other purpose but to execute a segment of code at regularly timed intervals. This could be useful when you want to run a script in the background that checks for certain conditions on the page or updates the page. You can change the timing interval of the loop by changing the dur property of the time element that fires the event. The following example shows how to change the precision of the previous timer by changing the duration of the timer's repetitions.
... <BUTTON onclick = "Timer.dur = '.01'">More Precise Timer</BUTTON> <BUTTON onclick = "Timer.dur = '1'">Less Precise Timer</BUTTON> <B>Media Timer:</B> <SPAN STYLE="color:red" id="Timer" class="time" dur="1" repeatCount="indefinite" fill="hold" onrepeat="innerText=oMedia.currTimeState.activeTime;">0</SPAN> <t:MEDIA id="oMedia" timeAction="visibility" src="countlead.wmv"/> ...
Special Objects
As already mentioned, all time elements in a document are exposed as objects. Most of the time objects that you access through script will be actual elements on your page, and you have already seen a number of examples of that in this article. There are a few time elements that do not correspond to document time elements. Two of these objects are currTimeState and playItem. The currTimeState object exposes a large number of unique properties, and the playItem object exposes properties and methods. The next two sections explore these objects in more detail.
currTimeState Object
The currTimeState object is available to you at run time. It provides access to the current values of time parameters as they are changing. This is similar to the currentStyle and runtimeStyle objects. The currTimeState object has its own run-time copy of all the properties associated with an element. These are read-only properties that you can use to determine the state of an element.
Using the properties available on the currTimeState object at run time, you can determine things such as:
- The playback speed of a media element
- Whether the element is currently active on the time line by using the isActive property.
- Whether the element is paused by using the isPaused property.
- The current volume of the media.
The following example demonstrates how to use the currTimeState object's isActive and isPaused properties to determine which media control buttons should be active. The activeTime property is used to obtain a timer for the media element. This example is very similar to the previous one that used events to detect which media control buttons should be active and which should be disabled.
<HTML XMLNS:t="urn:schemas-microsoft-com:time"> <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> .time{ behavior: url(#default#time2);} </STYLE> <SCRIPT LANGUAGE="JScript"> function fnUpdate() { // Check to see if the media is currently active, then update button status. if (oMedia.currTimeState.isActive) { btnStart.disabled=true; btnStop.disabled=false; btnPause.disabled=false; btnResume.disabled=true; // If the media is paused then update button status. if (oMedia.currTimeState.isPaused){ btnPause.disabled=true; btnResume.disabled=false; } } // If the media is not active, then update button status apropriately. else { btnStart.disabled=false; btnStop.disabled=true; btnPause.disabled=true; btnResume.disabled=true; } } </SCRIPT> </HEAD> <BODY> <!-- The HTML+TIME onrepeat event is used to update the timer value every time the SPAN repeats (once every tenth of a second). The current active time of the media is retrieved using the activeTime property of the currTimeState object. --> <B>Media Timer:</B> <SPAN ID="Timer" CLASS="time" DUR=".01" repeatCount="indefinite" FILL="hold" onrepeat="innerText=oMedia.currTimeState.activeTime;">0</SPAN><BR><BR> <!-- The HTML+TIME onend event is used to update the buttons when the media ends. --> <t:MEDIA id="oMedia" timeAction="visibility" src="bee.wmv" begin="indefinite" onend="fnUpdate();"/> <!-- Whenever a button is pressed, the appropriate method is carried out on the media and the fnUpdate() function is called to update the buttons. --> <BUTTON ID="btnStart" onclick="oMedia.beginElement(); fnUpdate();"> Start </BUTTON> <BUTTON ID="btnStop" onclick="oMedia.endElement(); fnUpdate();"> Stop </BUTTON> <BUTTON ID="btnPause" onclick="oMedia.pauseElement(); fnUpdate();"> Pause </BUTTON> <BUTTON ID="btnResume" onclick="oMedia.resumeElement(); fnUpdate();"> Resume </BUTTON> </BODY> </HTML>
playItem Object
A playItem is an object that represents an individual entry (also known as a track) in an Advanced Stream Redirector (ASX) file. An ASX file is an intermediary file that can link to a number of media files that can be used as a play list. The following is a example of a simple ASX file.
<ASX Version="1.0" > <Entry> <Title>Bee at Work</TITLE> <Author>Busy Bee</Author> <Copyright>Unknown</Copyright> <Abstract>Look how busy the bee is!</Abstract> <Ref HREF="http://msdn.microsoft.com../common/samples/author/behaviors/media/bee.wmv"/> </Entry> <Entry> <Title>Cashregister</TITLE> <Author>The Hand</Author> <Copyright>1950</Copyright> <Abstract>A good day at the shop.</Abstract> <Ref HREF="http://msdn.microsoft.com../common/samples/author/behaviors/media/cashregister.wmv"/> </Entry> <Entry> <Title>Subway</TITLE> <Author>Urban Dweller</Author> <Copyright>1973</Copyright> <Abstract>Riding the subway is easier than walking.</Abstract> <Ref HREF="http://msdn.microsoft.com../common/samples/author/behaviors/media/subway.wmv"/> </Entry> </ASX>
The ASX file is in an XML format with each <ENTRY> element corresponding to a playItem object. All of the entries of the ASX file collectively make up the playList collection. For further information, see playList Collection. When you play back an ASX file, the default action is to play the media corresponding to each entry in order from top to bottom, one after the other. As you can see, each <ENTRY> element has several child elements. The <Ref> element is required to specify the path to the respective media file. All of these child elements are exposed as read-only properties on the playItem object. You could retrieve the given author of a playItem object like this.
<t:MEDIA id="oMedia" src="short_3tracks.asx" begin="indefinite" onend="updateBtns();" ontrackchange="updateFields();"/> <SCRIPT> alert(oMedia.playList.activeTrack.author); </SCRIPT>
It is important to note that you only access individual playItem objects using the playList collection. In the preceding case, the oMedia.playList.activeTrack retrieves the currently active (playing) playItem object. Assuming that the previous sample is using the ASX file shown, oMedia.playList.activeTrack.author would retrieve either "Busy Bee," "The Hand," or "Urban Dweller," depending on what playItem is active when the alert method is called.
The following example plays the previously shown ASX file. The nextTrack and prevTrack methods, which are exposed by the playList collection, provide the functionality for the user to play different playItem objects (tracks) using buttons. In addition, information about the currently playing track is displayed using properties exposed by the playItem object.
<HTML XMLNS:t="urn:schemas-microsoft-com:time"> <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> .time{ behavior: url(#default#time2);} </STYLE> <SCRIPT LANGUAGE="JScript"> // This function displays information about the currently active playItem // object. Notice that the playItem object must be accessed through the // playItem collection. function updateFields() { oTitle.innerText = oMedia.playList.activeTrack.title; oAuthor.innerText = oMedia.playList.activeTrack.author; oAbstract.innerText = oMedia.playList.activeTrack.abstract; oCopyright.innerText = oMedia.playList.activeTrack.copyright; oFilename.innerText = oMedia.playList.activeTrack.src; } </SCRIPT> </HEAD> <BODY> <!-- The "ontrackchange" event is used to update the track information every time the active track changed in the play list. --> <t:MEDIA id="oMedia" src="short_3tracks.asx" begin="indefinite" ontrackchange="updateFields();"/> <SPAN>Title:<SPAN STYLE="color:red" id="oTitle"></SPAN></SPAN><BR> <SPAN>Author:<SPAN STYLE="color:red" id="oAuthor"></SPAN></SPAN><BR> <SPAN>Abstract:<SPAN STYLE="color:red" id="oAbstract"></SPAN></SPAN><BR> <SPAN>Copyright:<SPAN STYLE="color:red" id="oCopyright"></SPAN></SPAN><BR> <SPAN>Filename:<SPAN STYLE="color:red" id="oFilename"></SPAN></SPAN><BR><BR> <BUTTON id="btnStart" onclick="oMedia.beginElement();"> Start </BUTTON> <!-- The "prevTrack" and "nextTrack" methods are exposed by the "playList" collection. This allows you to move to the next track defined in the asx file or the last track relative to the currently active track. --> <BUTTON id="btnPrev" onclick="oMedia.playList.prevTrack();"> Previous Track </BUTTON> <BUTTON id="btnNext" onclick="oMedia.playList.nextTrack();"> Next Track </BUTTON> <BUTTON id="btnStop" onclick="oMedia.endElement();"> Stop </BUTTON> </BODY> </HTML>
Putting it together
The next two examples show examples of scripting with HTML+TIME methods, properties, and events together. The first sample shows how to use all of these components to make a simple progress bar that can be used to jump around in the media.
<HTML xmlns:t= "urn:schemas-microsoft-com:time" xmlns:control> <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> @media all{ control\:slider {behavior:url(slider.htc);} } .time{behavior:url(#default#time2)} </STYLE> <SCRIPT> // This function is fired whenever the slide bar changes value. function fnChangeBar() { // If the media is not active, then make it active. This makes it // possible to start the media back up again by just dragging the // slide bar to some value. if(!oMedia.currTimeState.isActive) { oMedia.beginElement() } // If the slider value is being moved by the user, the media will seek // to the user-specified value. if(oSlider.value!=Math.round(oMedia.currTimeState.activeTime)) { oMedia.seekTo(1, oSlider.value) } Timer.innerText=parseInt(oMedia.currTimeState.activeTime); } </SCRIPT> </HEAD> <BODY> <!-- The following SPAN element serves two purposes. First, it displays where on its time line the media is. Second, it changes the value of the slide bar every second to show the progression of the media on the slide bar. --> <B>Media timer:</B> <SPAN id="Timer" class="time" BEGIN="oMedia.begin" END="oMedia.end" DUR="1" REPEATCOUNT="indefinite" FILL="hold" onrepeat="oSlider.value=Math.round(oMedia.currTimeState.activeTime);">0</SPAN> <BR><BR> <t:video style="width:175px; height:150px;" id="oMedia" src="../common/samples/author/behaviors/media/movie.avi" /> <BR> <control:slider ID="oSlider" TICKINTERVAL="1" TICKNUMBER="19" onchange="fnChangeBar();"> </control:slider> </BODY> </HTML>
This next example is a context menu that moves to the location where the user right-clicks the mouse. The beginElement method is used to initiate the animation, the to and from properties are changed dynamically so that the animation moves along the correct path depending on the location of the user clicking the mouse, and the onend event is used to coordinate when the from property should be changed.
<!-- The vml behavior is used in this example to allow oval shaped menu boxes. Notice in the following the namespace declaration for the vml behavior. --> <HTML xmlns:t ="urn:schemas-microsoft-com:time" xmlns:v = "urn:schemas-microsoft-com:vml" > <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> v\:* {BEHAVIOR: url(#DEFAULT#VML)} BODY {font-family:arial; margin:0; font-size:80%} .time {behavior: url(#default#time2)} .box {position:absolute;top:100;left:100;width:150px;height:22px; padding:5px;padding-left:15px;display:none;cursor:hand; font-family:arial;} </STYLE> <SCRIPT> // Function is used to animate the menu box movement. function moveBox(){ var mouseLeft = null; var mouseTop = null; // Make the menu boxes visible oBox.style.display='block'; oBox2.style.display='block'; oBox3.style.display='block'; // Retrieve x and y coordinates of the mouse click mouseLeft=event.clientX; mouseTop=event.clientY; //////////////////////////////////////////////////////////////////////////// // Two t:ANIMATE elements are used for each menu box to control the // movement of each box. One t:ANIMATE element controls the x-coordinate // animation and the other controls the y-coordinate animation. The "to" // properties of the t:ANIMATE elements are set to a value relative to // the respective x and y coordinates of the mouse click. This ensures // that the menu boxes animate to the correct location. To begin the // animation, the t:ANIMATE elements are started on the time line using // the "beginElement()" method. /////////////////////////////////////////////////////////////////////////// // Code for top menu box animation. leftMove.to=mouseLeft; topMove.to=mouseTop; leftMove.beginElement(); topMove.beginElement(); // Code for middle menu box animation. leftMove2.to=mouseLeft; topMove2.to=mouseTop + 29; // notice that y-coordinate is offset leftMove2.beginElement(); topMove2.beginElement(); // Code for bottom menu box animation. leftMove3.to=mouseLeft; topMove3.to=mouseTop + 58; leftMove3.beginElement(); topMove3.beginElement(); } // Function is used to hide the menu when clicked. function closeMenu(){ oBox.style.display='none'; oBox2.style.display='none'; oBox3.style.display='none'; } </SCRIPT> </HEAD> <!-- The "oncontextmenu" event is used to make the menu become visible and animate. --> <BODY oncontextmenu="moveBox(); return false; "> <!-- The following are the menu blocks. Notice that the vml behavior is attached to them, which enables the rounded corners of the menu blocks. --> <v:ROUNDRECT ID="oBox" CLASS="box" ARCSIZE="90923f" STROKEWEIGHT="2" articles.style.display='none'; contacts.style.display='none'; closeMenu()"> Home</v:ROUNDRECT > <v:ROUNDRECT ID="oBox2" CLASS="box" ARCSIZE="90923f" STROKEWEIGHT="2" contacts.style.display='none'; closeMenu()"> Articles</v:ROUNDRECT > <v:ROUNDRECT ID="oBox3" CLASS="box" ARCSIZE="90923f" STROKEWEIGHT="2" contacts.style.display='block'; closeMenu()"> Contact</v:ROUNDRECT > <!-------------------------------------------------------------------------- The following are the t:ANIMATE elements that control the animation of the menu blocks. Notice that the "DUR" attribute values are different for each pair of elements. This causes the blocks to move at different rates and follow one another. Note that when each t:ANIMATE element ends, its "from" property is updated to the coordinate that the animation ends at. Thus, upon the next animation, the menu starts its animation from the new location. ---------------------------------------------------------------------------> <t:ANIMATE ID="leftMove" TARGETELEMENT="oBox" ATTRIBUTENAME='left' BEGIN="indefinite" DUR=".15" DECELERATE="1" FILL="freeze" onend="this.from=this.to"/> <t:ANIMATE ID="topMove" TARGETELEMENT="oBox" ATTRIBUTENAME='top' BEGIN="indefinite" DUR=".15" DECELERATE="1" FILL="freeze" onend="this.from=this.to"/> <t:ANIMATE ID="leftMove2" TARGETELEMENT="oBox2" ATTRIBUTENAME='left' BEGIN="indefinite" DUR=".3" DECELERATE="1" FILL="freeze" onend="this.from=this.to"/> <t:ANIMATE ID="topMove2" TARGETELEMENT="oBox2" ATTRIBUTENAME='top' BEGIN="indefinite" DUR=".3" DECELERATE="1" FILL="freeze" onend="this.from=this.to"/> <t:ANIMATE ID="leftMove3" TARGETELEMENT="oBox3" ATTRIBUTENAME='left' BEGIN="indefinite" DUR=".6" DECELERATE="1" FILL="freeze" onend="this.from=this.to"/> <t:ANIMATE ID="topMove3" TARGETELEMENT="oBox3" ATTRIBUTENAME='top' BEGIN="indefinite" DUR=".6" DECELERATE="1" FILL="freeze" onend="this.from=this.to"/> </BODY> </HTML>
Collections
The samples so far have only accessed time objects using their id values. Using collections allows an alternative way to access objects in a document. Just like normal HTML elements, time elements are available to standard collections. For example, all time elements are available to the all collection. In addition, there are a number of unique collections that are available to most time objects. These collections are listed in the table that follows:
activeElements | Returns a reference to the collection of all top-level child elements of the object that are currently active on the time line. |
timeAll | Retrieves a reference to the collection of all timed elements. |
timeChildren | Retrieves a reference to the collection of all top-level time container child elements. |
Another HTML+TIME collection, the playList collection, is applicable only when ASX files are being used. For more information, see playList Collection. To demonstrate the collections in the table, let's start by taking a look at the following example. It is a simple menu application.
<HTML XMLNS:t ="urn:schemas-microsoft-com:time"> <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> .time {behavior: url(#default#time2);} .block { margin-left:10px; color:red; font-weight:normal; font-size:18px; position:absolute; top:115px; left:12px; cursor:hand; } #oDiv { cursor:hand; color:#003399; font-weight:bold; font-size:15pt; position:absolute; top:85px; left:10px; border:2px solid #cccccc; background:#eeeeee; padding:10px; height:160px; width:200px; padding-top:5px; } </STYLE> </HEAD> <BODY> <DIV ID="oDiv" onclick="oContainer.beginElement();">Click Me!</DIV> <t:PAR ID="oContainer" BEGIN="indefinite" DUR="indefinite"> <t:PAR BEGIN=".25"> <DIV CLASS="time block">Menu Item 1 <t:ANIMATE BY="5px" ATTRIBUTENAME="top" FILL="FREEZE" DUR="1" /> </DIV> </t:PAR> <t:PAR BEGIN=".5"> <DIV CLASS="time block">Menu Item 2 <t:ANIMATE BY="35px" ATTRIBUTENAME="top" FILL="FREEZE" DUR="1" /> </DIV> </t:PAR> <t:PAR BEGIN=".75"> <DIV CLASS="time block">Menu Item 3 <t:ANIMATE BY="65px" ATTRIBUTENAME="top" FILL="FREEZE" DUR="1" /> </DIV > </t:PAR> <t:PAR BEGIN="1"> <DIV CLASS="time block">Menu Item 4 <t:ANIMATE BY="95px" ATTRIBUTENAME="top" FILL="FREEZE" DUR="1" /> </DIV> </t:PAR> </t:PAR> </BODY> </HTML>
As you can see, the container t:PAR (oContainer), has child t:PAR elements nested inside and these child t:PAR elements in turn have child time elements.
... // This is the container element <t:PAR ID="oContainer" BEGIN="indefinite" DUR="indefinite"> // The container element has direct child and grandchild time elements. <t:PAR BEGIN=".25"> <DIV CLASS="time block">Menu Item 1 <t:ANIMATE BY="5px" ATTRIBUTENAME="top" FILL="FREEZE" DUR="1" /> </DIV> </t:PAR> ...
The collections are used just like typical DHTML collections. Here is a simple example that uses the timeAll collection to retrieve all of the children of the container element and then loops though each member of the collection and displays the member's tag name.
<HTML XMLNS:t ="urn:schemas-microsoft-com:time"> <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> .time {behavior: url(#default#time2);} .block { color:red; width:130px; font-size:13pt; position:absolute; top:200px; left:22px; cursor:hand; } #oDiv { padding:10px; padding-top:7px; height:20px; background-color:#eeeeee; border:2px solid #cccccc; color:#003399; font-weight:bold; font-size:13pt; position:absolute; top:175px; left:10px; cursor:hand; width:150px; height:160px; } </STYLE> <SCRIPT> function fnSeeAllChildren() { // All of the time child elements are retrieved. var aAllTimeChildren = oContainer.timeAll; // Loop through all of the members of the collection and display their // tag names. for(i=0;i<aAllTimeChildren.length;i++) { alert(aAllTimeChildren[i].tagName); } } </SCRIPT> </HEAD> <BODY> <BUTTON onclick="fnSeeAllChildren();">Loop Through Collection</BUTTON> <DIV ID="oDiv" onclick="oContainer.beginElement();">Click me!</DIV> <t:PAR ID="oContainer" BEGIN="indefinite" DUR="indefinite"> <t:PAR BEGIN=".25"> <DIV CLASS="time block">Menu Item 1 <t:ANIMATE BY="5px" ATTRIBUTENAME="top" FILL="FREEZE" DUR="1" /> </DIV> </t:PAR> <t:PAR BEGIN=".5"> <DIV CLASS="time block">Menu Item 2 <t:ANIMATE BY="35px" ATTRIBUTENAME="top" FILL="FREEZE" DUR="1" /> </DIV> </t:PAR> <t:PAR BEGIN=".75"> <DIV CLASS="time block">Menu Item 3 <t:ANIMATE BY="65px" ATTRIBUTENAME="top" FILL="FREEZE" DUR="1" /> </DIV > </t:PAR> <t:PAR BEGIN="1"> <DIV CLASS="time block">Menu Item 4 <t:ANIMATE BY="95px" ATTRIBUTENAME="top" FILL="FREEZE" DUR="1" /> </DIV> </t:PAR> </t:PAR> </BODY> </HTML>
Similar to the last example, this example shows the members of the timeAll, timeChildren, and activeElements collections. This example uses a repeating time element (oRepeater) to run script every tenth of a second, which displays what elements are active.
<HTML XMLNS:t ="urn:schemas-microsoft-com:time"> <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> .time {behavior: url(#default#time2);} .block { margin-left:10px; color:red; font-weight:normal; font-size:18px; position:absolute; top:260px; left:12px; cursor:hand; } #oDiv { cursor:hand; color:#003399; font-weight:bold; font-size:15pt; position:absolute; top:230px; left:10px; border:2px solid #cccccc; background:#eeeeee; padding:10px; height:160px; width:200px; padding-top:5px; } </STYLE> </STYLE> <SCRIPT> // This function displays the members of the collections in their // respective display SPAN elements. function fnGetCollection(Col) { var sDisplayString = "" if(Col == "all") { var aCol = oContainer.timeAll; var oDisplayer = oTimeAllDisplay; } else if(Col == "child") { var aCol = oContainer.timeChildren; var oDisplayer = oTimeChildrenDisplay; } else if(Col == "active") { var aCol = oContainer.activeElements; var oDisplayer = oTimeActiveDisplay; } // Loop through the collection specified in the "if" block above and // create a string of the tag names of each object in the collection. for(i=0;i<aCol.length;i++) { if(i==0) sDisplayString = aCol.item(i).tagName; else sDisplayString = sDisplayString + ", " + aCol.item(i).tagName; } // The string populates the appropriate display SPAN. oDisplayer.innerText = sDisplayString; } </SCRIPT> </HEAD> <!-- When the document loads, the members of the "timeAll" and "timeChildren" collection are displayed. --> <BODY ONLOAD="fnGetCollection('all');fnGetCollection('child');"> <!-- This is a time element that fires the fnGetCollection() function every tenth of a second to display the members of the "oCollection.aciveElements" collection. --> <SPAN ID="repeat_span" CLASS="time" dur=".1" REPEATCOUNT="indefinite" onrepeat="fnGetCollection('active');"></SPAN> <DIV STYLE="position:absolute; top:130px"> <B>The tag names of all time elements in the following collections:</B> <DIV><I>containerElement</I>.timeAll (all children):</DIV> <SPAN STYLE="color:red" ID="oTimeAllDisplay"></SPAN> <DIV><I>containerElement</I>.timeChildren (top-level children): <SPAN STYLE="color:red" ID="oTimeChildrenDisplay"></SPAN> </DIV> <DIV><I>containerElement</I>.activeChildren (top-level active children): <SPAN STYLE="color:red" ID="oTimeActiveDisplay"></SPAN> </DIV> </DIV> <DIV ID="oDiv" onclick="oContainer.beginElement();">Click me!</DIV> <t:PAR ID="oContainer" BEGIN="indefinite" DUR="indefinite"> <t:PAR BEGIN=".25"> <DIV CLASS="time block">Menu Item 1 <t:ANIMATE BY="5px" ATTRIBUTENAME="top" FILL="FREEZE" DUR="1" /> </DIV> </t:PAR> <t:PAR BEGIN=".5"> <DIV CLASS="time block">Menu Item 2 <t:ANIMATE BY="35px" ATTRIBUTENAME="top" FILL="FREEZE" DUR="1" /> </DIV> </t:PAR> <t:PAR BEGIN=".75"> <DIV CLASS="time block">Menu Item 3 <t:ANIMATE BY="65px" ATTRIBUTENAME="top" FILL="FREEZE" DUR="1" /> </DIV > </t:PAR> <t:PAR BEGIN="1"> <DIV CLASS="time block">Menu Item 4 <t:ANIMATE BY="95px" ATTRIBUTENAME="top" FILL="FREEZE" DUR="1" /> </DIV> </t:PAR> </t:PAR> </BODY> </HTML>
At this point, it is useful to explore what scenarios are appropriate for the use of collections. Collections are often useful when you don't know how many time elements there will be. Let's say that you wanted to make a menu application similar to the preceding one but you don't know how many items will be in your menu. In the case of the preceding sample, we might want to access the timed menu items in such a way that we don't care how many menu items there are. You simply add an item and it works. This makes the application more reusable and generic. The following example shows how you can achieve the same time element timing as the preceding sample with the menu item looking like this.
... <t:PAR> <DIV CLASS="time bullet">Menu Item 1 <t:ANIMATE /> </DIV> </t:PAR> ...
Notice that all of the attributes are absent from the t:ANIMATE element. All of the properties of the t:ANIMATE elements are applied in script to the collection. Therefore, you could add another item to the menu by simply changing the inner text of the div. The timing and animation of the menu items of this sample are accessed through the HTML+TIME?B>timeAll and timeChildren collections, and the properties of these collection items are changed using script.
<HTML XMLNS:t ="urn:schemas-microsoft-com:time"> <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> .time {behavior: url(#default#time2);} .bullet { margin-left:10px; color:red; font-weight:normal; font-size:18px; position:absolute; top:180px; left:12px; cursor:hand; } #oDiv { cursor:hand; color:#003399; font-weight:bold; font-size:15pt; position:absolute; top:150px; left:10px; border:2px solid #cccccc; background:#eeeeee; padding:10px; height:160px; width:200px; padding-top:5px; } </STYLE> <SCRIPT> // This function is called when the page is loaded. It applies values to // "begin" property of the "t:PAR" menu item container elements and values // to the properties of the "t:ANIMATE" objects, which are needed for the // animation of the menu items. function fnInit() { // Create a collection of the top-level element time containers. var aParChildren = oContainer.timeChildren; var nBeginTime = 0; for(i=0;i<aParChildren.length;i++) { // Stagger the begin times of all of the menu items. aParChildren[i].begin = nBeginTime; nBeginTime = nBeginTime + .25 } var aAllTimeElements = oContainer.timeAll; var multiplier = 0; for(i=0;i<aAllTimeElements.length;i++) { if(aAllTimeElements[i].tagName == "ANIMATE") { // Stagger the values of the "by" properties of the "t:ANIMATE" // objects so the menu items cascade down to different positions. if(multiplier==0) { aAllTimeElements[i].by = 10; } else { aAllTimeElements[i].by = (multiplier*30) + 10; } // Assign appropriate values to other properties of the "t:ANIMATE" // objects. aAllTimeElements[i].fill = "freeze"; aAllTimeElements[i].dur = 1; aAllTimeElements[i].attributeName = "top"; multiplier ++; } } } </SCRIPT> </HEAD> <!-- The properties of the time elements are set when the page loads. --> <BODY onload="fnInit();" > <DIV ID="oDiv" onclick="oContainer.beginElement();">Click me! </DIV> <t:PAR ID="oContainer" BEGIN="indefinite" DUR="indefinite"> <!-- Notice how these entries are so generic now. Adding a new entry is as simple as altering the inner text of the DIV time elements. The rest of the details are taken care of by script. --> <t:PAR> <DIV CLASS="time bullet">Menu item 1 <t:ANIMATE /> </DIV> </t:PAR> <t:PAR> <DIV CLASS="time bullet">Menu item 2 <t:ANIMATE /> </DIV> </t:PAR> <t:PAR> <DIV CLASS="time bullet">Menu item 3 <t:ANIMATE /> </DIV > </t:PAR> <t:PAR> <DIV CLASS="time bullet">Menu item 4 <t:ANIMATE /> </DIV> </t:PAR> </t:PAR> </BODY> </HTML>
Here is the same example again with more menu entries. In addition, a background div element grows in height as the menu items move down. How far the div expands depends on the number of items in the menu (the collection size).
Because we use a collection and change the properties of the collection members through script, it is easy to change how the application behaves with little modification of the script. The following example shows what happens to the previous example when you change the begin times of the collection items.
... var aParChildren = oContainer.timeChildren; var nBeginTime = 0; for(i=0;i<aParChildren.length;i++) { // Stagger the begin times of all of the menu items. aParChildren[i].begin = nBeginTime; // This used to be "nBeginTime = nBeginTime + .25" by changing the // behavior of the time elements changes dramatically. nBeginTime = nBeginTime + .1 } ...
playList Collection
As already mentioned, the playList collection is only available when an ASX file is being used as the source of the media object. The playList collection consists of all of the playItem objects (tracks), defined in the ASX file. The ASX file essentially supplies a playList that is analogous to programming a jukebox to play a list of songs in a given order. The following example shows how to use the playList collection to display the play list supplied by an ASX file and allow the user to click on the play list items to skip directly to a media selection.
<HTML XMLNS:t="urn:schemas-microsoft-com:time"> <HEAD> <?IMPORT namespace="t" implementation="#default#time2"> <STYLE> .time{ behavior: url(#default#time2);} .trackDisplay {cursor:hand;} </STYLE> <SCRIPT LANGUAGE="JScript"> var bPlayListMade = false; // This function is called when the media is started. The function creates // a set of SPAN elements that map to the members of the play list collection (tracks). function fnCreatePlayList() { var playCollection = oMedia.playList; for(i=0;i<playCollection.length;i++) { var sElement = "<SPAN onclick='fnGoToTrack()'" + "CLASS = 'trackDisplay'>" + playCollection(i).title + "</SPAN>" var oDisplaySpan = document.createElement(sElement); oDisplaySpan.innerText = playCollection(i).title; oShowPlayList.insertBefore(oDisplaySpan); var oBr = document.createElement("BR"); oShowPlayList.insertBefore(oBr); } } // The following function is called every time the media track changes. The // function is used to highlight the SPAN inside the play list that corresponds // to the currently playing track. function updateFields() { // Retrieve collection of SPAN elements in display DIV var aSpans = oShowPlayList.childNodes; for (i=0; i<aSpans.length; i++) { aSpans(i).style.color = "blue"; if(aSpans(i).innerText == oMedia.playList.activeTrack.title) { // Only the SPAN that maps to the currently active track is highlighted. aSpans(i).style.color = "red"; } } } // This function is called when the user clicks on one of the items in the // play list. The function is used to activate the corresponding track. function fnGoToTrack() { var playCollection = oMedia.playList; for(i=0;i<playCollection.length;i++) { // Find which member of the collection corresponds to the clicked item. if(event.srcElement.innerText == playCollection(i).title) { // When the correct track is retrieved, play the track. playCollection(i).setActive(); } } } </SCRIPT> </HEAD> <BODY> <!-- Note that the media has an ASX file as its source. --> <t:MEDIA ID="oMedia" SRC="short_3tracks.asx" BEGIN="indefinite" ontrackchange="updateFields();"/> <!-- The following DIV contains all of the dynamically created SPAN elements map to playList collection items (tracks). --> <DIV ID="oShowPlayList"></DIV> <BUTTON ID="btnStart" onclick="oMedia.beginElement();fnCreatePlayList(); updateFields();"> Start </BUTTON> </BODY> </HTML>
Because this sample used the playList collection to dynamically find the number of tracks in the ASX file, you could use any ASX file with this sample and it would work (assuming that the tracks had titles!). Here is a slightly larger sample that displays the information about the currently playing track.
Where to Go from Here
This article provides a starting point for using scripting on time elements. We have shown only a small subset of the HTML+TIME methods, properties, and events available. For a complete list, see HTML+TIME 2.0 Reference. We have strived to show that HTML+TIME scripting components are used in a very similar way to other DHTML components. With this in mind, HTML+TIME is not only an extension to HTML but an extension to DHTML as well.