Categories
Technology

Save Dr. Dotty from the Quicksand

Copy from the Wayback Machine Archive.

Dr. Dotty is exploring in the jungle and accidently walks into quicksand. The object of the game is, of course, to save Dr. Dotty.

The Save Dr. Dotty from the Quicksand games are a variation of the old paper game “Hangmans’s Bluff”. The object of the game is to fill in all the missing letters from one or more words within a phrase and save Dr. Dotty from DOOM.

Playing the Game

-The playing board for each of the Quicksand games is 800 x 600 pixels.
-Each game has an opening animation, and an ending animation if you correctly find all the missing letters.
-Pressing the “ENTER” key after the opening animation stops will start the game.
-Type a letter into your keyboard and every place that the letter exists gets filled in.
-If you type in an incorrect letter, poor Dr. Dotty sinks a little further into the quicksand.
-Close the playing window at any time by clicking the space bar.

The Dr. Dotty games work with Navigator 4.x, Netscape 6.x, IE 4.x, IE 5.x, and IE 6.x, and Mozilla.

 

  • Game 1 If you know about hurricanes, you know the answer to the question in this game. However, you can find the answer in the How the Game Works section, below.
  • Game 2 If you know about minerals and mineral identifications, you know the answer to this game. However, you can find the answer in the How the Game Works section, below.

 

How the Game Works

The Dr. Dotty games use DHTML for all interactive effects. This includes the use of CSS to position elements as well as alter their presentation; and JavaScript to perform all actions, interactive or otherwise.

The elements of the game page are positioned using CSS2 absolute positioning. Doing this I can control the layout of the page at any time. Using absolute positioning also exposes all of the elements to script access once the page is loaded for a browser such as Navigator 4.x. All elements are exposed as part of a Document Object Model (DOM), and hence exposed to interactive scripting access for the IE 4.x and IE 5.x, Mozilla, and the upcoming Navigator 5.x browsers, but to ensure cross-browser and cross- version compatibility, I code to Navigator 4.x, which requires that the elements be positioned absolutely:

   <DIV id="dotty1" style="position:absolute; left: 20px; top: 20px">
   ...
   </DIV>

To ensure that the games work with both IE, Mozilla, and Netscape I used the Burning Bird X-Objects to hide most DHTML implementation techniques. Because of this, when I want to move an object to an absolute position of 100 pixels from the left, I can use the method exposed on the cross-browser objects:

   theobjs["dotty1"].objSetLeft(100);

I’ve also created a set of higher-level animation objects that use the cross-browser scripting objects and JavaScript timers to manage all animation effects. These Animation objects, are basically arrays of animations, with each step in the animation synchronized with matching steps in the other associated animations.

So, to show an element that both moves and clips, I create a new animation sequence (co-ordinated animations that are all performed sequentially in the order they are added to the sequence), and then attach one animation object to the sequence. The animation object is defined with a set number of animation steps, each played out after a specified number of microseconds. After the animation object is created, I then crate new Animator objects, one for the clipping animation and one for the move animation:

   var seq = new animatorSequence();
	
   anim = seq.newAnimator(10,100);
   anim.addAnimator(theobjs["dotty4"],"M",180,160);
   anim.addAnimator(theobjs["dotty4"],"C",0,0,330,200);

When the animation sequence is played, each successive steps of the animations contained in the sequence is synchronized and played:

    seq.play();

So, when Dr. Dotty moves across the screen in the opening sequence, one Animation Sequence is created for the entire opening animation; several Animations are created to handle such things as moving Dr. Dotty, showing the different word bubbles and words that he says, and even changing Dr. Dotty’s expression. Different types of Animators objects are created for each animation effect, and the Animators are added to Animation objects to provide for the synchronization for multiple-animator effects.

Want to see the code creating the animation sequence, animations, and animators for the opening animation? Well, it’s a bit long, but here you go:

function setup_start(){
   seq = new animatorSequence();

   // add animation objects to sequence
   // this animation plays for 200 microsecond, with each
   // step in the animation sequence timed at 20 microseconds --
   // 10 animation steps in all
   var anim = seq.newAnimator(20,200);

   // add animators
   anim.addAnimator(theobjs["dotty1"],"M",180,160);
   anim.addAnimator(theobjs["bubble1"],"M",180,400);
   anim.addAnimator(theobjs["bubble1"],"Z",4);
   anim.addAnimator(theobjs["words1"],"M",230,460);
   anim.addAnimator(theobjs["words1"],"Z",5);

   // second animation, this time one step at 100 microseconds 
   anim = seq.newAnimator(1, 100);
   anim.addAnimator(theobjs["words1"],"H");

   // third animation, 1 step, 1500 ms
   anim = seq.newAnimator(1, 1500);
   anim.addAnimator(theobjs["dotty2"],"S");
   anim.addAnimator(theobjs["words2"],"S");
   anim.addAnimator(theobjs["words2"],"Z",5);

   // fourth animation, 1 step 200 ms
   anim = seq.newAnimator(1, 300);
   anim.addAnimator(theobjs["dotty3"],"S");
   anim.addAnimator(theobjs["dotty2"],"H");
   anim.addAnimator(theobjs["words2"],"H");
   anim.addAnimator(theobjs["words3"],"S");

   // fifth animation, 1 step 2000 ms
   anim = seq.newAnimator(1, 2000);
   anim.addAnimator(theobjs["dotty1"],"H");
   anim.addAnimator(theobjs["words3"],"S");
   anim.addAnimator(theobjs["words3"],"Z",5);

   // sixth animation, 1 step, 100 ms
   anim = seq.newAnimator(1, 100);
   anim.addAnimator(theobjs["words3"],"H");
   anim.addAnimator(theobjs["dotty3"],"H");
   anim.addAnimator(theobjs["dotty4"],"S");
   anim.addAnimator(theobjs["words4"],"Z",5);

   // seventh animation, 1 step, 1000 ms
   anim = seq.newAnimator(1, 1000);
   anim.addAnimator(theobjs["words4"],"S");

   // eighth animation, 1 step, 1000 ms
   anim = seq.newAnimator(1, 1000);
   anim.addAnimator(theobjs["words4"],"H");
   anim.addAnimator(theobjs["words4"],"Z",5);

   // ninth animation, 1 step, 3000 ms
   anim = seq.newAnimator(1, 3000);
   anim.addAnimator(theobjs["words4"],"H");
   anim.addAnimator(theobjs["words5"],"S");
   anim.addAnimator(theobjs["words5"],"Z",5);

   // tenth animation, 1 step, 1000 ms
   anim = seq.newAnimator(1, 1000);
   anim.addAnimator(theobjs["words5"],"H");

   // eleventh animation, 1 step, 3000 ms
   anim = seq.newAnimator(1, 3000);
   anim.addAnimator(theobjs["bubble1"],"H");
   anim.addAnimator(theobjs["dotty5"],"S");
   anim.addAnimator(theobjs["bubble2"],"S");
   anim.addAnimator(theobjs["bubble2"],"Z",4);
   anim.addAnimator(theobjs["words6"],"S");
   anim.addAnimator(theobjs["words6"],"Z",5);

   // twelth animation, 1 step, 1000 ms
   anim = seq.newAnimator(1, 1000);
   anim.addAnimator(theobjs["words6"],"H");

   // thirteenth animation, 10 steps, 100ms (10 ms each) 
   anim = seq.newAnimator(10, 100);
   anim.addAnimator(theobjs["words7"],"S");
   anim.addAnimator(theobjs["title"], "M",200,500);

    // fourteenth animation, 10 steps, 100 ms
    anim = seq.newAnimator(10, 100);
    anim.addAnimator(theobjs["dotty5"],"H");
    anim.addAnimator(theobjs["words7"],"H");
    anim.addAnimator(theobjs["words8"],"S");
    anim.addAnimator(theobjs["words8"],"Z",5);
    anim.addAnimator(theobjs["title"], "M",300,500);
	

     // play all 14 animations, in order 
     // as they are defined in sequence array
     seq.play();
}

At the very end of the function that creates the opening animation sequence, the sequence is played.

One last aspect of the Dr. Dotty games is the interactive portion. This is handled by capturing the keypress event for the Web page, and passing the event to a function:

// handle keyboard events
if (navigator.appName != "Microsoft Internet Explorer") 
   document.captureEvents(Event.KEYDOWN);

document.onkeydown=keypress;

In the function, the keycode for the keypress event is accessed and compared to the letters for the answer: if a match is found, the letter is “filled in” in the answer (using DHTML to replace the existing underscore character with the letter); if a match is not found, Dr. Dotty is lowered into the quicksand further (using clipping to lower the good doctor, and DHTML “hide and show” to change Dr. Dotty’s message and expression).

Download the game source code from the link to the right and make your own version of the game. Have fun!

 

Categories
Technology

Demonstrating Photoshop Functionality with X-Objects

Copy found at Wayback Machine Archive.

I upgraded my original cross-browser DHTML objects into the new X-Objects — with support for Mozilla and Navigator 6.0.

To try out the new objects, I migrated 4 DHTML demos into using the new objects. These demos demonstrate different aspects of using Adobe PhotoShop’s photo filters and editors. I’ll be going over each demo in this article, but first, I wanted to describe another set of JavaScript objects used in the examples: the Animators.

Animator scripting objects

When I first started working with DHTML, and creating DHTML effects, I used to manually code animations within the page where the animation displayed. These animations used a JS timer created using the function setTimeout.

For instance, my first DHTML animation was about an animated menu that didn’t do much more than move pieces of the menu around the Web page (see animated menu). This example has changed based on new versions of both IE and Navigator, but for the most part the animation starts once the page is loaded, and continues until the last animation step is reached:

function MoveObjects() {
   theobjs["menu1"].objMoveAbsolute(
		TimingObjectsX[0][currentTick], 
                TimingObjectsY[0][currentTick])
   theobjs["menu2"].objMoveAbsolute(
		TimingObjectsX[1][currentTick], 
                TimingObjectsY[1][currentTick])
   theobjs["menu3"].objMoveAbsolute(
		TimingObjectsX[2][currentTick], 
                TimingObjectsY[2][currentTick])
   theobjs["menu4"].objMoveAbsolute(
		TimingObjectsX[3][currentTick], 
                TimingObjectsY[3][currentTick])
   theobjs["menu5"].objMoveAbsolute(
		TimingObjectsX[4][currentTick], 
                TimingObjectsY[4][currentTick])
   theobjs["menu6"].objMoveAbsolute(
		TimingObjectsX[5][currentTick], 
                TimingObjectsY[5][currentTick])


   currentTick++
   if (currentTick < 8) 
      setTimeout("MoveObjects()", 400)
}

I’ve also used setInterval as well as setTimeout to create repeating timers as well as non-repeating ones.

After a while, I started creating more complex animations such as DHTML demonstrations of how a hurricane forms, or the Dr. Dotty games, covered in a previous InterActZone article. One thing I found out is that the amount of code to control multiple objects in a synchronized animation was enormous and I got a bit tired of coding and re-coding the same timer synchronization efforts.

So, I created what I call the Animator objects. This set of objects provides for most major types of DHTML animation — movement, clipping, visibility, ordering, and sliding (combination of movement and clipping) — and has built in animation synchronization. So, you can have an animation sequence that moves 3 DIV blocks around the page, while a fourth block is being clipped and a fifth block is being hidden.

You can take a look at the Animator script file as I go through how the objects work.

How Animators work

There are three types of Animation objects used to build an animation.

First, an Animation Sequence is created. An Animation Sequence is a container for several different animator objects, all of which are animated as a unit. To create the Sequence object, use the following:

      seq = new animatorSequence();

Once the Animator Sequence is created, then Animators are added. An Animator adds a specific animation effect over a specified period of time, and over a specified number of steps. So, if I want an animation to last 100 microseconds, and occur over 15 steps, I would create the following Animator:

      var anim = seq.newAnimator(15,100);

I can add more than one Animator to a sequence, and each Animator is played in turn, as they are added to the sequence.

Once the Animator object is created, Animations can be added to the object. For instance, to move two different Web page DIV blocks at the same time, I would use Animator code similar to the following:

anim.addAnimator(theobjs["box1"],"M",160,320);
anim.addAnimator(theobjs["image2"],"M",-240,320);

In this, two DIV elements, named “box1” and “image2”, are moved at the same time, so they’re added to the same Animator. Each Animation is defined by the animation type — move type is defined with a letter “M” — and any characteristics of the animation, such as the end location of the move.

I could at this point add more Animator objects (there really is no limit to the number of Animators added to an Animation Sequence, other than limits within the underlying JS, such as array elements and number of timers). I could also add more Animations to the current Animator.

When I’m finished defining the existing Animation Sequence, I can then play it:

    seq.play();

What happens is that the animation starts, beginning with the first Animator added to the sequence, and continuing, in order, until all of the Animators have been played. Additionally, when an Animator is associated with more than one HTML element, the DHTML effect assigned is applied to each HTML element, in turn, in a synchronized manner over all of the steps of the animation.

The types of animations currently implemented are:

  • Clipping: addAnimator(theobj, “C”, newtop, newleft, newbottom, newright)
  • Movement: addAnimator(theobj, “M”, newtop_position, newleft_position)
  • Visibility: addAnimator(thobj, “H”) //Hide, use “S” for show
  • Display: addAnimator(theobj, “D”, display_type) // “block” or “none” for display
  • Z-Order: addAnimator(theobj, “Z”, z-order)
  • Replacement: addAnimator(theobj, “R”, replacement_string) // based on innerHTML
  • Parm_Replace: addAnimator(theobj, “P”, tag, classname, id, contents) // parameterized replacement
  • Slider: addAnimator(theobjs, “SL”, sliding_value, sliding_direction)

The Slider has a type of “SL”, but anything will do — it is the default animation. The first parameter is the type of slider (top to bottom, left to right, right to left, or bottom to top), and the direction specifies which way the slide will move. Note: The Slider has not been converted to working with Mozilla/Navigator 6.0 and may actually be deactivated from the Animators at some point.

Next up, we’ll use both the Animators and the X-Objects to create the PhotoShop demos.

Correcting a Photo

The first PhotoShop DHTML demo is based on using PhotoShop’s various tools and techniques to correct a bad photo. This demo is the simplest of the PhotoShop animations — using CSS absolute positioning and visibility, as you can see by accessing the Example page and picking the first demo.

To create the DHTML effects, the X-Objects and Animators are added to the page first:

<!-- cross-browser objects -->
<SCRIPT; src="cbobjects.js" language="javascript" 
type="text/javascript">>
</SCRIPT>

<!-- animation and sequence objects -->
<SCRIPT; src="animator.js" language="javascript" 
type="text/javascript">
</SCRIPT>

The content of the page is added, next. To ensure that the page’s DHTML effects work with Navigator 4.x as well as Mozilla/Navigator 6.0 and IE, all animated content is enclosed in CSS absolutely positioned DIV blocks:

<DIV; id="top" style="position:absolute; left: 20; top: 50; 
       width: 600;visibility:hidden">
Unfortunately, most photos do not turn out as we would like,
 or at least as much as I would like. Usually I'll re-take
 the photo, but sometimes this isn't feasible, or I like
 most aspects of the photo and hate to 
lose the shot. Enter Photoshop to the rescue. 
</DIV>

Once the content has been added, code is added to handle user actions. As the demo is opened into its own browser window, to close this window, the user can press the Space Bar. Additionally, hitting the ENTER key will cause the PhotoShop demo to proceed to the next “slide” in the DHMTL effect. As both actions are based on keyboard events, the keyboard events are captured and assigned to a event handler function:

   // capture events
   if (navigator.appName == "Microsoft Internet Explorer") {
	wdth = document.body.clientWidth;
	}
   else if (navigator.appName == "Netscape"){
	wdth = window.innerWidth;
   	document.captureEvents(Event.KEYPRESS);
      }
   else
      document.addEventListener("keyup",keypress,true);


   // assign function to event handler
  if (navigator.appName != "Mozilla")  
    document.onkeypress=keypress;

The event handler function pulls the keyCode from the event, and tests to see if the space bar or the ENTER key has been hit. If the space bar is hit, the page is closed. If the ENTER key is hit, the next slide is played if the demo is not at the last slide already :

function keypress(e) {
   if (!canbeginnow) return;

   if (navigator.appName == "Microsoft Internet Explorer") 
      tmp = window.event.keyCode;
   else if (navigator.appName == "Navigator")
	tmp = e.which;
   else if (navigator.appName == "Mozilla")
       tmp = e.keyCode;
   
   // if space bar hit
   if (tmp == 32) {
	window.close();
	return;
	}
   if (tmp == 13) {
      if (slide == MAX_SLIDES) 
	   return;
      next_slide();
      }
}

The code for the demon’s Animation Sequence is added next. There’s only one used for this PhotoShop demo — the animation played at the end of the demo. Its Sequence and Animators are created and played within one function:

// create animation sequence
function do_animation() { 

      // create new sequence
      seq = new animatorSequence();

      // add animator objects to sequence
      var anim = seq.newAnimator(20,100);

      // add animators
      anim.addAnimator(theobjs["image1"],"M", 200, 20, 0, 0);
      anim.addAnimator(theobjs["image2"],"M",-214,-300,0,0);
      anim.addAnimator(theobjs["image3"],"M",-214,500,0,0);
      anim.addAnimator(theobjs["image4"],"M",200,340,0,0);

      anim = seq.newAnimator(1,100);
      anim.addAnimator(theobjs["top"], "H", 0,0,0,0);
      anim.addAnimator(theobjs["top2"], "S", 0,0,0,0);
 
      anim = seq.newAnimator(20,100);
      anim.addAnimator(theobjs["image1"], "M", 150, 20, 0, 0);
      anim.addAnimator(theobjs["image4"], "M", 150,325,0,0);
     
      
      // play sequence
      seq.play();
}

There is only one Animation Sequence created, and contains 3 separate Animators. The first moves Animator is played over 100 ms, and consists of 20 steps. In this Animator, two of the demo page’s images are moved “off screen” (beyond page boundaries), and the remaining 2 are lined up next to each other. The second Animator consists of 1 step: hiding current text at top of demo page, and showing new text. The last Animator again is played out over 100 ms, and 20 steps, and this animation again moves the two images that are still showing on the page. After the Animation Sequence is created, and the three animators are added, the sequence is immediately played.

Finally, the code to handle each demo “slide” is added:

// play next presentation slide
// until last slide
function next_slide() {

   // return if animation is playing, or past last slide
   if (animatorPlaying) return;

   if (slide == 5) {
	alert("That's the end of the demonstration. 
               Re-load page to run again.");
	return;
      }

   theobjs["text" + slide].objHide();
   slide++;
   if (slide <= 4) {
    theobjs["image" + slide].objShow();
    theobjs["text" + slide].objShow();
    }
   else if (slide == 5) { 
    theobjs["text4"].objHide();
    do_animation();
    }	
}

Each “slide” consists of an image and text that are hidden together, or shown together. Notice that after the last slide is shown (slide 5) that the animation is then run.

Again, try out Demo1 from the the Example page. After clicking through all of the slides, you’ll trigger the animation. Note that it plays three separate animations, each after the other.

The next PhotoShop animation uses the tools layering ability to create a double image.

Creating a Double Image

The concept of “layers” existed in graphics, and particularly in PhotoShop long before they appeared in DHTML. An image can consist of more than one layer, with each layer having different content, color, what have you, but all of the layers combined forming the image.

A fun use of Adobe PhotoShop layers is to create a double image, and the second PhotoShop DHTML demo presents this, as you can see for yourself by accessing Demo 2 from the Example page.

 

Again, as with Demo1, the Animator and X-Object JS files are added to the page, and event handling is added. This doesn’t change from the previous example, so the code is ommitted.

In this new demo, there are several animation sequences, which are created individually as part of an array:

// setup animations
function animationSetup() { 

seq[0] = new animatorSequence(); 

var anim = seq[0].newAnimator(1,100);
anim.addAnimator(theobjs["top"],"H");
anim.addAnimator(theobjs["top1"],"S");
anim.addAnimator(theobjs["box1"],"M",160,wdth);
anim.addAnimator(theobjs["box1"],"S");

anim = seq[0].newAnimator(20,100);
anim.addAnimator(theobjs["box1"],"M",160,320);
anim.addAnimator(theobjs["image2"],"M",-240,320);
  
seq[1] = new animatorSequence();

var anim = seq[1].newAnimator(1,100);
anim.addAnimator(theobjs["top1"],"H");
anim.addAnimator(theobjs["top2"],"S");

anim = seq[1].newAnimator(5,110);
anim.addAnimator(theobjs["image1"],"M",160,145);

anim = seq[1].newAnimator(10,110);
anim.addAnimator(theobjs["image1"],"M",160,320);
anim.addAnimator(theobjs["image3"], "C",0,0,193,248);

anim = seq[1].newAnimator(1,100);
anim.addAnimator(theobjs["image1"], "H");
anim.addAnimator(theobjs["box1"], "H");

seq[2] = new animatorSequence();

anim = seq[2].newAnimator(1,100);
anim.addAnimator(theobjs["image2"],"M",160,-260);
anim.addAnimator(theobjs["image2"],"S");
anim.addAnimator(theobjs["top2"],"H");
anim.addAnimator(theobjs["top3"],"S");

anim = seq[2].newAnimator(20,100);
anim.addAnimator(theobjs["image2"], "M",160 ,60);

seq[3] = new animatorSequence();

var anim = seq[3].newAnimator(1,100);
anim.addAnimator(theobjs["top3"],"H");
anim.addAnimator(theobjs["top4"],"S");

anim = seq[3].newAnimator(5,100);
anim.addAnimator(theobjs["image3"],"M",160,310);

anim = seq[3].newAnimator(20,100);
anim.addAnimator(theobjs["image3"],"M",160,60);
anim.addAnimator(theobjs["image4"],"C",0,0,214,250);

anim = seq[3].newAnimator(1,100);
anim.addAnimator(theobjs["image3"], "H");
anim.addAnimator(theobjs["image1"], "H");

seq[4] = new animatorSequence();

var anim = seq[4].newAnimator(1,100);
anim.addAnimator(theobjs["top4"],"H");
anim.addAnimator(theobjs["top5"],"S");

anim = seq[4].newAnimator(10,100);
anim.addAnimator(theobjs["image4"],"M",110,180);
    
anim = seq[4].newAnimator(1, 100);
anim.addAnimator(theobjs["image2"], "H");
anim.addAnimator(theobjs["image3"], "H");
anim.addAnimator(theobjs["image4"],"H");
anim.addAnimator(theobjs["image5"],"S");
}

As you can see, there are 5 different animation sequences defined, for the 5 different slides of the DHTML demo. By using an array, only one sequence is played at a time.

This demo uses clipping. At this time, clipping as defined for IE 4.x and 5.x as well as Navigator 4.x, has a different frame of reference than that for Mozilla/Navigator 6.x. Because of this, in my current examples I test for the type of browser and use different clipping values based on the findings within the X-Objects (read more about clipping in X-Objects: Clipping).

Playing each slide of this demo actually plays each Animation Sequence as it is defined within the animation array:

// play next presentation slide
function next_slide() {
   if (animatorPlaying) return;
   if (slide > 4) {
	alert("That's the end of the demonstration. 
               Re-load page to run again.");
	return;
      }
   seq[slide].play();
   slide++;

}

Again, try out Demo2 from the the Example page.

Cropping a Photo

The third Adobe PhotoShop DHTML demo demonstrates how to use cropping within this tool. The DHTML used is primarily clipping with some movement, as well as hiding and showing content. I won’t repeat the code here, but you can see Demo3 yourself from the Example page. To see the demo code, choose View…Source from the browser menu.

Using the Rubberstamp tool to correct a background

The third Adobe PhotoShop DHTML demo demonstrates how to use cropping within this tool. The DHTML used is primarily clipping with some movement, as well as hiding and showing content. Again, I won’t repeat the code here, but you can see Demo4 yourself from the Example page. To see the demo code, choose View…Source from the browser menu.

Categories
Technology

X-Objects: Clipping

Copy found at Wayback Machine archive.

Clipping in the earlier releases of Mozilla and with Navigator 6.0 followed the CSS 2.0 release specification. Clipping boundaries are set at “0” for all four sides of the rectangle containing the content — to clip, you would offset the values, using positive numbers only.

This differed from the original implementation of IE 4.x and up and Navigator 4.x, and the original CSS-P specification submission. With these, the element’s right and bottom clipping value is set to the width and height of the element by default. To offset the top and left, increment the top, left values. To offset the right and bottom, decrement these same values.

Having a different clipping region framework was a challenge when it came to cross-browser development.

However, beginning with Mozilla build M16 (and Navigator 6.0 PR 2.0 and up), clipping for the new browsers is the same as it was for Navigator 4.x and for IE 4.x and up. Supposedly, the W3C will be issuing errata for CSS 2.0 to change the clipping framework to match this common usage.

On to the X-Objects

The X-Object does have several methods exposed on the interface to handle clipping:

  • objSetClipRect – set the clipping values
  • objGetClipRect – get the current clipping value, if any
  • objGetClipLeft – get the clipping rectangle’s left value
  • objGetClipRight – get the clipping rectangle’s right value
  • objGetClipTop – get the clipping rectangle’s top value
  • objGetClipBottom – get the clipping rectangle’s bottom value

Adding support for Mozilla/Navigator 6.0 didn’t change the implementation for clipping with the original cross-browser objects. The implementation for Navigator 4.x is still:

// clip object
function nsobjSetClipRect (top,left,bottom,right) {
	if (top == null) top = this.objGetClipTop();
	if (left == null) left = this.objGetClipLeft();
	if (bottom == null) bottom = this.objGetClipBottom();
	if (right == null) right = this.objGetClipRight();
	this.css2.clip.left = left;
	this.css2.clip.right = right;
	this.css2.clip.top = top;
	this.css2.clip.bottom = bottom;
}

function nsobjGetClipRect () {
   var strng;
   var left = this.css2.clip.left;
   var right = this.css2.clip.right;
   var bottom = this.css2.clip.bottom;
   var top = this.css2.clip.top;

   strng = "rect(" + top + "px, " + right + "px, " + 
                 bottom + "px, " + left + "px)";
   return strng;
}

// get current clip right 
function nsobjGetClipRight() {
	return this.css2.clip.right;
}

// get current clip left
function nsobjGetClipLeft() {
	return this.css2.clip.left;
}

// get current clip top
function nsobjGetClipTop() {
	return this.css2.clip.top;
}

// get current clip bottom
function nsobjGetClipBottom() {
	return this.css2.clip.bottom;
}

Navigator 4.x implemented the clipping values as separate values, which actually made the use of this CSS attribute a little easier than how it was implemented in IE. However, the IE implementation is one that can be shared by Mozilla/Navigator 6.0 — though it is more complicated:

// return clipping rectangle
function domGetClipRect() {
   return this.css2.style.clip;
}

// clip object
function domSetClipRect(top, left, bottom, right) {
   if (top == null) top = this.objGetClipTop();
   if (left == null) left = this.objGetClipLeft();
   if (bottom == null) bottom = this.objGetClipBottom();
   if (right == null) right = this.objGetClipRight();
   strng = "rect(" + top + "px, " + right + "px, " + 
                 bottom + "px, " + left + "px)";
  this.css2.style.clip = strng;
}

	
// clip object on left
function domGetClipLeft() {
	return get_entry(this,"left");
}

// clip object on right
function domGetClipRight() {
	return get_entry(this, "right");
}

// clip object at top
function domGetClipTop() {
	return get_entry(this,"top");
}

// clip object at bottom
function domGetClipBottom() {
	return get_entry(this,"bottom");
}

The main difference between the Navigator 4.x and IE implementation is that the IE treats the clipping property as one setting rather than four distinct settings. Based on this when accessing the existing the clipping property, it has to be parsed to get each of the clipping rectangle’s values.

Microsoft did provide read only properties to access each value of the clipping rectangle. However, as these aren’t usable with Mozilla/Navigator 6.0, I left the code within the object as is, and used parsing.

The parsing is handled through two helper functions, convert and get_entry, which are not exposed on the X-Object’s interface:

// convert string to value
function convert(strng) {
    var i = parseInt(strng);
    return i;
}

// get clipping value for specific dimension
function get_entry(obj,indx) {
	strng = obj.css2.style.clip;
        if (strng.length > 0) {
	   strng = strng.slice(5,strng.length-1);
	   var entries = strng.split(" ");
           }
        else {
            var entries = new Array(5);
            for (i = 0; i < entries.length; i++)
                entries[i] = "auto";
            }
	if (indx == "top") {
		if (entries[0] == "auto") 
                   return 0;
		else
		   return convert(entries[0]);
            }
	else if (indx == "left") {
		if (entries[3] == "auto") 
		   return 0;
		else
		   return convert(entries[3]);
		}
	else if (indx == "bottom"){
		if (entries[2] == "auto") {
		   return obj.objGetHeight();
                   }
		else
		   return convert(entries[2]);
              }
	else if (indx == "right") {
		if (entries[1] == "auto") 
		   return obj.objGetWidth();
		else
		   return convert(entries[1]);
		}
	
}

Let’s try the X-Object clipping functionality, with an example application created specifically for testing clipping.

Clipping Test Application

The test page has two different blocks in the page, each created in separate positioned DIV blocks. The topmost block contains an HTML form and form buttons, used to clip the second block’s left, top, right, and bottom rectangle’s values:

<BODY onload="create_objects(); setup()">
<DIV style="position:absolute; left:10; 
top:10; background-color: yellow; 
layer-background-color: yellow; padding-top: 20px; 
overflow: hidden;
width: 500; height:150; clip: auto">
<form action="">
<center>
<INPUT type="button" value="Clip to the Left" 
   onclick="clip_left()"> 
<INPUT type="button" value="Clip to the right" 
   onclick="clip_right()"><p>
<INPUT type="button" value="Clip on the top" 
   onclick="clip_top()"> 
<INPUT type="button" value="Clip the bottom" 
   onclick="clip_bottom()"><p>
<input type="button" value="Show the clip property" 
   onclick="show_clip()">
</center>
</FORM>
</DIV>

<DIV id="info" style="position:absolute; 
left: 250px; top: 180px; background-color: red; 
  width: 300px; height: 300px; overflow: hidden; 
layer-background-color: red">
<H1>Block with info</H1>
<p>
This is a block that contains two HTML elements: 
a header and a paragraph
</p>
</DIV>
</BODY>

A function is called when the page is loaded, setup, which sets the clipping region of the target DIV block, using values that differ based on browser type:

function setup() {
  
   theobjs["info"].objSetClipRect(0,0,300,300);
   }
}

These values set the clipping region to fit the existing DIV block parameters (300px wide, 300px tall).

The functions to create the clipping effect are:

// clip element
function clip_left() {
   var lft = theobjs["info"].objGetClipLeft();
   lft+= 10;
   theobjs["info"].objSetClipRect(null,lft,null,null);
}

function clip_right() {
   var rt = theobjs["info"].objGetClipRight();
   rt-= 10;
   theobjs["info"].objSetClipRect(null,null,null,rt);
}

function clip_top() {
   var top = theobjs["info"].objGetClipTop();
   top+= 10;
   theobjs["info"].objSetClipRect(top,null,null,null);
}

function clip_bottom() {
   var bt = theobjs["info"].objGetClipBottom();
   bt-=  10;
   theobjs["info"].objSetClipRect(null,null,bt,null);
}

function show_clip() {
   alert(theobjs["info"].objGetClipRect());
}

Try out the Clipping Example for yourself, using IE 4.x and up, Navigator 4.x, or Mozilla/Navigator 6.0.

For more complicated demonstrations of clipping, check out the associated article Demonstrating PhotoShop functionality using DHTML and X-Objects, which has four DHTML demonstrations using various effects, including clipping.