SCRIPTING:

Tatiana Zavialova
CREATE AND EMULATE A BOOK WITH LAYERS PERFORMING AS PAGES AND BROWSE THE LAYERS VIA DHTML AS A BOOK WOULD. LEARN ABOUT THE CSS CLIP PROPERTY.
Create a book via dynamic HTML: add to it layers as its pages, remove, insert, swap pages, add more layers at a second time.
It will perform as a collection of layers that you can position wherever you prefer and which can be browsed upon user request.

Browse the book upon events such as button clicks triggering the page flips. Arrange amount and speed, define if the book must open as a standard book or as a notebook. Close it, jump to a specified page.

A class that has also some interest as a collection of clipping methods and of array entries management.
June 2005
{ @ }

The model above is Tatiana Zavialova
LOADS OF TATIANA ZAVIALOVA ON THE NET


WHY THIS SCRIPT AND WHAT IT DOES
What the scripts performs and a few caveats

a Robert Indiana painting
A Robert Indiana painting
I was asked many years ago to produce a script that could emulate the browsing of a book for a site whose owners wanted its whole contents to be browsed like pages of a book.
I confess that I dismissed the request with some sense of annoyance: the misconceptions that led many persons to believe that since a PC screen could be a cathodic tube and therefore it had or was expected to behave like a TV set and perform animations as if it were gathering broadcasting waves (whence the Flash paradigm), were seemingly finding a match in the idea of more literate persons that since on a PC screen you also read text, then such text had to be browsed like those paper books we are used to read text in.

Milla Jovovich
The model above is Milla Jovovich
This failure to understand the specific nature of the medium (without having now to call in Mc Luhan) was as absurd to me as expecting a book showing animations or a TV image being browsed.
Moreover, I have never been inclined to write frill codes for effects whose purpose is not instrumental to a serious purpose. Perhaps this is why on Unitedscripters you can find a way to use javascript, power javascript, that you won't find elsewhere.

Then, many years later, I came across this very nice example, which though it provides no codes (or at least I have not been able to spot them), gave to me the right stimulation to develop this application. That script emulates the browsing of a book composed of images by resizing their width and height.
It then came to my mind that I had to correct my old perception, arguably slightly polluted by a wrong "emotional" side: in the DHTML era, browsing not just images but whole layers containing whatever type of HTML might be more than just a caprice: in fact, if the information on a page is ample, it might be not so inconvenient an idea to arrange it as a book that the surfer can browse. For instance, in the example I provide, browsing a few Shakespeare's sonnets seems to be a web experience significantly enhanced if those sonnets are given each its own layer and presented each upon request, adding to it the browsing effect.

As it is my custom, I provided the class I developed for this purpose with as many options and features as possible. Yet it became so long, despite the considerable amount of planning that I devoted to it (I just do not fling myself into coding without first writing down what I am going to tackle and envisioning a few solutions) that I eventually had to drop a couple of options.
So there are two things that you cannot do, and I do like to account for shortcomings, I just consider it the decent thing to do and a good fingerprint to be seen:
  • a Richard Estes painting
    A Richard Estes painting (that's not a photo)
    You cannot change your mind while browsing a page, as you could do with a book, and go backward while the browsing effect is still on: you have to wait for a page being fully flipped before going back or forward.
    It is possible to argue that it could have been easy to include this feature so to provide the application with an ultra realistic book browsing effect (say like those paintings that you can't tell from a photo of the same thing), but my experience as a scripter proves to me that when at coding it just does not exist over there such a thing whose qualification is: simple. A simple thing: long time since last time.
    Like Raymond Chandler wrote in a short story of his own:
    «Safe. A word that in my business we never use.»
    Simple: a word that when we code seriously we never use.
    Of course, feel free to implement it, but once done come back to me and say to me how "simple" that was and how that felt. The way this class is structured, anyway, won't easily allow for that, and this is its only real limit - which I acknowledge as you can see.
  • The class includes methods meant to manipulate the available pages: these are methods whose names seem to me rather self explicative: addLayers, removeLayer, moveLayer, swapLayers, insertLayer.
    Now, if the book is undergoing a browsing process (that is: a page is currently being flipped), you can use none of the methods above mentioned (even if called in, they would return boolean false): it would have been fully and entirely within my skills and capabilities to arrange things so that a book could have undergone such methods also on the fly and to suit it so that the new situation would have been immediately reproduced. Yet this would have meant including possibly 200 more lines of codes, or maybe more; so I decided not to add this feature and to inhibit any further action once a flipping of a page has been triggered and is not finished yet.
  • When a page is being flipped, a theoretical option came up to my mind: start showing the next page in the line while the previous one was still being flipped, rather than awaiting for the former to be fully flipped before starting showing the incoming next one.
    I bailed out of that: it would have "simply" required to add a few extra specific methods, which the code of this class definitely would allow to include, but since you're going to complain with me about my codes for they are long, I guess somebody this round might complain that it's not extra long enough.
  • The same reasoning as above applies to the possibility of triggering a consecutive browsing of more pages: the class code is structured so that per each page you want to browse (though you can suddenly jump to a specified page skipping all the browsing process that could have been in within), you have to issue the specific command (say click a button): one page, one command.
    Arranging for additional methods that would perform a specified consecutive browsing of x pages after one single command, is definitely within the possibilities of the class as it is, but it would have required adding even more code too.
  • All the rest, you can do. I have taken care of that.
a Keith Haring painting
A Keith Haring painting

Once described what the class cannot do, and since all the rest it can do, I specify now what are the caveats you have to be aware of and that are meant as guidelines you have to keep in mind if you want this class to work properly:
  • The initialization of the class is very easy, as you will see shortly.
    Yet since the class uses some dhtml properties such as offsetWidth, offsetHeight that the browser will calculate only when the page html body tags pair has got fully loaded and thereby all of its offsets will be available for those calculations, you will initialize your class soon before closing the </BODY> tag, and not earlier.

    You are not going to forget this point, ok?
  • Your book pages are layers. You must nest all these layers within an encapsulating layer.
    You can place or move this latter wherever you want, but your layers performing as pages of a book must be encapsulated within this container.
    An encapsulating layer, to be such, must have a css style position declared, either as absolute or as relative: say sort of, minimal syntax example:

    <span style="position:relative">

    <div id="pageX" style="background-color:#ffffff;">an encapsulated page</div>

    <div id="pageY" style="background-color:#ff00ff;">another encapsulated page</div>

    </span>


    Such a span html tag encapsulating other layers would perform like sort of a pin that nails all the pages by their uppermost left corner: the class will position all the pages within such encapsulating layer with top and left coordinates equal to zero.
    This is cool actually, because you can move around your whole book, if you want, by manipulating just this one single container.
  • Your encapsulated layers performing as pages, should have a css background-color property set, or the background would be assumed as being transparent, and from one page above you could see through throughout all the pages below it! That would confuse you, I'm telling you.
  • All your layers acting as pages must have unique IDs assigned:
    <div id="anID_here">layer contents blah blah</div>
  • Since the book starts closed, you may probably want to allow either on the left of the encapsulating layer or on its top (depending on whether your book is going to be browsed like a standard book or a notepad, respectively), for some leeway or space so that when the book opens it won't hide possible critical data to the eyes of the user.
    Also, remember that layers will never be able to cover form menus by overlapping them (an html limitation actually, not a class issue).
  • a Keith Haring painting
    A Keith Haring painting
    A browsers issue.
    Criss crossed css overflow property issue; in Internet Explorer if the css overflow property is set to "hidden", namely it is instructed that the contained html must be invisible if it exceeds the assigned layer width and height, it will happen the following: Internet Explorer will start clipping the layer as if its width and height wouldn't be the visible and declared ones, but the whole width and height that would have been necessary to make visible in order to accommodate the... whole contents of the layer. Therefore a clipping process might appear as not starting for a while, namely for all the time Internet Explorer needs to clip the... already hidden and not showing layer portion.

    Conversely, if the overflow property is set to "visible" (that is, the layer will outstretch to accommodate all the contents even if they exceed its declared width and height) and you have declared such layer width and height via css, Netscape (even its version 8) would not show the contents as belonging inside the layer if they outrange it, but you will see the "exceeding" parts as if they were seemingly pouring out. Beware, some even dare call it the "right" way to do things (because it is Netscape, and as such imagined as the fantastic opposer of the evil capitalism incarnation that many undoubtedly see in Microsoft). I call it just the way it is and by its proper name: a bug like any other, and with this name I have no problem at dealing with it.
    Not setting the width and height of the layers via css may not suffice to solve this Netscape oddity.

    Morale: be sure that the contents of your layer do not exceed too much the layer visible area, namely that its width and height are declared and are ample enough to accommodate all the contents of each given layer.
    By the way, it is not paramount that all the layers belonging to your book share the same width and height: if they won't, the only thing that will happen is that your book would, probably, look ugly or funny, but that will not hinder this application logics and functionalities.

    Also beware: Netscape even in its version 8 may show ghost layers flickering around if the overflow properties are set to auto or to scroll (its rendering engine is apparently too slow, and so Netscape reveals traces of what it is probably doing in the background: it seems that those overflow values burden its engine). Internet Explorer won't cause problems with that.

    It is not true, by the way, that only layers whose css overflow property is set to hidden can be clipped (this class uses clipping css procedures): even those with scrollbars (namely with the css property overflow set either to "auto" or to "scroll") can be effectively clipped.
    Conversely, it seems to me that layers whose css position has not been set either to relative or possibily to absolute, those yeah they can't be clipped. I at times wonder whether it's me who lives in Wonderland or whether it's the offical high profiled documentations that occasionally enjoy a ride in Wonderland.

    Just for the record, previous scripts that dealt with clipping are here and here. But the clipping (private) methods that I feature in this current script may look somewhat more interesting if you're a scripter and you want to devote some time to check the codes.
a Robert Williams painting
A Robert Williams painting

As for the rest, this class code has several nice features. Yet for being complete, I want to account for two circumstances, the only ones, when I had to twist and torture the codes a bit.
The class stores the layers acting as pages in an array. Makes sound sense.

When browsing a book instance created with this class, I opted out any approach that would have implied a rearrangement in the order of the entries of this array that stores the layers acting as pages; that is, I decided not to make this array perform like a stack or a queue that would get rearranged so to reflect the forward or backward ongoing browsing process: in fact, that would have not solved the basic problem namely that when browsing a book page (virtual books too!) you still have to account for as many as three pages and not just the one that is going to be flipped; in fact you have to account for:
  1. The page that must be flipped off.
  2. The next page in the line which has to be flipped on (in).
  3. The page that must show up beneath the one just flipped off.
Thus, I decided that an index that kept track of the positions of the involved entries in the class array that stores the pages, was enough. No need to meddle with the actual array.
So, rather than pushing popping shifting or unshifting the array entries, I stored at each forward or backward motion a new tiny array (of course of three entries) in a class property named:
this.currentSlice
where the indexes of the three mentioned pages (layers) were temporarily stored.
This seems more efficient than popping or shifting and then still (this "still" is the whole point) having to keep track of those three entries.

a Robert Indiana painting
A Robert Indiana painting
Yet I eventually discovered the following that I didn't envision at first, upon testing a couple of bad exceptions: when reached the end of a forward browsing and then going backward, if after the first backward you instructed to go forward once again (which may well happen after all), the currentSlice reported indexes that should have no longer been dealt with as any previous default "all forward" process; in other terms, I discovered exceptions when dealing with actions performed on the boundaries of the book.

I tormented the code a bit to accommodate that, because when at programming, exceptions discovered at a second time have always the gait of afterthoughts.
This process can be sensed in the class methods named:
this.forward
this.backward

and in the creation of the following few methods specific for those exceptions:
this.forwardLR3
this.forwardTB3
this.backwardLR3
this.backwardTB3


The name of these latest methods are less cryptic than you may think: TB stands for processes that involve the vertical axis (Top Bottom), and LR for process that involve the horizontal axis (Left Right): the number 3 derives from the fact I firstly realized about this problem when testing a book with only 3 pages. With a book like that, the reached boundary exceptions, as I called them and as I mentioned them, recurred nearly continuously.
So the methods were originally thought to fix this exception, and only at a second moment I realized this exception occurred also when playing around backward and forward at the end of a book.
I do not like fixes at all: but after all it's also true that you can tell me when was the last time that you did not see even several millions paid big IT corporation engineers being forced to release their own fixes.

We then have the zIndex issue.
The css property z-index determines the visibility of a layer when it is on a stack of layers: which is precisely our case, the case of the pages piled up in a book.

When you are to browse pages as piled layers, you necessarily have to reassign higher z indexes to the three involved layers, or the animation may occur but not be visible at all because the layers may reside beneath the stack namely below the bulk of the preceding ones.
So zIndexes, being an entirely css based issue (and not an application or programming problem) had to be dealt with necessarily; and then, additionally, I also had to reset them once finished a page flipping, unless you want to go on escalating upon the zIndexes forever (an option, actually).

a Keith Haring painting
A Keith Haring painting
Thus there are some tasks where the only way to prove that a layer was indeed affected, was to move its stack on top, namely to reassign its zIndex as the highest in the lot.
Moreover, given that and therefore before that, it was necessary, prior to starting any action, to reassign all the original zIndexes to all the layers as a safety measure (lest leaving around some missing layer with an exceedingly high zIndex and which might jeopardize at some time the correct vision of a process that none the less was going on properly).
I think that computers, though notoriously stupid, are really good indeed at one thing at least: looping. Looping, namely performing quickly repetitive actions in kind of a dull or obtuse manner, is any computer's daily bread.
Thus, to solve this zIndex minor problem, I did not consider an issue that outside timeouts (that is, outside animations) the class could loop all the layers acting as pages, so to reassign to them their default zIndexes.

If your books include a few dozens or also hundreds of pages, that is going to be just fine.
Of course, if you plan to provide as a browse-able DHTML book the whole Encyclopedia Britannica, I might advice against using a dhtml class for that, and suggest to get the actual volumes.

Keeping track of the latest layers whose zIndexes got increased, so to reset the defaults to these latest ones only, would have been possible, undoubtedly.
But, then, it would also have needed including more coding and storing more data (about the previously browsed pages), still opening some margin to unpredictable exceptions; and this all, just in order to accommodate not a real programming shortcoming but just a way css works: that is, the application would have done its duty also if a layer would have not appeared as being manipulated, and yet it would, just because it got a z-index that conceals it under other unaffected layers.
So, zIndexes necessarily had to been dealt with, and yet as a purely and entirely css issue, not as an apllication flaw or fault; and, indeed, I would have liked a default browser engines approach such that if a layer is involved in an action and no adverse instruction is given, such layer gets automatically assigned the highest z-index: among the several weird things browsers do, this would have not been the weirdest of them all.
a David LaChapelle photo
A David LaChapelle photo


THE CODES AND THE CLASS INITIALIZATION
Codes for copy and paste and class initialization instructions

Here are your codes, and then the details about how to start a book of your own using this class codex (beware, Netscape or Mozilla may compact them beyond recognition. I suggest to you to use Internet Explorer to view these codes).

a Robert Williams painting
A Robert Williams painting

lines (with comments and blank lines):
a Jean-Michel Basquiat painting
A Jean-Michel Basquiat painting

The initialization of the class is as follows, and it requires you add as first argument, and as the only truly necessary one, the very same name of the variable you are initializing as a String (namely in between quotes); such variable name placeholder, in our case, will be "foo"; do not forget to put before it the keyword var, namely:

var foo = new book("foo");

Do not capitalize book, unless you change the class name.
Theoretically, the class name passed as a String parameter could also be an Object, but I have not tested this.

After the first argument, other arguments are optional; therefore the arguments overview is:
  • bookName: String: the name of the initialized variable, as a string (namely in between quotes).
  • direction: if zero, opens the book horizontally; if passed as 1 opens it vertically (say "notepad" mode).
  • amount: a number, defaults to 5 (pixels). It is the amount of the page (layer) that will be clipped at each iteration upon browsing.
  • speed: a number, defaults to 10 (milliseconds): the speed of the clipping of the amount at each iteration upon browsing.
  • overflow: a String, defaults to «visible»; accepts only the following possible values (and still defaults to visible if other): visible, hidden, auto, scroll.
    Applies such overflow css properties to each added layer.
After initialized, you need to add the layers that compose your book. You can add layers at any time, though the most obvious thing is to add all of them soon. The class methods that you can use are detailed later on, but you may need knowing this one soon:
foo.addLayers
which takes in as arguments the IDs of the layers as String, listed either as a list or passed as an array or as a mix of list and arrays. So:

var foo = new book("foo");

foo.addLayers("layerID1", "layerID2", "layerID3", "layerID4", "layerID5", "layerID6" /*etc...*/);


Of course, all the added layers have to exist in the html code and have their given ids and, as said earlier, should be encapsulated in a container layer which must have a declared position either as absolute or as relative.

Still before detailing the class methods, it may be added that to flip pages forward and back you just issue simply looking commands like:

foo.forward();

foo.backward();


THE TEST FORM
Before further details, play around

a Robert Williams painting
A Robert Williams painting
Though it would have been better to introduce first the description of the class available methods, here is first the test form (with a dhtml book including a small set of Shakespeare's sonnets) so that you can play around and have some fun too. All the thereby featured functionalities have their own method implementation in the class codex.


Below you will find the most typical commands, then you'll see the book. It is allocated a bit eccentrically so to allow for browsing space in case you run it in the notebook mode.
After this set of commands you will find a few more. These right below this cell are the basic or most common ones:

amount =
speed =





[close reversed? »]
CLICK FORWARD to start:
a Roy Lichtenstein painting
A Roy Lichtenstein painting






a Tom Wesselmann painting
A Tom Wesselmann painting











the few
DHTML PORTABLE
SHAKESPEARE SONNETS
[Note: this layer performs as page 0]
USS
A UnitedScripters editions d-book (dhtml book)
[note: this layer performs as page 1]
page 2
page 3
page 4
page 5
page 6
page 7
page 8
page 9
page 10
printed on 2005
United Scripters
http://www.unitedscripters.com/
Available layers: Layers Removed:
[moveAfter? ]
or
a David LaChapelle photo
A David LaChapelle photo


METHODS OVERVIEW
The class methods you can use

a Robert Williams painting
A Robert Williams painting
There are many methods in the class but only a few of them can be used, whereas most of the others either are private or it would be advisable to keep as private to avoid possible exceptions.
Here I document briefly the public methods only:
  1. addLayers: use it to add pages to your book. Arguments can be a list or an array or a mix of both (a cool private method named argumentsUnfolder takes care of that), and each entry should be the ID of an existing layer already parsed by the browser prior to the call to this method.

    It is advisable that your layers have a css visibility property openly declared in the html tag itself (and not just in any external style sheet that is) because this method will also store in a class variable the original css style settings of the layer and if no visibility is found set in the tag, it will record as its original visibility the value: hidden; thus if you remove the layer via the method removeLayer, its original values will be reapplied to it upon removal, but the layer might not appear to your eyes any longer and apparently it could be nowhere to be found for its css visibility property could have been set to invisible.
  2. getId: pass to it either the String id of a layer or the numerical index of the position of a layer in the class this.layers variable: the method will return false if this layer does not belong to the book pages, or the layer ID (String) anyway if it does.

    It is complemented by a method named getNum which given a layer ID returns its numerical index position in the class array, or a typeof boolean if no layer with such id belongs to the book.
  3. insertLayer: this first adds the layer via a call to addLayers and then moves it to the specified location in the class array: in fact it accepts two parameters, the id of the layer to add, and then a number that represents the position where such layer has to be inserted.

    This method does not verify whether the argument layer is a duplicate of itself, because theoretically it should be allowed inserting duplicates as long as one is cautious to keep them apart from each other of several pages: I try to impede only what I cannot but impede.
    Do not move along the class array a layer already added to the book, to move use rather the method moveLayer(): this because insertLayer() also adds the layer to the book rather than just moving its position in the array ladder.
    To swap layers use swapLayers.
    The second argument namely the numerical one defaults to -1 which means insert on the top. To insert elsewhere provide a positive number.
    It inserts as such the passed index: so, for instance, to insert at index 1 pass 1, to insert at 5 pass 5
  4. swapLayers: swaps the positions of two layers, which must be the methods first and second arguments.
  5. moveLayer: gets two mandatory arguments, both must be layer Ids (String): it moves the first layer to the position of the second layer, and switches the latter upward of one slot. If you want to move the latter downward of one slot, pass to the method a third argument as number 1.
    So for instance if in an array like [0,1,2,3,4,5] you decide to move say 1 to 4, the returned order will be: [0,2,3,1,4,5]. If you want rather [0,2,3,4,1,5] pass to the method its third argument.
  6. removeLayer: pass to it a layer ID, if present in the book it removes it from the book, and restores its original css properties, with the caveat mentioned in the addLayer paragraph about its css visibility.
  7. swapProperties: accepts two parameters, which both must be layers Ids: it gets the top, left, zIndex, clip properties of the first layer, and applies it to the second layer; and viceversa.
    It returns an array of two entries, the first the css clip property of the first passed layer as it was before the swapping (String), the second the css clip property of the second passed layer as it was before the swapping (String).
  8. restoreClip: gets as its parameter a layer id, and restores such layer css clip property to the normal one. Yet this method can work only with layers that belong to the book (thence I originally thought it would have been better to flag it as a private method).
  9. getClip: pass to this method a layer ID. It will grab its clip css property and will return not the String of its 4 values (which might look like: "rect(1px,20px,30px,0px)") as normally a clip property gets reported, but it will rather return an array of Integer Numbers as extracted from that clip string: [Top, Right, Bottom, Left] - in the example: [1,20,30,0].
    If a clip property has not been specified (or the layer has undergone no clipping process), the array will contain 4 NaN (Not A Number): [NaN, NaN, NaN, NaN].

    Perhaps it may be benefical reminding (as written elsewhere) that a css clip property is made up of 4 values (top, right, bottom, left) and that they follow these logics:
    • TOP: adding value X to top would make the layer invisibile from top to value X (from top 0 until X: in-visible)
    • RIGHT: adding value X to right would increase the visibility of the layer from left to right (from left 0 until X: visible).
    • BOTTOM: adding value X to bottom would increase the visibility of the layer from top to bottom (from top 0 until X: visible).
    • LEFT: adding value X to left would make the layer invisibile from left to value X (from left 0 until X: in-visible)

    If you want to test the clip properties for educational purposes or to get acquainted with them, here is a small section for it:

     
    A Layer To Clip
    Current clip values:
    top: right: bottom: left:
  10. close: closes the book. If passed as its parameter number 1, it closes the book in its reversed position.
  11. open: pass to this method as its parameter a page number, and it will flip open the book to such page, showing one single browsing effect.
  12. forward: needs no argument. A call to it moves forward the book by a page. If such a process is still on the run, no further calls to the method will be accepted till the flipping of the page is fully over.
    The method will detect if a flipping action is still running by checking a class property named: isRunning, which is case sensitive and is set to 1 by a running process and reset to zero when a process ends.
  13. backward: needs no argument. A call to it moves back the book by a page. If such a process is still on the run, no further calls to the method will be accepted till the flipping of the page is fully over.
    The method will detect if a flipping action is still running by checking a class property named: isRunning, which is case sensitive and is set to 1 by a running process and reset to zero when a process ends.
a Roy Lichtenstein painting
A Roy Lichtenstein painting