Crawling Web Pages and Creating Sitemaps

Creating a Sitemap Based on all the Links within a Website

I built this web crawler because I wanted a way to create a sitemap of this website I was building. I know there are a few websites out there that will do this for you but I didn’t want to rely on someone else and I wanted to change a few things. So in order to do this I used php and cURL.
I started out creating a class for the crawler. When I create a new crawler class I pass in the url of the website I want to start with. This also uses cURL to access the webpage and get the content and headers. Inside this class are also methods to get all the links of a page, the page title, the entire content, just the body content, and the headers. But you could easily add more to say grab all the images on a page.

The Crawler Class

  

class Crawler {
  protected $markup='';
  protected $httpinfo='';

  public function __construct($uri, $justheaders=0){
    $output = $this->getMarkup($uri, $justheaders);
    $this->markup = $output['output'];
    $this->httpinfo = $output['code'];
  }

  public function getMarkup($uri, $justheaders) {
    $ch = curl_init($uri);
    curl_setopt($ch, CURLOPT_HEADER, 1);
    if($justheaders){
      curl_setopt($ch, CURLOPT_NOBODY, 1);
      curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
      curl_setopt($ch, CURLOPT_FAILONERROR, 1);
    }
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_MAXREDIRS, 5);

    $output['output'] = curl_exec($ch);
    $output['code'] = curl_getinfo($ch);
    curl_close($ch);
    return $output;
  }

  public function get($type){
    $method = "_get_{$type}";
    if (method_exists($this, $method)){
      return call_user_method($method, $this);
    }
  }

  protected function _get_info(){
    return $this->httpinfo;
  }

  protected function _get_links(){
    if(!empty($this->markup)){
      preg_match_all('/<a(?:.*?)href=(["|\'].*?["|\'])(.*?)>(.*?)\<\/a\>/i',
                               $this->markup, $links);
      return !empty($links[1]) ? array_flip(array_flip($links[1])) : FALSE;
    }
  }

  protected function _get_body(){
    if(!empty($this->markup)){
      preg_match('/\<body\>(.*?)\<\/body\>/msU', $this->markup, $body);
      return $body[1];
    }
  }
  protected function _get_content(){
    if(!empty($this->markup)){
      return $this->markup;
    }
  }

  protected function _get_pagetitle() {
    if (!empty($this->markup)){
     preg_match_all('/<title>(.*?)\<\/title\>/si', $this->markup, $pagetitles);
     return !empty($pagetitles[1]) ? $pagetitles[1] : FALSE;
    }
  }
}


After this I create a recursive function that will follow each of the links. Each time I call this function I create a new instance of the Crawler class. If the url isn’t valid I just return. If the url is redirected curl has an option to follow links, the CURLOPT_FOLLOWLOCATION option. Since this is set to on, you need to get the actual url which is contained in the header information. After this I call the get links function. This will return all the unique links on a page. (Calling the array_flip twice makes them unique).

I then get the title tag for each page. This is used when creating the sitemap. I remove all the script tags and all the html tags. The next thing I do is get a base url. Since I’m creating a sitemap of just one website I don’t need any links to external pages. So if any of the links don’t contain this base url I wont follow it.

Now I begin looping through all the links on the page. If its an external link I return. If it is an absolute link and it contains a “/” I replace the whole thing with a slash. If it doesn’t have the slash but is an absolute link then the new val is “”. I do this because I am prepending the base url that we got earlier to it.

I then explode on the “/”. If the first element is empty then I put the base url in there. Otherwise I prepend the url to it. This is done to get the correct link no matter if it is a relative link, root relative, or absolute.
The complete link is formed and checked to see if it already exist in the globallinkarr. If it doesn’t I add it and then begin getting the different levels. Basically everytime there is a “/” in the url then that is a different level. This is used when I am creating the html sitemap. Also to create this array of levels, I have to call array_merge_recursive. Well the regular php function didn’t quite work. If a url has numbers as one of its levels for example blog/2009/12/post then that function would turn the 2009 to its own key. So I needed it to keep the keys the same so I just got another function off of php.net.

The Function to Get all the Links


$dontfollow = array('pdf', 'jpg', 'png', 'jpeg','zip', 'gz', 'tar', 'txt');

function findAllLinks($url){
  global $globallinkarr;
  global $depthlinks;
  global $dontfollow;
  global $pagetitles;
  global $contentarr;

  $crawl = new Crawler($url);
  $info = $crawl->get('info');

  $validcodes = array(200,301,302);
  if(!in_array($info['http_code'], $validcodes))
    return;
  $url = $info['url'];
  $links = $crawl->get('links');
  $title = $crawl->get('pagetitle');
  $title = $title[0];
  $body = $crawl->get('body');
  $count++;

  $content = strip_tags(preg_replace('//msU', '', $body));

  if(!array_key_exists($url, $contentarr))
    $contentarr[$url] = array('title'=>"$title", 'pagecontent'=>"$content");

  if(!count($links) || !is_array($links)) return;
  else{
    if(preg_match('/http(?:s)?:\/\/(.*?)\/(.*)/', $url, $pattern)){
      $baseurl = $pattern[1];
    }else{
      $baseurl = $url;
    }

    foreach($links as $val){
      if(preg_match('/.*?javascript:void\(0\)/', $val) || ereg('#', $val)){
        continue;
      }
      if(!preg_match('/[0-9a-zA-Z]/', $val)) continue;
      $val = trim($val, '"\'');

      /**
       * CHECK IF LINK IS GOING TO ANOTHER DOMAIN.  IF SO DONT FOLLOW IT.
      */

      if(preg_match('/^http(s)?:\/\//', $val) &&
               !strpos($val, preg_replace('/http(s)?:\/\//', '', $baseurl))){
        continue;
      }

      if(ereg('http', $val) && preg_match('/^http(s)?:\/\/.*?\//', $val)){
        $val = preg_replace('/^http(s)?:\/\/.*?\//', '/', $val);
      }else if(ereg('http', $val)){
        $val = '';
      }

      $sl = explode('/', $val);

      if(!preg_match('/[0-9a-zA-Z]/', $sl[0])){
        $sl[0] = preg_replace('/^http(s)?:\/\//', '', $baseurl);
        $complink = implode('/', $sl);
        $sl = explode('/', $complink);

      }else{
        $prepend = explode('/', preg_replace('/^http(s)?:\/\//', '', $url));
        if(count($prepend)>1){
          array_pop($prepend);
          $prep = implode('/', $prepend);
        }else $prep = $prepend[0];
        $sl[0] = $prep.'/'.$sl[0];
        $complink = implode('/', $sl);

        $sl = explode('/', $complink);

      }
      if(!end($sl)) array_pop($sl);

      if(!in_array($complink, $globallinkarr)){
        $globallinkarr[] = $complink;
        $pagetitles[$complink] = $title;

        $depth = count($sl);
        $templinks = array();
        $newlinks = array();
        if($depth > 1){
           if(!$sl[$depth-1]) $sl[$depth-1] = 'index';
           $templinks[$sl[$depth-2]][] = $sl[$depth-1];

           if($depth > 2){
	     for($i=$depth-2; $i>0; $i--){
               $hold = $templinks;
               $templinks = array();
               $templinks[$sl[$i-1]] = $hold;

             }
          }

          $temp = $templinks[$sl[0]];
          $newlinks[$sl[0]] = $temp;

        }
        $depthlinks = array_merge_recursive2($newlinks,$depthlinks);
        $end = strtolower(end(explode(".", $complink)));

        if(!preg_match('/^http(s)?:\/\//', $complink))
          $complink = 'http://'.$complink;
          if(!in_array($end, $dontfollow) && !ereg("sitemap", $complink)){
            findAllLinks($complink);
          }
       }
    }
    return;
  }
}

The Array Merge Recursive Function I Used From php.net

function array_merge_recursive2($array1, $array2){
  $arrays = func_get_args();
  $narrays = count($arrays);

  // check arguments
  // comment out if more performance is necessary
  //   (in this case the foreach loop will trigger a warning if the argument is not an array)
  for ($i = 0; $i < $narrays; $i ++) {
   if (!is_array($arrays[$i])) {
   // also array_merge_recursive returns nothing in this case
     trigger_error('Argument #' . ($i+1) . ' is not an array - trying to merge array with scalar! Returning null!', E_USER_WARNING);
     return;
    }
  }

    // the first array is in the output set in every case
  $ret = $arrays[0];

  // merege $ret with the remaining arrays
  for ($i = 1; $i < $narrays; $i ++) {
    foreach ($arrays[$i] as $key => $value) {
     /***  KEEP THIS COMMENTED OUT TO KEEP THE ORIGINAL KEYS
     //if (((string) $key) === ((string) intval($key))) { // integer or string as integer key - append
     //   $ret[] = $value;
    // }
    // else { // string key - merge
      if (is_array($value) && isset($ret[$key])) {
        // if $ret[$key] is not an array you try to merge an scalar
        // value with an array - the result is not defined (incompatible arrays)
        // in this case the call will trigger an E_USER_WARNING and the $ret[$key] will be null.
        $ret[$key] = array_merge_recursive2($ret[$key], $value);
      }
      else {
        $ret[$key] = $value;
      }
           // }
    }
  }
  return $ret;
}

So after I created the arrays with all the links I create the sitemaps. The first one here is an xml sitemap used for the robots.txt file.
Its really simple and used the globallinkarr array.

XML Sitemap

function createXMLSiteMap($globallinkarr){
  $xml = '<?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
   if(count($globallinkarr)){
   foreach($globallinkarr as $val){
     if(!preg_match('/http(s)?:\/\//', $val)){
       $val = 'http://'.$val;
     }
     $xml .= '
       <url>
          <loc>'.str_replace('&', '&amp',$val).'</loc>
       </url>';
    }
  }
  $xml .= '</urlset>';
  return $xml;
}

The html sitemap is created using a recursive function that goes through depthlinkarr. Also it checks if the link is valid if it isn’t in the globallinkarr since all those are already checked. When it does check it only needs the headers so to speed things up I set the curl option CURLOPT_NOBODY to true. The page was timing out on me a lot but with this option set and checking to see if it already existed in the globallinkarr helped stop it from timing out. But if you have a whole lot of links there is a good chance this will cause your page to timeout.

The HTML Sitemap

function createSiteMap($depthlinks, $before = ''){
  global $globallinkarr;
  global $pagetitles;
  $validcodes = array(200,301,302);
  if(count($depthlinks)){
  $sitetree = '<ul style="padding:5px; margin:5px;">';
  foreach($depthlinks as $key=>$val){
    if(is_array($val)){
      if($before) $newbefore = $before.'/';
      $newbefore .= $key;

      $newkey = preg_replace('/^http(s)?:\/\//', '', $newbefore);

    $title = ($pagetitles[$newkey] != "") ? $pagetitles[$newkey] : $newbefore;
      if(!preg_match('/^http(s)?:\/\//', $newbefore))
         $newbefore = 'http://'.$newbefore;
      $exist = 0;
      if(in_array($newbefore, $globallinkarr)){
        $exist = 1;
      }else{
        $test = new Crawler($newbefore, 1);
        $info = $test->get('info');
        if(in_array($info['http_code'], $validcodes))
          $exist = 1;
        }
        if($exist){
          $sitetree .= '
           <li><a style="display:block;" href="'.$newbefore.'"
           target="_blank" title="'.$title.'" />'.$title.'</a></li>';
        }else{
          $sitetree .= '
             <li>'.$title;
        }
        $temp = createSiteMap($val, $newbefore);
        if($temp){
          $sitetree .= $temp;
          $sitetree .= '</li>';
        }
      }else{
        if($before != '') $newval = $before.'/'.$val;
        else $newval = $val;
        $newkey = preg_replace('/^http(s)?:\/\//', '',$newval);
        $title = $pagetitles[$newkey] ? $pagetitles[$newkey] : $newval;
        if(!preg_match('/^http(s)?:\/\//', $newval))
           $newval = 'http://'.$newval;

        $exist = 0;
        if(in_array($newval, $globallinkarr)){
            $exist = 1;
        }else{
          $test = new Crawler($newval, 1);
          $info = $test->get('info');
          if(in_array($info['http_code'], $validcodes))
            $exist = 1;
        }

        if($exist){
          $sitetree .= '
            <li><a href="'.$newval.'" title="'.$title.'"
                target="_blank">'.$title.'</a></li>';
        }
      }
    }
  $sitetree .= '</ul>';
  return $sitetree;
  }
}

Execute Code After Browser Finishes Loading

How to execute a script after the browser stops loading

Recently I needed to execute a bit of code but the browser was timing out. I was actually using the php exec function to tar up some files. The browser kept showing a time out page. So in order to make the browser think it was done loading and then execute this bit of script, I put everything in the buffer with ob_start(). Then when I’m ready to stop the browser I get the size of the buffer. I then set the header content-length equal to this size and flush out the buffer. Now the browser will no longer show that its loading, since it has received all the content it is going to receive. So now you can execute any script.

Here is the code


<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort();  // optional
ob_start();

echo "some content";

$size = ob_get_length();
 header("Content-Length: $size");
 ob_end_flush();
 flush();
//Browser done loading

//put your script here

?>

Round Robin Algorithm

Generating a Schedule Automatically

I need to create a way for every team to play each other in each round of games. I first started out creating a chart of who each team will play. After I created a chart that worked I built an algorithm to build that chart.

The Game Schedule

The Teams: Team 1 Team 2 Team 3 Team 4 Team 5 Team 6
Rounds: Round 1 Team 6 Team 5 Team 4 Team 3 Team 2 Team 1
Round 2 Team 4 Team 6 Team 5 Team 1 Team 3 Team 2
Round 3 Team 2 Team 1 Team 6 Team 5 Team 4 Team 3
Round 4 Team 5 Team 3 Team 2 Team 6 Team 1 Team 4
Round 5 Team 3 Team 4 Team 1 Team 2 Team 6 Team 5

Now you just need to figure out the pattern and we can code it up. I was told there is a name for this pattern but I didn’t know it, so if you know it let me know.

The Variables

$gamearr AND $tempgamearr are both an array of all the teams. The array would look like this: $gamearr[0] = “firstteam”, $gamearr[1] = “secondteam” and so on.

$numteamspadded is the number of teams. If the number was odd I add another team to the end of $gamearr and $tempgamearr as a “byteam”.

$gamenumperteam is however many number of games you want each team to play.

The Logic

As you can see at the beginning I set the variable $firstteam. You realize why I did this if you figured out the pattern above. The pattern above being that I have an array of teams playing the array of teams in reverse order. After each round I decrement the reverse order array and move the last one to the front. So we would have 654321 and then 165432 and then 216543. This would cause teams to play themselves though which is where the $firstteam comes in. Every time a team would play itself we switch it out with the firstteam.

I start out by looping through the number of games and then looping through just half of the teams since I’m setting both the home and away team in there. If I looped through all the teams I would have 1 vs 6 and then 6 vs 1 but I just want the unique games.

I then loop through all the games and set the $gamearr to the values that it would be had I been just decrementing the array and moving the last item to the beginning. Meaning the gamearr will look like this after each round 234561, 345612, 456123, etc. I use the $tempgamearr because I need to know the original order of the teams since we have to switch the first team out when there is a game playing itself.
After this I remove the first team from the array. I then loop through the game to find if one of the teams will be playing itself and get its value and position. This is one of the reasons why I remove the first team from the array. If I left it in there then 1 would play itself and overwrite the fact that another team was playing itself.
I then switch the team that is playing itself with the $firstteam.

After this you have an array of the games for each round.
I then create an array of all the games and check to see if the “byweek” is playing. If it is I don’t add it to the array cause we don’t want a byweek taking up a game slot.

The Round Robin Algorithm


$bottom = $numteamspadded-1;
$half = $numteamspadded/2;

$firsteam = $gamearr[0];
for($i=0; $i<$gamenumperteam; $i++){
    for($j=0; $j<$half; $j++){
         if($numteamspadded==4)
           $rrarr[$i][$j]['home'] = $tempgamearr[$j];
        else $rrarr[$i][$j]['home'] = $gamearr[$j];
        $rrarr[$i][$j]['away'] = $gamearr[$bottom-$j];
    }

    for($j=1; $j<=$bottom+1; $j++){
        $start = ($i+$j)%$numteamspadded;
        $gamearr[$j-1] = $tempgamearr[$start];
    }

    array_splice($gamearr, array_search($firsteam, $gamearr), 1);
    foreach($gamearr as $key=>$val){
        if($tempgamearr[$bottom-$key] == $val){
              $TempGameValue = $val;
              $switch = $key;
          }
    }

    $gamearr[$bottom] = $TempGameValue;
    $gamearr[$switch] = $firsteam;
}
foreach($rrarr as $key=>$val){
  foreach($val as $gkey=>$games){
    if($games['home'] != 'byweek' && $games['away'] != 'byweek'){
      $allgames[] = $games;
    }
  }
}

UPDATE: I came across another good implementation of the round robin here at http://phpbuilder.com/board/showthread.php?p=10935200

Sorting with Umlauts

Recently I needed to have a drop down box for countries. In english I just simply order by name but when you look at the german translation, ordering by name puts all the countries that have an umlaut character at the bottom. For example, I know the German translation for Austria is Österreich. So when I sort the translated names I now want this name to appear after the country named Oman. I know that the german umlaut html entity for ö is &ouml. I use that to get the character to sort the countries with.
Here is the code using PHP.

foreach($country as $val){
  $charset = htmlentities(utf8_decode($val['country_name']));
  if(substr($charset, 0,1)== '&'){
    $newcharset = str_replace(substr((html_entity_decode($charset)), 0,1),
                substr($charset, 1,1), html_entity_decode($charset));
    $origcountry[$val['country_id']] = $val['country_name'];
    $testarr[$val['country_id']]= $newcharset;
    $swap[$val['country_id']] = $newcharset;
  }else{
    $testarr[$val['country_id']] = $val['country_name'];
  }
}

asort($testarr);
foreach($testarr as $ckey=>$val){
  if($key= array_search($val, $swap)){
    $newcountryarr[$ckey] = $origcountry[$key];
  }else{
    $newcountryarr[$ckey] = $val;
  }
}

Scrolling Text using jQuery


Animating Text using jQuery Examples

Scroll Text Up
+++First Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+++Second Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Scroll Text to Left mouseover to stop the animation
+++Lorem ipsum dolor sit amet, consectetur adipiscing elit.     

A client of mine recently wanted the option to have scrolling text for breaking news type items. For the most part I think that scrolling text isn’t the greatest thing to use but there are certain situations where its good. The real simple and fast way to do it is to simply use a <marquee> tag like this <marquee width=”100%;” behavior=”SCROLL” direction=”left” scrollamount=”10″> Lorem ipsum doler </marquee>

The problem with this is it doesn’t do a continuous loop. What I mean by this is that it doesn’t start over till the text has completely finished scrolling. This means you see a lot of white space. Also jQuery seems to be a smoother animation. I knew jQuery had the animate function, so I used that. I found some plugins and examples where they had images being repeated but said they couldn’t do text cause they didn’t know the width. I guess they didn’t know css. So lets start with the css/html part of it.

Scrolling Text to the Left

The first thing we need to do is to get the width of the text that is being scrolled. So we need to create a div wrapper and set the width to be really big. This way the text wont be wrapped because of parent elements. We also set the display to none and visibility to hidden. This is done so that we don’t ever see this text. The display none keeps the browser from rendering the div in the browser, otherwise with just the visibility set to hidden we would see a big white space. Then we add another element inside with the text.


<div id="textwrapper" style="width:5000px; display:none; visibility:hidden;">
  <span id="textwidth" style="disply:none;">
    +++Lorem ipsum dolor sit amet,  consectetur adipiscing elit.
    Sed magna  ligula, tempus feugiat pellentesque et,pulvinar
    eu tellus. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  </span>
</div>
;

Now the html for the actual text being scrolled. We have a scrollwrapper element which contains the width of the the text being scrolled. We also need to set the position to relative so that the text being scrolled will be absolutely positioned relative to this element and not the page. The next element is used to make sure the text doesn’t wrap just like we did previously, we set the width to an arbitrarily large number. Then we have the scrollcontent element. This is the element that is going to be moving. This is positioned absolutely and we set the left position to the width of the scrollwrapper element to make sure it starts on the far right. Then we need one more wrapper inside.


<div id="scrollwrapper" style="overflow:hidden; border:1px solid #004F72;
      position:relative; width:500px; height:20px;">
  <div style="width:5000px;">
     <span id="scrollcontent" style="position:absolute; left:500px;">
       <span id="firstcontent" style="float:left; text-align:left;">
             +++Lorem ipsum dolor sit amet, consectetur adipiscing elit.
               Sed magna ligula, tempus feugiat pellentesque et, pulvinar
             eu tellus.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
       </span>
    </span>
  </div>
</div>

Here is the javascript but first let me explain how I am using the jquery animation function. The first object passed to it is what I’m animating. I am changing the property “left” to go to the negative of the scrollwidth. The next thing I pass to it is an object of some of the properties. The first property I am setting is “step”. This is a function that will get called everytime the animation runs. The next property is “duration” which is the amount of time it will take to finish the animation in milliseconds. The next is “easing” which is the equation used to do the animation. jQuery comes with 2 equations: linear and swing. Default is swing but we’ll set it to linear. The last property we’ll set is “complete” which is a function that’ll be called when the animation is done.


$("#textwrapper").css({"display":"block"});
var scrollwidth = $("#textwidth").width();
$("#textwrapper").remove();

var scrollwrapperwidth = $("#scrollwrapper").width();
if(scrollwidth < scrollwrapperwidth) scrollwidth = scrollwrapperwidth;
$("#scrollcontent").css({"width":scrollwidth});
$("#firstcontent").css({"width":scrollwidth});

var appending = scrollwrapperwidth-scrollwidth;
var noappend = 0;

function animatetext(rate){
  var dist = scrollwidth+parseFloat($("#scrollcontent").css("left"));
  if(!rate)rate = 1;
  var duration = Math.abs(dist/(rate/100));
  $("#scrollcontent").animate({"left":"-"+scrollwidth},{
    step: function() {
      if(parseFloat($("#scrollcontent").css("left"))< parseFloat(appending)
          && !noappend){
          noappend = 1;
          $("#scrollcontent").css({"width":scrollwidth+scrollwidth});
          $("#scrollcontent").append("<span style='float:left; text-align:left;
               width:"+scrollwidth+"px;'>"
               +$("#scrollcontent").children(":first").html()+"</span>");
      }
    },
    duration: duration,
    easing: "linear",
    complete:function(){
      $("#scrollcontent").children(":first").remove();
      $("#scrollcontent").css({"left":0});
      $("#scrollcontent").css({"width":scrollwidth});
      noappend=0;
      animatetext(6);
    }
  });
}
$("#scrollcontent").mouseover(function(e){
	$("#scrollcontent").stop();
});
$("#scrollcontent").mouseout(function(e){
	animatetext(6);
});

$(document).ready(function(){
	animatetext(6);
});

First in the javascript we will get the width of the text. We first need to set the css of the textwrapper to "block" so that it will render the child elements width. Then we will get the width of the inner element "textwidth" and then delete the textwrapper element.
If the javascript variable "scrollwidth" is less than the width that we want it to scroll we need to set it. Then we set 2 variables, appending and noappend. These variables are used in the "step" function. So when the position of the scrolling text is less than the position "appending" and we haven't already appended text (the noappend flag), then we'll set the width of the scrollcontent to double the width since we are appending the same text in there again. Then when the animation is completed we will remove the first element, change the left position to 0, change the width of the scrolling content back to the scrollwidth and then call this function again. Also if we want to stop the animation on mouseover as I have it, we simply call the .stop().

NOTE: since the .animate function takes a duration and not a speed, to keep the same speed we need to calculate the duration. That way if you are dynamically changing the text the speed wont change just because you have more or less text. To do this it is pretty straight forward. Since we are using linear as our easing property, the equation is simply d=rt, where d is the distance we need to travel and r is the rate and t is the time. So we get the distance by taking the scrollwidth and adding it to the current left position of the scrollcontent. Then we simple pass in a rate of 1- whatever and calcuate the time in milliseconds.

Scrolling Text Up

Scolling text up is even easier. The html looks like this. We need a wrapper with position relative and height equal to whatever you want. Then we need the child element which will be the element being moved. Then we need at least one element inside that to contain the text but can have more if you want. Also the line-height needs to be set to whatever the height is of the outerwrapper.


<div id="scrolltextup" style="border:1px ridge #004F72; overflow:hidden;
     position:relative; width:500px; height:20px;">
  <div id="textup" style="text-align:center; position:absolute; top:0;
      left:0; width:500px;">
    <div style="line-height:20px;">
      +++First Lorem ipsum dolor sit amet, consectetur adipiscing elit.
      +++Second Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    </div>
  </div>
</div>

Here is the javascript to do it. We get height of the entire thing. This time we are change the "top" property. Since the distance to animate will always be the same we don't need to calculate duration and can just pass in the duration.


var scrollheight = $("#textup").height();

function scrolltextup(dur){
  $("#textup").animate({"top":"-=20"},{
    duration: dur,
    easing: "linear",
    complete:function(){
      $("#textup").children(":last").after("<div style='line-height:20px;'>"+
          $("#textup").children(":first").html()+"</div>");
      if($("#textup").children(":first").height() <=
           (parseInt($("#textup").css("top"))*-1)){
        $("#textup").children(":first").remove();
        $("#textup").css({"top":0});
      }
      setTimeout("scrolltextup(3000)", 500);
    }
  });
}

And thats it!

Create Images From HTML

The Problem:

I want a simple way to create an image from dynamically generated html.

So Recently I’ve been working a lot inside tinymce, a javascript html WYSIWYG editor. I’ve wrote a couple plugins to manage/upload files and images utilizing my flash uploader. Tinymce has a couple plugins you can buy, but if you’re a developer seems like a waste of money. Maybe I’ll write a basic tutorial on how to upload pics and insert them into tinymce next time. Anyway on top of that I wanted tinymce to work like word in that it would have paging. Once I got that paging working, for the most part, I wanted to have thumbnails of each page. This is where my problem comes in. Basically each page in tinymce is wrapped in a div with a unique id. Therefore I have access to the html of each page, but how can I create an image from just a string of html? I looked all over the web and there were tools to do it but all of them cost some money and seemed more complicated than it should be. I knew I needed some way to render the html server side. There are tools out there that are open source like Gecko and Webkit, but this just seemed more complicated than it should be to create an image.
So I started thinking of other ways and I knew specifically that php has built in pdf support. I ended up finding dompdf. The rendering engine for this seemed to be really accurate and supports both inline styles and css. After creating the pdf I found that ImageMagick can convert pdfs to an image using GhostScript, an interpreter for the PostScript language and portable document format.
After those are installed, it is really simple. In my situation I’m updating the thumbnails via ajax to keep them up to date. I’m also using jQuery to make the ajax call. I pass the id of the page and the content of the page. I then return the id of the page with the name of the image separated by a colon. Also if you were wondering, the id of the actual page is the same as the id of the thumbnail. This can be done because tinymce is actually in an iframe so technically there is only one id per page. Side Note: “Apparently a lot of people don’t realize that ids are supposed to be unique per page. If you want to style more than one element use classes not ids. People then tell me, ‘But I’ve never had a problem with using the same id.’ It’ll render just fine but if you ever use javascript and use document.getElementById(‘someid’) then you’ll have a problem. Anyway I digress, back to creating images.” Here is the code to make the ajax call.


   $.post('ajax/test.php', {id:pageid, content:str}, function(data){
	var d = data.split(':');
	$('#'+d[0]).html('<img src="'+d[1]+'" alt="thumbnail" />');
  });

Now here is the ajax page that actually does the conversion. I first create the pdf from the content I passed to it. I have to add the <html> <body> tags for this to work correctly. There is also a lot of things you can do when creating the pdf, just look at the documentation to learn more. I catch the output of the pdf and then put the content into a file called test.pdf. I then use the time() function to create a unique image name so the browser wont display a cached version of the image. Finally I call the system function which is used to execute an external command. In this case the command is ImageMagick’s convert function. Side Note: When trying to run “convert” in Windows without the path in front it will possibly fail because it is trying to execute Windows convert program. There are a couple ways to get around it. One way being to look in the PATH environment variables and set the ImageMagick path closer to the front in the PATH system variables so that it comes before the Windows convert program. Some people also said that changing ImageMagick’s convert.exe name to imconvert.exe and call that instead will solve the issue.

   
require_once("../dompdf-0.5.1/dompdf_config.inc.php");

 $id = $_POST['id'];
 $html = '<html><body>'.html_entity_decode($_POST['content']).'</body></html>';

 $paper = 'letter';
 $orientation = 'portrait';

 $old_limit = ini_set("memory_limit", "32M");

 $dompdf = new DOMPDF();
 $dompdf->load_html($html);
 $dompdf->set_paper($paper, $orientation);
 $dompdf->render();
 $pdf = $dompdf->output();
 file_put_contents("test.pdf", $pdf);

$imagesample =  'sample'.time().'.jpg';

$source = 'C:\\your\\director\\here\\test.pdf';
$dest = 'C:\\your\\directory\\here\\'.$imagesample;
system("C:\\ImageMagick-6.5.4-Q16\\convert.exe $source $dest", $ret);

echo $id.':'.$imagesample;

And that is it! Cheers!

Scrolling with jQuery

I was creating a photo gallery and wanted to show a few pictures at a time. Then when you hit the next button it would scroll over one picture at a time. I found these plugins for jQuery called serialScroll and scollTo that work great. Plus I added fancybox to it so you could click on the picture to see the full size image.

This is basically how you should have the html set up. Basically what you need to have is an outer wrapper div with the width set to how wide you want the actual gallery. You need to set the overflow to hidden so we only see a few pictures at a time. The next thing is the element that will contain the scolling elements. In this case, the scrolling elements are the “li” tags which are wrapped in a “ul” tag. What is important here is to set a width for the ul tag. If you set a width that is less than the width of all the scrolling elements then they will wrap to the bottom which doesn’t look good. So you can calculate what the width of all the scolling elements are going to be and then set it or just set a really high number if you know that the width of all the elements wont be larger than that.
Another thing to note here is that I wrapped all the image tags in “a” tags with a class=’sidegroup’. This isn’t required for the scolling but is used for fancybox.


<div id='photoprev' style='float:left; cursor:pointer;' >Previous</div>
<div id='photos' style='float:left; width:260px; height:90px; overflow:hidden;'>
  <ul id='innerphotos' style='margin:0; padding:0; list-style:none; width:2000px; padding-left:10px;'>
    <li style='float:left; margin:0 10px; text-align:center; width:100px; height:110px;'>
      <a class='sidegroup' href='photos/341.jpg'>
        <img src='photos/thumbnails/341.jpg'  />
      </a>
    </li>
  </ul>
</div>
<div id='photonext' style='float:left; cursor:pointer;'>Next</div>
<div style='clear:both;'></div>

This is the javascript to initialize the scolling and the fancybox. To see a list of what each option will do click here. There is another option called axis that isn’t mentioned but it will tell the function which way to scoll. Default is ‘xy’ which means it will go down the list in the x direction and then go in the y direction.


	$(document).ready(function(){
		$('#photos').serialScroll({
			items:'li',
			prev:'#photoprev',
			next:'#photonext',
			offset:-5,
			start:0,
			duration:500,
			interval:4000,
			force:true,
			stop:true,
			lock:false,
			exclude:1,
			cycle:true, //don't pull back once you reach the end
			jump: false //click on the images to scroll to them
		});

		$('#photos a.sidegroup').fancybox({
			hideOnContentClick: false,
			zoomSpeedIn:	400,
			zoomSpeedOut:	400
		});
	});

You can view an example of the serial scoll in action here.

Equal Column Heights with Divs using CSS or jQuery

I am not a huge fan of using tables, in fact, most front end projects I do I usually use divs just because I think they’re nicer to look at than tables. So recently I needed to display some data in a table format which means I needed a way to make all the columns in each row the same height. There are a few different ways to do it without using tables.
One of the ways that I don’t like is faux columns. Basically this is just using a repeating background image to give the illusion of equal height columns.

Another way that is rather nice is using jQuery. All you need to do is include jQuery and a plugin called ‘equal heights’. Then include the following code. The “row1” is the class name that you’ll give each of the columns in row one.


$(document).ready(function(){
    	$(function(){
    	    $('.row1').equalHeight();
   	});
    });

The html of it would look something like this.


 <div class='row1'>
   lLorem ipsum dolor sit amet, consectetur adipiscing elit. <br/> Ut aliquet
 nulla eu felis. Donec porta <br/>
    aliquam ipsum. In ac nibh. Nunc dictum. Curabitur rhoncus facilisis nunc.
  </div>
  <div class='row1'>
   lLorem ipsum dolor sit amet, consectetur adipiscing elit. <br/> Ut aliquet
   nulla eu felis. In ac nibh. Nunc dictum. Curabitur rhoncus facilisis nunc.
  </div>
  <div class='row1'>
  	This is some text.
  </div>

And that is it besides styling each div however you want.

A lot of people might not want to include the javascript or just rather not use javascript to change the height of a div. So another way equal columns can be done is by setting a very high padding for each div and then setting the margin to be negative that. You need to wrap a div around it setting the overflow to hidden. Plus if you want borders around each row you won’t be able to set the border-bottom any more so you can simply set the border-top for each one and then add a border-bottom to the wrapper div.


<div id='row_wrapper' style='float:left; overflow:hidden;
                 border-bottom:1px solid black;'>
    <div id='column_1' class='other'>  lLorem ipsum dolor sit amet, consectetur adipiscing elit.
       <br/> lLorem ipsum dolor sit amet, consectetur adipiscing elit.
    </div>
    <div id='column_2' class='other'>  lLorem ipsum dolor sit amet,
       consectetur adipiscing elit.<br/>lLorem ipsum dolor
    </div>
    <div id='column_3' class='other'> lLorem ipsum dolor </div>
    <div id='column_4' class='other' style='border-right:1px solid black;'>
     lLorem ipsum dolor
    </div>
    <div style='clear:both;'></div>
</div>
    <div style='clear:both;'></div>

View the example of using both jQuery and just CSS here

PHP Menu with jQuery Drop Down

I wanted to create a menu where each menu could have submenus. I wanted a way where I could keep adding more submenus very easily. Also when a you are on a page it would be selected and all the parent menus would still stay selected. I decided to make a function to recursively go through an array to create the menu.

I started out with an array() like this.

$tabs = array(“page.php”=>”Page Name”, “page2.php”=>”Page Two”,
“page3.php”=>”Page Three”, “page4″=>”Page Four”);

This is very simple to go through but lets say Page Two has submenus and those submenus have submenus. So I would set it up like this.

$subpage[‘Subpage 2’] = array(“subpg3.php”=>”Sub Sub Page”);
$page2[‘Page Two’] = array(“subpage.php”=>”Subpage Name”, “subpage2.php”=>$subpage);

$tabs = array(“page.php”=>”Page Name”, “page2.php”=>$page2,
“page3.php”=>”Page Three”, “page4″=>”Page Four”);

So now when you’re looping through all the $tabs you’ll need to loop through the $page2 array() as well and then the $subpage array and take the keys for each of the submenus to set the name for the parent menu. Also if I am on subpg3.php I still want to keep “Subpage 2” and “Page Two” selected.

Another thing is if we don’t want to show the page name in the menu but we still want the parent menus selected. All we do is set the value to “HIDDEN” like so:
$subpage[‘Page Two’] = array(“profile.php”=>”Profile”, “profile_edit.php”=>”HIDDEN”);

So here is the function to get all the tabs.

First I declare some variables.
The global $filename could simply be $_SERVER[“PHP_SELF”]. but I personally like to use preg_match to get only the filename since $_SERVER[‘PHP_SELF’] will return the filename along with any subfolder its in. So I do

preg_match("/.*\/(.*)/", $_SERVER['PHP_SELF'], $name);
$filename = $name[1];

The global $dropdown is used to store all the submenus. I used this with jquery to create dropdown menus. If you dont want dropdowns then you can disregard anything commented for dropdowns.

The $found gets set to 1 as soon as the $filename matches one of the tabs.
The $menu is the menu that gets returned. The menu gets returned in an array format like this:
$menu[0] = array(“Home”=>”<a href=’home.php’ class=’selected’>Home</a>”, “About”=>”<a href=’about.php’ >About</a>”);
$menu[2] = array(“test”=>”<a href=’test.php’ class=’selected’>Test</a>”);

you need to do a ksort on menu to make it go $menu[0], $menu[2], $menu[4] instead of $menu[0], $menu[4], $menu[2];


function recursive_tabs($tabs, $row=1){
    global $filename;
    global $dropdown;
    static $found=0;

    $row = $row-1;
    $menu= array();
    foreach($tabs as $key=>$tab){

I check to see if $tab is an array; if it is and $found == 0 then I go into this “if statement” which calls this function again.

     
        if(is_array($tab) && !$found){
            $row = $row+2;
            $submenu = recursive_tabs($tab, $row);

            $tabkey = array_keys($tabs[$key]);

The next block is used for the dropdown array. We need to get all the keys of the $submenu. We need this to match up the $dropdown array correctly with its parent’s row. And then we need the keys of $tabs to match up the $dropdown with the name of its parent.
For example: if the $menu was
$menu[0] = array(“Home”=>”<a href=’home.php’ class=’selected’>Home</a>”, “About”=>”<a href=’about.php’ >About</a>”);

and $menu[0][‘Home’] had submenus then the dropdown would be $dropdown[0][‘Home’] = array(0=>”<a href=’drop.php’>Drop</a>”); so that they match up.


            $subkeys = array_keys($submenu);
            $sizeofkeys = array_keys($subkeys);

            $tabkeys = array_keys($tabs);

            if($subkeys[0]+1 == $row){
                foreach($submenu[$subkeys[0]] as $skey=>$sval){
                    $dropdown[$subkeys[0]-2][$tabkeys[0]][] = $sval;
                }
            }

if the $row is not 0, 2, 4, etc.. then I want to go ahead and return the $submenu.


            if($row%2 != 0  && $row !=0){
                return $submenu;
            }

Next I need to set the row back to the previous number. Then if the filename matches the current tab I loop through the submenu and set it to the menu. Also I check to see if $tabkey[0] == ‘HIDDEN’. $tabkey[0] is the name of each file. If it equals HIDDEN I don’t store it in the menu array but the parents of it will still be selected.
If it is not found I simply add the current $key to the menu.


            $row = $row -2;
            if($filename==$key) $found = 1;
            if($found){
                foreach($submenu as $subkey=>$sub){
                   $menu[$subkey] = $sub;
                }
                if($tabkey[0] != "HIDDEN"){
                   $menu[$row][$tabkey[0]] = '<a class="selected"
                      href="'.$key.'">'.$tabkey[0].'</a>
                }
            }else{
                if($tabkey[0] != "HIDDEN")
                   $menu[$row][$tabkey[0]] = "<a
                        href='$key'>".$tabkey[0]."</a>";
            }

If $tab is an array but $found == 1 I go into this one.


        }else if(is_array($tab)){

            $tabkey = array_keys($tabs[$key]);
            if($tabkey[0] != "HIDDEN")
                $menu[$row][$tabkey[0]] = "<a href='$key'>
                      {$tabkey[0]}</a>";

This next block is for the dropdown menu. Same thing we used above.


            $row = $row+2;
            $submenu = recursive_tabs($tab, $row);

            $subkeys = array_keys($submenu);

            $tabkeys = array_keys($tabs);

            if($subkeys[0]+1 == $row){
                foreach($submenu[$subkeys[0]] as $skey=>$sval){
                    $dropdown[$subkeys[0]-2][$tabkeys[0]][] = $sval;
                }
            }
             $row = $row -2;

The last block is if $tab is not an array. If the $filename == $key then I set $found = 1 and also a change the class to “selected”.


        }else{
            if($filename==$key){
              if($tab != "HIDDEN"){
                $menu[$row][$tab] = '<a class="selected"
                                       href="'.$key.'" >'.$tab.'</a>
              }
              $found = 1;
            }
            else {
                if($tab != "HIDDEN")
                $menu[$row][$tab] = "<a href='$key'  >$tab</a>";
            }
        }
    }
     return $menu;
}

That’s it. Here is all the code together. If you want to see how to use this menu to create a drop down menu keep reading.


function recursive_tabs($tabs, $row=1){
    global $filename;
    global $dropdown;
    static $found=0;

    $row = $row-1;
    $menu= array();
    foreach($tabs as $key=>$tab){
        if(is_array($tab) && !$found){
            $row = $row+2;
            $submenu = recursive_tabs($tab, $row);

            $tabkey = array_keys($tabs[$key]);
            $subkeys = array_keys($submenu);

            $tabkeys = array_keys($tabs);

            if($subkeys[0]+1 == $row){
                foreach($submenu[$subkeys[0]] as $skey=>$sval){
                    $dropdown[$subkeys[0]-2][$tabkeys[0]][] = $sval;
                }
            }
            if($row%2 != 0  && $row !=0){
                return $submenu;
            }

            $row = $row -2;
            if($filename==$key) $found = 1;
            if($found){
                foreach($submenu as $subkey=>$sub){
                   $menu[$subkey] = $sub;
                }
                if($tabkey[0] != "HIDDEN"){
                $menu[$row][$tabkey[0]] = '<a class="selected"
                                      href="'.$key.'" >'.$tabkey[0].'</a>
                }
            }else{
                if($tabkey[0] != "HIDDEN")
                    $menu[$row][$tabkey[0]] = "<a
                                     href='$key'>".$tabkey[0]."</a>";
            }
        }else if(is_array($tab)){
            $tabkey = array_keys($tabs[$key]);
            if($tabkey[0] != "HIDDEN")
                $menu[$row][$tabkey[0]] = "<a href='$key' >
                                                          {$tabkey[0]}</a>";

            $row = $row+2;
            $submenu = recursive_tabs($tab, $row);
            $subkeys = array_keys($submenu);
            $tabkeys = array_keys($tabs);

            if($subkeys[0]+1 == $row && $row>2){
                foreach($submenu[$subkeys[0]] as $skey=>$sval){
                    $dropdown[$subkeys[0]-2][$tabkeys[0]][] = $sval;
                }
            }
             $row = $row -2;
        }else{
            if($filename==$key){
              if($tab != "HIDDEN"){
                 $menu[$row][$tab] = '<a class="selected"
                                             href="'.$key.'" >'.$tab.'</a>
              }
              $found = 1;
            }
            else {
                if($tab != "HIDDEN")
                  $menu[$row][$tab] = "<a href='$key' >$tab</a>";
            }
        }
    }
     return $menu;
}

Once this function returns we’ll need to loop through all the menu’s.

This first thing I do is create an array of divs to wrap the menus in. This is used for styling.


$subdiv[0] = "<div id='toplinks_css'>";
$subdiv[1] = "<div id='sublinks_css'>";
$subdiv[2] = "<div id='sublinks2_css'>";

So as I’m looping through each of the menu items I add a wrapper div around each of them with a unique ID so it can be used with the jquery drop down. The drop down is also wrapped in this div. I also wrap a div around just the link. This is so jquery can can change the style of just that link if you wanted.
The next thing is if there is a drop down menu to add a wrapper around it with position of relative. This will allow each of the drop downs to be positioned under their parent menu. I then wrap the drop down in another div with a unique Id used by jquery and a class called dropdown. The main thing that you need to have in this class is position:absolute, display:none; and z-index:999 (“some number to make sure drop down goes on top”).


$head .= "<script type='text/javascript'>
$(document).ready(function(){";
$count = 0;
foreach($menu as $key=>$tabs){

$str .= $subdiv[$i];
foreach($tabs as $tkey=>$tab){
  $str .= "<div id='header$count' style='float:left; margin-left:20px;'>
       <div id='head$count'>$tab</div>";
  if($dropdown[$key][$tkey]){
     $str .= "<div style='position:relative;'>
                     <div id='dropdowncontainer$count' class='dropdown'>
                         <div class='inner'>";
     foreach($dropdown[$key][$tkey] as $dkey=>$dval){
       $str .= $dval;
    }
$str .= "</div>
</div>
</div><div style='clear:both;'></div>";
}

The next bit is the jQuery. I use a jQuery plugin called hoverIntent which allows you to set a time that the mouse must be over the link before it will execute. This way if you’re just moving the mouse across the page the drop down wont show up. It takes two functions. The first one is on hover and the second is off hover.


$head .= "$('#header$count').hoverIntent(function(){
    $('#head$count a').css('background-color','#0167B1');
";

   if($dropdown[$key][$tkey])
     $head .= "$('#dropdowncontainer$count').show();";
$head .= "    },

  function(){
    $('#head$count a').css('background-color','');";
    if($dropdown[$key][$tkey])
    $head .= "        $('#dropdowncontainer$count').hide();";

$head .= "  });  ";

$str .= "</div>";
$count++;
}
$str .= "<div style='clear:both;'></div></div>";
$i++;
}
$head .=   "});
</script>";

And that will give you a dropdown box which you can now style however you want.

Click here to view the example.

Cross Domain Scripting using Flash

I was building a site for a client when I came across this cross domain security issue. My client had users that needed to be able to send data from their website to my clients server which would process the data and return a new set of data to replace the old. I couldn’t just make an ajax call since the browser wont allow you to receive data from another server. My options to get around this were that I could use iframes or use flash. I decided to go with the flash for no particular reason.

I’m going to go through each file I created. First I’m going to go through the .fla file and then the two .as files and then the javascript file.

I am using Flash 8 and actionscript 2.

To start, create a new .fla file.

Import the following class to allow flash and javascript to communicate.


import flash.external.ExternalInterface;

Flash wont allow you to access another domain without adding the following code. The ‘*’ will allow all domains to be able to access the file on your server or you can add individual domains.


System.security.allowDomain("*");

I create a new instance of DebugHelper which is created in a separate .as file which I’ll show later. This is only used to help me print out text to test the code. It creates a text field and then when you call the Log function will print out the string you pass to it.


var deb = new DebugHelper();
DebugHelper.Log("test");

This is a button I created in the .fla. When it is clicked it loads the policy file. This is an xml file that tells the flash which domains are allowed to access the files that are in the same folder the xml file is in and all subfolders on your server. You can use a ‘*’ to allow all domains. The ExternalInterface.call calls a javascript function which takes the content of the webpage and passes it to flash.


The crossdomain.xml file
<?xml version="1.0"?>
   <!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/
dtds/cross-domain-policy.dtd">
   <cross-domain-policy>
       <allow-access-from domain="www.company.com" />
       <allow-access-from domain="*" />
   </cross-domain-policy>

btn.onRelease = function() {
  System.security.loadPolicyFile("http://yourdomain.com/crossdomain.xml");
  var successful = ExternalInterface.call("sendit");
};

This next bit allows the flash element in javascript to send data via the function “sendData” which calls the “xmlhttp” flash function. Then this function creates a new HttpConnection object which is in another .as file which I’ll explain later. You pass to it the filename of where you want to send the data. You then can set the body with the data you passed to the function and whether you want to use POST or GET. I then add the onData event handler to the object so I know when I get data back from the server. When that is fired I set the data to a variable called retText and then pass it to javascript with the call “setData”.


ExternalInterface.addCallback("sendData", this, xmlhttp);
function xmlhttp(body:String) {
	var http = new HttpConnection("http://yoursite.com/crossdomain.php");
	http.setAction("POST");
		http.setBody(body);
		http.setContentType("application/x-www-form-urlencoded");
		http.onData = function(src:String) {
				_root.retText = src;
				ExternalInterface.call("setData", "retText");
		};
		http.send();
}

This last function calls the javascript function “storageOnLoad” so we know the flash is loaded.


var ret = ExternalInterface.call("storageOnLoad");

This next bit is the DebugHelper.as file. This just creates a text field that allows me to call Log() on DebugHelper to help me debug by printing out text to the screen.


class DebugHelper {
	static var log:String;
	static var debug:Boolean;
	function DebugHelper() {
		_root.createTextField("tf", 0, 0, 0, 315, 238);
		log = "";
		var ret;
		_root.tf.text = "Running Flash version: "+getVersion();
		DebugHelper.Debug();
	}

	public static function Debug() {
		debug = true;
	}
	public static function Log(input:String) {
		log = log+" ___ "+input;
		if (debug) {
			_root.tf.text = input+"\r"+_root.tf.text;
		}
	}
}

This next file is the HttpConnection.as file. I start out by defining some variables and functions. The sendAndLoad and Load functions I set to the default LoadVars.prototype.sendAndLoad and LoadVars.prototype.load function which allows me to send this objects data to the given url using the LoadVars class.


 class HttpConnection {
	private var action:String = "POST";
	private var body:String = "";
	private var url:String = "";
	var contentType:String;
	var sendAndLoad:Function = LoadVars.prototype.sendAndLoad;
	var load:Function = LoadVars.prototype.load;
	// note: many headers can't be added. http://livedocs.macromedia.com/
       //   flash/8/main/wwhelp/wwhimpl/common/html/
       //   wwhelp.htm?context=LiveDocs_Parts&file=00002324.html
	var addRequestHeader:Function = LoadVars.prototype.addRequestHeader;
	var onData:Function;

This function creates an instance of this class.


	function HttpConnection(_urls:String) {
		url = _urls;
	}

The next four function allow me to set the url, action, body and contentType.


	function setUrl(_urls:String) {
		if (_urls) {
			url = _urls;
		}
	}
	function setAction(_action:String) {
		if (_action) {
			action = _action;
		}
	}
	function setBody(_body:String) {
		if (_body) {
			body = _body;
		}
	}
	function setContentType(_contentType:String) {
		if (_contentType) {
			contentType = _contentType;
		}
	}

This function is overriding the default toString() function. This automatically gets called when the sendAndLoad function is called. If I wasn’t using an object to call the sendAndLoad function I would have to create an instance of LoadVars and set the variables like this:

var my_lv:LoadVars = new LoadVars();
var result_lv:LoadVars = new LoadVars();
my_lv.id=10;
my_lv.text = “some text”;
my_lv.sendAndLoad(url, result_lv, ‘POST’);

The toString() function would get called to convert my_lv to “id=10&text=some+text”. Since I already have the body in that format I don’t need to do that so I just return the body.


	function toString():String {
		return body;
	}

This is the function that sends the data to the server.


	function send() {
		DebugHelper.Log("HttpConnection send this(action="+action+")");
		// note: if we did a sendAndLoad on a GET, the Flash API would add
                //a question mark '?' at the end of the querystring

		if (action == "GET") {
			load(url);
		} else {
			sendAndLoad(url, this, action);
		}
	}
}

Now the javascript file.

I create a javacript object to hold the flash.


var FlashObject = new Object();
FlashObject.height = 138;
FlashObject.width = 215;

This function will check to see if the flash is installed.


FlashObject.isFlashInstalled = function() {
	var ret;
	if (typeof(this.isFlashInstalledMemo) != "undefined") {
            return this.isFlashInstalledMemo;
        }
	if (typeof(ActiveXObject) != "undefined") {
  	   try {
		var ieObj = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
	    } catch (e) { }
	     ret = (ieObj != null);
	} else {
		var plugin = navigator.mimeTypes["application/x-shockwave-flash"];
		ret = (plugin != null) && (plugin.enabledPlugin != null);
	}
	this.isFlashInstalledMemo = ret;
	return ret;
}

This simply gets the flash element.


FlashObject.getFlash = function() {
	return document.getElementById("flash");
}

This function will write the flash object on your page. For me, I needed to send the entire content of the body to flash and then replace it without replacing the flash object. So I needed to add a wrapper div around the entire content of the body. That way when I get the data back from flash I would replace everything inside that wrapper div. This will keep the flash from getting over written which can cause some errors. If you don’t need to replace the entire page then it’s not necessary.


FlashObject.writeFlash = function() {
	var swfName = "http://yourdomain.com/crossdomain.swf";
	if (window.ActiveXObject && !FlashObject.isFlashInstalled())
	{
	  // browser supports ActiveX
	 // Create object element with
	 // download URL for IE OCX
	 document.body.innerHTML="<div id='wrapperdiv'>"+document.body.innerHTML+
                                                    "</div>";
	 document.write('<object classid=
                           "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"');
	 document.write(' codebase="http://download.macromedia.com');
	 document.write('/pub/shockwave/cabs/flash/swflash.cab#version=8,5,0,0"');
	 document.write(' height="'+this.height+'" width="'+this.width+'"
                                   id="flash">');
	 document.write(' <param name="movie" value="' + swfName + '">');
	 document.write(' <param name="quality" value="high">');
	 document.write(' <param name="allowScriptAccess" value="always">');
	 document.write(' <param name="swliveconnect" value="true">');
	 document.write('<\/object>');
	}
	else
	{
	 // browser supports Netscape Plugin API
	 document.body.innerHTML="<div id='wrapperdiv'>"+document.body.innerHTML+
                                           "</div>";
         document.write('<object id="flash" data="' + swfName + '"');
	 document.write(' type="application/x-shockwave-flash"');
	 document.write(' height="'+this.height+'" width="'+this.width+'">');
	 document.write('<param name="movie" value="'+swfName+'">');
	 document.write('<param name="quality" value="high">');
	 document.write('<param name="swliveconnect" value="true">');
	 document.write('<param name="pluginurl"
                    value="http://www.macromedia.com/go/getflashplayer">');
	 document.write('<param name="pluginspage"
                     value="http://www.macromedia.com/go/getflashplayer">');
	 document.write(' <param name="allowScriptAccess" value="always">');
	 document.write('<p>You need Flash for this.');
	 document.write(' Get the latest version from');
	 document.write(' <a href="http://www.macromedia.com/software/
                                    flashplayer/">here<\/a>.');
	 document.write('<\/p>');
	 document.write('<\/object>');
	}
}

The next few functions are used to load up the flash.


FlashObject.load = function() {
	if (typeof(FlashObject.onload) != "function") { return; }

	if (FlashObject.isFlashInstalled()) {
		// if we expect Flash to work, wait for both flash and the document to be loaded
		var finishedLoading = this.flashLoaded && this.documentLoaded;
		if (!finishedLoading) { return; }
	}
	// todo: cancel timer
	var fs = FlashObject.getFlash();

	if ((!FlashObject.isFlashInstalled() || this.flashLoaded) && fs) {
		if (FlashObject.checkFlash()) {
			callAppOnLoad(fs);
		} else {
			callAppOnLoad(null);
		}
	} else {
		callAppOnLoad(null);
	}

	function callAppOnLoad(fs) {
		if (FlashHelper.onloadCalled) { return; } // todo: figure out why this case gets hit
		FlashHelper.onloadCalled = true;
		FlashHelper.onload(fs);
	}
}

function storageOnLoad() {
	FlashObject.flashLoaded = true;
	FlashObject.load();
}

This function is nice because if you already have a window.onload event and need to add more onload events this will add them.


FlashObject.addLoadEvent = function(func) {
	var oldonload = window.onload;
	if (typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function() {
			oldonload();
			func();
		}
	}
}

This initializes the flash object variables and onload functions. If you wanted the flash to automatically run on page load you could simply add the function call (in this case “sendit()”) inside the onload function.


FlashObject.init = function() {
	this.flashLoaded = false;
	this.documentLoaded = false;
	// attach to the window.onload event
	this.addLoadEvent(onload);
	function onload() {
		FlashObject.documentLoaded = true;
		FlashObject.load();
	}
}

FlashObject.init();

The function that actually gets the data you want to send and then calls the flash function “sendData”.


 function sendit() {
    var body = 'uid='+testdata;
    body += '&pagetext='+
       encodeURIComponent(document.getElementById('wrapperdiv').innerHTML);
    FlashObject.getFlash().sendData(body);
 }

When the data gets returned it stores it in a variable which we set in the http.onData function. We then pass the name of the variable to this function and call GetVariable which returns the data.


function setData(varname){
	var str = FlashObject.getFlash().GetVariable(varname);
	document.getElementById("wrapperdiv").innerHTML = str
}

And finally this writes out the flash object to the page.


FlashObject.writeFlash();

Now all anyone would need to do would be to include this javascript file at then end of their page right before the body tag.

Here are the .fla, .as and .js files.
CrossDomain.zip