Showing posts with label code. Show all posts
Showing posts with label code. Show all posts

Expanding a DIV to Size of 'background-image'

I struggled with this for a bit yesterday and found naught but forums telling me 'it can't be done,' or 'why are you doing that, it's stupid,' so I thought I'd share the solution I developed.

The Problem


Here's the scenario: You have a DIV that will contain some text. You have different background-images that you want displayed under the text depending on which page is showing. But the background images are of varying sizes.

What you want, therefore, is a way to expand your DIV to the dimensions of whatever background-image you happen to stick inside it, without having to explicitly declare width and height values, either at your style sheet or in-line.

...and now you're off the rails.

One could surely pose a number of "why not" scenarios, such as: 'Why not just declare the width and height in-line?' or 'Why not just make the text part of the images?' Or one could pontificate upon the myriad of client and server-side solutions for querying the image size and declaring the DIV dimensions equal by script. But instead, let's just assume that we are coders and geeks, and that if there is a simpler solution requiring less explicit declaration and more uniform code distribution, then that elegance of design is our tacit desire and reward.

Solving


First off we must accept and acknowledge the limitations before us. The 'background-image' declaration does not care for width and height. It will fill a DIV with the image you choose, but it cannot under any circumstances be used to alter the boundaries of the DIV. Give that up right now!

Besides which, a 'background-image' declaration here would have to be placed in-line to meet our need of changing it across different pages, and I think it a best-practice to avoid in-line styles wherever possible, favoring HTML tags for dynamic content.

The solution, therefore, must be implemented from within the DIV. Anything placed inside a DIV of undeclared width and height will expand that DIV to fit. So in place of a 'background-image' declaration, we will set an <img> tag inside the DIV. The IMG's dimensions will be determined upon page load and the DIV will expand to match.

<div id="my_div">
<img src="images/bg1.gif">
</div>


Now comes the text we want displayed atop our IMG. Just shoehorn it in there with a <p> tag. We'll make it work in a minute.

<div id="my_div">
<p>Contact Us!</p>
<img src="images/bg1.gif">
</div>


So now we have a DIV containing two blocks: P and IMG. What we want is for the P tag to overlay the IMG tag, creating the effect of IMG as a background. We accomplish this by use of negative margins and 'z-index.'

First off, the P tag has its own vertical margins which we'll want to zero so we can have a simple text-block with no extraneous white-space to work with. As for making our IMG into a background, we can force the P tag to overlap IMG's boundaries by giving IMG a negative 'margin-top.'

#my_div p { margin: 0 }
#my_div img { margin-top: -80px }


If you looked now, you would find that the text of the P tag has more or less disappeared behind your IMG. Here the IMG tag has a higher natural z-index than the text, meaning it is being drawn after, or overtop of P. We can change that by giving the IMG a negative z-index, which places it dimensionally beneath P. Note here that when you set a 'z-index' you must also make the 'position' of the element explicit, or it will not take effect.

#my_div p { margin: 0 }
#my_div img {
margin-top: -80px;
position: relative;
z-index: -1;
}


Finally your P tag should be sitting proudly atop your IMG which has effectively become a background-image. Huzzah! Full code follows...

The Solution



<head>
<style type="text/css">
#my_div p { margin: 0 }
#my_div img {
margin-top: -80px;
position: relative;
z-index: -1;
}
</style>
</head>

<body>
<div id="my_div">
<p>Contact Us!</p>
<img src="images/bg1.gif">
</div>
</body>


Beautiful isn't it? Just four lines of CSS and you can now switch out one background for another of an entirely different width - and even a different height if the design is right - without so much as a whisper regarding those properties. All you declare explicitly is how far down to drop your text over the image. "Can't be done," my eye!

There are some pitfalls to watch out for when implementing this method. For instance, it might seem just as easy to make the 'margin-bottom' of P a negative instead of the 'margin-top' of IMG, but if you do it this way Internet Explorer, in particular, just might chop off the vertical edges of your IMG if P's block fails to fill the DIV out to the full height of IMG. This can be countered by augmenting line-height, but then you're fighting battles you don't have to. Yeah, I may have learned that one the hard way.

There you go: Dynamically expanding a DIV to the size of a background image. Tada!

Collapsing Margins / Borders in CSS

Here's a simple example of the kind of unexpected behavior caused by collapsing margins in CSS.

<div style="background-color: red">
<h2 style="background-color: cyan">
Hello flawed world!
</h2>
</div>


The Problem


When displayed, (tested in Firefox 3.5.7) the code above produces the text "Hello flawed world!" on a cyan background, and nothing else. This is not as expected.

The <h#> tags have a built-in top and bottom margin to them of around 20px. Now, a margin, specifically, is white-space surrounding a tag, outside of it. Margins push out from a block's position to create empty space around it. With this in mind, the code above should produce 20px worth of red - the background color of the <div> containing the <h2> - above and below our cyan-backed text. But it doesn't.

Meet the concept of collapsing margins. Because nothing whatsoever has been placed in the space between the div's borders and the h2's borders, the browser is taking it upon itself to remove what it perceives as empty-space by collapsing the margins together.

I don't know why it might be beneficial for the browser to collapse margin-space this way, nevertheless here we are. We must work around it.

The Solution


In order to get the desired margins to appear we must prevent the browser from perceiving them as empty. The simplest, most unobtrusive thing to shove in the gap is a bit of padding. Padding is sort of the inverse of margins, in that it creates white space within the block, rather than outside it; pushing inward from the borders. 1px worth of padding will keep your margins and borders from falling in on themselves.

<div style="background-color: red; padding: 1px 0;">
<h2 style="background-color: cyan">
Hello flawed world!
</h2>
</div>


The corrected code inserts 1px worth of top and bottom padding to the div. Again, note that the padding is added to the outer container. It pushes inward, giving the previously empty space between the edge of the h2 block and the edge of the div block a tiny bit of content. In the example I've used the 2-value format of the 'padding' style, where the first value represents top and bottom padding, and the second left and right.

I've always looked at margins and padding as being the same thing in a different direction. Apparently this isn't entirely true. For some reason the CSS standard views white-space created by margins as sacrificial when unoccupied, but maintains a hallowed view of white-space declared as padding. Preserving padding makes sense. Not preserving margins... I'm sure there's a reason, but from the coders perspective it seems an inconsistency.

Slice() is a Lie!

Today I tripped over a long forgotten Actionscript woe: De-Referencing arrays. What'll happen is, you'll slice() yourself off a copy of some multidimensional array, then later you'll change one of the values of your duplicate only to find it isn't really a duplicate, and the original it was slice()'d from has changed as well.

The problem is that slice() and concat() apparently only make true duplicates of the top level of an Array. So when you slice() off a 2D array, you end up with a genuine copy of that array - sure, sure - but what you've copied is really just a bunch of shallow references to the original.

Here's some code to try and make sense of it:

var sacred:Array = [ [0,1,2], [3,4,5] ];
var heretical:Array = sacred.slice();

heretical[0][0] = 99;

trace(heretical); //99,1,2,3,4,5
trace(sacred); //99,1,2,3,4,5

Mind you that if you alter the reference itself, say by changing the value of heretical[0] rather than going straight down to [0][0], you will have replaced the path to 'sacred' and heretical[0] will be thereafter wholly independent from sacred[0], such that any changes to heretical[0][0] will nolonger be reflected in 'sacred.'

It's a really old problem in Actionscript made new again by the Vector class brought in with Flash 10 and AS3. The Vector class is, after all, little more than a type-specified Array(), and so it is subject to the same slice() and concat() pitfalls. But in practice it's easy not to think of your Vectors as Arrays and that's how I managed to butt heads with slice() again today.

Here's an example of the slice issue using the Vector class:

var sacred:Vector.<Vector.<int>> = new Vector.<Vector.<int>>;
var heretical:Vector.<Vector.<int>> = new Vector.<Vector.<int>>;
var vInt:Vector.<int> = new Vector.<int>;

vInt.push(0,1,2, 3,4,5);
sacred.push( vInt.slice(0,3) );
sacred.push( vInt.slice(3,6) );

heretical = sacred.slice();

heretical[0][0] = 99;

trace(heretical); //99,1,2,3,4,5
trace(sacred); //99,1,2,3,4,5

Once you get deep into your own code - when your eyes have glossed over from too much trigonometry and micromanaging nested loops - it may not be immediately obvious to you that your Vector.<Vector.<int>> is, at end, an Array containing an Array containing some integers, or Array[Array[int]]. And that any duplication of that top level array really only passes along the inner array, which is still just a collection of references.

I don't know why Adobe hasn't provided a deep copy method for Arrays by now. I understand that the issue presented here isn't really a bug; that slice() is doing just what it says in the livedocs: Returning "a new array that consists of a range of elements from the original array, without modifying the original array." And that the shallow-references we end up with are the "elements" of the original array, as stated. Still, how many people have to bump their heads on the ceiling before Adobe integrates a method of really, truly, no shit, deep-copying an array? I suggest Array.noShitCopy();

Truly Write Protecting a USB Drive

No registry hacks required.

I came up with this solution after my thumbdrive fell victim to a 'Virut' infected machine at work. 'Virut' attached itself to two executables on the drive, so that running either of the programs infected whatever system my USB drive was attached to.

After cleaning my drive with AVG Free Anti-Virus I searched far and wide for a way to be make my USB drive read-only. I looked into mounting the drive as a CD under the ISO9660 standard; I thought of encrypting the drive's contents; I played with multiple partitions...

I'm saddened to report that there is no way to truly write-protect a USB drive at the software level, and so the method I present here is admittedly imperfect. Nonetheless it provides some real advantages. Further, it is the only portable solution you will find short of buying a USB drive with a write-protect switch.

The Solution


The trick is to fill the disk space entirely. When the USB drive is full all write operations will be denied. Viruses will neither be able to infect existing files, nor create their own.

I found this concept here at Jared Heinrichs' blog. All credit is due to Jared Heinrichs for exposing this all-too simple method, used elsewhere in pay-software.

Using the command-line tool 'fsutil' included in windows we can create a dummy file that fills every last bit of free space remaining on our drive, thus securing it. For an explanation of how to do this manually, visit Jared's post.

As for me, I wrote a batch file to automate the job.

The Code


@echo off

REM * Written by: Roy Tousignant
REM * Date: May 19th, 2009
REM * URL: youfuckingpeople.blogspot.com

SETLOCAL EnableDelayedExpansion
ECHO.

SET sizelimit=1024000000
SET tmpfile=%cd:~0,1%:\readonly.tmp

FOR /f "tokens=3 delims= " %%A in ('dir \ /-c ^| find /i "bytes free"') DO (
SET freespace=%%A
)

IF /i %freespace% lss %sizelimit% (
IF /i %freespace% gtr 0 (
IF EXIST !tmpfile! (
FOR /f "tokens=3 delims= " %%A in ('dir !tmpfile! /-c ^| find /i "1 file(s)"') DO (
SET tmpfilesize=%%A
)
) ELSE SET tmpfilesize=0
SET /a freespace = freespace + tmpfilesize
ECHO Writing !freespace! bytes to !tmpfile!
SET /P okay=Okay?[y/n]
SET okay=!okay:Y=y!
IF !okay! equ y (
ECHO.
del !tmpfile!
fsutil file createnew !tmpfile! !freespace!
GOTO End
) ELSE GOTO Abort
)
ECHO Disc is already full.
GOTO Abort
)

ECHO Script detects more than !sizelimit! bytes of free space.
ECHO This script refuses to fill more than !sizelimit! bytes of space.
GOTO Abort

:Abort
ECHO The procedure is aborted.
ECHO.
PAUSE

:End


Usage


Copy the code above into 'notepad' and save it to your usb drive as 'readonly.bat'. Run it and answer 'y' to the prompt to create your dummy file. The window will close itself upon successful completion. To "unlock" your drive, just delete the readonly.tmp file it creates.

More Usage


I've restricted the code from writing a dummy file larger than 1Gb. This is a cheap way to prevent fsutil from filling up your hard drive or some other disk, in the event the batch file is accidentally run from the wrong drive. Remember: The batch file must be run from the USB drive itself.

If your USB drive is larger than 1Gb you can change this value in the batch script on the line that reads SET sizelimit=1024000000

To change it to the exact size of your thumbdrive, open My Computer, right click the 'Removable Disk' that represents your thumbdrive and click Properties. Use the number listed as "Capacity" to replace the 1024000000 in the code, making sure to remove all the commas. Careful here! One too many digits and the size limit could end up being 20Gb instead of 2Gb.

Limitations


The flaw in this method of protection is that nothing prevents the deletion of files. If a virus is so inclined it can still wipe out your thumbdrive the instant you connect it. And if a virus' author were sufficiently devious, he could write a sneaky little function that deletes a few adjacent files, freeing just enough space on the disk to place the virus or infect an executable.

The flipside here is that even virus authors have lives and I'm not aware of any virus that makes such a herculean effort out of anticipating a full disk.

It's a flawed solution. But it is the best we can do without a hardware fix. It's greatest virtue is how unlikely it is that a virus will infect the drive without the user knowing. A virus may remove and replace a file completely, but cannot easily infect one to keep itself hidden. It can delete files to make room for itself, but we have gained warning by way of our missing files.

Design Notes


To prevent the program from running on a large local disc, it would be better to evaluate the total capacity of the drive instead of the free space remaining. But I could find no reasonable method of gaining a drive's true capacity at the command line. At first I figured adding together the free space and the used space values provided by dir \ /s /-c would work, but in testing the value of used space reported over 100k less than correct. The only other method I could find involved the wmic, which added an intolerable delay to initializing an otherwise simple script.

End of Line


As a computer repair technician, I'm satisfied with this solution for myself and I'm happy to recommend it. Though, if pressed, I recommend buying a USB drive with a write-protect switch even more.

Embed Flash MP3 Players w/ Javascript

What it Does:


The code provided here finds any <a> tags on the page that have been assigned the attribute, name="mp3". Then, using the URL from the <a> tag, it generates and places the proper code, embedding a flash mp3 player just above that <a> tag's link. Unaltered, this script uses the "Maxi" mp3 player hosted and available for download at http://flash-mp3-player.net/. (Though you need not download anything to run this script.)

Why?


First: Convenience. With this implementation I need only create a standard link and give it a name. From there, my mp3 player will appear on it's own, without all the ubiquitous <object><param><embed> malarky typically required to embed a flash file.

Secondly, to enable a reasonable level of dynamism to my Blogspot hosted site. If and when someone designs a slicker or more efficient mp3 player or... let's say the mp3 player I've been linking to disappears from its server someday - I'd like to be able to fix the broken links and integrate a new player into my site without having to edit every single post in which I'd ever embedded an audio file. This javascript standardizes the website's mp3 player, so that changing the embed code in the script effectively changes the embed code everywhere on the site.

How?


<script type="text/javascript">
/*
-----------------------------------------------
Dynamic Flash Embedder (for Linking MP3s)

Written by: Roy Tousignant
Date: 15 May 2009
URL: tvopiate.blogspot.com
----------------------------------------------- */

var playerloc = 'http://flash-mp3-player.net/medias/player_mp3_maxi.swf'

function buildObj(mp3) {
var flashtags = '<object data="'+playerloc+'" width="200" height="20" type="application/x-shockwave-flash">'
flashtags += '<param value="'+playerloc+'" name="movie">';
flashtags += '<param value="mp3='+mp3.href+'&amp;showstop=1&amp;showvolume=1" name="FlashVars">';
flashtags += '</object><br/>';

var obj = document.createElement('span');
obj.innerHTML = flashtags;
return obj;
}

function addPlayers() {
var mp3objs = document.getElementsByName('mp3');
var i = 0;
while (i < mp3objs.length) {
if(mp3objs[i].tagName == "A") {
mp3objs[i].parentNode.insertBefore(buildObj(mp3objs[i]), mp3objs[i]);
}
i++;
}
}

window.onload=addPlayers;
</script>


Usage


If you're using Blogger add a HTML/JavaScript gadget to your layout and paste in the code provided above. It doesn't matter where in your Blogspot layout you place the gadget. It will work just the same.

With the code in place you simply create links to the mp3 files you want to embed, making sure to include name="mp3" in the <a> tag. You can do this anywhere on your page: in posts; in the sidebars; in the header...

<a href="http://www.mymp3host.com/song.mp3" name="mp3">Download my Song</a>


That's it. Any links you make that carry the name "mp3" will magically receive an embedded mp3 player one line break above the link, allowing visitors to stream the file right there.

More Usage


Changing the flash player will probably require some basic understanding of simple coding. It's pretty straightforward to those who know, but for those who don't I'll provide an example. (Be it any help to you or not.)

To change the mp3 player that is being embedded you just need to edit the 'playerloc' and 'flashtags' variables near the top of the script to reflect the object/embed code for the new player.

For example, Google has a flash MP3 player that can be linked to. To embed Google's player manually onto a page you would use this object/embed code:

<object data="http://www.google.com/reader/ui/3247397568-audio-player.swf?audioUrl=http://www.mymp3host.com/song.mp3" width="400" height="27" type="application/x-shockwave-flash">
<param value="http://www.google.com/reader/ui/3247397568-audio-player.swf?audioUrl=http://www.mymp3host.com/song.mp3" name="movie">
<param name="flashVars" value="playerMode=embedded">
</object>


Therefore, to integrate Google's embed code into our script you would replace the playerloc and flashtags variables in our javascript code with:

var playerloc='http://www.google.com/reader/ui/3247397568-audio-player.swf?audioUrl=';

var flashtags = '<object data="' + playerloc + mp3.href + '" width="400" height="27" type="application/x-shockwave-flash">';
flashtags += '<param value="' + playerloc + mp3.href + '" name="movie">';
flashtags += '<param name="flashVars" value="playerMode=embedded">';
flashtags += '</object><br/>';


Note here that Google's player looks for the mp3 location to be included in-line with the link to the audio player itself, whereas the Maxi MP3 Player being used by default expects the mp3's url to be declared in the flashVar param.

And that's all the hints you get. ...ya' bunch of noobs.

Design Notes


"Why did you use a while loop instead of a for loop," you ask? To avoid a bug in blogger's code. In development whenever I saved the script to Blogger's HTML/Javascript gadget, use of the for loop caused everything thereafter to be scrambled.

End of Line


I suppose that's it. Wow, it might've taken as long documenting it as it did writing the damned thing. And I was trying to keep it brief! Actually it took me about 10 hours, start to finish, developing this script; forced to learn all sorts of javascript functions I'd never worked with before; fighting the Blogspot bugs to make it work in the gadget... Well.

Do leave me a comment if you're using it. I'd love to see my handiwork gracing other sites. And if you have any problems with it... 'the hell do I care?