[gegl] docs: add gir parsing capabilities do js doc viewer



commit e00851c07f6df364be622cf783d2273712b5abc8
Author: Øyvind Kolås <pippin gimp org>
Date:   Tue Jun 10 22:28:50 2014 +0200

    docs: add gir parsing capabilities do js doc viewer

 docs/operations.html | 2263 ++++++++++++++++++++++++++++++++++++++++++++++++--
 docs/showdown.js     | 1296 +++++++++++++++++++++++++++++
 docs/xml2json.js     |  155 ++++
 3 files changed, 3640 insertions(+), 74 deletions(-)
---
diff --git a/docs/operations.html b/docs/operations.html
index a6385f4..2beec61 100644
--- a/docs/operations.html
+++ b/docs/operations.html
@@ -1,5 +1,7 @@
 <!--
 
+// G_DIR_SEPARATOR_S is broken in GLib-.gir,. a \ in xmlattr
+
 /* This js doc browser is part of GEGL
  *
  * GEGL is free software; you can redistribute it and/or
@@ -16,8 +18,8 @@
  * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
  *
  * 2014 Øyvind Kolås.
+ *
  */
-
 -->
 
 <html>
@@ -26,63 +28,110 @@
   <style>
     body { font-size: 1.0em; font-family: Sans; margin:0; }
     #query { border:0;padding:0;margin:0; background: rgba(250,250,250,0.8); width: 100%;
-      position: fixed; top:0;left:0; z-index: 1;
+      position: fixed; top:0;left:0; z-index: 1; padding-bottom: 0.2em;
+      padding-top: 0.2em;
       font-size: 1.5em;
       padding-left: 0.75em;
     }
-    #view { padding-top: 4em; margin-left: 1em; margin-right: 1em; }
+    #debug { bottom: 0em; position: fixed; }
+    #status { bottom: 0em; right: 0em; background: white; position: fixed;
+       color: #666}
+    #view { padding-top: 4em; margin-left: auto; margin-right: auto;
+       max-width: 55em; padding-left: 1ex; padding-right: 1ex;
+       padding-bottom: 4em;}
+    .arguments {display: inline-block; vertical-align: top; padding:0;
+         position: relative; top: -0.1em; left: -0.1em; margin:0;border:0; }
+    .argtype { padding-right: 1em;}
+    .argname {font-style: italic; }
+    a { text-decoration: none; }
+    a:hover { text-decoration: underline; }
+    dt { font-style: italic; }
+    .declaration { font-weight: bold; }
     .categories {
-      font-size: 0.8em;
-      position: fixed;
-      width: auto;
-      top: 0; right: 0;
-      padding-top: 0.25em;
+      font-size:     0.8em;
+      position:      fixed;
+      width:         auto;
+      top:           0;
+      right:         0;
+      padding-top:   0.25em;
       padding-right: 0.25em;
-      z-index: 2;
-     }
-     .category { 
-       display: inline-block;
-       padding-left: 0.25em;
-       padding-right: 0.25em;
-       color: blue;
-       cursor: hand;
-     }
-
-     .view-source { 
-       display: inline-block;
-       padding-left: 0.25em;
-       padding-right: 0.25em;
-       color: blue;
-       cursor: hand;
-     }
-     .category:hover { background: black; color: white; }
-
-     .res_category { 
-       display: inline-block;
-       padding-left: 0.25em;
-       padding-right: 0.25em;
-       color: blue;
-       cursor: hand;
-     }
-     .res_category:hover { background: black; color: white; }
-
+      z-index:       2;
+    }
+    .field { }
+    .field_type { padding-left: 2em; padding-right: 1em; }
+    .field_name { }
+    .linklist {
+       -moz-column-width: 16em; -webkit-column-width: 16em; column-width:
+       16em; margin-top: 1em;
+    }
+    .linklist a { display: block; }
+    dl { }
+    dt {
+      /* display: inline-block;
+      min-width: 6em;
+      float : left;
+      clear: left; 
+      width: 7.5em;
+      */
+    }
+    dd {
+      /* display: inline-block;
+      float: left; */
+      margin:0; padding: 0; padding-left: 1em;
+      /*margin-top: -1.2em;*/
+    }
+    .ancestry {margin-bottom: 1em;}
+    .constructor, .function, .method {
+      border:        0px solid green;
+      margin-top:    0.5em;
+      margin-bottom: 0.5em;
+      padding:       0.5em;
+      margin-left:  -0.5em;
+      margin-right: -0.5em;
+    }
+    .link, .view-source { cursor: pointer; color:blue }
+    .category { 
+      display:       inline-block;
+      padding-left:  0.25em;
+      padding-right: 0.25em;
+      color:         blue;
+      cursor:        pointer;
+    }
+    .view-source { 
+      display: inline-block;
+      padding-left: 0.25em;
+      padding-right: 0.25em;
+      color: blue;
+    }
+    .category:hover, .link:hover { background: black; color: white; }
+    .res_category { 
+      display: inline-block;
+      padding-left: 0.25em;
+      padding-right: 0.25em;
+      color: blue;
+      cursor: pointer;
+    }
+    .res_category:hover { background: black; color: white; }
     .op-image { margin: 1em; margin-top: 0em; margin-left: 0em; float: left }
     .properties {margin-top: 1em; clear: left; }
-    .property { margin-top: 1em; margin-right: 2em; clear: both;
+    .property { margin-top: 1.5em; margin-right: 2em; clear: both;
        padding-top: 1em;
     }
+    .link {padding:0; margin:0; }
     .property>div { }
-
     .property>.property-label {
         display: block;
         float: none;
         font-size: 1.4em;
         min-width: 10em;
       }
-
+    h2 { font-weight:normal; font-size: 2em; }
+    h3 { font-weight:normal; font-size: 1.5em; }
+    h4 { font-weight:normal; font-size: 1.3em; font-weight: bold; }
+    h5 { font-weight:normal; font-size: 1.0em; font-weight: bold; }
     .key {
       -webkit-opacity: 0.5; -moz-opacity: 0.5; opacity: 0.5 ;
-      font-size: 60%;
+      font-size: 70%;
       min-width: 7em;
       display: inline-block;
       vertical-align: text-bottom;
@@ -96,25 +145,31 @@
       float :left;
       padding-right: 1em;
     }
-
     .property-description {
       clear: left;
       padding-top: 0.5em;
     }
-
+    .property>div.property-meta .key {
+      min-width: 8em;
+      max-width: 8em;
+    }
+    .property>div.property-meta .value {
+      min-width: 4em;
+      max-width: 4em;
+    }
     .property>div.property-meta {
       float: right;
-      min-width: 11em;
+      max-width: 24em;
+      min-width: 21em;
+      min-height: 2em;
       padding-left: 1em;
       clear: right;
+      -webkit-column-width: 7em;
+      -moz-column-width: 7em;
+      column-width: 7em;
     }
-
     .opname  { font-weight: bold; margin-bottom: 0.2em; display: none; }
-    .optitle { margin-bottom: 0.2em; 
-    
-               font-size: 1.4em;
-    }
-
+    .optitle { margin-bottom: 0.2em; font-size: 1.4em; }
     .description { margin-bottom: 1em;}
     .result       { border: 2px solid transparent; }
     .result:hover { border: 2px solid black; background : #555; color :
@@ -140,11 +195,82 @@
     .result.text>div { position:relative; top: 0.4em;}
   </style>
 
-  <script src='operations.json'>
-  </script>
+  
+  <script type="text/javascript" src="showdown.js"></script>
+  <script type="text/javascript" src="xml2json.js"></script>
+
+  <script src='operations.json'></script>
 
   <script>
 
+  var showdown = new Showdown.converter();
+
+    var gir_paths = [
+       "GObject-2.0.gir", 
+       "Gegl-0.3.gir",
+       "Gtk-3.0.gir",
+       "GLib-2.0.gir"]
+    if(false)gir_paths = [
+       "GObject-2.0.gir", 
+       "Gegl-0.3.gir",
+       "Gtk-3.0.gir",
+       "Gdk-2.0.gir",
+       "GLib-2.0.gir",
+       "Gtk-3.0.gir",
+       "Gio-2.0.gir",
+       "Atk-1.0.gir",
+       "Gdk-2.0.gir",
+       "Polkit-1.0.gir",
+       "Pango-1.0.gir",
+       "PangoCairo-1.0.gir"]
+
+var index = [];
+var gir_max_results = 500;
+
+var core_types =
+[
+   {'name':'gint'
+   ,'doc':'Corresponds to the standard C int type. Values of this type can range from G_MININT to G_MAXINT.' 
},
+  ,{'name':'guint'
+   ,'doc':'Corresponds to the standard C unsigned int type. Values of this type can range from G_MININT to 
G_MAXUINT.' }
+  ,{'name':'void'
+   ,'doc':'nothing'}
+  ,{'name':'gboolean'
+   ,'doc':'A standard boolean type. Variables of this type should only contain the value TRUE or FALSE.'}
+  ,{'name':'gpointer'
+   ,'doc':'An untyped pointer. gpointer looks better and is easier to use than void*.'}
+  ,{'name':'gconstpointer'
+   ,'doc':'An untyped pointer to constant data. The data pointed to should not be changed.\n\n This is 
typically used in function prototypes to indicate that the data pointed to will not be altered by the 
function.'}
+  ,{'name':'gchar'
+   ,'doc':'Corresponds to the standard C char type.'}
+  ,{'name':'guchar'
+   ,'doc':'Corresponds to the standard C unsigned char type.'}
+  ,{'name':'gshort'
+   ,'doc':'Corresponds to the standard C short type. Values of this type can range from G_MINSHORT to 
G_MAXSHORT.'}
+  ,{'name':'gushort'
+   ,'doc':'Corresponds to the standard C unsigned short type. Values of this type can range from 0 to 
G_MAXUSHORT.'}
+  ,{'name':'glong'
+   ,'doc':'Corresponds to the standard C long type. Values of this type can range from G_MINLONG to 
G_MAXLONG.'}
+  ,{'name':'gulong'
+   ,'doc':'Corresponds to the standard C unsigned long type. Values of this type can range from 0 to 
G_MAXULONG.'}
+  ,{'name':'gfloat'
+   ,'doc':'Corresponds to the standard C float type. Values of this type can range from -G_MAXFLOAT to 
G_MAXFLOAT.'}
+  ,{'name':'gdouble'
+   ,'doc':'Corresponds to the standard C double type. Values of this type can range from -G_MAXDOUBLE to 
G_MAXDOUBLE.'}
+  ,{'name':'Babl'
+   ,'doc':'A Babl identifier reference, outside babl itself this means a specific babl pixel format. as 
created with babl_format("RGBA u8");'}
+  ,{'name':'int'
+   ,'doc':'Core C integer type'}
+  ,{'name':'float'
+   ,'doc':'Single precision floating point value'}
+  ,{'name':'double'
+   ,'doc':'Double precision floating point value'}
+  ,{'name':'utf8'
+   ,'doc':'String encoded in utf8'}
+
+];
+
+
 function id(idi) { return document.getElementById(idi) }
 function show_op(op)
 {
@@ -172,7 +298,6 @@ function filter_db(query)
             return [opdb[i]];
         }
       }
-
       if (command == 'category')
       {
         for (var i = 0; i < opdb.length; i++)
@@ -189,8 +314,6 @@ function filter_db(query)
         }
         return result;
       }
-
-
     }
 
   for (var i = 0; i < opdb.length; i++)
@@ -228,7 +351,7 @@ function filter_db(query)
         score += 2;
 
     if (op.description &&
-        op.description.indexOf(query) >= 0)  score += 10;
+        op.description.indexOf(query) >= 0)      score += 10;
 
     if (uop_name.indexOf(uquery) == 0)           score += 1000;
     if (uop_name.slice(5).indexOf(uquery) == 0)  score += 900;
@@ -267,7 +390,9 @@ function filter_db(query)
   result = result.sort(function(a,b){
     if (a.score != b.score)
       return (b.score - a.score);
-    return a.op > b.op;
+
+    var x = a.op.toLowerCase(), y = b.op.toLowerCase();
+    return x < y ? -1 : x > y ? 1 : 0;
   });
   return result;
 }
@@ -361,8 +486,8 @@ function render_item (item)
       result += '</div></div>';
   }
 
-    if (item.properties)
-      result += render_properties (item.properties);
+  if (item.properties)
+    result += render_properties (item.properties);
 
     result += '<div style="clear:all">&nbsp;</div>';
     result += '<div style="margin-top:4em;"></div>';
@@ -376,7 +501,13 @@ function go (query)
   window.location.hash=encodeURIComponent (id('query').value);
   window.location.hash=encodeURIComponent (query);
   id("query").value=query;
-  show_matches();
+  setTimeout(1200,function(){
+  id("query").focus();
+  id("query").select();
+  id('debug').innerHTML = 'foo';
+  });
+  document.body.scrollTop = document.documentElement.scrollTop = 0;
+  update_view();
 }
 
 function render_list2 (items, maxitems)
@@ -399,7 +530,6 @@ function render_list2 (items, maxitems)
       result += '<div class="title">' + item.op + '</div>';
       result += '</div>';
     }
-
   }
   return result;
 }
@@ -463,11 +593,1869 @@ function render_categories ()
 window.onhashchange = function ()
 {
   id('query').value = decodeURIComponent (window.location.hash.slice(1));
-  show_matches ();
+  update_view ();
+}
+
+
+function gir_get_doc (gir, item, pre, post)
+{
+  if (item && item.doc && item.doc['#text'])
+  {
+    //var text = gir_recode_code (item.doc['#text']);
+    var text = item.doc['#text'];
+    //text = text.replace(/&lt;/g, '<').replace(/&gt;/g, '>');
+    //return item.doc['#text'];
+    //return "<pre>" + (pre?pre:"") + (expand_links_md(text)) + (post?post:"") + "</pre>";
+   return "<p>" + (pre?pre:"") + expand_links_code(showdown.makeHtml(expand_links_md(text))) + 
(post?post:"") + "</p>";
+   // return "<pre>" + (pre?pre:"") + text + (post?post:"") +
+    "</pre>";
+  }
+  return "";
+}
+
+function gir_list_arguments (gir, root)
+{
+  var result = '';
+  var first = true;
+
+  if (root.parameters && root.parameters.parameter)
+  {
+    var list = [].concat(root.parameters.parameter);
+    for (var k = 0; k < list.length; k++)
+    {
+      var pitem = list[k];
+
+      result += '<tr class="argument"><td class="argtype">';
+
+      if (pitem.type) {
+         if (pitem.type['@c:type'])
+           result += expand_links(pitem.type['@c:type']) + '</td>';
+         else if (pitem.type['@name'])
+           result += expand_links (
+           gir_prefix (gir) + pitem.type['@name']) + '*</td>';
+      } else if (pitem.array)
+      {
+          result += pitem.array['@c:type'] + '</td>';
+      } else /* if (pitem.varargs) */
+      {
+          result += '...</td>';
+      }
+
+      if (pitem['@name'])
+      {
+        result += '<td><span class="argname">' + pitem['@name'] + '</span>';
+
+        if (k == list.length-1)
+          result += ');';
+        else
+          result += ',';
+        result += '</td>';
+      }
+
+      result += '</tr>'
+      first = false;
+    }
+  }
+  return result;
+}
+
+var showdown = new Showdown.converter();
+
+function gir_list_arguments_doc (gir, root, pre)
+{
+  var result = '';
+  var first = true;
+
+  if (root.parameters && root.parameters.parameter)
+  {
+    var list = [].concat(root.parameters.parameter);
+    result += '<dl>' + (pre?pre:"");
+    for (var k = 0; k < list.length; k++)
+    {
+      var pitem = list[k];
+      if (pitem['@name']) result += '<dt>' + pitem['@name'] + '</dt>';
+
+      result += gir_get_doc (gir, pitem);
+    }
+    result += '</dl>';
+  }
+  else if (pre)
+  {
+    return "<dl>" + pre + "</dl>";
+  }
+  return result;
+}
+
+function gir_list_arguments_text (gir, root)
+{
+  var result = '';
+  var first = true;
+
+  if (root.parameters && root.parameters.parameter)
+  {
+    var list = [].concat(root.parameters.parameter);
+    for (var k = 0; k < list.length; k++)
+    {
+      var pitem = list[k];
+
+      if (first)
+      first = false;
+      else
+      result += ',';
+      
+      if (pitem.type) {
+         if (pitem.type['@c:type'])
+           result += '' + expand_links(pitem.type['@c:type']) + ' ';
+         else if (pitem.type['@name'])
+           result += '' + expand_links (
+           gir_prefix (gir) + pitem.type['@name']) + '* ';
+      }
+
+      if (pitem['@name']) result += '' + pitem['@name']
+        + '';
+    }
+    if (!first)
+      result += '';
+  }
+  return result;
+}
+
+function gir_find_enum (gir, name)
+{
+  if (gir)
+  {
+    var list = gir.repository.namespace['enumeration'];
+    if (list)
+    {
+      for (var i =0; i < list.length; i++)
+        {
+          var item = list[i];
+          if (item['@c:type'] == name)
+            return item;
+        }
+    }
+  }
+  return '';
+}
+
+function gir_find_class (gir, name)
+{
+  if (gir)
+  {
+    if (gir.repository.namespace['class']);
+    var list = [].concat(gir.repository.namespace['class']);
+    if (list)
+    {
+      for (var i =0; i < list.length; i++)
+        {
+          var item = list[i];
+          if (item['@c:type'] == name)
+            return item;
+        }
+    }
+  }
+  return '';
 }
 
+function gir_int_get_parent (gir, klass)
+{
+  if (!klass['@parent'])
+    return 'unknown';
+    /* XXX: ugly special casing; needs to be made generic */
+  if (klass['@parent'].indexOf('GObject.') == 0)
+  {
+    if (klass['@parent'].slice(8) == 'InitiallyUnowned')
+      return 'GObject';
+    return 'G' + klass['@parent'].slice(8);
+  }
+  return gir_prefix (gir) + klass['@parent'];
+}
 
-function show_matches(e)
+function gir_class_get_subclasses (name)
+{
+  var result = [];
+
+  for (var r = 0; r < window.apidb.length; r++)
+  {
+    var gir = window.apidb[r];
+    if (gir.repository.namespace['class'])
+    {
+      var list = [].concat(gir.repository.namespace['class']);
+      for (var i =0; i < list.length; i++)
+        {
+          var klass = list[i];
+          if (gir_int_get_parent (gir, klass) == name)
+            result.push(klass['@c:type']);
+        }
+    }
+  }
+  return result;
+}
+
+function gir_class_get_parent (gir, name)
+{
+  if (gir)
+  {
+    var klass = gir_find_class (gir, name);
+    if (klass)
+      return gir_int_get_parent (gir, klass);
+  }
+  return 'unknown';
+}
+
+function gir_prefix (gir)
+{
+  return gir.repository.namespace['@c:identifier-prefixes'];
+}
+
+function gir_symbol_prefix (gir)
+{
+  return gir.repository.namespace['@c:symbol-prefixes'];
+}
+
+
+function list_callbacks (gir, label, id)
+{
+  var result = '';
+  result += '<h3>' + label + '</h3>';
+  if (gir)
+  {
+    var list = gir.repository.namespace[id];
+    if (list)
+    for (var i =0; i < list.length; i++)
+      {
+        var item = list[i];
+        if (item['@c:type'])
+          {
+            if (item['return-value'] && item['return-value'].type)
+            {
+            result += '<b>';
+               result += item['return-value'].type['@c:type'] + '';
+
+            result += '&nbsp;&nbsp;' + item['@c:type'] + '(<table class="arguments">';
+            result += gir_list_arguments (gir, item);
+
+            result +='</table>';
+            result += '</b>';
+            }
+          }
+        else if (item['@c:type'])
+          result += '&nbsp;' + item['@c:type'];
+        else if (item['@glib:type-name'])
+          result += '&nbsp;' + item['@glib:type-name'];
+        else
+          result += item['@name'];
+        result += '<br/>';
+
+        result += gir_get_doc (gir, item);
+      }
+  }
+  return result;
+}
+
+
+function each_render (gir, item, cb, data)
+{
+  var result = ''
+  if (!item)
+    return result;
+  var list = [].concat (item);
+  for (var j = 0; j < list.length; j++)
+    result += cb (gir, list[j], data);
+  return result;
+}
+
+function gir_render_method (gir, citem, item)
+{
+  var result = '';
+    result += '<div class="method">';
+
+  if (citem['return-value'] && citem['return-value'].type)
+  {
+     result += expand_links(citem['return-value'].type['@c:type']) + '&nbsp;';
+  } else if (citem['return-value'] && citem['return-value'].array)
+  {
+     result += expand_links(citem['return-value'].array['@c:type']) + '&nbsp;';
+  }
+
+  result += '<span class="declaration">';
+
+  if (citem['@c:identifier'])
+    result += citem['@c:identifier'];
+  else 
+    result += citem['@name'];
+  result += '</span>&nbsp;(';
+
+  result += "<table class='arguments'><tr class='argument'><td class='argtype'>" + expand_links 
(item['@c:type']) + "*</td>" + 
+    "<td class='argname'>"+item['@c:symbol-prefix'] + (citem.parameters?',':'') + "</td></tr>";
+
+  if (citem.parameters && citem.parameters.parameter)
+  {
+    result += gir_list_arguments (gir, citem);
+  } else {
+    result += ');';
+  }
+  result +='</table>';
+  
+  var argdoc = "<dt>" + item['@c:symbol-prefix'] + '</dt>' +
+               '<dd>A ' + expand_links (item['@c:type']) + "</dd>";
+  result += gir_list_arguments_doc (gir, citem, argdoc);
+
+  result += gir_get_doc (gir, citem);
+  result += gir_get_doc (gir, citem['return-value'], "Returns ");
+
+  return result;
+}
+
+function gir_render_function (gir, citem, item)
+{
+  var result = '';
+  result += '<span class="declaration">';
+
+  if (citem['@c:identifier'])
+    result += citem['@c:identifier'];
+  else 
+    result += citem['@name'];
+  result += '</span>&nbsp;(';
+
+  result += "<table class='arguments'>";
+
+  if (citem.parameters && citem.parameters.parameter)
+  {
+    result += gir_list_arguments (gir, citem);
+  } else {
+    result += ');';
+  }
+  result +='</table>';
+
+
+  result += gir_get_doc (gir, citem);
+  result += gir_get_doc (gir, citem['return-value'], "Returns ");
+
+  return result;
+}
+
+function gir_render_property (gir, citem, item)
+{
+  var result = '';
+  if (citem.type)
+  {
+    if (citem.type['@glib:type-name'])
+    result += '<div><div style="width: 10em;display: inline-block;">'
+       + citem.type['@glib:type-name'] + '</div>';
+    else
+    result += '<div><div style="width: 10em;display: inline-block;">'
+       + citem.type['@name'] + '</div>';
+
+    result += '<div style="width: 30em;display: inline-block;">'
+        + citem['@name'] + '</div></div>';
+
+    result += gir_get_doc (gir, citem);
+  }
+  return result;
+}
+
+function gir_render_constructor (gir, citem, item)
+{
+  var result = '';
+
+  result += '<div class="constructor">';
+
+  if (citem['return-value'])
+  {
+    if(citem['return-value'].type)
+      result += expand_links (citem['return-value'].type['@c:type']) + ' ';
+  }
+
+  result += '<span class="declaration">' + citem['@c:identifier'] + '</span> (';
+
+  if (citem.parameters)
+  {
+    result += "<table class='arguments'>";
+    result += gir_list_arguments (gir, citem);
+    result += '</table>';
+  }
+  else
+    result += ');';
+
+  result += '</div>';
+
+  result += gir_list_arguments_doc (gir, citem);
+
+  result += gir_get_doc (gir, citem);
+  result += gir_get_doc (gir, citem['return-value'], "Returns ");
+
+  return result;
+}
+
+function gir_render_signal(gir, citem, item)
+{
+  var result = '';
+
+  result += '<b>';
+  if (citem['return-value'])
+  {
+     if (citem['return-value'].type['@name'] == 'none')
+       result += 'void ';
+     else
+       result += citem['return-value'].type['@name'] + ' ';
+  }
+
+  result += citem['@name'] + '(<table class="arguments"><tr class="argument"><td class="argtype">' + 
expand_links (gir_prefix(gir) +
+  item['@name']) +'*</td><td class="argname">' + item['@c:symbol-prefix'] +
+  (citem.parameters?',':'') + '</td></tr>';
+
+
+  if (citem.parameters && citem.parameters.parameter)
+  {
+    result += gir_list_arguments (gir, citem);
+  }
+  else
+  {
+    result += ');';
+  }
+
+  result +='</span>';
+  result += '</b>';
+
+  result += gir_list_arguments_doc (gir, citem);
+
+  result += gir_get_doc (gir, citem);
+
+  return result;
+}
+
+function href (target, label)
+{
+  return "<a href='#"+encodeURIComponent(target)+"'>" + label + "</a>";
+
+  //return "<span class='link' onclick='go(\"" + target + "\")'>" + label + "</span>" + post;
+  //return "<a href='#"+target+"'>" + label + "</a>" + post;
+}
+
+function href_missing (target, label)
+{
+  return "<style color='red'>" + label + "</style>";
+
+  //return "<span class='link' onclick='go(\"" + target + "\")'>" + label + "</span>" + post;
+  //return "<a href='#"+target+"'>" + label + "</a>" + post;
+}
+
+function expand_token (token)
+{
+  var i;
+
+  var target = false;
+  var label = token;
+  var post = "";
+
+  if (!token)
+    return '';
+
+  if (token.indexOf('@') == 0)
+    {
+      return '<em>' + expand_token(token.slice(1)) + '</em>';
+    }
+  else if (token.indexOf('%') == 0)
+    {
+      return '<tt>' + expand_token(token.slice(1)) + '</tt>';
+    }
+
+  if (index[token] && index.hasOwnProperty(token))
+  {
+    target = token;
+    label = index[token];
+  }
+  else  if (index[token.slice(1)] && index.hasOwnProperty(token.slice(1)))
+  {
+    target = token.slice(1);
+    label = index[token.slice(1)];
+  }
+
+  if (!target && token[token.length-1] == 's')
+  { /* also linkify plurals.. */
+    var singular_token = token.slice(0, token.length-1);
+    if (index[singular_token] && index.hasOwnProperty(singular_token))
+    {
+      target = singular_token;
+      label = index[singular_token];
+      post = 's';
+    }
+    else  if (index[singular_token.slice(1)] && index.hasOwnProperty(singular_token.slice(1)))
+    {
+      target = singular_token.slice(1);
+      label = index[singular_token.slice(1)];
+      post = 's';
+    }
+
+  }
+  if (target)
+    return href (target, label) + post;
+
+  if (token.indexOf('#') == 0)
+    return href_missing (token.slice(1), token.slice(1)) +  post;
+
+  return label;
+}
+
+function expand_links (text)
+{
+  var result = "";
+  var i;
+  var nls = 0;
+
+  var token = "";
+  if (!text)
+    return text;
+
+  var swallow = false;
+
+  for (i = 0; i < text.length; i++)
+  {
+    switch (text[i])
+    {
+      case '&':
+        if (!swallow)
+          result += expand_token (token);
+
+        if ((text[i+1] == 'l') &&
+            (text[i+2] == 't') &&
+            (text[i+3] == ';'))
+        {
+          swallow = true;
+          i+=3;
+        }
+        else if ((text[i+1] == 'g') &&
+                 (text[i+2] == 't') &&
+                 (text[i+3] == ';'))
+        {
+          swallow = false;
+          i+=3;
+        }
+        else
+        {
+          result += '&';
+        }
+        token = '';
+        break;
+      case ' ':
+      case ',':
+      case '"':
+      case ';':
+      case '*':
+      case '\n':
+      case "<":
+      case '(':
+      case ')':
+      case ">":
+      case '.':
+
+        if (!swallow)
+        {
+          result += expand_token (token);
+          result += text[i];
+          if (text[i] == '\n')
+          {
+            nls ++;
+            if (nls == 2)
+              result += '<div style="height:1em;margin-top:1em;"/>';
+          }
+          else
+          {
+            nls = 0;
+          }
+        }
+        token = '';
+        break;
+      default:
+        token += text[i];
+    }
+  }
+  if (!swallow)
+    result += expand_token (token);
+
+  return result;
+}
+
+
+function expand_links_code (text)
+{
+  var result = "";
+  var i;
+
+  var token = "";
+  if (!text)
+    return text;
+
+  var in_code = false;
+
+  for (i = 0; i < text.length; i++)
+  {
+    switch (text[i])
+    {
+      case "<":
+        {
+          if (in_code)
+          {
+            if (text[i+1]=='/' &&
+                text[i+2]=='c' &&
+                text[i+3]=='o' &&
+                text[i+4]=='d' &&
+                text[i+5]=='e')
+            {
+              in_code = false;
+              while (text[i] !='>')i++;
+              break;
+            }
+          }
+            else
+          {
+            if (text[i+1]=='c' &&
+                text[i+2]=='o' &&
+                text[i+3]=='d' &&
+                text[i+4]=='e')
+            {
+              in_code = true;
+              while (text[i] !='>')i++;
+
+              if (text[i+1]=='&' &&
+                  text[i+2]=='l' &&
+                  text[i+3]=='t' &&
+                  text[i+4]==';' &&
+                  text[i+5]=='!')
+              {
+                i+=5;
+                while (!(text[i] && 
+                       text[i+1] =='&' &&
+                       text[i+2] =='g' &&
+                       text[i+3] =='t' &&
+                       text[i+4] ==';'))i++;
+                i+=4;
+              }
+
+              break;
+            }
+          }
+        }
+      case '&':
+      case ' ':
+      case ',':
+      case '"':
+      case ';':
+      case '*':
+      case '\n':
+      case '(':
+      case ')':
+      case ">":
+      case '.':
+
+          if (in_code)
+            result += expand_token (token);
+          else
+            result += token;
+          result += text[i];
+
+        token = '';
+        break;
+      default:
+        token += text[i];
+    }
+  }
+ if (in_code)
+   result += expand_token (token);
+ else
+   result += token;
+
+  return result;
+}
+
+function md_escape (string)
+{
+  var ret = '';
+  if (string)
+  for (var i = 0; i < string.length; i++)
+  {
+    switch (string[i])
+    {
+      case '_': ret += '\\_'; break;
+      default:  ret += string[i]; break;
+    }
+  }
+  return ret;
+}
+
+function href_md (target, label)
+{
+  return "[" + md_escape (label) + "](#" + encodeURIComponent(target)+")";
+}
+
+function href_missing_md (target, label)
+{
+  return "<style color='red'>" + label + "</style>";
+}
+
+function expand_token_md (token)
+{
+  var i;
+
+  var target = false;
+  var label = token;
+  var post = "";
+
+  if (!token)
+    return '';
+
+  if (token.indexOf('@') == 0)
+    {
+      return '<em>' + expand_token_md(token.slice(1)) + '</em>';
+    }
+  else if (token.indexOf('%') == 0)
+    {
+      return '<tt>' + expand_token_md(token.slice(1)) + '</tt>';
+    }
+
+  if (index[token] && index.hasOwnProperty(token))
+  {
+    target = token;
+    label = index[token];
+  }
+  else  if (index[token.slice(1)] && index.hasOwnProperty(token.slice(1)))
+  {
+    target = token.slice(1);
+    label = index[token.slice(1)];
+  }
+
+  if (!target && token[token.length-1] == 's')
+  { /* also linkify plurals.. */
+    var singular_token = token.slice(0, token.length-1);
+    if (index[singular_token] && index.hasOwnProperty(singular_token))
+    {
+      target = singular_token;
+      label = index[singular_token];
+      post = 's';
+    }
+    else  if (index[singular_token.slice(1)] && index.hasOwnProperty(singular_token.slice(1)))
+    {
+      target = singular_token.slice(1);
+      label = index[singular_token.slice(1)];
+      post = 's';
+    }
+
+  }
+  if (target)
+    return href_md (target, label) + post;
+
+  if (token.indexOf('#') == 0)
+    return href_missing_md (token.slice(1), token.slice(1)) +  post;
+
+  return label;
+}
+
+function gir_recode_code (text)
+{
+  var result = '';
+
+  var in_code = false;
+
+  var prev = '-';
+
+  for (var i = 0; i < text.length; i++)
+  {
+    var c = text[i];
+    switch (c)
+    {
+      case '|':
+        if (in_code)
+        {
+          if (prev == ']')
+          {
+            in_code = false;
+            result = result.slice(0,result.length-1) + '\n';
+          }
+        }
+        else
+        {
+          result += c;
+        }
+        break;
+      case '[':
+        if (!in_code)
+        {
+          if (prev == '|')
+            { 
+              in_code = true;
+              result = result.slice(0,result.length-1) + '\n   ';
+            }
+          break;
+        }
+        else
+        {
+          result += c;
+        }
+        break;
+      case '\n':
+        if (in_code)
+          result += '\n    ';
+        else
+          result += '\n';
+      break;
+      default:
+        result += c;
+    }
+    prev = c;
+  }
+  return result;
+}
+
+function expand_links_md (text)
+{
+  var result = "";
+  var i;
+  var nls = 0;
+  var in_code = false;
+
+  var token = "";
+  if (!text)
+    return text;
+
+  var swallow = false;
+  var prev = 'a';
+
+  var in_code = false;
+
+  for (i = 0; i < text.length; i++)
+  {
+    if (in_code)
+    {
+      var c = text[i];
+      switch (c)
+      {
+        case '|':
+          if (prev == ']')
+          {
+            in_code = false;
+            result = result.slice(0,result.length-1) + '\n';
+          }
+        break;
+        case '\n':
+          result += '\n    ';
+          break;
+        case '&':
+          if (!swallow)
+            result += expand_token_md (token);
+          token = '';
+
+          if ((text[i+1] == 'l') &&
+              (text[i+2] == 't') &&
+              (text[i+3] == ';'))
+          {
+          //  swallow = true;
+            i+=3;
+            result += '<';
+          }
+          else if ((text[i+1] == 'g') &&
+                   (text[i+2] == 't') &&
+                   (text[i+3] == ';'))
+          {
+            //swallow = false;
+            i+=3;
+            result += '>';
+          }
+          else
+          {
+            result += '&';
+          }
+          break;
+        default:
+          result += c;
+      }
+    }
+    else
+    {
+      var c = text[i];
+      switch (c)
+      {
+        case '[':
+          if (prev == '|')
+          { 
+            in_code = true;
+            result += token;
+            token = '';
+            result = result.slice(0, result.length-1) + '\n    ';
+          }
+          else
+          {
+            token += c;
+          }
+          break;
+        case '&':
+          if (!swallow)
+            result += expand_token_md (token);
+
+          if ((text[i+1] == 'l') &&
+              (text[i+2] == 't') &&
+              (text[i+3] == ';'))
+          {
+            swallow = true;
+            i+=3;
+          }
+          else if ((text[i+1] == 'g') &&
+                   (text[i+2] == 't') &&
+                   (text[i+3] == ';'))
+          {
+            swallow = false;
+            i+=3;
+          }
+          else
+          {
+            result += '&';
+          }
+          token = '';
+          break;
+        case ' ':
+        case ',':
+        case '"':
+        case ';':
+        case '*':
+        case '\n':
+        case "<":
+        case '(':
+        case ')':
+        case ">":
+        case '.':
+          if (!swallow)
+          {
+            result += expand_token_md (token);
+            result += c;
+          }
+          token = '';
+          break;
+        case '_':
+          token += '\\'; // disabling this part of markdown, to avoid false positives
+        default:
+          token += c;
+      }
+    }
+    prev = c;
+  }
+  if (!swallow)
+    result += expand_token_md (token);
+
+  return result;
+}
+
+function gir_render_ancestry (gir, klass, tail)
+{
+  var result = '';
+
+  var tklass = klass;
+
+  if (tail)
+  {
+    result = expand_token (klass) + result;
+  }
+  else
+    result = klass;
+
+  var level = 0;
+
+  if (!tail)tail ='';
+
+  klass = gir_class_get_parent (gir, klass);
+  while (klass && klass!='unknown')
+  {
+    result = expand_token (klass) + '/' + result;
+    klass = gir_class_get_parent (gir, klass);
+    level++;
+  }
+
+  return '<div class="ancestry">' + result + ' ' + tail + '</div>';
+}
+
+function gir_render_subclasses (gir, tklass, linkself)
+{
+  var subs = '';
+  {
+    var subklasses = gir_class_get_subclasses (tklass);
+    subklasses = subklasses.sort();
+    if (subklasses.length)
+    {
+      subs = "<b>Subclasses</b>";
+      for (var i = 0; i < subklasses.length; i++)
+      {
+          var klass = subklasses[i];
+          subs += expand_token(klass);
+      }
+      subs  = '<div> ' +subs +' </div>';
+    }
+  }
+  return subs;
+}
+
+function gir_render_class (gir, item)
+{
+  var result = gir_render_ancestry (gir, gir_prefix(gir) + item['@name']);
+  
+  if (item.field) /* some GObject subclasses in GEGL have no public struct */
+  {
+    result += expand_links (item['@c:type']) + gir_render_struct (gir, item.field);
+  }
+  
+  result += gir_get_doc (gir, item);
+  result +='<div class="linklist">';
+
+  if (item.implements)
+  {
+    result += '<b>Implements</b>';
+    result += each_render (gir, item.implements, 
+       function(gir, citem, item){return '<div>' + expand_links (gir_prefix(gir) + citem['@name']) + 
'</div>';});
+  }
+  result += '</div>'
+  result +='<div class="linklist">';
+
+  if (item.property)
+  {
+    result += '<b>Properties</b>';
+    result += each_render (gir, item['property'], 
+       function(gir, citem, item){
+         var ref = gir_prefix(gir) + item['@name'] + ':' + citem['@name'];
+         return href(ref, ':' + citem['@name']);
+       }, item);
+  }
+
+  result += '</div>'
+  result +='<div class="linklist">';
+
+  if (item['constructor'] && item.hasOwnProperty('constructor'))
+  {
+    result += '<b>Constructors</b>';
+      result += each_render (gir, item['constructor'], 
+       function(gir, citem, item){
+         var ref = citem['@c:identifier'];
+         return href(ref, '.' + citem['@name'] + '()');
+       });
+  }
+  if (item.function)
+  {
+    result += '<b>Functions</b>';
+    result += each_render (gir, item.function, 
+       function(gir, citem, item){
+         var ref = citem['@c:identifier'];
+         return href(ref, '.' + citem['@name'] + '()');
+       });
+  }
+
+  if (item.method)
+  {
+    result += '<b>Methods</b>';
+    result += each_render (gir, item.method, 
+       function(gir, citem, item){
+         var ref = citem['@c:identifier'];
+         return href(ref, '.' + citem['@name'] + '()');
+       });
+  }
+  result += '</div>'
+  result +='<div class="linklist">';
+
+  if (item['virtual-method'])
+  {
+    result += '<b>Virtual methods</b>';
+    result += each_render (gir, item['virtual-method'], 
+       function(gir, citem, item){
+         var ref = gir_prefix(gir) +  item['@name'] + ':.' + citem['@name'];
+         return href(ref, ':.' + citem['@name'] + '()');
+       }, item);
+  }
+
+  result += '</div>'
+  result +='<div class="linklist">';
+
+  if (item['glib:signal'])
+  {
+    result += '<b>Signals</b>';
+    result += each_render (gir, item['glib:signal'], 
+       function(gir, citem, item){
+         var ref = gir_prefix(gir) + item['@name'] + '::' + citem['@name'];
+         return href(ref, '::' + citem['@name'] + '()');
+       }, item);
+  }
+
+  result += '</div>'
+  result +='<div class="linklist">';
+  result += gir_render_subclasses (gir, gir_prefix(gir) + item['@name']);
+  result += '</div>';
+  return result;
+}
+
+
+function gir_render_class_old (gir, item)
+{
+  var result = gir_render_ancestry (gir, gir_prefix(gir) + item['@name']);
+
+  result += gir_get_doc (gir, item);
+
+  if (item.property)
+  {
+    result += '<h4>Properties</h4>';
+    result += each_render (gir, item.property, gir_render_property, item);
+  }
+  if (item['glib:signal'])
+  {
+    result += each_render (gir, item["glib:signal"], gir_render_signal, item);
+  }
+  result += each_render (gir, item.constructor, gir_render_constructor, item);
+  /* does this really happen,. functions for classes... ? */
+  result += each_render (gir, item.method, gir_render_method, item);
+  result += each_render (gir, item.function, gir_render_function, item);
+  return result;
+}
+
+function list_class (gir, label, id)
+{
+  var result = '';
+  result += '<h3>' + label + '</h3>';
+  if (gir)
+  {
+    var list = gir.repository.namespace[id];
+    if (list)
+    for (var i =0; i < list.length; i++)
+      {
+        var item = list[i];
+        result += gir_render_class (gir, item);
+      }
+  }
+  return result;
+}
+
+function gir_render_enum (gir, item)
+{
+  var result = '';
+
+  result += '<h3>' + item['@c:type'] + '</h3>';
+
+  if (item.member)
+  {
+    result += '<table>';
+    for (j = 0; j < item.member.length; j ++)
+    {
+      var member = item.member[j];
+      result += '<tr><td class="key">' + member["@name"] + 
+         '</td><td class="value" valign="top">' + member["@c:identifier"] + '</td>';
+
+      result += '<td valign="top">' + gir_get_doc (gir, item.member[j]) + '</td>';
+
+      result += '</tr>';
+    }
+    result += '</table>';
+  }
+
+  result += gir_get_doc (gir, item);
+  //if (item.doc && item.doc['#text'])
+  //  result += '<br/><p style="clear:both">' + expand_links(item.doc['#text']) + '</p>';
+
+  return result;
+}
+
+function list_enums (gir, label, id)
+{
+  var result = '';
+  result += '<h3>' + label + '</h3>';
+  if (gir)
+  {
+    var list = gir.repository.namespace[id];
+    if (list)
+    for (var i =0; i < list.length; i++)
+      {
+        var item = list[i];
+        result += gir_render_enum (gir, item);
+      }
+  }
+  return result;
+}
+
+function list_enumerations (gir, label, id)
+{
+  var result = '';
+  result += '<h3 style="clear:both;margin-top:2em">' + label + '</h3>';
+  if (gir)
+  {
+    var list = gir.repository.namespace[id];
+    if (list) /* breaks with only one enum */
+    for (var i =0; i < list.length; i++)
+      {
+        var item = list[i];
+        result += '<h4 style="clear:both;margin-top:2em;">' +
+        gir_prefix(gir) + item['@name'] + '</h4>';
+        result += '<br/>';
+
+        if (item.member)
+        {
+          for (j = 0; j < item.member.length; j ++)
+          {
+            var member = item.member[j];
+            result += '<div class="key">' + member["@name"] + '</div>' +
+            '<div class="value">' + member["@c:identifier"] + '</div>';
+          }
+        }
+      }
+  }
+  return result;
+}
+
+function gir_render_struct (gir, item)
+{
+  var result = '';
+  result += " {<br/>";
+  if (item)
+  {
+    var list = [].concat(item);
+    result += '<table>';
+    for (var i = 0; i < list.length; i++)
+    {
+      result += '<tr class="field">'
+      if(list[i].type)
+        result += '<td class="field_type">' + expand_links
+        (list[i].type['@c:type']) + '</td>';
+      else if(list[i].array)
+        result += '<td class="field_type">' + expand_links
+        (list[i].array['@c:type']) + '[]</td>';
+      else
+        result += '<td class="field_type">' + list[i]  + '[]</td>';
+
+      result += '<td class="field_name">' + list[i]['@name'] + '</td>';
+      result += '</tr>'
+    }
+      result += '</table>'
+  }
+  result += "<div style='clear:both'>}</div>";
+  return result;
+}
+
+function gir_render_record (gir, item)
+{
+  var result = expand_links (item['@c:type']) + gir_render_struct (gir, item.field);
+
+  result += gir_get_doc (gir, item);
+
+  //result += each_render (gir, item.function, gir_render_function, item);
+  //result += each_render (gir, item.method, gir_render_method, item);
+
+  return result;
+}
+
+function list_constants (gir, label, id)
+{
+  var result = '';
+  result += '<h3>' + label + '</h3>';
+  if (gir)
+  {
+    var list = gir.repository.namespace[id];
+    if (list)
+    for (var i =0; i < list.length; i++)
+      {
+        var item = list[i];
+        if (item['@c:type'])
+          result += '<div><div class="key">' + item['@c:type'] + '</div>';
+        result += '<div>' + item['@value'] + '</div></div>';
+      }
+  }
+  return result;
+}
+
+/*
+ * classes = class hierarchy
+ *
+ * index = classes, enumerations, bitfields
+ */
+function gir_index (gir, arg)
+{
+  var result = '';
+
+  if (arg.indexOf('enum ') == 0)
+  {
+    var ref = gir_find_enum (gir, arg.slice(5));
+    if (ref)
+      result += render_enum (gir, ref);
+    else
+      result += "["+arg.slice(6)+']';
+  } 
+  else if (arg.indexOf('class') == 0)
+  {
+    var ref = gir_find_class (gir, arg.slice(6));
+    if (ref)
+      result += gir_render_class (gir, ref);
+    else
+      result += "["+arg.slice(6)+']';
+  } else if (arg == 'enums')
+  {
+    result += list_enumerations ('Enumerations', 'enumeration');
+  }
+  else if (arg == 'bitfields')
+  {
+    result += list_enumerations ('Bitfields',    'bitfield');
+  }
+  else if (arg == 'all')
+  {
+    result += list_class (gir, 'Classes', 'class');
+    result += list_callbacks (gir, 'Callbacks', 'callback');
+    result += list_constants    ('Constants', 'constant');
+    result += list_enumerations ('Enums', 'enumeration');
+    result += list_enumerations ('Bitfields', 'bitfield');
+  }
+
+  return result;
+}
+
+function gir_filter_index (query)
+{
+  var result = [];
+  var temp_result = [];
+
+  if (!index)
+    return [];
+
+  for (var term in index)
+  {
+    if (term)
+    {
+    var score = 0;
+
+    if(term == query)                                           score += 50;
+    else if(term.indexOf(query)==0)                             score += 30;
+    else if(term.toUpperCase() == query.toUpperCase())          score += 20;
+    else if(term.indexOf(query)>=0)                             score += 10;
+    else if(term.toUpperCase().indexOf(query.toUpperCase())==0) score += 5;
+    else if(term.toUpperCase().indexOf(query.toUpperCase())>=0) score += 1;
+
+    /* penalize signal matches */
+    if (query.indexOf(':')>0  && query.indexOf('::') < 0 &&
+        term.indexOf('::') > 0) score -= 5;
+
+    if (score > 0)
+      temp_result.push ([term, score]);
+    }
+  }
+
+  temp_result = temp_result.sort(
+    function(a,b){
+      if (a[1] != b[1])
+        return (b[1] - a[1]);
+      var x = a[0].toLowerCase(), y = b[0].toLowerCase();
+      return x < y ? -1 : x > y ? 1 : 0;
+    }
+  );
+
+  for (var i = 0; i < temp_result.length; i++)
+  {
+    result[result.length]=(temp_result[i][0]);
+  }
+
+  return result;
+}
+
+function gir_filter_index_exact (query)
+{
+  var result = [];
+
+  if (!index)
+    return [];
+
+  for (term in index)
+  {
+    if (term && term == (query))
+      result.push (term);
+  }
+
+  return result;
+}
+
+//\\/\
+
+function ns_list (gir, name, label, prefix)
+{
+  var list = [].concat(gir.repository.namespace[name]);
+  var result = '';
+  if (list[0])
+  {
+    result += '<div class="linklist">'
+    result += '<b>' + label + '</b>';
+  for (var i = 0; i < list.length; i++)
+  {
+    var term;
+    if (list[i]['@c:type'])
+      term = list[i]['@c:type'];
+    else 
+      term = prefix + list[i]['@name'];
+    result += href(term, term);
+  }
+    result +='</div>';
+  }
+  return result;
+}
+
+function gir_render_namespace (gir)
+{
+  var result = "";
+  result = "<div>Namespace " + gir.repository.namespace['@name'] + "</div>";
+
+  result += ns_list (gir, 'class', 'Classes', gir_prefix(gir));
+  result += ns_list (gir, 'record', 'Records', gir_prefix(gir));
+  result += ns_list (gir, 'function', 'Global functions',
+  gir_symbol_prefix(gir) + '_');
+  result += ns_list (gir, 'enumeration', 'Enumerations', gir_prefix(gir));
+  result += ns_list (gir, 'bitfield', 'Bitfields', gir_prefix(gir));
+  result += ns_list (gir, 'alias', 'Aliases', gir_prefix(gir));
+
+  return result;
+}
+
+
+/*
+
+render view,.. on basis of?
+
+   string
+
+   search term or title
+
+   each title in index should get associated view mode
+
+   the view mode itself;.. should be able to render from xml fragment
+                           ., it would be subtrees of the json..
+   
+
+
+
+*/
+
+
+function gir_search_class (gir, klass, query, collect_index)
+{
+  if (!klass)
+    return false;
+  var term = klass['@c:type'];
+  if (collect_index)
+    index[term] = term;
+  else if (query == term)
+    return gir_render_class (gir, klass);
+   
+  if (klass.method)
+  {
+    var methods = [].concat(klass.method);
+    for (var m = 0; m < methods.length; m ++)
+    {
+      var term = methods[m]['@c:identifier'];
+      if (collect_index)
+        index[term] = term;
+      else if (query == term)
+      {
+         return "<div>" + gir_render_ancestry (gir, gir_prefix(gir) + klass['@name'], '.' +
+         methods[m]['@name'] + '()') +
+         "</div>" + gir_render_method (gir, methods[m], klass);
+      }
+
+      var term = gir_prefix (gir) + klass['@name'] + '.' + methods[m]['@name'];
+      if (collect_index)
+        index[term] = term;
+      else if (query == term)
+      {
+        return "<div>" + gir_render_ancestry (gir, gir_prefix(gir) + klass['@name'], '.' +
+         methods[m]['@name'] + '()') +
+         "</div>" + gir_render_method (gir, methods[m], klass);
+      }
+    }
+  }
+
+  if (klass['glib:signal'])
+  {
+    var signals = [].concat(klass['glib:signal']);
+    for (var m = 0; m < signals.length; m ++)
+    {
+      var term = klass['@c:type'] + '::' + signals[m]['@name'];
+      if (collect_index)
+        index[term] = term;
+      else if (query == term)
+        return "<div>" + gir_render_ancestry (gir, gir_prefix(gir) + klass['@name'], '::' + 
signals[m]['@name'] + '') + "</div>" +
+        gir_render_signal (gir, signals[m], klass);
+    }
+  }
+
+  if (klass.property)
+  {
+    var properties = [].concat(klass.property);
+    for (var m = 0; m < properties.length; m ++)
+    {
+      var term = klass['@c:type'] + ':' + properties[m]['@name'];
+      if (collect_index)
+        index[term] = term;
+      else if (query == term)
+        return "<div>" + gir_render_ancestry (gir, gir_prefix(gir) + klass['@name'], ':' +
+         properties[m]['@name'] + '') +
+         "</div>" + gir_render_property (gir, properties[m], klass);
+    }
+  }
+
+  if (klass.constructor)
+  {
+    var constructors = [].concat(klass.constructor);
+    for (var c = 0; c < constructors.length; c ++)
+    {
+      var term = constructors[c]['@c:identifier'];
+      if (collect_index)
+        index[term] = term;
+      else if (query == term)
+        return "<div>" + gir_render_ancestry (gir, gir_prefix(gir) +
+        klass['@name'], '.' + constructors[c]['@name'] + '()') + "</div>" +
+          gir_render_constructor (gir, constructors[c], klass);
+    }
+  }
+
+  if (klass.function)
+  {
+    var lst = [].concat(klass.function);
+    for (var j = 0; j < lst.length; j++)
+    {
+      var term = lst[j]['@c:identifier'];
+
+      if (collect_index)
+        index[term] = term;
+      else if (query == term)
+        return "<div>" + gir_render_ancestry (gir, gir_prefix(gir) +
+        klass['@name'], '.' + lst[j]['@name'] + '()') + "</div>" +
+          gir_render_constructor (gir, lst[j], klass);
+    }
+  }
+
+
+  if (klass['virtual-method'])
+  {
+    var lst = [].concat(klass['virtual-method']);
+    for (var j = 0; j < lst.length; j++)
+    {
+      var term = klass['@c:type'] + ':.' + lst[j]['@name'];
+
+      if (collect_index)
+        index[term] = term;
+      else if (query == term)
+        return "<div>" + gir_render_ancestry (gir,
+        gir_prefix(gir) + klass['@name'], ':.' + lst[j]['@name'] + '()') +
+        "</div>" + gir_render_method (gir, lst[j], klass);
+    }
+  }
+
+  return false;
+}
+
+function gir_search (gir, query, collect_index)
+{
+  var result = "";
+  if (!gir)
+    return "";
+
+  if (!collect_index){
+    var qa = query.split(' ')
+
+    var matches = gir_filter_index (qa[0]);
+    var ematches = [];
+
+    if (qa.length > 1)
+    {
+      for (var j = 1; j < qa.length; j++)
+      {
+        var foo = matches;
+        matches = [];
+        for (var i = 0; i < foo.length; i ++)
+        {
+          if (foo[i].indexOf(qa[j]) >= 0)
+             matches.push (foo[i]);
+        }
+      }
+    }
+
+    for (var i = 0; i < matches.length; i ++)
+    {
+      if (matches[i] == query)
+         ematches.push (matches[i]);
+    }
+
+    if (ematches.length != 1 && matches.length > 1)
+    {
+
+      for (var i = 0; i < matches.length && i < gir_max_results; i ++)
+        result += href(matches[i], matches[i]) + '<br/>';
+
+      if (matches.length > gir_max_results)
+      {
+        return result + '<div>... ' + (matches.length - gir_max_results) + ' matches trimmed</div>';
+      }
+
+      return result;
+    }
+
+    if (matches.length == 1)
+      query = matches[0];
+    if (matches.length == 0)
+      return "";
+  }
+
+  /* real search; to find single unique match, or collect index */
+  for (var i = 0; i < core_types.length; i++)
+  {
+    if (core_types[i])
+    {
+    var term = core_types[i].name;
+    if (collect_index)
+      index[term]=term;
+    else if (query == term)
+    {
+      return "<div>" + term + '</div>' + '<div>' + expand_links (core_types[i].doc) + "</div>";
+    }
+    }
+  }
+
+  {
+    var term = gir.repository.namespace['@name'];
+    if (term == 'GObject')
+      term = 'Gobject'; /* special name space deconflicting hack */
+    if (collect_index)
+      index[term]=term;
+    else if (query == term)
+    {
+      return gir_render_namespace (gir);
+    }
+  }
+
+  if(gir.repository.namespace['alias'])
+  {
+    var list = [].concat(gir.repository.namespace['alias']);
+    for (var i = 0; i < list.length; i++)
+    {
+      var term = list[i]['@c:type'];
+      if (collect_index)
+        index[term]=term;
+      else if (query == term)
+      {
+        return "<div>" + term + '</div>' + gir_get_doc (gir, list[i]);
+      }
+    }
+  }
+
+  var list = [].concat(gir.repository.namespace['class']);
+  for (var i = 0; i < list.length; i++)
+  {
+    var res = gir_search_class (gir, list[i], query, collect_index);
+    if (res)
+      return res;
+  }
+
+  var list = [].concat(gir.repository.namespace['interface']);
+  for (var i = 0; i < list.length; i++)
+  {
+    var res = gir_search_class (gir, list[i], query, collect_index);
+    if (res)
+      return res;
+  }
+  
+  var list = [].concat(gir.repository.namespace['function']);
+  if (list[0])
+  for (var i = 0; i < list.length; i++)
+  {
+    var term = list[i]['@c:identifier'];
+    if (collect_index)
+      index[term] = term;
+    else if (query == term)
+      return gir_render_function (gir, list[i]);
+  }
+
+  var list = [].concat(gir.repository.namespace['enumeration']);
+  if (list[0])
+  for (var i = 0; i < list.length; i++)
+  {
+    var term = list[i]['@glib:type-name'];
+
+    if (collect_index)
+      index[term] = term;
+    else if (query == term)
+      return gir_render_enum (gir, list[i]);
+  }
+
+  if(gir.repository.namespace['record'])
+  {
+    var list = [].concat(gir.repository.namespace['record']);
+    for (var i = 0; i < list.length; i++)
+    {
+      var term = list[i]['@c:type'];
+      if (collect_index)
+        index[term] = term;
+      else if (query == term)
+        return gir_render_record (gir, list[i]);
+
+      var record = list[i];
+
+      if (record.method)
+      {
+        var lst = [].concat(record.method);
+        for (var j = 0; j < lst.length; j++)
+        {
+          var term = lst[j]['@c:identifier'];
+
+          if (collect_index)
+            index[term] = term;
+          else if (query == term)
+            return "<div>" + gir_render_ancestry (gir, gir_prefix(gir) +
+            record['@name'], '.' + lst[j]['@name'] + '()') + "</div>" +
+              gir_render_method (gir, lst[j], record);
+
+          term = gir_prefix(gir) + record['@name'] + '.' + lst[j]['@name'];
+
+          if (collect_index)
+            index[term] = term;
+          else if (query == term)
+            return "<div>" + gir_render_ancestry (gir, gir_prefix(gir) +
+            record['@name'], '.' + lst[j]['@name'] + '()') + "</div>" +
+              gir_render_method (gir, lst[j], record);
+
+        }
+      }
+
+      if (record['function'])
+      {
+        var lst = [].concat(record['function']);
+        for (var j = 0; j < lst.length; j++)
+        {
+          var term = lst[j]['@c:identifier'];
+
+          if (collect_index)
+            index[term] = term;
+          else if (query == term)
+            return "<div>" + gir_render_ancestry (gir, gir_prefix(gir) +
+            record['@name'], '.' + lst[j]['@name'] + '()') + "</div>" +
+              gir_render_method (gir, lst[j], record);
+        }
+      }
+
+    }
+  }
+
+  if(gir.repository.namespace['bitfield'])
+  {
+    var list = [].concat(gir.repository.namespace['bitfield']);
+    for (var i = 0; i < list.length; i++)
+    {
+      {
+        for (j = 0; j < list[i].member.length; j ++)
+          {
+            var member = list[i].member[j];
+            term = member["@c:identifier"];
+
+            if (collect_index)
+            {
+              index[term] = term;
+            }
+            else if (query == term)
+              return gir_render_enum (gir, list[i]);
+          }
+      }
+
+      var term = list[i]['@c:type'];
+      if (collect_index)
+      {
+        index[term] = term;
+      }
+      else if (query == term)
+        return gir_render_enum (gir, list[i]);
+    }
+  }
+
+  if(gir.repository.namespace['constant'])
+  {
+    var list = [].concat (gir.repository.namespace['constant']);
+    for (var i =0; i < list.length; i++)
+      {
+        var term = list[i]['@c:type'];
+        if (collect_index)
+        {
+          index[term] = term;
+        }
+        else if (query == term)
+          return '<div><div class="key">' + term + '</div>' + '<div>' + list[i]['@value'] + '</div></div>';
+      }
+  }
+
+  if(gir.repository.namespace['enumeration'])
+  {
+    var list = [].concat(gir.repository.namespace['enumeration']);
+    for (var i = 0; i < list.length; i++)
+    {
+      {
+        for (j = 0; j < list[i].member.length; j ++)
+          {
+            var member = list[i].member[j];
+            term = member["@c:identifier"];
+
+            if (collect_index)
+            {
+              index[term] = term;
+            }
+            else if (query == term)
+              return gir_render_enum (gir, list[i]);
+          }
+      }
+
+      var term = list[i]['@c:type'];
+      if (collect_index)
+      {
+        index[term] = term;
+      }
+      else if (query == term)
+        return gir_render_enum (gir, list[i]);
+    }
+  }
+  return "";
+}
+
+function foobail (e)
+{
+  return false;
+}
+
+function keydown (e)
+{
+  if (e && (e.keyCode != 9))
+    id('query').focus();
+  if (e && (e.keyCode == 40))
+  {
+    window.scrollBy(0,window.document.body.clientHeight * 0.1);
+    e.stopPropagation();
+    e.preventDefault();
+    return false;
+  }
+  if (e && (e.keyCode == 38))
+  {
+    window.scrollBy(0,-window.document.body.clientHeight * 0.1);
+    e.stopPropagation();
+    e.preventDefault();
+    return false;
+  }
+  if (e && (e.keyCode == 33))
+  {
+    window.scrollBy(0,-window.document.body.clientHeight * 0.8);
+    e.stopPropagation();
+    e.preventDefault();
+    return false;
+  }
+  if (e && (e.keyCode == 34))
+  {
+    window.scrollBy(0,Math.floor(window.document.body.clientHeight * 0.8));
+    e.stopPropagation();
+    e.preventDefault();
+    return false;
+  }
+  id('status').innerHTML = e.keyCode;
+  return false;
+}
+
+function update_view(e)
 {
   var items;
   var result = '';
@@ -484,21 +2472,54 @@ function show_matches(e)
     initialized = true;
   }
 
+  var girres = undefined;
+
+  if (window.apidb)
+  {
+
+    if (id('query').value == "")
+    {
+      girres = "<div>" + href('GEGL operations','GEGL operations')  + '</div>';
+      girres += "<br/>";
+      girres += '<b>Namespaces</b>';
+      for (var i = 0; i < window.apidb.length; i++)
+      {
+        girres += '<div>' + href (window.apidb[i].repository.namespace['@name'],
+                        window.apidb[i].repository.namespace['@name']) +
+                        '</div>';
+      }
+    }
+    else 
+      for (var i = 0; i < window.apidb.length && !girres; i++)
+      {
+        girres = gir_search(window.apidb[i], id('query').value);
+        if (girres)
+          break;
+      }
+  }
+  
   items = filter_db(id('query').value);
 
-  if (id('query').value == 'list of all ops')
+  if (id('query').value == 'GEGL operations')
   {
-    result += render_all ();
+    result = render_categories ();
+  }
+  else if (girres)
+  {
+    result = girres;
   }
-  else if (id('query').value == '')
+  else if (id('query').value == 'list of all ops')
   {
-    result += render_categories ();
+    result += render_all ();
   }
   else if (items.length == 0)
   {
-    result = 'eek';
+    if (!window.apidb || window.apidb.length < 4)
+      result = 'no matches (still loading, completa data set is 8.5mb of GIR files)';
+    else
+      result = 'no matches';
   }
-  else if (items.length == 1)
+  else if ((items.length == 1) || (items[0].op == id('query').value))
   {
     if (items[0].op == id('query').value ||
     id('query').value.indexOf('op')==0)
@@ -513,26 +2534,120 @@ function show_matches(e)
   else
     result += render_list_small (items);
 
-
   if (result != previous_content)
   {
     id('view').innerHTML = result;
     previous_content = result;
   }
 }
-
   </script>
 
-  <body>
+  <body onkeydown='keydown(event)'>
     <input id='query' value='' placeholder="type to search"
-    onkeyup='show_matches(event);'
+    onkeyup='update_view(event);'
+    onkeydown='foobail(event);'
     onactivate='setloc()'
      autocomplete='off'></input>
-     <div id='view'>foo</div>
+     <pre id='debug'></pre>
+     <pre id='status'></pre>
+     <div id='view'>Loading dynamic javascript content,.. or perhaps something
+     is broken?</div>
   </body>
 
   <script>
-    show_matches();
+    update_view();
     id('query').focus();
+
+    function load_xhtr (path, cb) { /* load gobject introspection data */
+    /* XXX: should have iframe code path for http(s) prefixed paths */
+
+      id('status').innerHTML = 'downloading ' + path;
+      var xhr = new XMLHttpRequest ();
+      xhr.open("GET", path, true);
+      xhr.onreadystatechange = function() {
+      if (this.readyState == this.DONE)
+        { 
+          if (xhr.responseText)
+            cb (xhr.responseText);
+        }
+      }
+      xhr.send();
+    }
+
+    function gir_loaded (content)
+    {
+      id('status').innerHTML = 'parsing';
+      var json = xml2json
+      (parseXml(content)).replace(/undefined/,
+      '').replace(/:null/g, ':""').
+      replace(/function Object()....\s*\[native code]\s*},/g,'');
+      var html = ("" + json+"\n").replace(/&/g, 
"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\n/g, "<br/>"); 
+      //id('debug').innerHTML = json;
+
+      var newdb = JSON.parse(json);
+      window.apidb.push(newdb);
+      gir_search (newdb, "foo", true);
+      update_view ();
+
+      id('status').innerHTML = '';
+      load_next_gir ();
+    }
+
+    var gir_path_i = 0;
+
+function load_next_gir ()
+{
+  if (gir_path_i < gir_paths.length)
+  {
+    load_xhtr (gir_paths[gir_path_i], gir_loaded);
+    gir_path_i ++;
+  }
+}
+
+function parseXml(xml) {
+   var dom = null;
+   if (window.DOMParser) {
+      try { 
+         dom = (new DOMParser()).parseFromString(xml, "text/xml"); 
+      } 
+      catch (e) { dom = null; }
+   }
+   else if (window.ActiveXObject) {
+      try {
+         dom = new ActiveXObject('Microsoft.XMLDOM');
+         dom.async = false;
+         if (!dom.loadXML(xml)) // parse error ..
+
+            window.alert(dom.parseError.reason + dom.parseError.srcText);
+      } 
+      catch (e) { dom = null; }
+   }
+   else
+      alert("cannot parse xml string!");
+   return dom;
+}
+    index = [];
+    window.apidb = [];
+
+    load_next_gir ();
+
+    window.addEventListener('keydown',
+ function(event) {
+    if (event.ctrlKey || event.metaKey) {
+        switch (String.fromCharCode(event.which).toLowerCase()) {
+        case 's':
+            event.preventDefault();
+            id('query').focus();
+            id('query').select();
+            
+            break;
+        }
+    }
+});
+
+    //function 
readfile(){alert(document.getElementById('iframe').contentDocument.body.firstChild.innerHTML);}
   </script>
+  <!--<iframe id='iframe' src = 'journal.txt' onload='readfile()'>
+  </iframe>-->
+
 </html>
diff --git a/docs/showdown.js b/docs/showdown.js
new file mode 100644
index 0000000..a960309
--- /dev/null
+++ b/docs/showdown.js
@@ -0,0 +1,1296 @@
+//
+// showdown.js -- A javascript port of Markdown.
+//
+// Copyright (c) 2007 John Fraser.
+//
+// Original Markdown Copyright (c) 2004-2005 John Gruber
+//   <http://daringfireball.net/projects/markdown/>
+//
+// Redistributable under a BSD-style open source license.
+// See license.txt for more information.
+//
+// The full source distribution is at:
+//
+//                             A A L
+//                             T C A
+//                             T K B
+//
+//   <http://www.attacklab.net/>
+//
+
+//
+// Wherever possible, Showdown is a straight, line-by-line port
+// of the Perl version of Markdown.
+//
+// This is not a normal parser design; it's basically just a
+// series of string substitutions.  It's hard to read and
+// maintain this way,  but keeping Showdown close to the original
+// design makes it easier to port new features.
+//
+// More importantly, Showdown behaves like markdown.pl in most
+// edge cases.  So web applications can do client-side preview
+// in Javascript, and then build identical HTML on the server.
+//
+// This port needs the new RegExp functionality of ECMA 262,
+// 3rd Edition (i.e. Javascript 1.5).  Most modern web browsers
+// should do fine.  Even with the new regular expression features,
+// We do a lot of work to emulate Perl's regex functionality.
+// The tricky changes in this file mostly have the "attacklab:"
+// label.  Major or self-explanatory changes don't.
+//
+// Smart diff tools like Araxis Merge will be able to match up
+// this file with markdown.pl in a useful way.  A little tweaking
+// helps: in a copy of markdown.pl, replace "#" with "//" and
+// replace "$text" with "text".  Be sure to ignore whitespace
+// and line endings.
+//
+
+
+//
+// Showdown usage:
+//
+//   var text = "Markdown *rocks*.";
+//
+//   var converter = new Showdown.converter();
+//   var html = converter.makeHtml(text);
+//
+//   alert(html);
+//
+// Note: move the sample code to the bottom of this
+// file before uncommenting it.
+//
+
+
+//
+// Showdown namespace
+//
+var Showdown = {};
+
+//
+// converter
+//
+// Wraps all "globals" so that the only thing
+// exposed is makeHtml().
+//
+Showdown.converter = function() {
+
+//
+// Globals:
+//
+
+// Global hashes, used by various utility routines
+var g_urls;
+var g_titles;
+var g_html_blocks;
+
+// Used to track when we're inside an ordered or unordered list
+// (see _ProcessListItems() for details):
+var g_list_level = 0;
+
+
+this.makeHtml = function(text) {
+//
+// Main function. The order in which other subs are called here is
+// essential. Link and image substitutions need to happen before
+// _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>
+// and <img> tags get encoded.
+//
+
+       // Clear the global hashes. If we don't clear these, you get conflicts
+       // from other articles when generating a page which contains more than
+       // one article (e.g. an index page that shows the N most recent
+       // articles):
+       g_urls = new Array();
+       g_titles = new Array();
+       g_html_blocks = new Array();
+
+       // attacklab: Replace ~ with ~T
+       // This lets us use tilde as an escape char to avoid md5 hashes
+       // The choice of character is arbitray; anything that isn't
+    // magic in Markdown will work.
+       text = text.replace(/~/g,"~T");
+
+       // attacklab: Replace $ with ~D
+       // RegExp interprets $ as a special character
+       // when it's in a replacement string
+       text = text.replace(/\$/g,"~D");
+
+       // Standardize line endings
+       text = text.replace(/\r\n/g,"\n"); // DOS to Unix
+       text = text.replace(/\r/g,"\n"); // Mac to Unix
+
+       // Make sure text begins and ends with a couple of newlines:
+       text = "\n\n" + text + "\n\n";
+
+       // Convert all tabs to spaces.
+       text = _Detab(text);
+
+       // Strip any lines consisting only of spaces and tabs.
+       // This makes subsequent regexen easier to write, because we can
+       // match consecutive blank lines with /\n+/ instead of something
+       // contorted like /[ \t]*\n+/ .
+       text = text.replace(/^[ \t]+$/mg,"");
+
+       // Turn block-level HTML blocks into hash entries
+       text = _HashHTMLBlocks(text);
+
+       // Strip link definitions, store in hashes.
+       text = _StripLinkDefinitions(text);
+
+       text = _RunBlockGamut(text);
+
+       text = _UnescapeSpecialChars(text);
+
+       // attacklab: Restore dollar signs
+       text = text.replace(/~D/g,"$$");
+
+       // attacklab: Restore tildes
+       text = text.replace(/~T/g,"~");
+
+       return text;
+}
+
+
+var _StripLinkDefinitions = function(text) {
+//
+// Strips link definitions from text, stores the URLs and titles in
+// hash references.
+//
+
+       // Link defs are in the form: ^[id]: url "optional title"
+
+       /*
+               var text = text.replace(/
+                               ^[ ]{0,3}\[(.+)\]:  // id = $1  attacklab: g_tab_width - 1
+                                 [ \t]*
+                                 \n?                           // maybe *one* newline
+                                 [ \t]*
+                               <?(\S+?)>?                      // url = $2
+                                 [ \t]*
+                                 \n?                           // maybe one newline
+                                 [ \t]*
+                               (?:
+                                 (\n*)                         // any lines skipped = $3 attacklab: 
lookbehind removed
+                                 ["(]
+                                 (.+?)                         // title = $4
+                                 [")]
+                                 [ \t]*
+                               )?                                      // title is optional
+                               (?:\n+|$)
+                         /gm,
+                         function(){...});
+       */
+       var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ 
\t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,
+               function (wholeMatch,m1,m2,m3,m4) {
+                       m1 = m1.toLowerCase();
+                       g_urls[m1] = _EncodeAmpsAndAngles(m2);  // Link IDs are case-insensitive
+                       if (m3) {
+                               // Oops, found blank lines, so it's not a title.
+                               // Put back the parenthetical statement we stole.
+                               return m3+m4;
+                       } else if (m4) {
+                               g_titles[m1] = m4.replace(/"/g,"&quot;");
+                       }
+                       
+                       // Completely remove the definition from the text
+                       return "";
+               }
+       );
+
+       return text;
+}
+
+
+var _HashHTMLBlocks = function(text) {
+       // attacklab: Double up blank lines to reduce lookaround
+       text = text.replace(/\n/g,"\n\n");
+
+       // Hashify HTML blocks:
+       // We only want to do this for block-level HTML tags, such as headers,
+       // lists, and tables. That's because we still want to wrap <p>s around
+       // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
+       // phrase emphasis, and spans. The list of tags we're looking for is
+       // hard-coded:
+       var block_tags_a = 
"p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"
+       var block_tags_b = 
"p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math"
+
+       // First, look for nested blocks, e.g.:
+       //   <div>
+       //     <div>
+       //     tags for inner block must be indented.
+       //     </div>
+       //   </div>
+       //
+       // The outermost tags must start at the left margin for this to match, and
+       // the inner nested divs must be indented.
+       // We need to do this before the next, more liberal match, because the next
+       // match will start at the first `<div>` and stop at the first `</div>`.
+
+       // attacklab: This regex can be expensive when it fails.
+       /*
+               var text = text.replace(/
+               (                                               // save in $1
+                       ^                                       // start of line  (with /m)
+                       <($block_tags_a)        // start tag = $2
+                       \b                                      // word break
+                                                               // attacklab: hack around khtml/pcre bug...
+                       [^\r]*?\n                       // any number of lines, minimally matching
+                       </\2>                           // the matching end tag
+                       [ \t]*                          // trailing spaces/tabs
+                       (?=\n+)                         // followed by a newline
+               )                                               // attacklab: there are sentinel newlines at 
end of document
+               /gm,function(){...}};
+       */
+       text = 
text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[
 \t]*(?=\n+))/gm,hashElement);
+
+       //
+       // Now match more liberally, simply from `\n<tag>` to `</tag>\n`
+       //
+
+       /*
+               var text = text.replace(/
+               (                                               // save in $1
+                       ^                                       // start of line  (with /m)
+                       <($block_tags_b)        // start tag = $2
+                       \b                                      // word break
+                                                               // attacklab: hack around khtml/pcre bug...
+                       [^\r]*?                         // any number of lines, minimally matching
+                       .*</\2>                         // the matching end tag
+                       [ \t]*                          // trailing spaces/tabs
+                       (?=\n+)                         // followed by a newline
+               )                                               // attacklab: there are sentinel newlines at 
end of document
+               /gm,function(){...}};
+       */
+       text = 
text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[
 \t]*(?=\n+)\n)/gm,hashElement);
+
+       // Special case just for <hr />. It was easier to make a special case than
+       // to make the other regex more complicated.  
+
+       /*
+               text = text.replace(/
+               (                                               // save in $1
+                       \n\n                            // Starting after a blank line
+                       [ ]{0,3}
+                       (<(hr)                          // start tag = $2
+                       \b                                      // word break
+                       ([^<>])*?                       // 
+                       \/?>)                           // the matching end tag
+                       [ \t]*
+                       (?=\n{2,})                      // followed by a blank line
+               )
+               /g,hashElement);
+       */
+       text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);
+
+       // Special case for standalone HTML comments:
+
+       /*
+               text = text.replace(/
+               (                                               // save in $1
+                       \n\n                            // Starting after a blank line
+                       [ ]{0,3}                        // attacklab: g_tab_width - 1
+                       <!
+                       (--[^\r]*?--\s*)+
+                       >
+                       [ \t]*
+                       (?=\n{2,})                      // followed by a blank line
+               )
+               /g,hashElement);
+       */
+       text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);
+
+       // PHP and ASP-style processor instructions (<?...?> and <%...%>)
+
+       /*
+               text = text.replace(/
+               (?:
+                       \n\n                            // Starting after a blank line
+               )
+               (                                               // save in $1
+                       [ ]{0,3}                        // attacklab: g_tab_width - 1
+                       (?:
+                               <([?%])                 // $2
+                               [^\r]*?
+                               \2>
+                       )
+                       [ \t]*
+                       (?=\n{2,})                      // followed by a blank line
+               )
+               /g,hashElement);
+       */
+       text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);
+
+       // attacklab: Undo double lines (see comment at top of this function)
+       text = text.replace(/\n\n/g,"\n");
+       return text;
+}
+
+var hashElement = function(wholeMatch,m1) {
+       var blockText = m1;
+
+       // Undo double lines
+       blockText = blockText.replace(/\n\n/g,"\n");
+       blockText = blockText.replace(/^\n/,"");
+       
+       // strip trailing blank lines
+       blockText = blockText.replace(/\n+$/g,"");
+       
+       // Replace the element text with a marker ("~KxK" where x is its key)
+       blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";
+       
+       return blockText;
+};
+
+var _RunBlockGamut = function(text) {
+//
+// These are all the transformations that form block-level
+// tags like paragraphs, headers, and list items.
+//
+       text = _DoHeaders(text);
+
+       // Do Horizontal Rules:
+       var key = hashBlock("<hr />");
+       text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);
+       text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);
+       text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);
+
+       text = _DoLists(text);
+       text = _DoCodeBlocks(text);
+       text = _DoBlockQuotes(text);
+
+       // We already ran _HashHTMLBlocks() before, in Markdown(), but that
+       // was to escape raw HTML in the original Markdown source. This time,
+       // we're escaping the markup we've just created, so that we don't wrap
+       // <p> tags around block-level tags.
+       text = _HashHTMLBlocks(text);
+       text = _FormParagraphs(text);
+
+       return text;
+}
+
+
+var _RunSpanGamut = function(text) {
+//
+// These are all the transformations that occur *within* block-level
+// tags like paragraphs, headers, and list items.
+//
+
+       text = _DoCodeSpans(text);
+       text = _EscapeSpecialCharsWithinTagAttributes(text);
+       text = _EncodeBackslashEscapes(text);
+
+       // Process anchor and image tags. Images must come first,
+       // because ![foo][f] looks like an anchor.
+       text = _DoImages(text);
+       text = _DoAnchors(text);
+
+       // Make links out of things like `<http://example.com/>`
+       // Must come after _DoAnchors(), because you can use < and >
+       // delimiters in inline links like [this](<url>).
+       text = _DoAutoLinks(text);
+       text = _EncodeAmpsAndAngles(text);
+       text = _DoItalicsAndBold(text);
+
+       // Do hard breaks:
+       text = text.replace(/  +\n/g," <br />\n");
+
+       return text;
+}
+
+var _EscapeSpecialCharsWithinTagAttributes = function(text) {
+//
+// Within tags -- meaning between < and > -- encode [\ ` * _] so they
+// don't conflict with their use in Markdown for code, italics and strong.
+//
+
+       // Build a regex to find HTML tags and comments.  See Friedl's 
+       // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
+       var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
+
+       text = text.replace(regex, function(wholeMatch) {
+               var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");
+               tag = escapeCharacters(tag,"\\`*_");
+               return tag;
+       });
+
+       return text;
+}
+
+var _DoAnchors = function(text) {
+//
+// Turn Markdown link shortcuts into XHTML <a> tags.
+//
+       //
+       // First, handle reference-style links: [link text] [id]
+       //
+
+       /*
+               text = text.replace(/
+               (                                                       // wrap whole match in $1
+                       \[
+                       (
+                               (?:
+                                       \[[^\]]*\]              // allow brackets nested one level
+                                       |
+                                       [^\[]                   // or anything else
+                               )*
+                       )
+                       \]
+
+                       [ ]?                                    // one optional space
+                       (?:\n[ ]*)?                             // one optional newline followed by spaces
+
+                       \[
+                       (.*?)                                   // id = $3
+                       \]
+               )()()()()                                       // pad remaining backreferences
+               /g,_DoAnchors_callback);
+       */
+       text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ 
]*)?\[(.*?)\])()()()()/g,writeAnchorTag);
+
+       //
+       // Next, inline-style links: [link text](url "optional title")
+       //
+
+       /*
+               text = text.replace(/
+                       (                                               // wrap whole match in $1
+                               \[
+                               (
+                                       (?:
+                                               \[[^\]]*\]      // allow brackets nested one level
+                                       |
+                                       [^\[\]]                 // or anything else
+                               )
+                       )
+                       \]
+                       \(                                              // literal paren
+                       [ \t]*
+                       ()                                              // no id, so leave $3 empty
+                       <?(.*?)>?                               // href = $4
+                       [ \t]*
+                       (                                               // $5
+                               (['"])                          // quote char = $6
+                               (.*?)                           // Title = $7
+                               \6                                      // matching quote
+                               [ \t]*                          // ignore any spaces/tabs between closing 
quote and )
+                       )?                                              // title is optional
+                       \)
+               )
+               /g,writeAnchorTag);
+       */
+       text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ 
\t]*)?\))/g,writeAnchorTag);
+
+       //
+       // Last, handle reference-style shortcuts: [link text]
+       // These must come last in case you've also got [link test][1]
+       // or [link test](/foo)
+       //
+
+       /*
+               text = text.replace(/
+               (                                                       // wrap whole match in $1
+                       \[
+                       ([^\[\]]+)                              // link text = $2; can't contain '[' or ']'
+                       \]
+               )()()()()()                                     // pad rest of backreferences
+               /g, writeAnchorTag);
+       */
+       text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
+
+       return text;
+}
+
+var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
+       if (m7 == undefined) m7 = "";
+       var whole_match = m1;
+       var link_text   = m2;
+       var link_id      = m3.toLowerCase();
+       var url         = m4;
+       var title       = m7;
+       
+       if (url == "") {
+               if (link_id == "") {
+                       // lower-case and turn embedded newlines into spaces
+                       link_id = link_text.toLowerCase().replace(/ ?\n/g," ");
+               }
+               url = "#"+link_id;
+               
+               if (g_urls[link_id] != undefined) {
+                       url = g_urls[link_id];
+                       if (g_titles[link_id] != undefined) {
+                               title = g_titles[link_id];
+                       }
+               }
+               else {
+                       if (whole_match.search(/\(\s*\)$/m)>-1) {
+                               // Special case for explicit empty url
+                               url = "";
+                       } else {
+                               return whole_match;
+                       }
+               }
+       }       
+       
+       url = escapeCharacters(url,"*_");
+       var result = "<a href=\"" + url + "\"";
+       
+       if (title != "") {
+               title = title.replace(/"/g,"&quot;");
+               title = escapeCharacters(title,"*_");
+               result +=  " title=\"" + title + "\"";
+       }
+       
+       result += ">" + link_text + "</a>";
+       
+       return result;
+}
+
+
+var _DoImages = function(text) {
+//
+// Turn Markdown image shortcuts into <img> tags.
+//
+
+       //
+       // First, handle reference-style labeled images: ![alt text][id]
+       //
+
+       /*
+               text = text.replace(/
+               (                                               // wrap whole match in $1
+                       !\[
+                       (.*?)                           // alt text = $2
+                       \]
+
+                       [ ]?                            // one optional space
+                       (?:\n[ ]*)?                     // one optional newline followed by spaces
+
+                       \[
+                       (.*?)                           // id = $3
+                       \]
+               )()()()()                               // pad rest of backreferences
+               /g,writeImageTag);
+       */
+       text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);
+
+       //
+       // Next, handle inline images:  ![alt text](url "optional title")
+       // Don't forget: encode * and _
+
+       /*
+               text = text.replace(/
+               (                                               // wrap whole match in $1
+                       !\[
+                       (.*?)                           // alt text = $2
+                       \]
+                       \s?                                     // One optional whitespace character
+                       \(                                      // literal paren
+                       [ \t]*
+                       ()                                      // no id, so leave $3 empty
+                       <?(\S+?)>?                      // src url = $4
+                       [ \t]*
+                       (                                       // $5
+                               (['"])                  // quote char = $6
+                               (.*?)                   // title = $7
+                               \6                              // matching quote
+                               [ \t]*
+                       )?                                      // title is optional
+               \)
+               )
+               /g,writeImageTag);
+       */
+       text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ 
\t]*)?\))/g,writeImageTag);
+
+       return text;
+}
+
+var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
+       var whole_match = m1;
+       var alt_text   = m2;
+       var link_id      = m3.toLowerCase();
+       var url         = m4;
+       var title       = m7;
+
+       if (!title) title = "";
+       
+       if (url == "") {
+               if (link_id == "") {
+                       // lower-case and turn embedded newlines into spaces
+                       link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");
+               }
+               url = "#"+link_id;
+               
+               if (g_urls[link_id] != undefined) {
+                       url = g_urls[link_id];
+                       if (g_titles[link_id] != undefined) {
+                               title = g_titles[link_id];
+                       }
+               }
+               else {
+                       return whole_match;
+               }
+       }       
+       
+       alt_text = alt_text.replace(/"/g,"&quot;");
+       url = escapeCharacters(url,"*_");
+       var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";
+
+       // attacklab: Markdown.pl adds empty title attributes to images.
+       // Replicate this bug.
+
+       //if (title != "") {
+               title = title.replace(/"/g,"&quot;");
+               title = escapeCharacters(title,"*_");
+               result +=  " title=\"" + title + "\"";
+       //}
+       
+       result += " />";
+       
+       return result;
+}
+
+
+var _DoHeaders = function(text) {
+
+       // Setext-style headers:
+       //      Header 1
+       //      ========
+       //  
+       //      Header 2
+       //      --------
+       //
+       text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
+               function(wholeMatch,m1){return hashBlock("<h1>" + _RunSpanGamut(m1) + "</h1>");});
+
+       text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
+               function(matchFound,m1){return hashBlock("<h2>" + _RunSpanGamut(m1) + "</h2>");});
+
+       // atx-style headers:
+       //  # Header 1
+       //  ## Header 2
+       //  ## Header 2 with closing hashes ##
+       //  ...
+       //  ###### Header 6
+       //
+
+       /*
+               text = text.replace(/
+                       ^(\#{1,6})                              // $1 = string of #'s
+                       [ \t]*
+                       (.+?)                                   // $2 = Header text
+                       [ \t]*
+                       \#*                                             // optional closing #'s (not counted)
+                       \n+
+               /gm, function() {...});
+       */
+
+       text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
+               function(wholeMatch,m1,m2) {
+                       var h_level = m1.length;
+                       return hashBlock("<h" + h_level + ">" + _RunSpanGamut(m2) + "</h" + h_level + ">");
+               });
+
+       return text;
+}
+
+// This declaration keeps Dojo compressor from outputting garbage:
+var _ProcessListItems;
+
+var _DoLists = function(text) {
+//
+// Form HTML ordered (numbered) and unordered (bulleted) lists.
+//
+
+       // attacklab: add sentinel to hack around khtml/safari bug:
+       // http://bugs.webkit.org/show_bug.cgi?id=11231
+       text += "~0";
+
+       // Re-usable pattern to match any entirel ul or ol list:
+
+       /*
+               var whole_list = /
+               (                                                                       // $1 = whole list
+                       (                                                               // $2
+                               [ ]{0,3}                                        // attacklab: g_tab_width - 1
+                               ([*+-]|\d+[.])                          // $3 = first list item marker
+                               [ \t]+
+                       )
+                       [^\r]+?
+                       (                                                               // $4
+                               ~0                                                      // sentinel for 
workaround; should be $
+                       |
+                               \n{2,}
+                               (?=\S)
+                               (?!                                                     // Negative lookahead 
for another list item marker
+                                       [ \t]*
+                                       (?:[*+-]|\d+[.])[ \t]+
+                               )
+                       )
+               )/g
+       */
+       var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ 
\t]+)))/gm;
+
+       if (g_list_level) {
+               text = text.replace(whole_list,function(wholeMatch,m1,m2) {
+                       var list = m1;
+                       var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
+
+                       // Turn double returns into triple returns, so that we can make a
+                       // paragraph for the last item in a list, if necessary:
+                       list = list.replace(/\n{2,}/g,"\n\n\n");;
+                       var result = _ProcessListItems(list);
+       
+                       // Trim any trailing whitespace, to put the closing `</$list_type>`
+                       // up on the preceding line, to get it past the current stupid
+                       // HTML block parser. This is a hack to work around the terrible
+                       // hack that is the HTML block parser.
+                       result = result.replace(/\s+$/,"");
+                       result = "<"+list_type+">" + result + "</"+list_type+">\n";
+                       return result;
+               });
+       } else {
+               whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ 
\t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
+               text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
+                       var runup = m1;
+                       var list = m2;
+
+                       var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
+                       // Turn double returns into triple returns, so that we can make a
+                       // paragraph for the last item in a list, if necessary:
+                       var list = list.replace(/\n{2,}/g,"\n\n\n");;
+                       var result = _ProcessListItems(list);
+                       result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";   
+                       return result;
+               });
+       }
+
+       // attacklab: strip sentinel
+       text = text.replace(/~0/,"");
+
+       return text;
+}
+
+_ProcessListItems = function(list_str) {
+//
+//  Process the contents of a single ordered or unordered list, splitting it
+//  into individual list items.
+//
+       // The $g_list_level global keeps track of when we're inside a list.
+       // Each time we enter a list, we increment it; when we leave a list,
+       // we decrement. If it's zero, we're not in a list anymore.
+       //
+       // We do this because when we're not inside a list, we want to treat
+       // something like this:
+       //
+       //    I recommend upgrading to version
+       //    8. Oops, now this line is treated
+       //    as a sub-list.
+       //
+       // As a single paragraph, despite the fact that the second line starts
+       // with a digit-period-space sequence.
+       //
+       // Whereas when we're inside a list (or sub-list), that line will be
+       // treated as the start of a sub-list. What a kludge, huh? This is
+       // an aspect of Markdown's syntax that's hard to parse perfectly
+       // without resorting to mind-reading. Perhaps the solution is to
+       // change the syntax rules such that sub-lists must start with a
+       // starting cardinal number; e.g. "1." or "a.".
+
+       g_list_level++;
+
+       // trim trailing blank lines:
+       list_str = list_str.replace(/\n{2,}$/,"\n");
+
+       // attacklab: add sentinel to emulate \z
+       list_str += "~0";
+
+       /*
+               list_str = list_str.replace(/
+                       (\n)?                                                   // leading line = $1
+                       (^[ \t]*)                                               // leading whitespace = $2
+                       ([*+-]|\d+[.]) [ \t]+                   // list marker = $3
+                       ([^\r]+?                                                // list item text   = $4
+                       (\n{1,2}))
+                       (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
+               /gm, function(){...});
+       */
+       list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ 
\t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
+               function(wholeMatch,m1,m2,m3,m4){
+                       var item = m4;
+                       var leading_line = m1;
+                       var leading_space = m2;
+
+                       if (leading_line || (item.search(/\n{2,}/)>-1)) {
+                               item = _RunBlockGamut(_Outdent(item));
+                       }
+                       else {
+                               // Recursion for sub-lists:
+                               item = _DoLists(_Outdent(item));
+                               item = item.replace(/\n$/,""); // chomp(item)
+                               item = _RunSpanGamut(item);
+                       }
+
+                       return  "<li>" + item + "</li>\n";
+               }
+       );
+
+       // attacklab: strip sentinel
+       list_str = list_str.replace(/~0/g,"");
+
+       g_list_level--;
+       return list_str;
+}
+
+
+var _DoCodeBlocks = function(text) {
+//
+//  Process Markdown `<pre><code>` blocks.
+//  
+
+       /*
+               text = text.replace(text,
+                       /(?:\n\n|^)
+                       (                                                               // $1 = the code 
block -- one or more lines, starting with a space/tab
+                               (?:
+                                       (?:[ ]{4}|\t)                   // Lines must start with a tab or a 
tab-width of spaces - attacklab: g_tab_width
+                                       .*\n+
+                               )+
+                       )
+                       (\n*[ ]{0,3}[^ \t\n]|(?=~0))    // attacklab: g_tab_width
+               /g,function(){...});
+       */
+
+       // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
+       text += "~0";
+       
+       text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
+               function(wholeMatch,m1,m2) {
+                       var codeblock = m1;
+                       var nextChar = m2;
+               
+                       codeblock = _EncodeCode( _Outdent(codeblock));
+                       codeblock = _Detab(codeblock);
+                       codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
+                       codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
+
+                       codeblock = "<pre><code>" + codeblock + "\n</code></pre>";
+
+                       return hashBlock(codeblock) + nextChar;
+               }
+       );
+
+       // attacklab: strip sentinel
+       text = text.replace(/~0/,"");
+
+       return text;
+}
+
+var hashBlock = function(text) {
+       text = text.replace(/(^\n+|\n+$)/g,"");
+       return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
+}
+
+
+var _DoCodeSpans = function(text) {
+//
+//   *  Backtick quotes are used for <code></code> spans.
+// 
+//   *  You can use multiple backticks as the delimiters if you want to
+//      include literal backticks in the code span. So, this input:
+//      
+//              Just type ``foo `bar` baz`` at the prompt.
+//      
+//        Will translate to:
+//      
+//              <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
+//      
+//     There's no arbitrary limit to the number of backticks you
+//     can use as delimters. If you need three consecutive backticks
+//     in your code, use four for delimiters, etc.
+//
+//  *  You can use spaces to get literal backticks at the edges:
+//      
+//              ... type `` `bar` `` ...
+//      
+//        Turns to:
+//      
+//              ... type <code>`bar`</code> ...
+//
+
+       /*
+               text = text.replace(/
+                       (^|[^\\])                                       // Character before opening ` can't 
be a backslash
+                       (`+)                                            // $2 = Opening run of `
+                       (                                                       // $3 = The code block
+                               [^\r]*?
+                               [^`]                                    // attacklab: work around lack of 
lookbehind
+                       )
+                       \2                                                      // Matching closer
+                       (?!`)
+               /gm, function(){...});
+       */
+
+       text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
+               function(wholeMatch,m1,m2,m3,m4) {
+                       var c = m3;
+                       c = c.replace(/^([ \t]*)/g,""); // leading whitespace
+                       c = c.replace(/[ \t]*$/g,"");   // trailing whitespace
+                       c = _EncodeCode(c);
+                       return m1+"<code>"+c+"</code>";
+               });
+
+       return text;
+}
+
+
+var _EncodeCode = function(text) {
+//
+// Encode/escape certain characters inside Markdown code runs.
+// The point is that in code, these characters are literals,
+// and lose their special Markdown meanings.
+//
+       // Encode all ampersands; HTML entities are not
+       // entities within a Markdown code span.
+       text = text.replace(/&/g,"&amp;");
+
+       // Do the angle bracket song and dance:
+       text = text.replace(/</g,"&lt;");
+       text = text.replace(/>/g,"&gt;");
+
+       // Now, escape characters that are magic in Markdown:
+       text = escapeCharacters(text,"\*_{}[]\\",false);
+
+// jj the line above breaks this:
+//---
+
+//* Item
+
+//   1. Subitem
+
+//            special char: *
+//---
+
+       return text;
+}
+
+
+var _DoItalicsAndBold = function(text) {
+
+       // <strong> must go first:
+       text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,
+               "<strong>$2</strong>");
+
+       text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
+               "<em>$2</em>");
+
+       return text;
+}
+
+
+var _DoBlockQuotes = function(text) {
+
+       /*
+               text = text.replace(/
+               (                                                               // Wrap whole match in $1
+                       (
+                               ^[ \t]*>[ \t]?                  // '>' at the start of a line
+                               .+\n                                    // rest of the first line
+                               (.+\n)*                                 // subsequent consecutive lines
+                               \n*                                             // blanks
+                       )+
+               )
+               /gm, function(){...});
+       */
+
+       text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
+               function(wholeMatch,m1) {
+                       var bq = m1;
+
+                       // attacklab: hack around Konqueror 3.5.4 bug:
+                       // "----------bug".replace(/^-/g,"") == "bug"
+
+                       bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0");       // trim one level of quoting
+
+                       // attacklab: clean up hack
+                       bq = bq.replace(/~0/g,"");
+
+                       bq = bq.replace(/^[ \t]+$/gm,"");               // trim whitespace-only lines
+                       bq = _RunBlockGamut(bq);                                // recurse
+                       
+                       bq = bq.replace(/(^|\n)/g,"$1  ");
+                       // These leading spaces screw with <pre> content, so we need to fix that:
+                       bq = bq.replace(
+                                       /(\s*<pre>[^\r]+?<\/pre>)/gm,
+                               function(wholeMatch,m1) {
+                                       var pre = m1;
+                                       // attacklab: hack around Konqueror 3.5.4 bug:
+                                       pre = pre.replace(/^  /mg,"~0");
+                                       pre = pre.replace(/~0/g,"");
+                                       return pre;
+                               });
+                       
+                       return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");
+               });
+       return text;
+}
+
+
+var _FormParagraphs = function(text) {
+//
+//  Params:
+//    $text - string to process with html <p> tags
+//
+
+       // Strip leading and trailing lines:
+       text = text.replace(/^\n+/g,"");
+       text = text.replace(/\n+$/g,"");
+
+       var grafs = text.split(/\n{2,}/g);
+       var grafsOut = new Array();
+
+       //
+       // Wrap <p> tags.
+       //
+       var end = grafs.length;
+       for (var i=0; i<end; i++) {
+               var str = grafs[i];
+
+               // if this is an HTML marker, copy it
+               if (str.search(/~K(\d+)K/g) >= 0) {
+                       grafsOut.push(str);
+               }
+               else if (str.search(/\S/) >= 0) {
+                       str = _RunSpanGamut(str);
+                       str = str.replace(/^([ \t]*)/g,"<p>");
+                       str += "</p>"
+                       grafsOut.push(str);
+               }
+
+       }
+
+       //
+       // Unhashify HTML blocks
+       //
+       end = grafsOut.length;
+       for (var i=0; i<end; i++) {
+               // if this is a marker for an html block...
+               while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
+                       var blockText = g_html_blocks[RegExp.$1];
+                       blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
+                       grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
+               }
+       }
+
+       return grafsOut.join("\n\n");
+}
+
+
+var _EncodeAmpsAndAngles = function(text) {
+// Smart processing for ampersands and angle brackets that need to be encoded.
+       
+       // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
+       //   http://bumppo.net/projects/amputator/
+       text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");
+       
+       // Encode naked <'s
+       text = text.replace(/<(?![a-z\/?\$!])/gi,"&lt;");
+       
+       return text;
+}
+
+
+var _EncodeBackslashEscapes = function(text) {
+//
+//   Parameter:  String.
+//   Returns:  The string, with after processing the following backslash
+//                        escape sequences.
+//
+
+       // attacklab: The polite way to do this is with the new
+       // escapeCharacters() function:
+       //
+       //      text = escapeCharacters(text,"\\",true);
+       //      text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
+       //
+       // ...but we're sidestepping its use of the (slow) RegExp constructor
+       // as an optimization for Firefox.  This function gets called a LOT.
+
+       text = text.replace(/\\(\\)/g,escapeCharacters_callback);
+       text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
+       return text;
+}
+
+
+var _DoAutoLinks = function(text) {
+
+       text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"<a href=\"$1\">$1</a>");
+
+       // Email addresses: <address domain foo>
+
+       /*
+               text = text.replace(/
+                       <
+                       (?:mailto:)?
+                       (
+                               [-.\w]+
+                               \@
+                               [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
+                       )
+                       >
+               /gi, _DoAutoLinks_callback());
+       */
+       text = text.replace(/<(?:mailto:)?([- \w]+\ [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
+               function(wholeMatch,m1) {
+                       return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
+               }
+       );
+
+       return text;
+}
+
+
+var _EncodeEmailAddress = function(addr) {
+//
+//  Input: an email address, e.g. "foo example com"
+//
+//  Output: the email address as a mailto link, with each character
+//     of the address encoded as either a decimal or hex entity, in
+//     the hopes of foiling most address harvesting spam bots. E.g.:
+//
+//     <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
+//        x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
+//        &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
+//
+//  Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
+//  mailing list: <http://tinyurl.com/yu7ue>
+//
+
+       // attacklab: why can't javascript speak hex?
+       function char2hex(ch) {
+               var hexDigits = '0123456789ABCDEF';
+               var dec = ch.charCodeAt(0);
+               return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
+       }
+
+       var encode = [
+               function(ch){return "&#"+ch.charCodeAt(0)+";";},
+               function(ch){return "&#x"+char2hex(ch)+";";},
+               function(ch){return ch;}
+       ];
+
+       addr = "mailto:"; + addr;
+
+       addr = addr.replace(/./g, function(ch) {
+               if (ch == "@") {
+                       // this *must* be encoded. I insist.
+                       ch = encode[Math.floor(Math.random()*2)](ch);
+               } else if (ch !=":") {
+                       // leave ':' alone (to spot mailto: later)
+                       var r = Math.random();
+                       // roughly 10% raw, 45% hex, 45% dec
+                       ch =  (
+                                       r > .9  ?       encode[2](ch)   :
+                                       r > .45 ?       encode[1](ch)   :
+                                                               encode[0](ch)
+                               );
+               }
+               return ch;
+       });
+
+       addr = "<a href=\"" + addr + "\">" + addr + "</a>";
+       addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
+
+       return addr;
+}
+
+
+var _UnescapeSpecialChars = function(text) {
+//
+// Swap back in all the special characters we've hidden.
+//
+       text = text.replace(/~E(\d+)E/g,
+               function(wholeMatch,m1) {
+                       var charCodeToReplace = parseInt(m1);
+                       return String.fromCharCode(charCodeToReplace);
+               }
+       );
+       return text;
+}
+
+
+var _Outdent = function(text) {
+//
+// Remove one level of line-leading tabs or spaces
+//
+
+       // attacklab: hack around Konqueror 3.5.4 bug:
+       // "----------bug".replace(/^-/g,"") == "bug"
+
+       text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
+
+       // attacklab: clean up hack
+       text = text.replace(/~0/g,"")
+
+       return text;
+}
+
+var _Detab = function(text) {
+// attacklab: Detab's completely rewritten for speed.
+// In perl we could fix it by anchoring the regexp with \G.
+// In javascript we're less fortunate.
+
+       // expand first n-1 tabs
+       text = text.replace(/\t(?=\t)/g,"    "); // attacklab: g_tab_width
+
+       // replace the nth with two sentinels
+       text = text.replace(/\t/g,"~A~B");
+
+       // use the sentinel to anchor our regex so it doesn't explode
+       text = text.replace(/~B(.+?)~A/g,
+               function(wholeMatch,m1,m2) {
+                       var leadingText = m1;
+                       var numSpaces = 4 - leadingText.length % 4;  // attacklab: g_tab_width
+
+                       // there *must* be a better way to do this:
+                       for (var i=0; i<numSpaces; i++) leadingText+=" ";
+
+                       return leadingText;
+               }
+       );
+
+       // clean up sentinels
+       text = text.replace(/~A/g,"    ");  // attacklab: g_tab_width
+       text = text.replace(/~B/g,"");
+
+       return text;
+}
+
+
+//
+//  attacklab: Utility functions
+//
+
+
+var escapeCharacters = function(text, charsToEscape, afterBackslash) {
+       // First we have to escape the escape characters so that
+       // we can build a character class out of them
+       var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g,"\\$1") + "])";
+
+       if (afterBackslash) {
+               regexString = "\\\\" + regexString;
+       }
+
+       var regex = new RegExp(regexString,"g");
+       text = text.replace(regex,escapeCharacters_callback);
+
+       return text;
+}
+
+
+var escapeCharacters_callback = function(wholeMatch,m1) {
+       var charCodeToEscape = m1.charCodeAt(0);
+       return "~E"+charCodeToEscape+"E";
+}
+
+} // end of Showdown.converter
\ No newline at end of file
diff --git a/docs/xml2json.js b/docs/xml2json.js
new file mode 100644
index 0000000..23650fe
--- /dev/null
+++ b/docs/xml2json.js
@@ -0,0 +1,155 @@
+/*     This work is licensed under Creative Commons GNU LGPL License.
+
+       License: http://creativecommons.org/licenses/LGPL/2.1/
+   Version: 0.9
+       Author:  Stefan Goessner/2006
+       Web:     http://goessner.net/ 
+*/
+function xml2json(xml, tab) {
+   var X = {
+      toObj: function(xml) {
+         var o = {};
+         if (xml.nodeType==1) {   // element node ..
+            if (xml.attributes.length)   // element with attributes  ..
+               for (var i=0; i<xml.attributes.length; i++)
+                  o["@"+xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue||"").toString();
+            if (xml.firstChild) { // element has child nodes ..
+               var textChild=0, cdataChild=0, hasElementChild=false;
+               for (var n=xml.firstChild; n; n=n.nextSibling) {
+                  if (n.nodeType==1) hasElementChild = true;
+                  else if (n.nodeType==3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) textChild++; // 
non-whitespace text
+                  else if (n.nodeType==4) cdataChild++; // cdata section node
+               }
+               if (hasElementChild) {
+                  if (textChild < 2 && cdataChild < 2) { // structured element with evtl. a single text 
or/and cdata node ..
+                     X.removeWhite(xml);
+                     for (var n=xml.firstChild; n; n=n.nextSibling) {
+                        if (n.nodeType == 3)  // text node
+                           o["#text"] = X.escape(n.nodeValue);
+                        else if (n.nodeType == 4)  // cdata node
+                           o["#cdata"] = X.escape(n.nodeValue);
+                        else if (o[n.nodeName]) {  // multiple occurence of element ..
+                           if (o[n.nodeName] instanceof Array)
+                              o[n.nodeName][o[n.nodeName].length] = X.toObj(n);
+                           else
+                              o[n.nodeName] = [o[n.nodeName], X.toObj(n)];
+                        }
+                        else  // first occurence of element..
+                           o[n.nodeName] = X.toObj(n);
+                     }
+                  }
+                  else { // mixed content
+                     if (!xml.attributes.length)
+                        o = X.escape(X.innerXml(xml));
+                     else
+                        o["#text"] = X.escape(X.innerXml(xml));
+                  }
+               }
+               else if (textChild) { // pure text
+                  if (!xml.attributes.length)
+                     o = X.escape(X.innerXml(xml));
+                  else
+                     o["#text"] = X.escape(X.innerXml(xml));
+               }
+               else if (cdataChild) { // cdata
+                  if (cdataChild > 1)
+                     o = X.escape(X.innerXml(xml));
+                  else
+                     for (var n=xml.firstChild; n; n=n.nextSibling)
+                        o["#cdata"] = X.escape(n.nodeValue);
+               }
+            }
+            if (!xml.attributes.length && !xml.firstChild) o = null;
+         }
+         else if (xml.nodeType==9) { // document.node
+            o = X.toObj(xml.documentElement);
+         }
+         else
+            alert("unhandled node type: " + xml.nodeType);
+         return o;
+      },
+      toJson: function(o, name, ind) {
+         var json = name ? ("\""+name+"\"") : "";
+         if (o instanceof Array) {
+            for (var i=0,n=o.length; i<n; i++)
+               o[i] = X.toJson(o[i], "", ind+"\t");
+            json += (name?":[":"[") + (o.length > 1 ? ("\n"+ind+"\t"+o.join(",\n"+ind+"\t")+"\n"+ind) : 
o.join("")) + "]";
+         }
+         else if (o == null)
+            json += (name&&":") + "null";
+         else if (typeof(o) == "object") {
+            var arr = [];
+            for (var m in o)
+               arr[arr.length] = X.toJson(o[m], m, ind+"\t");
+            json += (name?":{":"{") + (arr.length > 1 ? ("\n"+ind+"\t"+arr.join(",\n"+ind+"\t")+"\n"+ind) : 
arr.join("")) + "}";
+         }
+         else if (typeof(o) == "string")
+            json += (name&&":") + "\"" + o.toString() + "\"";
+         else
+            json += (name&&":") + o.toString();
+         return json;
+      },
+      innerXml: function(node) {
+         var s = ""
+         if ("innerHTML" in node)
+            s = node.innerHTML;
+         else {
+            var asXml = function(n) {
+               var s = "";
+               if (n.nodeType == 1) {
+                  s += "<" + n.nodeName;
+                  for (var i=0; i<n.attributes.length;i++)
+                     s += " " + n.attributes[i].nodeName + "=\"" + 
(n.attributes[i].nodeValue||"").toString() + "\"";
+                  if (n.firstChild) {
+                     s += ">";
+                     for (var c=n.firstChild; c; c=c.nextSibling)
+                        s += asXml(c);
+                     s += "</"+n.nodeName+">";
+                  }
+                  else
+                     s += "/>";
+               }
+               else if (n.nodeType == 3)
+                  s += n.nodeValue;
+               else if (n.nodeType == 4)
+                  s += "<![CDATA[" + n.nodeValue + "]]>";
+               return s;
+            };
+            for (var c=node.firstChild; c; c=c.nextSibling)
+               s += asXml(c);
+         }
+         return s;
+      },
+      escape: function(txt) {
+         return txt.replace(/[\\]/g, "\\\\")
+                   .replace(/[\"]/g, '\\"')
+                   .replace(/[\n]/g, '\\n')
+                   .replace(/[\r]/g, '\\r');
+      },
+      removeWhite: function(e) {
+         e.normalize();
+         for (var n = e.firstChild; n; ) {
+            if (n.nodeType == 3) {  // text node
+               if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) { // pure whitespace text node
+                  var nxt = n.nextSibling;
+                  e.removeChild(n);
+                  n = nxt;
+               }
+               else
+                  n = n.nextSibling;
+            }
+            else if (n.nodeType == 1) {  // element node
+               X.removeWhite(n);
+               n = n.nextSibling;
+            }
+            else                      // any other node
+               n = n.nextSibling;
+         }
+         return e;
+      }
+   };
+   if (xml.nodeType == 9) // document node
+      xml = xml.documentElement;
+   var json = X.toJson(X.toObj(X.removeWhite(xml)), xml.nodeName, "\t");
+   return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}";
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]