Hide Feedjit Header and Footer

Feedjit is a useful add-on to a website for tracking visitors to your blog. Feedjit shows a little snapshot of when and where each visitor to your site came from. One can easily see visitors from around the world.

However, one difficulty with Feedjit is that it contains a ‘real-time view’ hyperlink to the Feedjit site for traffic statistics. I don’t particularly want this accessible from the front page of my site. I also don’t want the Feedjit advertising to show if I can eliminate it.

So, with a little Javascript, I managed to intercept the Feedjit HTML and modify the resulting display so that it would appear as follows:

How is it done?

The first thing was to figure out how Feedjit works. Feedjit requires that you include in your page a request to load a Feedjit javascript file. What comes back from this is script to create the necessary HTML in your page. This HTML creates an IFRAME window for the Feedjit results so that it can load the visitor information independently from your page load. The Feedjit output is put in the iframe in an HTML table element where each table row represents one visitor to your site. If we can somehow identify the iframe object and the table object in the frame then we can use some javascript to fiddle with the table and remove the top and bottom rows.

Use the Firebug console in Firefox to see the CSS and HTML that was loaded by Feedjit into the browser. This will show that the iframe name was ‘FJIframe’ and the table ID was ‘FJ_Tlist’. This is good, but please understand that this solution is just a hack that is dependent on these coded names. Once Feedjit updates their script or rebuilds their process and changes these names this hack will fail to work.

The first step is to figure out how to edit the table so it appears as we want. Assume for the moment that the Feedjit table has been rendered fully and we are coming in after the fact to fix it up. The function ‘hideFeedjit()’ shown in the code below performs the table row deletions. We must remove the top two rows of advertising and the bottom two rows to get rid of the Feedjit links to the real-time view. We will hide the rows in the table by setting their display style to none so that they take no space.

One difficulty with this solution is that the container div block has already been rendered and sized to fit the full table. When we hide the rows we end up with unused white space at the bottom of the container. To fix this we need to resize our container. This is why we have the named ‘visitor’ div block set up around the Feedjit code. We can resize this object to remove the white space from the missing rows.

The next problem to solve is a timing problem. We can only run the ‘hideFeedjit()’ function after the Feedjit server has responded to the request to load its javascript and its HTML has been created. We don’t know when this will happen. So, we run a little timing loop to periodically check if the iframe has been loaded. This is the purpose of the ‘checkIframeLoading()’ function. If we can see that the iframe is fully populated and the table in the iframe exists then we can perform our edits.

The operative test to see if the iframe is loaded is to check if readyState == ‘complete’. This works in Internet Explorer and Firefox version 3.6 or later. But, for some reason Internet Explorer versions prior to version 8 would not identify the iframe object if it was searched for by name.

The last step is to start this whole process once my web page is loaded. We activate the function as soon as it is declared but we know that the page load may not be complete. This is why we check for existance of the iframe and table objects. If they are undefined we simply wait a bit and try again.

This solution is fault tolerant. If it fails to find the required objects or delays in load times occur then the worst thing that will happen is that you will see the uncorrected Feedjit display. We also shut the timing loop down after 100 iterations or 10 seconds to be nice to our web browser. So, as web transfer delays are always possible there is no guarantee that it will hide the Feedjit links all the time, every time.

Good luck! You can see this Feedjit hack operating on the home page of Kisekae World

.

 

WordPress Text Widget Contents

Validated in Firefox, Internet Explore 8.0 and later, Chrome, and Safari

Copy this code into a WordPress text widget. Or, if you are not using WordPress or some other content management system then simply insert this code into your web page where you want the Feedjit output to appear. Note, you will have to replace the identified Feedjit script link with your own code. See the Feedjit site.

<div id="visitors">

<script type="text/javascript" src="http://feedjit.com/serve/?... ">
</script>


<script type="text/javascript">
var loop = 100 ;
function checkIframeLoading() {
   // Get a handle to the iframe element

   var iframe = window.frames['FJIframe'];
   // Check if loading is complete
    if (iframe) {
      if ( iframe.document.readyState == 'complete' ) {
        var table = iframe.document.getElementById('FJ_TList') ;
        if (table) {
           // The loading is complete, call the function we want executed once the iframe is loaded
           hideFeedjit();
           return;
        }
      }
   }

   // If we are here, it is not loaded. Set things up so we check the status again in 100 milliseconds.
   loop = loop - 1 ;
   if (loop > 0) {
      window.setTimeout('checkIframeLoading();', 100);  
   }
}
checkIframeLoading();
</script>

<script type="text/javascript">
function hideFeedjit() {
   var iframe = window.frames['FJIframe'];
   var table = iframe.document.getElementById('FJ_TList') ;
   var div = document.getElementById('visitors') ;
   var th1 = table.offsetHeight ;
   var dh1 = div.offsetHeight ;
   table.rows[0].style.display = 'none' ;
   table.rows[1].style.display = 'none' ;
   table.rows[table.rows.length-1].style.display = 'none' ;
   table.rows[table.rows.length-2].style.display = 'none' ;
   var th2 = table.offsetHeight ;
   var dh2 = dh1 - (th1-th2) ;
   div.style.height = dh2+'px' ;
}
</script>
</div>

Gallery2 Website Option Sidebar Block

My website is Lightbox enabled for image viewing and slideshow viewing of Gallery2 contents. Although the default case is to enable these Lightbox services, many website viewers may not want this. Website visitors should have the option to configure their viewing experience to suit their preference. This introduces the idea of user specific customization or configuration of their profile on my website.

To enable customization I implemented a user options section as a new Gallery2 sidebar block. The first challenge was to create a new Gallery2 block. Fortunately there is a very simple HTML page custom plugin for Gallery2 available on the Internet, from Bharat Mediratta, that can be used to do this. I didn’t care about the intended capability to link to HTML pages but I did want the ability to establish a sidebar block. I wanted to display checkbox options in the sidebar that the user could modify. This sidebar block would be shown on all Gallery2 pages so I also needed some way to retain the settings of these checkboxes as the user traversed the gallery pages.

To do this, I had to maintain the user option state in session cookies. The cookies provide persistance to the option values across individual page changes within one session and also across different sessions. I had two Lightbox options custom configured in my Matrix theme that could be used for initial settings. One was a global Lightbox option that identified if Lightbox services were available, and the second identified if Lightbox slideshow capabilities were available. If these options were set in the theme and if cookie values did not exist then I could assume that the checkbox options should be set, otherwise the checkbox option value would be the cookie value. For persistance the cookies would be retained within the browser for five days.

The Gallery2 sidebar block contents, found in the HtmlBlock.tpl template file in the plugin blocks directory, were replaced with the following Javascript:

templates/blocks/HtmlBlock.tpl

{*
 * $Revision: 1.5 $
 * If you want to customize this file, do not edit it directly since future upgrades
 * may overwrite it.  Instead, copy it into a new directory called "local" and edit that
 * version.  Gallery will look for that file first and use it if it exists.
 *}
{if ($theme.params.lightbox == 1)} 
    <script type="text/javascript">
      // <![CDATA[
      function set(s) {ldelim}
        var x = readCookie(s) ;
        if (!x) return ;
        eraseCookie(s) ;
        (x.charAt(0)=='1') ? x = "0" : x = "1" ;
        createCookie(s,x,5) ;
      {rdelim}

	function createCookie(name,value,days) {ldelim}
	  if (days) {ldelim}
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	  {rdelim}
	  else var expires = "";
	  document.cookie = name+"="+value+expires+"; path=/";
	{rdelim}

	function readCookie(name) {ldelim}
	  var nameEQ = name + "=";
	  var ca = document.cookie.split(';');
	  for(var i=0;i < ca.length;i++) {ldelim}
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	  {rdelim}
        if (name == "cb1") return {if ($theme.params.lightbox == 1)} "1" ; {else} "0" ; {/if}
        if (name == "cb2") return {if ($theme.params.autoLightbox == 1)} "1" ; {else} "0" ; {/if}
	  return null;
	{rdelim}

	function eraseCookie(name) {ldelim}
	  createCookie(name,"",-1);
	{rdelim}


    document.write('<div class="{$class}">') ;
    if (readCookie('cb1') == "1")
      document.write('<input type="checkbox" id="cb1" checked="checked" onClick="set('+"'cb1'"+');"/><b>Photo Lightbox on</b><br />') ;
    else 
      document.write('<input type="checkbox" id="cb1" onClick="set('+"'cb1'"+');"/><b>Photo Lightbox on</b><br />') ;
  {if ($theme.params.autoLightbox == 1)} 
    if (readCookie('cb2') == "1")
      document.write('<input type="checkbox" id="cb2" checked="checked" onClick="set('+"'cb2'"+');"/><b>Gallery Slideshow on</b>') ;
    else 
      document.write('<input type="checkbox" id="cb2" onClick="set('+"'cb2'"+');"/><b>Gallery Slideshow on</b>') ;
  {/if}
    document.write('</div>') ;
      // ]]>
    </script>
{/if} 

Meta Tags for Gallery2 and WordPress

When Gallery2 is embedded in WordPress there is no standard capability to generate page meta descriptions and keyword tags for individual Gallery2 albums or photos.  The WPG2 plugin creates one WordPress page, the WPG2 page, in which all Gallery2 content is rendered.  Each time a Gallery2 page is rendered by WordPress we need to be able to generate unique meta tags so that search engines can index and identify our site Gallery2 pages. Without the capability to uniquely tag individual Gallery2 pages our photo galleries may not be indexed.

To generate meta tags for each WordPress Gallery2 page, the first step is to generate meta tags in the Gallery2 header.  In many cases the meta description tag is based upon the Gallery2 page description or title and the meta keyword tag is based upon the Gallery2 page keywords.  In my case I follow similar technique for the page description, but for keywords I want to automatically generate these values from the album contents if keywords were not specified.

Some postings on the Internet show how to do this. Meta tags are inserted into your Gallery2 theme.tpl file near the beginning, after the generation of the <title> directive. I have modified the code to default the keyword values from the Gallery2 summary text and album title. I do not assign Gallery2 keywords to album and photo pages. Instead, my pages use the summary text as a keyword to identify the album contents.

theme.tpl

    
    {* If Gallery doesn't provide a header, we use the album/photo title (or filename) *}
    {if empty($head.title)}
      <title>{$theme.item.title|markup:strip|default:$theme.item.pathComponent}</title>
    {/if}

    {* For meta tags, if no keywords for the page we default to use the title and summary text from all children in the page*}
    {assign var="keywordlist" value=$theme.item.title|markup:strip|default:$theme.item.pathComponent}
    {if count($theme.children) > 0}
       {foreach from=$theme.children item=child}
          {if !empty($child.summary)}
            {if !empty($child.title)}
 	       {assign var="keywordlist" value=$keywordlist|cat:", "}
 	       {assign var="keywordlist" value=$keywordlist|cat:$child.title|markup:strip|entitytruncate:1000}
            {/if}
            {assign var="keywordlist" value=$keywordlist|cat:", "}
	    {assign var="keywordlist" value=$keywordlist|cat:$child.summary|markup:strip|entitytruncate:1000}
         {/if}
       {/foreach}
    {/if}

    <meta name="description" content="{$theme.item.description|markup:strip|truncate:180:'...'|default:$theme.parent.title|markup:strip|default:$theme.item.pathComponent}" />
    <meta name="keywords" content="{$theme.item.keywords|markup:strip|default:$keywordlist}" />


The next step, once the <meta> tags are being generated in the Gallery2 HTML <head> section, is to write a new function in the WPG2 plugin to extract these tags from the Gallery2 page and insert them into the WordPress header. We will use this function in the WordPress header template when we want to write the Gallery2 meta tags. Note, this code isolates only the meta tags which have the ‘name=’ directive. This selects only the meta tags identified above and does not bring forward any other Gallery2 meta tags of different types that may duplicate existing WordPress tags. Edit the wpg2functions.inc file and add the following code:

wpg2functions.inc

/**
* Function Hook on WordPress Header
* WP action to add the Gallery2 meta tags to the WP header
*
* @param NULL
* @return NULL
*/
function g2_addmeta() {

	if (!defined('G2INIT')) {
		$ret = g2_login();
		if ($ret) {
			echo '<h2>' . __('Fatal G2 error', 'wpg2') . '</h2> ' . __("Here's the error from G2: ", 'wpg2') . $ret->getAsHtml();
			exit;
		}
	}

	global $g2data;

	if (defined('WPG2PAGE')) {
		// Merge Gallery2 HEAD Outputs Into WP Header 
		if (isset($g2data['headHtml'])) {
			list($g2_title, $g2_css, $g2_javascript, $g2_meta) = GalleryEmbed::parseHead($g2data['headHtml']);
			foreach ($g2_meta as $wpg2_meta) { 
				if (strpos("$wpg2_meta","name=")) { echo "$wpg2_meta rn"; }
			}
		}
	}
		
	// Add BTEV Event Message
	if (function_exists('btev_trigger_error')) {
		btev_trigger_error('WPG2 ADDED G2 HEADER TO WP HEADER', E_USER_NOTICE, __FILE__);
	}
}


Lastly, you need to find the section in your WordPress header template where meta tags are written. In my case I am using the Atahualpa theme and this theme has put some hooks into the header generation to call special functions. I updated bfa_meta_tags.php and inserted the call to our new function g2_addmeta() in the section where meta tags were generated for static and single post pages. Note, for this solution to work correctly you do not want to define meta tags or descriptions within your WordPress theme for the WPG2 page, otherwise you will generate both the theme meta tags and the Gallery2 meta tags.

functions/bfa_meta_tags.php


// META DESCRIPTION & KEYWORDS Tag for single post pages and static pages:
if ( is_single() OR is_page() ) {
	$bfa_meta_description = get_post_meta($post->ID, 'bfa_ata_meta_description', true);
	$bfa_meta_keywords = get_post_meta($post->ID, 'bfa_ata_meta_keywords', true);
	if ( $bfa_meta_description != '' ) {
		echo "<meta name="description" content="" .
    	htmlentities($bfa_meta_description,ENT_QUOTES,'UTF-8') . "" />n";
	}
	if ( $bfa_meta_keywords != '' ) {
		echo "<meta name="keywords" content="" .
    	htmlentities($bfa_meta_keywords,ENT_QUOTES,'UTF-8') . "" />n";
	}  
	// META DESCRIPTION Tag for WPG2 pages:
	g2_addmeta() ;
}

Atahualpa Post Navigation

Altahualpa ‘next’ and ‘previous’ post navigation entries are global across all site postings.  When we are looking at postings by category we would like to our ‘next’ and ‘previous’ navigation links to show only postings within the category.

Fortunately, the WordPress functions next_post_link and previous_post_link provide an optional parameter to restrict links to categories.

Note: This functionality is now configurable in Atahualpa version 3.6.4


functions/bfa_next_previous_links.php

Modify the identified lines in function bfa_next_previous_post_links to be as shown:

function bfa_next_previous_post_links($location = "Top") {

global $bfa_ata;

	if ( is_single() AND strpos($bfa_ata['location_single_next_prev'],$location) !== FALSE AND

    // don't display on WP Email pages
    intval(get_query_var('email')) != 1 )  {

		echo '<div class="navigation-'.strtolower($location).'">
		<div class="older' . ($bfa_ata['home_single_next_prev'] != '' ?
        '-home' : '') . '">';

		$bfa_ata['next_prev_orientation'] == 'Older Left, Newer Right' ?
		previous_post_link('« %link', '%title', TRUE) :
		next_post_link('%link »', '%title', TRUE);

		echo '  </div>' . ($bfa_ata['home_single_next_prev'] != '' ?
        '<div class="home"><a href="' . $bfa_ata['get_option_home'] . '/">' .
        $bfa_ata['home_single_next_prev'] . '</a></div>' : '') .
		'<div class="newer' . ($bfa_ata['home_single_next_prev'] != '' ?
        '-home' : '') . '">  ';

		$bfa_ata['next_prev_orientation'] == 'Older Left, Newer Right' ?
		next_post_link('%link »', '%title', TRUE) :
		previous_post_link('« %link', '%title', TRUE);

		echo '</div><div class="clearboth"></div></div>';

	}
}

Atahualpa Custom Excerpts

There is a problem with Atahualpa custom excerpts not being processed correctly in category lists or other places where the post excerpt is displayed.  Note, this problem exists in Atahualpa version 3.4.

If you have this problem you will see ellipsis […] and word truncation when you have specified custom excerpts.  When a custom excerpt is provided on the post then the excerpt should be displayed in its entirety, without truncation.


functions.php

Add the identified line to function bfa_wp_trim_excerpt($text) as shown:

// Custom Excerpts
function bfa_wp_trim_excerpt($text) { // Fakes an excerpt if needed

	global $bfa_ata;

	if ( '' == $text ) {
		$text = get_the_content('');
		$text = apply_filters('the_content', $text);
		$text = str_replace(']]>', ']]>', $text);
		$text = strip_tags($text, $bfa_ata['dont_strip_excerpts']);
		$excerpt_length = $bfa_ata['excerpt_length'];
		$words = explode(' ', $text, $excerpt_length + 1);
	} else {
		$words = explode(' ', $text);
		$excerpt_length = count($words);  		// bug fix
	}

	if (count($words) > $excerpt_length) {
		array_pop($words);
		$custom_read_more = str_replace('%permalink%', get_permalink(), $bfa_ata['custom_read_more']);
		$custom_read_more = str_replace('%title%', the_title('','',FALSE), $custom_read_more);
		array_push($words, $custom_read_more);
		$text = implode(' ', $words);
	}

	return $text;
}

Show Atahualpa Comment

WordPress themes, including Atahualpa, typically show only a text link in the post footer that the user must click to enter a comment.  This does not encourage immediate user comment entry after viewing the post.

The comment entry screen can be displayed after a post if the the WordPress comments-template is invoked in the Loop.  The comments-template file references a PHP variable $withcomments which if true will cause the comment form to be displayed.


index.php

Replace the Atahualpa index.php with the following:

<?php 	/* get all options: */
include (TEMPLATEPATH . '/functions/bfa_get_options.php');
get_header(); ?>

<?php $withcomments = 1 ;  /* Required for the comment entry screen */ ?>

<?php /* If there are any posts: */
if (have_posts()) : $bfa_ata['postcount'] == 0; /* Postcount needed for option "XX first posts full posts, rest excerpts" */ ?>

	<?php /* This outputs the next/previous post or page navigation.
	This can be edited at Atahualpa Theme Options -> Style & edit the Center column */
	bfa_center_content($bfa_ata['content_above_loop']); ?>

	<?php /* The LOOP starts here. Do this for all posts: */
	while (have_posts()) : the_post(); $bfa_ata['postcount']++; ?>

		<?php /* Add Odd or Even post class so post containers can get alternating CSS style (optional) */
		$odd_or_even = (($bfa_ata['postcount'] % 2) ? 'odd-post' : 'even-post' ); ?> 

		<?php /* This is the actual WordPress LOOP.
		The output can be edited at Atahualpa Theme Options -> Style & edit the Center column */
		bfa_center_content($bfa_ata['content_inside_loop']); ?>

                <?php /* Show the comment entry screen on blog posts */
		if (is_home()) comments_template(); ?>

	<?php /* END of the LOOP */
	endwhile; ?>

	<?php /* This outputs the next/previous post or page navigation and the comment template.
	This can be edited at Atahualpa Theme Options -> Style & edit the Center column */
	bfa_center_content($bfa_ata['content_below_loop']); ?>

<?php /* END of: If there are any posts */
else : /* If there are no posts: */ ?>

<?php /* This outputs the "Not Found" content, if neither posts, pages nor attachments are available for the requested page.
This can be edited at Atahualpa Theme Options -> Style & edit the Center column */
bfa_center_content($bfa_ata['content_not_found']); ?>

<?php endif; /* END of: If there are no posts */ ?>

<?php bfa_center_content($bfa_ata['center_content_bottom']); ?>

<?php get_footer(); ?>


The comment form is shown for full posts and for excerpts on the home page. If the comment form should not show when posts are excerpts then the function bfa_post_bodycopy() in bfa_post_parts.php must be modified.

bfa_post_parts.php

function bfa_post_bodycopy($before = '<div class="post-bodycopy clearfix">', $after = '</div>') {

	global $bfa_ata, $post, $withcomments;

	echo $before;
	if ( (is_home() AND $bfa_ata['excerpts_home'] == "Full Posts") OR
	(is_category() AND $bfa_ata['excerpts_category'] == "Full Posts") OR
	(is_date() AND $bfa_ata['excerpts_archive'] == "Full Posts") OR
	(is_tag() AND $bfa_ata['excerpts_tag'] == "Full Posts") OR
	(is_search() AND $bfa_ata['excerpts_search'] == "Full Posts") OR
	(is_author() AND $bfa_ata['excerpts_author'] == "Full Posts") OR
	is_single() OR is_page() OR
	(is_home() AND !is_paged() AND $bfa_ata['postcount'] <= $bfa_ata['full_posts_homepage']) ) {
		$withcomments = 1 ;
		$bfa_ata_more_tag_final = str_replace("%post-title%", the_title('', '', false), $bfa_ata['more_tag']);
		the_content($bfa_ata_more_tag_final);
	} else {
		$withcomments = 0 ;
		the_excerpt();
	}
	echo $after;
}


As of the time of this post, a typing error in the function bfa_post_headline() in bfa_post_parts.php should be corrected.  This error will lead to incorrect post title display.

function bfa_post_headline($before = '<div class="post-headline">', $after = '</div>') {

	global $bfa_ata, $post;

	if ( is_single() OR is_page() ) {
		$bfa_ata_body_title = get_post_meta($post->ID, 'bfa_ata_body_title', true);
		$bfa_ata_display_body_title = get_post_meta($post->ID, 'bfa_ata_display_body_title', true);
	} else {
		$bfa_ata_body_title_multi = get_post_meta($post->ID, 'bfa_ata_body_title_multi', true);
	}

	if ( (!is_single() AND !is_page()) OR $bfa_ata_display_body_title == '' ) {

		echo $before; ?>
		<h<?php echo $bfa_ata['h_posttitle']; ?>><?php 

		if( !is_single() AND !is_page() ) { ?>

			<a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title(); ?>"><?php
		} 

		if ( (is_single() OR is_page()) AND $bfa_ata_body_title != "" ) {
			echo htmlentities($bfa_ata_body_title,ENT_QUOTES,'UTF-8');
		} else {
			if ( $bfa_ata_body_title_multi != '' ) echo htmlentities($bfa_ata_body_title_multi,ENT_QUOTES,'UTF-8');
			else the_title();
		}

		if ( !is_single() AND !is_page() ) { ?></a><?php } ?></h<?php echo $bfa_ata['h_posttitle']; ?>>
		<?php echo $after;
	}
}