Difference between revisions of "JQuery HTML Updater"

From Code4Lib
Jump to: navigation, search
(before_update(html, count, target_obj))
 
(36 intermediate revisions by 3 users not shown)
Line 1: Line 1:
If you want to include [[Umlaut]]-generated HTML directly on a third party page via javascript, there is a Javascript helper object to make that very easy. This helper uses the [[Umlaut partial html API]], but does everything for you.  The helper will update your divs, and keep polling Umlaut for new results, continuing to re-update your divs until Umlaut is finished.   
+
[[Category:Umlaut]]
 +
 
 +
=WARNING: This is Outdated Documentation!!!!=
 +
 
 +
'''THIS IS OUTDATED DOCUMENTATION''' See new Umlaut documentation at http://github.com/team-umlaut/umlaut/wiki
 +
---------
 +
 
 +
If you want to include [[Umlaut]]-generated HTML directly on a third party page via javascript, there is a Javascript helper object to make that very easy. This helper uses the [[Umlaut partial html API]], but does everything for you.  The helper will update your page at DOM locations you specify, and keep polling Umlaut for new results, continuing to re-update your page until Umlaut is finished.   
  
 
The Javascript helper object relies on JQuery.
 
The Javascript helper object relies on JQuery.
  
This Javascript helper is actually also used internally by Umlaut to load background results as they come in.
+
This Javascript helper is actually also used internally by Umlaut to load background results as they come in, on the Umlaut resolver menu page.
 +
 
 +
<b>NOTE</b>: The intended use case at the moment is a "single item" or "item detail" page. You may think "Gee, I want to embed umlaut content for every item on a 10-item results page!"  Handling this case isn't fully fleshed out yet, in part becuase it might put unreasonable load on an Umlaut server. But I have it in mind, and we may be able to get there, let jrochkind know if you demand it.  
  
 
== Requirements ==
 
== Requirements ==
  
You need to have the ability to add custom javascript to the page you'd like to Embed Umlaut content in. You also need to be able to somehow make an OpenURL Context Object representing the thing you'd like to load Umlaut content for, and pass this to the Javascript.  
+
You need to have the ability to add html <script> tags referencing external js files to the page you'd like to Embed Umlaut content in. You also need to be able to somehow make an OpenURL Context Object representing the thing you'd like to load Umlaut content for, and pass this to the Javascript.  If an OpenURL link is already on your HTML somewhere, Javascript can easily pull the 'context object' out of this (see example).  
  
You need to be willing to load the JQuery library on your page, although it can be loaded in "no conflict" mode if you also need Prototype or another JS library.  
+
You need to be willing to load the JQuery library on your page, although it can be loaded in "no conflict" mode if you also need Prototype or another JS library. jQuery 1.4 is highly recommended, although jQuery 1.3 will likely work too if you already depend on that in your application.
  
 
== Step by Step ==
 
== Step by Step ==
Line 17: Line 26:
 
=== Load JQuery ===
 
=== Load JQuery ===
  
You can load JQuery from your installed Umlaut if you want. It's probably best to load it in "no conflict" mode:
+
You can load JQuery from your installed Umlaut if you want. This is not neccesary if your app already includes JQuery. It's probably best to load it in "no conflict" mode:
  
 
<code>
 
<code>
Line 32: Line 41:
 
</code>
 
</code>
  
=== Optionally, load Umlaut Javascript Behaviors ===
+
=== Optionally, load Umlaut Javascript UI Behaviors ===
  
 
HTML loaded in by the HtmlUpdater may include javascript behaviors when displayed in Umlaut, such as expand/contract toggles.  You can choose to load JS files providing those behaviors into your local app. If you don't, certain links will degrade to linking out to Umlaut, but everything should still work fine.  
 
HTML loaded in by the HtmlUpdater may include javascript behaviors when displayed in Umlaut, such as expand/contract toggles.  You can choose to load JS files providing those behaviors into your local app. If you don't, certain links will degrade to linking out to Umlaut, but everything should still work fine.  
  
Currently, expand/contract toggles are actually the only js behavior supported on external pages; dialog boxes will neccesarily degrade to links out to your Umlaut app.  
+
Currently, expand/contract toggles are actually the only js behavior supported on external pages; dialog boxes will necessarily degrade to links out to your Umlaut app.  
  
 
<code>
 
<code>
 
   <script type="text/javascript" src="http://app01.mse.jhu.edu:3000/js_helper/loader"></script>
 
   <script type="text/javascript" src="http://app01.mse.jhu.edu:3000/js_helper/loader"></script>
 
   <script type="text/javascript">
 
   <script type="text/javascript">
     jQuery(document).ready(function($) {
+
     jQuery(function($) {
 
       var loader = new Umlaut.Loader();
 
       var loader = new Umlaut.Loader();
 
       loader.load();
 
       loader.load();
Line 50: Line 59:
 
=== Instantiate an HtmlUpdater ===
 
=== Instantiate an HtmlUpdater ===
  
You need to instantiate an Umlaut.HtmlUpdater, and tell it your Umlaut base url, and a context object key-encoded-value query parameter string. If you already have an OpenURL link on the page, it may be convenient to pull the context object out of that, see example below.  
+
You need to instantiate an Umlaut.HtmlUpdater, and tell it your Umlaut base url, and a context object key-encoded-value query parameter string. If you already have an OpenURL link on the page, it may be convenient to pull the context object out of that, see [[#Complete Example|example]] below.  
  
 
<code>
 
<code>
Line 65: Line 74:
  
 
<code>
 
<code>
   updater.add_section_target({ umlaut_section_id: "fulltext", selector:"#my_full_text_div" });
+
   updater.add_section_target({  
 +
    umlaut_section_id: "fulltext",  
 +
    selector:"#my_full_text_div"
 +
  });
 
</code>
 
</code>
  
Line 73: Line 85:
  
 
<code>
 
<code>
   updater.add_section_target({ umlaut_section_id: "highlighted_links", selector: ".sidebar", position: "append"});
+
   updater.add_section_target({  
 +
    umlaut_section_id: "highlighted_links",  
 +
    selector: ".sidebar",  
 +
    position: "append"
 +
  });
 
</code>
 
</code>
  
 
+
=== Call the updater ===
 
+
== Call the updater ==
+
  
 
<code>
 
<code>
Line 91: Line 105:
 
When configuring the HtmlUpdate object, there are several callbacks you can supply functions to, for your own code to be called.  
 
When configuring the HtmlUpdate object, there are several callbacks you can supply functions to, for your own code to be called.  
  
== Section Target callbacks ==
+
=== Section Target callbacks ===
  
Additonally, you can specify some javascript callback functions to add_section_target, if desired. These can be useful for modifying the HTML returned by Umlaut before it's placed on your page, or modifying other parts of your page upon receiving content.  
+
Callback functions provided with add_section_target can be useful for modifying the HTML returned by Umlaut before it's placed on your page, or modifying other parts of your page upon receiving content.  
  
=== before_update(html, count, target_obj) ===
+
 
 +
==== before_update(html, count, target_obj) ====
  
 
Called before the Umlaut html is actually on the page, you can use it to modify the HTML before it gets added to the page. "count" is a JS integer count of how many items are included in the section; sometimes you may wish to hide the whole section from the page if there are 0 items; returning "false" from before_update will cause the HtmlUpdater to *not* place the block on the page.  
 
Called before the Umlaut html is actually on the page, you can use it to modify the HTML before it gets added to the page. "count" is a JS integer count of how many items are included in the section; sometimes you may wish to hide the whole section from the page if there are 0 items; returning "false" from before_update will cause the HtmlUpdater to *not* place the block on the page.  
Line 101: Line 116:
 
The "target_obj" is an HtmlUpdate internal object that's probably not too useful unless you want to look at the source, but can be used for some complex things.  
 
The "target_obj" is an HtmlUpdate internal object that's probably not too useful unless you want to look at the source, but can be used for some complex things.  
  
<code>
+
<pre>
   updater.add_section_target({ umlaut_section_id: "highlighted_links", selector: ".sidebar", position: "append", before_update: function(html, count) {
+
   updater.add_section_target({  
 +
    umlaut_section_id: "highlighted_links",  
 +
    selector: ".sidebar",  
 +
    position: "append",  
 +
    before_update: function(html, count) {
 
     $(html).find(".section_heading h3").hide();
 
     $(html).find(".section_heading h3").hide();
  
 
     return (count > 0);
 
     return (count > 0);
  }
+
    }
 
   });
 
   });
</code>
+
</pre>
  
=== after_update(html, count, target_obj) ===
+
==== after_update(html, count, target_obj) ====
  
Similar to before_update, but called after the html has been placed in the DOM on the page. Not sure what this is useful for, but in after_update you can call JQuery closest() on the html to look up in the DOM if you want. Whereas that's not available in before_update since the html has not yet been placed on the page.
+
Similar to before_update, but called after the html has been placed in the DOM on the page. In after_update (unlike before_update) you can call JQuery closest() on the html to look up in the DOM if you want. This could be used for instance to show a previously hidden parent element only if there are umlaut items available:
  
 
<code>
 
<code>
 
   updater.add_section_target({umlaut_section_id: "fulltext", selector:"#my_fulltext",  
 
   updater.add_section_target({umlaut_section_id: "fulltext", selector:"#my_fulltext",  
 
             after_update: function(html, count) {
 
             after_update: function(html, count) {
               $(html).closest("div.something").something...
+
               if (count !=0 ) {
 +
                $(html).closest("div.something").show();
 +
              }
 
             }
 
             }
 
   });
 
   });
 
</code>
 
</code>
  
=== complete(target_obj) ===
+
==== complete(target_obj) ====
  
 
Complete on a given section is called after there are no more updates for that section. Currently, the HtmlUpdate isn't smart enough to know that until the entire Umlaut update is done however, so it'll be called at the same time as complete() on the updater as a whole. This may change in the future.  
 
Complete on a given section is called after there are no more updates for that section. Currently, the HtmlUpdate isn't smart enough to know that until the entire Umlaut update is done however, so it'll be called at the same time as complete() on the updater as a whole. This may change in the future.  
Line 131: Line 152:
 
</code>
 
</code>
  
== HtmlUpdate callbacks ==
+
=== HtmlUpdate callbacks ===
  
 
Currently just one, for when the Umlaut update is complete (all content has been fetched).  
 
Currently just one, for when the Umlaut update is complete (all content has been fetched).  
  
=== complete(updater_obj) ===
+
==== complete(updater_obj) ====
updater.complete = function() {
+
  updater.complete = function() {
  alert("all content has been fetched!");
+
    alert("all content has been fetched!");
}
+
  }
  
 
== Complete Example ==
 
== Complete Example ==
 +
For clarity, this example has some javascript source inline in a <script> tag, the code that specifies how content is to be loaded. That javascript code could of course instead be in an external js file referenced by a <script src=>.
 +
 +
<pre>
 +
<html>
 +
 +
<head>
 +
 
 +
  <script type="text/javascript" src="$UMLAUT_BASE/javascripts/jquery/jquery-1.4.2.min.js"></script>
 +
  <script type="text/javascript">
 +
    jQuery.noConflict();
 +
  </script>
 +
 
 +
  <script type="text/javascript" src="$UMLAUT_BASE/javascripts/jquery/umlaut/update_html.js"></script>
 +
 
 +
  <script type="text/javascript" src="$UMLAUT_BASE/js_helper/loader"></script>
 +
 +
<script type="text/javascript">
 +
 
 +
jQuery(function($) {
 +
  var loader = new Umlaut.Loader();
 +
  loader.load();
 +
 
 +
  /* pull openurl link out of the DOM, take umlaut base and context object
 +
    out of it */
 +
  var openurl_link = $("a.openurl_link").attr("href");
 +
  var ctx_object_kev = openurl_link.substring( openurl_link.indexOf("?") + 1);
 +
  var umlaut_base = openurl_link.substring(0, openurl_link.indexOf("/resolve"));
 +
  var updater = new Umlaut.HtmlUpdater( umlaut_base  ,  ctx_object_kev );
 +
 
 +
  updater.add_section_target({
 +
    umlaut_section_id: "fulltext",
 +
    selector:".my_full_text" ,
 +
    // only show ourself if we have umlaut hits, false return from
 +
    // before_update stops update. 
 +
    before_update: function(html, count) { return (count != 0); }
 +
  }); 
 +
 
 +
 
 +
  updater.add_section_target({
 +
    umlaut_section_id: "search_inside",
 +
    selector:".stuff",
 +
    position: "append" 
 +
  }); 
 +
 
 +
  updater.add_section_target({
 +
    umlaut_section_id: "excerpts",
 +
    selector:".stuff",
 +
    position: "append"
 +
  }); 
 +
 
 +
  updater.add_section_target({
 +
    umlaut_section_id: "holding",
 +
    selector:".stuff",
 +
    position: "append",
 +
    before_update: function(container_element) {
 +
      //insert some content into the umlaut-delivered html
 +
      //before it is placed on the page
 +
      container_element.find("h3").after("<h4>We love these holdings.</h4>");
 +
    }
 +
  }); 
 +
 
 +
  updater.add_section_target({
 +
      umlaut_section_id: "abstract",
 +
      selector:".stuff",
 +
      position: "append", 
 +
      before_update: function(html, count) {
 +
        //only show if there are hits
 +
        return ( count != 0);
 +
      }
 +
  });   
 +
 
 +
  updater.add_section_target({
 +
    umlaut_section_id: "related_items",
 +
    selector:".stuff",
 +
    position: "append"
 +
  }); 
 +
 
 +
  updater.add_section_target({
 +
    umlaut_section_id: "highlighted_link",
 +
    selector:".only_if_content",
 +
    after_update: function(updated_content, count) {
 +
      //show the ancestor DOM element with certain class, only
 +
      //if there are umlaut hits
 +
      if ( count != 0 ) {
 +
        updated_content.closest(".only_if_content").show();
 +
      }
 +
    }
 +
    }); 
 +
 
 +
    updater.add_section_target({
 +
      umlaut_section_id: "export_citation",
 +
      selector:".stuff",
 +
      position:"append",
 +
      before_update: function(content, count) {
 +
        $(content).find(".section_heading h3").text("My own crazy export options");
 +
      }
 +
    });
 +
   
 +
    updater.complete = function() { alert("Umlaut fully loaded") };
 +
 
 +
    updater.update(); 
 +
 +
  });
 +
</script>
 +
 +
</head>
 +
 +
<body>
 +
 +
<a class="openurl_link" href="http://$UMLAUT_BASE/resolve?sid=google&auinit=N&aulast=Chomsky&title=Aspects+of+the+Theory+of+Syntax&genre=book&isbn=0262530074&date=1965">Find It @ JH</a>
 +
 +
<h2>Full Text</h2>
 +
<div class="my_full_text">
 +
Replace me.
 +
</div>
 +
 +
<h2>More Stuff</h2>
 +
<div class="stuff">
 +
</div>
 +
 +
<div class="only_if_content" style="display:none">
 +
This will be shown only if it actually contains content.
 +
</div>
 +
 +
 +
</body>
 +
 +
 +
</html>
 +
</pre>

Latest revision as of 16:18, 19 June 2012


WARNING: This is Outdated Documentation!!!!

THIS IS OUTDATED DOCUMENTATION See new Umlaut documentation at http://github.com/team-umlaut/umlaut/wiki


If you want to include Umlaut-generated HTML directly on a third party page via javascript, there is a Javascript helper object to make that very easy. This helper uses the Umlaut partial html API, but does everything for you. The helper will update your page at DOM locations you specify, and keep polling Umlaut for new results, continuing to re-update your page until Umlaut is finished.

The Javascript helper object relies on JQuery.

This Javascript helper is actually also used internally by Umlaut to load background results as they come in, on the Umlaut resolver menu page.

NOTE: The intended use case at the moment is a "single item" or "item detail" page. You may think "Gee, I want to embed umlaut content for every item on a 10-item results page!" Handling this case isn't fully fleshed out yet, in part becuase it might put unreasonable load on an Umlaut server. But I have it in mind, and we may be able to get there, let jrochkind know if you demand it.

Requirements

You need to have the ability to add html <script> tags referencing external js files to the page you'd like to Embed Umlaut content in. You also need to be able to somehow make an OpenURL Context Object representing the thing you'd like to load Umlaut content for, and pass this to the Javascript. If an OpenURL link is already on your HTML somewhere, Javascript can easily pull the 'context object' out of this (see example).

You need to be willing to load the JQuery library on your page, although it can be loaded in "no conflict" mode if you also need Prototype or another JS library. jQuery 1.4 is highly recommended, although jQuery 1.3 will likely work too if you already depend on that in your application.

Step by Step

In the following examples, $UMLAUT_BASE stands for the base URL you have installed Umlaut at, for instance http://findit.library.jhu.edu for JHU.

Load JQuery

You can load JQuery from your installed Umlaut if you want. This is not neccesary if your app already includes JQuery. It's probably best to load it in "no conflict" mode:

 <script type="text/javascript" src="$UMLAUT_BASE/javascripts/jquery/jquery-1.4.2.min.js"></script>
 <script type="text/javascript">
     jQuery.noConflict();
 </script>

Load the Umlaut.HtmlUpdater object

 <script type="text/javascript" src="$UMLAUT_BASEjavascripts/jquery/umlaut/update_html.js"></script>

Optionally, load Umlaut Javascript UI Behaviors

HTML loaded in by the HtmlUpdater may include javascript behaviors when displayed in Umlaut, such as expand/contract toggles. You can choose to load JS files providing those behaviors into your local app. If you don't, certain links will degrade to linking out to Umlaut, but everything should still work fine.

Currently, expand/contract toggles are actually the only js behavior supported on external pages; dialog boxes will necessarily degrade to links out to your Umlaut app.

 <script type="text/javascript" src="http://app01.mse.jhu.edu:3000/js_helper/loader"></script>
 <script type="text/javascript">
   jQuery(function($) {
     var loader = new Umlaut.Loader();
     loader.load();
   });
 </script>

Instantiate an HtmlUpdater

You need to instantiate an Umlaut.HtmlUpdater, and tell it your Umlaut base url, and a context object key-encoded-value query parameter string. If you already have an OpenURL link on the page, it may be convenient to pull the context object out of that, see example below.

 //inside a jQuery ready()
 var ctx = "sid=google&auinit=N&aulast=Chomsky&title=Aspects+of+the+Theory+of+Syntax&genre=book&isbn=0262530074&date=1965"
 var updater = new Umlaut.HtmlUpdater($UMLAUT_BASE,  ctx  );

Configure HtmlUpdater

You use the add_section_target() method on your updater to tell it which blocks of Umlaut content you'd like to put where on your page. The argument to add_section_target() is a JS object containing options.

In the simplest case, you can simply supply an umlaut section in the "umlaut_section_id" option, and the ID of a div you'd like to place the content in on your page in "selector":

 updater.add_section_target({ 
    umlaut_section_id: "fulltext", 
    selector:"#my_full_text_div"
 });

The exact umlaut sections available may depend on your Umlaut configuration. To see the sections in a default Umlaut installation, see: AppConfig::Base.bg_update_map in resolve_views in svn

The selector argument can actually be any JQuery selector. By default, the first item on the page that matches this selector will have it's content entirely replaced by the specified umlaut section. However, you can also supply a "position" argument of "before", "after", "append", or "prepend", and the umlaut section will instead be inserted before or after the element matching your selector, or prepended or appended to the element's children.

 updater.add_section_target({ 
   umlaut_section_id: "highlighted_links", 
   selector: ".sidebar", 
   position: "append"
 });

Call the updater

 //in a JQuery ready block
 updater.update();

The updater will now make a call to Umlaut, grab the content from Umlaut, and do what you've told it to do with it. Additionally, it will keep polling Umlaut until all content is available. How often it polls is configured by application config 'poll_wait_seconds', which defaults to 4 seconds.

Callbacks

When configuring the HtmlUpdate object, there are several callbacks you can supply functions to, for your own code to be called.

Section Target callbacks

Callback functions provided with add_section_target can be useful for modifying the HTML returned by Umlaut before it's placed on your page, or modifying other parts of your page upon receiving content.


before_update(html, count, target_obj)

Called before the Umlaut html is actually on the page, you can use it to modify the HTML before it gets added to the page. "count" is a JS integer count of how many items are included in the section; sometimes you may wish to hide the whole section from the page if there are 0 items; returning "false" from before_update will cause the HtmlUpdater to *not* place the block on the page.

The "target_obj" is an HtmlUpdate internal object that's probably not too useful unless you want to look at the source, but can be used for some complex things.

  updater.add_section_target({ 
    umlaut_section_id: "highlighted_links", 
    selector: ".sidebar", 
    position: "append", 
    before_update: function(html, count) {
     $(html).find(".section_heading h3").hide();

     return (count > 0);
    }
  });

after_update(html, count, target_obj)

Similar to before_update, but called after the html has been placed in the DOM on the page. In after_update (unlike before_update) you can call JQuery closest() on the html to look up in the DOM if you want. This could be used for instance to show a previously hidden parent element only if there are umlaut items available:

  updater.add_section_target({umlaut_section_id: "fulltext", selector:"#my_fulltext", 
           after_update: function(html, count) {
              if (count !=0 ) {
                $(html).closest("div.something").show();
              }
           }
  });

complete(target_obj)

Complete on a given section is called after there are no more updates for that section. Currently, the HtmlUpdate isn't smart enough to know that until the entire Umlaut update is done however, so it'll be called at the same time as complete() on the updater as a whole. This may change in the future.

  updater.add_section_target({umlaut_section_id: "fulltext", selector:"#my_fulltext", 
                              complete: function() { alert("fulltext done!") }});

HtmlUpdate callbacks

Currently just one, for when the Umlaut update is complete (all content has been fetched).

complete(updater_obj)

 updater.complete = function() {
   alert("all content has been fetched!");
 }

Complete Example

For clarity, this example has some javascript source inline in a <script> tag, the code that specifies how content is to be loaded. That javascript code could of course instead be in an external js file referenced by a <script src=>.

<html>

<head>
  
  <script type="text/javascript" src="$UMLAUT_BASE/javascripts/jquery/jquery-1.4.2.min.js"></script>
  <script type="text/javascript">
     jQuery.noConflict();
  </script>
  
  <script type="text/javascript" src="$UMLAUT_BASE/javascripts/jquery/umlaut/update_html.js"></script>
  
  <script type="text/javascript" src="$UMLAUT_BASE/js_helper/loader"></script>

<script type="text/javascript">
  
jQuery(function($) {
  var loader = new Umlaut.Loader();
  loader.load();
  
  /* pull openurl link out of the DOM, take umlaut base and context object
     out of it */ 
  var openurl_link = $("a.openurl_link").attr("href");
  var ctx_object_kev = openurl_link.substring( openurl_link.indexOf("?") + 1);
  var umlaut_base = openurl_link.substring(0, openurl_link.indexOf("/resolve"));
  var updater = new Umlaut.HtmlUpdater( umlaut_base  ,  ctx_object_kev );
  
  updater.add_section_target({ 
    umlaut_section_id: "fulltext", 
    selector:".my_full_text" ,
    // only show ourself if we have umlaut hits, false return from
    // before_update stops update.  
    before_update: function(html, count) { return (count != 0); }
  });   
  
  
  updater.add_section_target({ 
    umlaut_section_id: "search_inside", 
    selector:".stuff", 
    position: "append"  
  });  
  
  updater.add_section_target({ 
    umlaut_section_id: "excerpts", 
    selector:".stuff", 
    position: "append"
  });   
  
  updater.add_section_target({ 
    umlaut_section_id: "holding", 
    selector:".stuff", 
    position: "append",
    before_update: function(container_element) {
      //insert some content into the umlaut-delivered html
      //before it is placed on the page
      container_element.find("h3").after("<h4>We love these holdings.</h4>");
    }
  });   
  
  updater.add_section_target({
      umlaut_section_id: "abstract", 
      selector:".stuff", 
      position: "append",  
      before_update: function(html, count) {
        //only show if there are hits
        return ( count != 0);
      }
   });     
  
  updater.add_section_target({ 
    umlaut_section_id: "related_items", 
    selector:".stuff", 
    position: "append"
  });   
  
  updater.add_section_target({ 
    umlaut_section_id: "highlighted_link", 
    selector:".only_if_content",
    after_update: function(updated_content, count) {
      //show the ancestor DOM element with certain class, only
      //if there are umlaut hits
      if ( count != 0 ) {
        updated_content.closest(".only_if_content").show();
      }
    }
    });   
  
    updater.add_section_target({
      umlaut_section_id: "export_citation",
      selector:".stuff",
      position:"append",
      before_update: function(content, count) {
        $(content).find(".section_heading h3").text("My own crazy export options");
      }
    });
    
    updater.complete = function() { alert("Umlaut fully loaded") };
  
    updater.update();   

  });
</script>

</head>

<body>

<a class="openurl_link" href="http://$UMLAUT_BASE/resolve?sid=google&auinit=N&aulast=Chomsky&title=Aspects+of+the+Theory+of+Syntax&genre=book&isbn=0262530074&date=1965">Find It @ JH</a>

<h2>Full Text</h2>
<div class="my_full_text">
Replace me.
</div>

<h2>More Stuff</h2>
<div class="stuff">
</div>

<div class="only_if_content" style="display:none">
This will be shown only if it actually contains content. 
</div>


</body>


</html>