moving the onelab www to a fresh location
authorThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Sat, 27 Dec 2008 21:48:32 +0000 (21:48 +0000)
committerThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Sat, 27 Dec 2008 21:48:32 +0000 (21:48 +0000)
42 files changed:
Makefile
modules/planetlab.module
planetlab/bsn/bsn.Ajax.js [moved from planetlab/includes/js/bsn.Ajax.js with 100% similarity]
planetlab/bsn/bsn.AutoSuggest.js [moved from planetlab/includes/js/bsn.AutoSuggest.js with 100% similarity]
planetlab/bsn/bsn.DOM.js [moved from planetlab/includes/js/bsn.DOM.js with 100% similarity]
planetlab/css/demo.css [new file with mode: 0644]
planetlab/css/plc_style.css [moved from planetlab/includes/plc_style.css with 100% similarity]
planetlab/css/tablesort.css [new file with mode: 0644]
planetlab/events/index.php
planetlab/icons/comon.png [moved from planetlab/includes/comon.png with 100% similarity]
planetlab/icons/delete.png [moved from planetlab/includes/delete.png with 100% similarity]
planetlab/icons/event.png [moved from planetlab/includes/event.png with 100% similarity]
planetlab/includes/plc_drupal.php
planetlab/includes/plc_footer.php
planetlab/includes/plc_functions.php
planetlab/includes/plc_header.php
planetlab/includes/plc_login.php
planetlab/includes/plc_script.js
planetlab/includes/plc_session.php
planetlab/includes/plc_sorts.php
planetlab/nodes/add_node.php
planetlab/nodes/comon.php
planetlab/nodes/index.php
planetlab/nodes/interfaces.php
planetlab/nodes/newindex.php [new file with mode: 0644]
planetlab/nodes/node_actions.php
planetlab/nodes/node_groups.php
planetlab/nodes/setting_action.php
planetlab/nodes/settings.php
planetlab/nodes/sliver_action.php
planetlab/nodes/slivers.php
planetlab/persons/index.php
planetlab/sites/index.php
planetlab/slices/attrib_action.php [deleted file]
planetlab/slices/attributes.php [deleted file]
planetlab/slices/index.php
planetlab/slices/slice_nodes.php
planetlab/slices/tag_action.php [new file with mode: 0644]
planetlab/slices/tags.php [new file with mode: 0644]
planetlab/tablesort/customsort.js [new file with mode: 0644]
planetlab/tablesort/paginate.js [new file with mode: 0644]
planetlab/tablesort/tablesort.js [new file with mode: 0644]

index 3c771a1..d1c2f44 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,9 +4,22 @@ tags:
 
 .PHONY: tags
 
-########## make sync PLCHOST=hostname
+########## sync
+# 2 forms are supported
+# (*) if your plc root context has direct ssh access:
+# make sync PLC=private.one-lab.org
+# (*) otherwise, entering through the root context
+# make sync PLCHOST=testbox1.inria.fr GUEST=vplc03.inria.fr
+
+ifdef GUEST
 ifdef PLCHOST
-PLCSSH:=root@$(PLCHOST)
+SSHURL:=root@$(PLCHOST):/vservers/$(GUEST)
+SSHCOMMAND:=ssh root@$(PLCHOST) vserver $(GUEST)
+endif
+endif
+ifdef PLC
+SSHURL:=root@$(PLC):/
+SSHCOMMAND:=ssh root@$(PLC)
 endif
 
 LOCAL_RSYNC_EXCLUDES   := --exclude '*.pyc' 
@@ -15,10 +28,21 @@ RSYNC_COND_DRY_RUN  := $(if $(findstring n,$(MAKEFLAGS)),--dry-run,)
 RSYNC                  := rsync -a -v $(RSYNC_COND_DRY_RUN) $(RSYNC_EXCLUDES)
 
 sync:
-ifeq (,$(PLCSSH))
-       echo "sync: You must define target host as PLCHOST on the command line"
-       echo " e.g. make sync PLCHOST=private.one-lab.org" ; exit 1
+ifeq (,$(SSHURL))
+       @echo "sync: You must define, either PLC, or PLCHOST & GUEST, on the command line"
+       @echo "  e.g. make sync PLC=private.one-lab.org"
+       @echo "  or   make sync PLCHOST=testbox1.inria.fr GUEST=vplc03.inria.fr"
+       @exit 1
 else
-       +$(RSYNC) planetlab modules $(PLCSSH):/plc/root/var/www/html/
+       +$(RSYNC) planetlab modules $(SSHURL)/var/www/html/
 endif
 
+#################### convenience, for debugging only
+# make +foo : prints the value of $(foo)
+# make ++foo : idem but verbose, i.e. foo=$(foo)
+++%: varname=$(subst +,,$@)
+++%:
+       @echo "$(varname)=$($(varname))"
++%: varname=$(subst +,,$@)
++%:
+       @echo "$($(varname))"
index 73523c3..73cd389 100644 (file)
@@ -141,13 +141,24 @@ function planetlab_block($op = 'list', $delta = 0, $edit = array())
       $slice_item .= '<ul>';
       if( $is_admin || $is_pi ) {
        $slice_item .= "<li><a href='/db/slices/add_slice.php'>Create Slice</a></li>";
-       $slice_item .= "<li><a href='/db/slices/attributes.php'>Attribute Types</a></li>";
       }
       if( !( $is_tech && ! $is_user && ! $is_pi && ! $is_admin ) ) 
        $slice_item .= '<li><a href="/db/sirius/index.php">Sirius</a></li>';
       $slice_item .= '</ul>';
       $items [] = $slice_item;
 
+      if( $is_admin || $is_pi ) {
+       $tag_item = '';
+       $tag_item .= '<a href="/db/slices/tags.php?type=all">Tags</a>';
+       $tag_item .= '<ul>';
+       $tag_item .= "<li><a href='/db/slices/tags.php?type=node'>Node Tags</a></li>";
+       $tag_item .= "<li><a href='/db/slices/tags.php?type=interface'>Interface Tags</a></li>";
+       $tag_item .= "<li><a href='/db/slices/tags.php?type=slice'>Slice Tags</a></li>";
+       $tag_item .= '</ul>';
+       $items [] = $tag_item;
+      }
+      
+
       if ( $is_admin )
        $items[] = l(t('Peers'),'db/peers/');
      
diff --git a/planetlab/css/demo.css b/planetlab/css/demo.css
new file mode 100644 (file)
index 0000000..0d55fcd
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+   Add the following to your CSS file should you wish the cursor to
+   "wait" while the script is processing the sort
+
+body.sort-active *
+        {
+        cursor:wait;
+        }
+*/
+
+body
+        {
+        padding:0;
+        border:0;
+        margin:0;
+        text-align:center;
+        font-size:12px;
+        font-family: verdana,arial,sans-serif;
+        color:#545454;
+        min-width: 800px;
+        }
+h2 span
+        {
+        font-variant:small-caps;
+        }
+p a
+        {
+        font-weight:normal;
+        outline:none;
+        }
+p a:link,
+p a:visited
+        {
+        color:#333;
+        text-decoration:underline;
+        }
+p a:hover
+        {
+        color:#fff;
+        text-decoration:none;
+        background:#000;
+        }
+p a:active
+        {
+        color:#000;
+        text-decoration:underline;
+        }
+code
+        {
+        font-family:'andale mono','lucida console','courier new',monospace;
+        font-size:1em;
+        }
+p
+        {
+        line-height:1.6em;
+        margin:0 0 1em 0;
+        }
+h1
+        {
+        font-weight:lighter;
+        font-family:georgia, times new roman, times, georgia, palatino, serif;
+        text-align:center;
+        margin-top:0.6em;
+        color:#000;
+        font-size:2em;
+        }
+h2
+        {
+        font-weight:lighter;
+        font-family:verdana,arial,sans-serif;
+        text-align:center;
+        margin-top:1em;
+        color:#333;
+        text-transform:uppercase;
+        letter-spacing:1px;
+        font-size:1.2em;
+        }
+h2 span
+        {
+        font-variant:small-caps;
+        text-transform:none;
+        }
+table
+        {
+        width: auto;
+        padding: 0;
+        margin: 0 auto 1.5em auto;
+        border-left: 1px solid #C1DAD7;
+        border-collapse:collapse;
+        }
+.cs1
+        {
+        width:30em;
+        }
+.cs2
+        {
+        width:20em;
+        }
+.cs1 td,
+.cs2 td,
+#scientificNotation
+        {
+        text-align:right;
+        }
+caption
+        {
+        padding: 0 0 5px 0;
+        margin:0 auto;
+        width:auto;
+        font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
+        text-align: right;
+        }
+th
+        {
+        font: bold 10px/22px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
+        color: #4f6b72;
+        border-right: 1px solid #C1DAD7;
+        border-bottom: 1px solid #C1DAD7;
+        border-top: 1px solid #C1DAD7;
+        letter-spacing: 1px;
+        text-transform: uppercase;
+        text-align: left;
+        padding: 8px 12px 4px 12px;
+        background: #CAE8EA url(../media/bg_header.jpg) no-repeat;
+        vertical-align:middle;
+        }
+td.total
+        {
+        border-top: 0;
+        border-left: 0;
+        border-right: 1px solid #C1DAD7;
+        background: none;
+        text-align:right;
+        font-weight:bold;
+        text-transform:uppercase;
+        letter-spacing:1px;
+        }
+th.sortable,
+th.sortable-text,
+th.sortable-date,
+th.sortable-keep,
+th.sortable-date-dmy,
+th.sortable-numeric,
+th.sortable-currency,
+th.sortable-sortByTwelveHourTimestamp,
+th.sortable-sortIPAddress,
+th.sortable-sortEnglishLonghandDateFormat,
+th.sortable-sortScientificNotation,
+th.sortable-sortImage,
+th.sortable-sortFileSize,
+th.sortable-sortAlphaNumeric,
+th.sortable-sortEnglishDateTime
+
+        {
+        cursor:pointer;
+        background: #CAE8EA url(../media/bg_header_sortable.jpg) no-repeat;
+        padding: 8px 12px 4px 16px;
+        }
+th.forwardSort
+        {
+        background:#CAE8EA url(../media/bg_header_down.jpg) no-repeat 0 0;
+        }
+th.reverseSort
+        {
+        background:#CAE8EA url(../media/bg_header_up.jpg) no-repeat 0 0;
+        }
+table thead th.forwardSort a,
+table thead th.reverseSort a
+        {
+        color:#000;
+        text-decoration:none;
+        }
+/*
+These styles should be added when very long tables are expected
+th.sort-active
+        {
+        background:#CAE8EA url(../media/bg_header_sorting.jpg) no-repeat 0 0;
+        cursor:wait;
+        }
+th.sort-active a
+        {
+        color:#a80000 !important;
+        cursor:wait;
+        }
+*/
+th a
+        {
+        text-decoration:none;
+        color: #4f6b72;
+        background:transparent;
+        }
+td a
+        {
+        text-decoration:none;
+        color:#239;
+        background:transparent;
+        }
+td img
+        {
+        margin:0 auto;
+        border:3px solid #ddd;
+        }
+td a:hover
+        {
+        color:#a84444;
+        border-bottom:1px dotted #a80000;
+        background:transparent;
+        }
+td
+        {
+        font: normal 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
+        border-right: 1px solid #C1DAD7;
+        border-bottom: 1px solid #C1DAD7;
+        padding: 6px 12px 6px 12px;
+        color: #4f6b72;
+        }
+td.lft
+        {
+        text-align:left;
+        }
+tr.alt
+        {
+        background: #F5FAFA;
+        color: #797268;
+        }
+
+/* Poor old Internet Explorer 6 has a bug that means we can't use background images for the table rows
+   as it trys to download the image each and every time that it is used (which means a 1000 row table
+   will produce 1000 http requests for the image in question) */
+tr[class="alt"] td
+        {
+        background: #F5FAFA url(../media/td_alt.jpg) no-repeat;
+        }
+td[class~="alt"]
+        {
+        background: #edf3f3 url(../media/col_alt.jpg) no-repeat !important;
+        }
+/* Poor old Internet Explorer won't see the next two rules either as it doesn't get :first-child */
+tbody tr.alt td:first-child
+        {
+        background: #F5FAFA url(../media/bullet2.gif) no-repeat;
+        font-weight:bold;
+        }
+tbody tr td:first-child
+        {
+        background: #fff url(../media/bullet1.gif) no-repeat;
+        font-weight:bold;
+        }
+/* Image free rules for Internet Explorer < 7 */
+* html tr.alt td
+        {
+        background-color:#F5FAFA;
+        }
+* html tr td.alt,
+* html tr.alt td.alt
+        {
+        background-color:#edf3f3;
+        }
diff --git a/planetlab/css/tablesort.css b/planetlab/css/tablesort.css
new file mode 100644 (file)
index 0000000..0d55fcd
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+   Add the following to your CSS file should you wish the cursor to
+   "wait" while the script is processing the sort
+
+body.sort-active *
+        {
+        cursor:wait;
+        }
+*/
+
+body
+        {
+        padding:0;
+        border:0;
+        margin:0;
+        text-align:center;
+        font-size:12px;
+        font-family: verdana,arial,sans-serif;
+        color:#545454;
+        min-width: 800px;
+        }
+h2 span
+        {
+        font-variant:small-caps;
+        }
+p a
+        {
+        font-weight:normal;
+        outline:none;
+        }
+p a:link,
+p a:visited
+        {
+        color:#333;
+        text-decoration:underline;
+        }
+p a:hover
+        {
+        color:#fff;
+        text-decoration:none;
+        background:#000;
+        }
+p a:active
+        {
+        color:#000;
+        text-decoration:underline;
+        }
+code
+        {
+        font-family:'andale mono','lucida console','courier new',monospace;
+        font-size:1em;
+        }
+p
+        {
+        line-height:1.6em;
+        margin:0 0 1em 0;
+        }
+h1
+        {
+        font-weight:lighter;
+        font-family:georgia, times new roman, times, georgia, palatino, serif;
+        text-align:center;
+        margin-top:0.6em;
+        color:#000;
+        font-size:2em;
+        }
+h2
+        {
+        font-weight:lighter;
+        font-family:verdana,arial,sans-serif;
+        text-align:center;
+        margin-top:1em;
+        color:#333;
+        text-transform:uppercase;
+        letter-spacing:1px;
+        font-size:1.2em;
+        }
+h2 span
+        {
+        font-variant:small-caps;
+        text-transform:none;
+        }
+table
+        {
+        width: auto;
+        padding: 0;
+        margin: 0 auto 1.5em auto;
+        border-left: 1px solid #C1DAD7;
+        border-collapse:collapse;
+        }
+.cs1
+        {
+        width:30em;
+        }
+.cs2
+        {
+        width:20em;
+        }
+.cs1 td,
+.cs2 td,
+#scientificNotation
+        {
+        text-align:right;
+        }
+caption
+        {
+        padding: 0 0 5px 0;
+        margin:0 auto;
+        width:auto;
+        font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
+        text-align: right;
+        }
+th
+        {
+        font: bold 10px/22px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
+        color: #4f6b72;
+        border-right: 1px solid #C1DAD7;
+        border-bottom: 1px solid #C1DAD7;
+        border-top: 1px solid #C1DAD7;
+        letter-spacing: 1px;
+        text-transform: uppercase;
+        text-align: left;
+        padding: 8px 12px 4px 12px;
+        background: #CAE8EA url(../media/bg_header.jpg) no-repeat;
+        vertical-align:middle;
+        }
+td.total
+        {
+        border-top: 0;
+        border-left: 0;
+        border-right: 1px solid #C1DAD7;
+        background: none;
+        text-align:right;
+        font-weight:bold;
+        text-transform:uppercase;
+        letter-spacing:1px;
+        }
+th.sortable,
+th.sortable-text,
+th.sortable-date,
+th.sortable-keep,
+th.sortable-date-dmy,
+th.sortable-numeric,
+th.sortable-currency,
+th.sortable-sortByTwelveHourTimestamp,
+th.sortable-sortIPAddress,
+th.sortable-sortEnglishLonghandDateFormat,
+th.sortable-sortScientificNotation,
+th.sortable-sortImage,
+th.sortable-sortFileSize,
+th.sortable-sortAlphaNumeric,
+th.sortable-sortEnglishDateTime
+
+        {
+        cursor:pointer;
+        background: #CAE8EA url(../media/bg_header_sortable.jpg) no-repeat;
+        padding: 8px 12px 4px 16px;
+        }
+th.forwardSort
+        {
+        background:#CAE8EA url(../media/bg_header_down.jpg) no-repeat 0 0;
+        }
+th.reverseSort
+        {
+        background:#CAE8EA url(../media/bg_header_up.jpg) no-repeat 0 0;
+        }
+table thead th.forwardSort a,
+table thead th.reverseSort a
+        {
+        color:#000;
+        text-decoration:none;
+        }
+/*
+These styles should be added when very long tables are expected
+th.sort-active
+        {
+        background:#CAE8EA url(../media/bg_header_sorting.jpg) no-repeat 0 0;
+        cursor:wait;
+        }
+th.sort-active a
+        {
+        color:#a80000 !important;
+        cursor:wait;
+        }
+*/
+th a
+        {
+        text-decoration:none;
+        color: #4f6b72;
+        background:transparent;
+        }
+td a
+        {
+        text-decoration:none;
+        color:#239;
+        background:transparent;
+        }
+td img
+        {
+        margin:0 auto;
+        border:3px solid #ddd;
+        }
+td a:hover
+        {
+        color:#a84444;
+        border-bottom:1px dotted #a80000;
+        background:transparent;
+        }
+td
+        {
+        font: normal 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
+        border-right: 1px solid #C1DAD7;
+        border-bottom: 1px solid #C1DAD7;
+        padding: 6px 12px 6px 12px;
+        color: #4f6b72;
+        }
+td.lft
+        {
+        text-align:left;
+        }
+tr.alt
+        {
+        background: #F5FAFA;
+        color: #797268;
+        }
+
+/* Poor old Internet Explorer 6 has a bug that means we can't use background images for the table rows
+   as it trys to download the image each and every time that it is used (which means a 1000 row table
+   will produce 1000 http requests for the image in question) */
+tr[class="alt"] td
+        {
+        background: #F5FAFA url(../media/td_alt.jpg) no-repeat;
+        }
+td[class~="alt"]
+        {
+        background: #edf3f3 url(../media/col_alt.jpg) no-repeat !important;
+        }
+/* Poor old Internet Explorer won't see the next two rules either as it doesn't get :first-child */
+tbody tr.alt td:first-child
+        {
+        background: #F5FAFA url(../media/bullet2.gif) no-repeat;
+        font-weight:bold;
+        }
+tbody tr td:first-child
+        {
+        background: #fff url(../media/bullet1.gif) no-repeat;
+        font-weight:bold;
+        }
+/* Image free rules for Internet Explorer < 7 */
+* html tr.alt td
+        {
+        background-color:#F5FAFA;
+        }
+* html tr td.alt,
+* html tr.alt td.alt
+        {
+        background-color:#edf3f3;
+        }
index 398530f..0e2f245 100644 (file)
@@ -289,11 +289,6 @@ if ( !in_array ('10', $_roles)) {
   
  } else {
 
-  //  // xxx this of any use at all ?
-  //  drupal_set_html_head('<script type="text/javascript" src="/planetlab/includes/js/bsn.Ajax.js"></script>
-  //    <script type="text/javascript" src="/planetlab/includes/js/bsn.DOM.js"></script>
-  //    <script type="text/javascript" src="/planetlab/includes/js/bsn.AutoSuggest.js"></script>');
-
   // handle dates
   list($from_date,$from_time,$until_date,$until_time) = parse_dates ();
   if ( ($from_time != 0) && ($until_time != 0) && ($from_time > $until_time) ) {
index 8034d5c..103762a 100644 (file)
@@ -5,7 +5,7 @@
 // Mark Huang <mlhuang@cs.princeton.edu>
 // Copyright (C) 2006 The Trustees of Princeton University
 //
-// $Id: plc_drupal.php 144 2007-03-28 07:52:20Z thierry $ $
+// $Id$ $
 //
 
 if (!function_exists('drupal_set_title')) {
index 252a218..ad509af 100644 (file)
@@ -6,7 +6,7 @@
 // Mark Huang <mlhuang@cs.princeton.edu>
 // Copyright (C) 2006 The Trustees of Princeton University
 //
-// $Id: plc_footer.php 144 2007-03-28 07:52:20Z thierry $ $
+// $Id$ $
 //
 
 require_once 'plc_drupal.php';
index 3809c76..9e7a81d 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+// $Id$
+
 function plc_person_link ($person_id) {
   if ( empty ($person_id)) {
     return "";
@@ -336,7 +338,7 @@ function plc_debug($message,$object) {
 
 // attempt to normalize the delete buttons and confirmations
 function plc_delete_button($width=15) {
-  return '<span title="Delete this entry"><img width=' . $width . ' alt="Delete this entry" src="/planetlab/includes/delete.png"></span>';
+  return '<span title="Delete this entry"><img width=' . $width . ' alt="Delete this entry" src="/planetlab/icons/delete.png"></span>';
 }
 
 function plc_js_confirm($message) {
@@ -352,7 +354,7 @@ function plc_delete_link_button($url,$delete_message,$width=15) {
 }
 
 function plc_event_button($type,$param,$id) {
-  return '<a href="/db/events/index.php?type=' . $type . '&' . $param . '=' . $id . '"> <span title="Related events"> <img src="/planetlab/includes/event.png" width=18></span></a>';
+  return '<a href="/db/events/index.php?type=' . $type . '&' . $param . '=' . $id . '"> <span title="Related events"> <img src="/planetlab/icons/event.png" width=18></span></a>';
 }
 
 function plc_comon_button ($field, $value,$target="") {
@@ -361,7 +363,7 @@ function plc_comon_button ($field, $value,$target="") {
     $result.='target="' . $target . '" ';
   }
   $result.='href="/db/nodes/comon.php?' . $field . "=" . $value . '">';
-  $result.='<span title="Link to Comon"> <img src="/planetlab/includes/comon.png" width="18"></span></a>';
+  $result.='<span title="Link to Comon"> <img src="/planetlab/icons/comon.png" width="18"></span></a>';
   return $result;
 }
 
index 34b552c..274ed54 100644 (file)
@@ -6,11 +6,11 @@
 // Mark Huang <mlhuang@cs.princeton.edu>
 // Copyright (C) 2006 The Trustees of Princeton University
 //
-// $Id: plc_header.php 144 2007-03-28 07:52:20Z thierry $ $
+// $Id$ $
 //
 
 require_once 'plc_drupal.php';
-drupal_set_html_head('<link href="/planetlab/includes/plc_style.css" rel="stylesheet" type="text/css"/>');
+drupal_set_html_head('<link href="/planetlab/css/plc_style.css" rel="stylesheet" type="text/css"/>');
 
 if (!function_exists('drupal_page_header')) {
   $title = drupal_get_title();
index b8d5b63..db9440a 100644 (file)
@@ -7,7 +7,7 @@
 // Mark Huang <mlhuang@cs.princeton.edu>
 // Copyright (C) 2006 The Trustees of Princeton University
 //
-// $Id: plc_login.php 144 2007-03-28 07:52:20Z thierry $ $
+// $Id$ $
 //
 
 require_once 'plc_session.php';
index 2c1e05d..d96af22 100644 (file)
@@ -1,35 +1,37 @@
 
 function addLoadEvent(func) {
-  if (!document.getElementById | !document.getElementsByTagName) return
-       var oldonload=window.onload
-       if (typeof window.onload != 'function') { window.onload=func }
-       else {
-               window.onload=function() { oldonload(); func() }
-       }
+  if (!document.getElementById | !document.getElementsByTagName) return;
+  var oldonload=window.onload
+    if (typeof window.onload != 'function') { 
+      window.onload=func 
+    } else {
+      window.onload=function() { oldonload(); func() }
+    }
 }
 
 // ----------------------------------------------------------------------------- //
 
-function show(id){
-       if (!document.getElementsByTagName) return
-       if (document.getElementById(id).style.display=='block'){
-               document.getElementById(id).style.display='none'
-       }
-       else{
-               document.getElementById(id).style.display='block'
-       }
-       //  focus is moved by the href of the link to the start of the help
+function show (id) {
+  if (!document.getElementsByTagName) return;
+  if (document.getElementById(id).style.display=='block') {
+    document.getElementById(id).style.display='none';
+  } else{
+    document.getElementById(id).style.display='block';
+  }
+  //  focus is moved by the href of the link to the start of the help
 }
 
-
-function hide(id){
-       if (document.getElementById) {document.getElementById(id).style.display='none'}
+function hide(id) {
+  if (document.getElementById) {
+    document.getElementById(id).style.display='none';
+  }
 }
 
 function copyValue (id1,id2) {
-  if (document.getElementById) {document.getElementById(id2).value=document.getElementById(id1).value}
+  if (document.getElementById) {
+    document.getElementById(id2).value=document.getElementById(id1).value;
+  }
 }
 
-
 // ----------------------------------------------------------------------------- //
 
index f092f0f..afd008b 100644 (file)
@@ -15,7 +15,7 @@
 // Mark Huang <mlhuang@cs.princeton.edu>
 // Copyright (C) 2006 The Trustees of Princeton University
 //
-// $Id: plc_session.php 804 2007-08-31 13:58:58Z thierry $ $
+// $Id$ $
 //
 
 // Usually in /etc/planetlab/php
index c29bc5f..4b5b1d4 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+// $Id$
+
 // person sort on last name, first name, email
 function __cmp_persons($a, $b) {
   $persona = $a['last_name'] . $a['first_name'] . $a['email'];
@@ -11,6 +13,10 @@ function sort_persons(&$persons) {
   return usort($persons, "__cmp_persons");
 }
 
+function topdomain ($hostname) {
+  $exploded=array_reverse(explode(".",$hostname));
+  return $exploded[0];
+}
 
 function __cmp_nodes($a, $b) {
  $as = array_reverse(explode(".", $a['hostname']));
index 03fe6f0..f899cde 100644 (file)
@@ -93,7 +93,7 @@ if( $_POST['submitted'] )
   {
     $success= 1;
 
-    // add new node and its network
+    // add new node and its interface
     $optional_vals= array( "hostname"=>$hostname, "model"=>$model );
 
     $site_id= $_person['site_ids'][0];
index 22ca872..18b2623 100644 (file)
@@ -51,17 +51,17 @@ if ($_GET['node_id']) {
   $nodes=$api->GetNodes(array("node_id"=>array($node_id)),$fields);
  } else if ($_GET['site_id']) {
   $site_id=intval($_GET['site_id']);
-  $nodes=$api->GetNodes(array("site_id"=>array($site_id)),$fields);
+  $nodes=$api->GetNodes(array("node_type"=>"regular","site_id"=>array($site_id)),$fields);
  } else if ($_GET['slice_id']) {
   $slice_id=intval($_GET['slice_id']);
   $return=$api->GetSlices(array("slice_id"=>array($slice_id)),array("node_ids"));
   $node_ids=$return[0]['node_ids'];
-  $nodes=$api->GetNodes(array("node_id"=>$node_ids),$fields);
+  $nodes=$api->GetNodes(array("node_type"=>"regular","node_id"=>$node_ids),$fields);
  } else if (isset($_GET['peer_id'])) {
   $peer_id=intval($_GET['peer_id']);
   if ( ($peer_id == 0) || ($peer_id == "") )
     $peer_id=NULL;
-  $nodes=$api->GetNodes(array("peer_id"=>$peer_id),$fields);
+  $nodes=$api->GetNodes(array("node_type"=>"regular","peer_id"=>$peer_id),$fields);
  } else {
   echo "<div class='plc-warning'> Unexpected args in comon.php </div>\n";
   exit();
index 355ade4..395ddce 100644 (file)
@@ -26,7 +26,7 @@ $_roles= $_person['role_ids'];
 // The set of columns to fetch
 // and the filter applied for fetching sites
 $columns = array( "node_id", "hostname", "boot_state", "peer_id" ) ;
-$filter = array();
+$filter = array("node_type"=>"regular");
 if ( in_array( '10', $_roles ) || in_array('20', $_roles) || in_array('40',$_roles)) {
   // admins, PIs and techs can see interface details
   $columns [] = "interface_ids";
@@ -162,9 +162,9 @@ elseif( !$_GET['id'] ) {
     $nodes = array_map(layout_node,$nodes);
     sort_nodes( $nodes );
 
-    drupal_set_html_head('<script type="text/javascript" src="/planetlab/includes/js/bsn.Ajax.js"></script>
-    <script type="text/javascript" src="/planetlab/includes/js/bsn.DOM.js"></script>
-    <script type="text/javascript" src="/planetlab/includes/js/bsn.AutoSuggest.js"></script>');
+    drupal_set_html_head('<script type="text/javascript" src="/planetlab/bsn/bsn.Ajax.js"></script>
+    <script type="text/javascript" src="/planetlab/bsn/bsn.DOM.js"></script>
+    <script type="text/javascript" src="/planetlab/bsn/bsn.AutoSuggest.js"></script>');
 
     echo "<div>\n
         <form method=get action='index.php'>\n";
@@ -246,7 +246,7 @@ if ( $_GET['id'] ) {
     if( !empty( $conf_file_ids ) )
       $conf_files= $api->GetConfFiles( $conf_file_ids );
 
-    // get node network info
+    // get interface info
     if( !empty( $interface_ids ) )
       $interfaces= $api->GetInterfaces( $interface_ids );
 
@@ -380,10 +380,10 @@ if ( $_GET['id'] ) {
 
       echo "<hr />\n";
 
-      // display node networks
+      // display interfaces
       if( $interfaces ) {
        echo "<p><table class='list_set' border=0 cellpadding=2>\n";
-       echo "<caption class='list_set'>Node Networks</caption>\n";
+       echo "<caption class='list_set'>Interfaces</caption>\n";
        echo "<thead><tr class='list_set'>";
        // placeholder for the delete buttons
        if ( $is_admin || ($is_pi && $in_site)) {
@@ -411,9 +411,9 @@ if ( $_GET['id'] ) {
          if ( $is_admin || ($is_pi && $in_site)) {
            echo "<td class='list_set'>";
            if (!$nn_primary) {
-             echo plc_delete_link_button('interfaces.php?id=' . $nn_id . '&delete=1&submitted=1', '\\nNode Network ' . $nn_ip);
+             echo plc_delete_link_button('interfaces.php?id=' . $nn_id . '&delete=1&submitted=1', '\\nInterface ' . $nn_ip);
            } else {
-             echo '<span title="This node network is primary"> P </span>';
+             echo '<span title="This interface is primary"> P </span>';
            }
            echo "</td>";
          }
@@ -429,10 +429,10 @@ if ( $_GET['id'] ) {
        echo "</tbody></table>\n";
 
       } else {
-       echo "<p><span class='plc-warning'>No Node Network</span>.  Please add a node network to make this a usable PLC node</p>.\n";
+       echo "<p><span class='plc-warning'>No interface</span>.  Please add an interface to make this a usable PLC node</p>.\n";
       }
 
-      echo "<br /><a href='interfaces.php?node_id=$node_id'>Add a node network</a>.\n";
+      echo "<br /><a href='interfaces.php?node_id=$node_id'>Add an interface</a>.\n";
       echo "<br /><hr />\n";
     }
 
index 7bada08..ab5e60e 100644 (file)
@@ -188,48 +188,44 @@ echo <<<EOF
 </form>
 EOF;
 
-// displays related settings, if supported by the API
-if (method_exists ($api,'GetInterfaceTags')) {
-
-  $is_admin=in_array( 10, $_roles );
-  $is_pi=in_array( 20, $_roles );
-  print "<hr />";
-
-  if (empty ($interface['interface_tag_ids'])) {
-    print "<p> This network interface has no additional setting</p>";
-    if( $is_admin || $is_pi )
-      echo "<p><a href='settings.php?add=$id'>Add a Network Setting</a></p>\n";
-  } else {
-    $interface_tags = $api->GetInterfaceTags($interface['interface_tag_ids']);
-    sort_interface_tags ($interface_tags);
-    print "<table cellpadding='5' cellspacing='5' class='list_set'><caption class='list_set'>Additional Settings</caption>";
-    print "<thead><tr class='list_set'>";
-    // the column for the delete button
-    if( $is_admin )
-      print "<th></th>";
-    print "<th class='list_set'>Name</th><th class='list_set'>Category</th><th class='list_set'>Description</th><th class='list_set'>Value</th></tr></thead><tbody>";
-    foreach ($interface_tags as $setting) {
-      echo "<tr class='list_set'>";
-      if ($is_admin) {
-       echo("<td>");
-       echo plc_delete_link_button('setting_action.php?rem_id=' . $setting['interface_tag_id'],
-                                   '\\n [ ' . $setting['name'] . ' = ' . $setting['value']);
-       echo("</td>");
+$is_admin=in_array( 10, $_roles );
+$is_pi=in_array( 20, $_roles );
+print "<hr />";
+
+if (empty ($interface['interface_tag_ids'])) {
+  print "<p> This network interface has no additional setting</p>";
+  if( $is_admin || $is_pi )
+    echo "<p><a href='settings.php?add=$id'>Add an Interface Setting</a></p>\n";
+ } else {
+  $interface_tags = $api->GetInterfaceTags($interface['interface_tag_ids']);
+  sort_interface_tags ($interface_tags);
+  print "<table cellpadding='5' cellspacing='5' class='list_set'><caption class='list_set'>Additional Settings</caption>";
+  print "<thead><tr class='list_set'>";
+  // the column for the delete button
+  if( $is_admin )
+    print "<th></th>";
+  print "<th class='list_set'>Name</th><th class='list_set'>Category</th><th class='list_set'>Description</th><th class='list_set'>Value</th></tr></thead><tbody>";
+  foreach ($interface_tags as $setting) {
+    echo "<tr class='list_set'>";
+    if ($is_admin) {
+      echo("<td>");
+      echo plc_delete_link_button('setting_action.php?rem_id=' . $setting['interface_tag_id'],
+                                 '\\n [ ' . $setting['tagname'] . ' = ' . $setting['value']);
+      echo("</td>");
     }
-      if ($is_admin || $is_pi) 
-       printf ("<td class='list_set'> <a href='settings.php?id=%s'>%s </a></td>",$setting['interface_tag_id'],$setting['name']);
-      else
-       printf ("<td class='list_set'> %s </td>",$setting['name']);
-      printf ("<td class='list_set'> %s</td><td class='list_set'> %s</td><td class='list_set'> %s </td></tr>",
-             $setting['category'],
-             $setting['description'],
-             $setting['value']);
-    }
-    if( $is_admin || $is_pi )
-      echo "<tr><td colspan=4><a href='settings.php?add=$id'>Add a Network Setting</td</tr>\n";
-    
-    print "</tbody></table>";
+    if ($is_admin || $is_pi) 
+      printf ("<td class='list_set'> <a href='settings.php?id=%s'>%s </a></td>",$setting['interface_tag_id'],$setting['tagname']);
+    else
+      printf ("<td class='list_set'> %s </td>",$setting['tagname']);
+    printf ("<td class='list_set'> %s</td><td class='list_set'> %s</td><td class='list_set'> %s </td></tr>",
+           $setting['category'],
+           $setting['description'],
+           $setting['value']);
   }
+  if( $is_admin || $is_pi )
+    echo "<tr><td colspan=4><a href='settings.php?add=$id'>Add a Network Setting</td</tr>\n";
+  
+  print "</tbody></table>";
  }
 
 echo <<<EOF
diff --git a/planetlab/nodes/newindex.php b/planetlab/nodes/newindex.php
new file mode 100644 (file)
index 0000000..c885159
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+
+// $Id: index.php 1175 2008-02-07 16:20:15Z thierry $
+
+// Require login
+require_once 'plc_login.php';
+
+// Get session and API handles
+require_once 'plc_session.php';
+global $plc, $api, $adm;
+
+// Print header
+require_once 'plc_drupal.php';
+drupal_set_title('New Nodes');
+include 'plc_header.php';
+
+// Common functions
+require_once 'plc_functions.php';
+require_once 'plc_sorts.php';
+
+// find person roles
+$_person= $plc->person;
+$_roles= $_person['role_ids'];
+
+$header_js='
+<script type="text/javascript" src="/planetlab/tablesort/tablesort.js"></script>
+<script type="text/javascript" src="/planetlab/tablesort/customsort.js"></script>
+<script type="text/javascript" src="/planetlab/tablesort/paginate.js"></script>
+<script type="text/javascript" src="/planetlab/tablesort/more.js"></script>
+<body OnLoad="init();">
+';
+$header_css='
+<link href="/planetlab/css/demo.css" rel="stylesheet" type="text/css" />
+<link href="/planetlab/css/more.css" rel="stylesheet" type="text/css" />
+<!--[if IE]>
+<style type="text/css">
+ul.fdtablePaginater {display:inline-block;}
+mul.fdtablePaginater {display:inline;}
+ul.fdtablePaginater li {float:left;}
+ul.fdtablePaginater {text-align:center;}
+table { border-bottom:1px solid #C1DAD7; }
+</style>
+<![endif]-->
+';
+
+$header_unused='
+';
+
+drupal_set_html_head($header_js);
+drupal_set_html_head($header_css);
+
+$site_columns=array("site_id","login_base");
+$site_filter=array("login_base"=>"*");
+$sites=$api->GetSites($site_filter,$site_columns);
+
+$site_hash=array();
+foreach ($sites as $site) {
+    $site_hash[$site["site_id"]]=$site;
+}
+
+$node_columns=array("hostname","site_id","node_id","boot_state");
+$node_filter=array("hostname"=>"*");
+$nodes=$api->GetNodes($node_filter,$node_columns);
+
+$interface_columns=array("ip","node_id");
+$interface_filter=array("is_primary"=>TRUE);
+$interfaces=$api->GetInterfaces($interface_filter,$interface_columns);
+
+$interface_hash=array();
+foreach ($interfaces as $interface) {
+    $interface_hash[$interface['node_id']]=$interface;
+}
+
+?>
+
+<table id="theTable" cellpadding="0" cellspacing="0" border="0" 
+class="sortable-onload-2 rowstyle-alt colstyle-alt no-arrow paginate-50 max-pages-10">
+<thead>
+<tr>
+<th align=top>Select </th>
+<th class="sortable">State</th>
+<th class="sortable">Hostname</th>
+<th class="sortable">Site</th>
+<th class="sortable">Region</th>
+<th class="sortable-sortIPAddress">IP</th>
+<th class="sortable">Load</th>
+<th class="sortable">Avg Load</th>
+</tr>
+</thead>
+<tbody>
+
+<?php
+
+foreach ($nodes as $node) {
+    $hostname=$node['hostname'];
+    $site=$site_hash[$node['site_id']];
+    $login_base = $site['login_base'];
+    $node_id=$node['node_id'];
+    $ip=$interface_hash[$node['node_id']]['ip'];
+    printf ('<tr id="%s">',$hostname);
+    printf ('<td> <input type="checkbox" id="%s"></td>',$hostname);
+    printf ('<td> %s </td>',$node['boot_state']);
+    printf ('<td> %s </td>',$hostname);
+    printf ('<td> %s </td>',$login_base);
+    printf ('<td> %s </td>',topdomain($hostname));
+    printf ('<td> %s </td>',$ip);
+    printf ('<td> 1.0 </td>');
+    printf ('<td> 10.0 </td>');
+    printf ( '</tr>');
+}
+
+?>
+</tbody>
+<tfoot>
+</tfoot>
+</table>
+
index 5d17aa0..20a88de 100644 (file)
@@ -357,7 +357,7 @@ a new node key, and any existing configuration file will be unusable and
 go into debug mode.
 
 <p>In order to create a configuration file for this node using this page,
-all the node network settings must be up to date. Below is summary of these
+all the interface settings must be up to date. Below is summary of these
 values. Any missing values must be entered before this can be used.
 
 EOF;
@@ -366,7 +366,7 @@ EOF;
 
    show_download_confirm_button($api, $node_id, $action, $can_gen_config, false);
    print ("<p>");
-   print ("<h3>Current node network settings</h3>\n");
+   print ("<h3>Current interface settings</h3>\n");
    
 if( $has_primary ) {
   print( "<table border=\"0\" cellspacing=\"4\">\n" );
@@ -378,7 +378,7 @@ if( $has_primary ) {
   print( "<td>" . $node_detail['hostname'] . "</td></tr>\n" );
 
   $nn_id = $interface_detail['interface_id'];
-  print( "<tr><th colspan=2><a href='interfaces.php?id=$nn_id'>Node Network Details</a></th></tr>" );
+  print( "<tr><th colspan=2><a href='interfaces.php?id=$nn_id'>Interface Details</a></th></tr>" );
 
   print( "<tr><th>Method:</th>" );
   print( "<td>" . $interface_detail['method'] . "</td></tr>\n" );
@@ -409,14 +409,14 @@ if( $has_primary ) {
   $settings=$api->GetInterfaceTags(array("interface_id" => array($nn_id)));
   foreach ($settings as $setting) {
     $category=$setting['category'];
-    $name=$setting['name'];
+    $name=$setting['tagname'];
     $value=$setting['value'];
     print (" <tr><th> $category $name </th><td> $value </td></tr>\n");
   }
 
   print( "</table>\n" );
 } else {
-  print( "<p class='plc-warning'>This node has no configured primary network.</p>\n" );
+  print( "<p class='plc-warning'>This node has no configured primary interface.</p>\n" );
 }
 
  show_download_confirm_button($api, $node_id, $action, $can_gen_config, true);
index aad5f18..e82b2b6 100644 (file)
@@ -68,7 +68,8 @@ if( !$_GET['id'] && !$_GET['nodegroup_id'] ) {
 elseif( $_GET['id'] ) {
   $nodegroup_id= $_GET['id'];
 
-  $nodegroup_info= $api->GetNodeGroups( array( intval( $nodegroup_id ) ), array( "name", "nodegroup_id", "node_ids" ) );
+  $nodegroup_info= $api->GetNodeGroups( array( intval( $nodegroup_id ) ), 
+                                       array( "name", "nodegroup_id", "node_ids" ) );
   $node_info= $api->GetNodes( $nodegroup_info[0]['node_ids'], array( "node_id", "hostname" ) );
 
   //display info 
@@ -183,7 +184,8 @@ elseif( $_GET['nodegroup_id'] )
     $api->UpdateNodeGroup( intval( $node_group_id ), $fields );
 
   // get node_group info
-  $group_info= $api->GetNodeGroups( array( intval( $node_group_id ) ), array( "node_ids", "name", "conf_file_ids", "description" ) );
+  $group_info= $api->GetNodeGroups( array( intval( $node_group_id ) ),
+                                   array( "node_ids", "name", "conf_file_ids", "description" ) );
 
   $node_ids = $group_info[0]['node_ids'];
   $name = $group_info[0]['name'];
@@ -192,7 +194,8 @@ elseif( $_GET['nodegroup_id'] )
 
   // get node info
   if( !empty( $node_ids ) )
-    $node_info= $api->GetNodes( $node_ids, array( "hostname", "node_id" ) );
+    $node_info= $api->GetNodes( $node_ids, 
+                               array( "hostname", "node_id" ) );
 
   // get site names and ids
   $site_info= $api->GetSites( NULL, array( "site_id", "name" ) );
index 22b5a79..1d1974b 100644 (file)
@@ -27,7 +27,7 @@ $_roles= $_person['role_ids'];
 //plc_debug('GET',$_GET);
 //plc_debug('POST',$_POST);
 
-// attribute type updates
+// tag type updates
 if( $_POST['edit_type'] ) {
   $setting_type_id= intval( $_POST['interface_tag_type_id'] );
   $setting_type = array ('category' => $_POST['category'],
@@ -46,7 +46,7 @@ if( $_POST['edit_type'] ) {
   exit();
 }
 
-// attribute type adds
+// tag type adds
 if( $_POST['add_type'] ) {
   $setting_type = array ('category' => $_POST['category'],
                         'name' => $_POST['name'],
@@ -60,25 +60,25 @@ if( $_POST['add_type'] ) {
 }
   
 
-// attribute deletion
+// tag deletion
 if( $_GET['rem_id'] ) {
-  // get the id of the attrib to remove from GET
+  // get the id of the tag to remove from GET
   $setting_id= intval( $_GET['rem_id'] );
 
   // get interface_id 
   $setting= $api->GetInterfaceTags( array( $setting_id ), array( "interface_id" ) );
   $interface_id= $setting[0]['interface_id'];
   
-  // delete the attribute
+  // delete the tag
   $api->DeleteInterfaceTag( $setting_id );
 
   header( "location: interfaces.php?id=$interface_id" );
   exit();
 }
 
-// attribute adds
+// tag adds
 if( $_POST['add_setting'] ) {
-  // get the interface_id, attribute_type_id, and value from POST
+  // get the interface_id, tag_type_id, and value from POST
   $interface_id= intval( $_POST['interface_id'] );
   $interface_tag_type_id= intval( $_POST['interface_tag_type_id'] );
   $value= $_POST['value'];
@@ -90,7 +90,7 @@ if( $_POST['add_setting'] ) {
   exit();
 }
 
-// attribute updates
+// tag updates
 if( $_POST['edit_setting'] ) {
   // get the id of the setting to update and the value from POST
   $setting_id= intval( $_POST['setting_id'] );
@@ -104,12 +104,11 @@ if( $_POST['edit_setting'] ) {
   exit();
 }
 
-// down here is some code from attrib_action.php that was not converted yet
 // Settings -------------------------------------------------
 
-// ATTRIBUTE TYPES ---------------------------------------------------
+// TAG TYPES ---------------------------------------------------
   
-// delete attribute types
+// delete tag types
 if( $_GET['del_type'] ) {
   // get vars
   $type_id= intval( $_GET['del_type'] );
@@ -120,8 +119,6 @@ if( $_GET['del_type'] ) {
   header( "location: settings.php" );
   exit();
 }
-
-  
   
 /*
 // Print footer
index a9c9d53..a535b19 100644 (file)
@@ -24,7 +24,7 @@ $_roles= $_person['role_ids'];
 
 //plc_debug("person", $_person );
 
-$columns=array( "interface_tag_type_id", "category", "name", "description", "min_role_id" );
+$columns=array( "tag_type_id", "category", "tagname", "description", "min_role_id" );
 
 // prepare dict role_id => role_name
 global $roles;
@@ -60,11 +60,11 @@ function layout_setting_type ($setting_type) {
   return $setting_type;
 }
 
-// if no id, display list of attributes types
+// if no id, display list of tag types
 if( !$_GET['id'] && !$_GET['add'] && !$_GET['add_type'] && !$_GET['edit_type'] ) {
   // get types
   global $person_role;
-  $filter = array (']min_role_id'=>$person_role);
+  $filter = array (']min_role_id'=>$person_role,'category'=>'interface*');
   $setting_types= $api->GetTagTypes( $filter, $columns );
   $setting_types = array_map(layout_setting_type,$setting_types);
   sort_interface_tags ($setting_types);
@@ -90,15 +90,15 @@ if( !$_GET['id'] && !$_GET['add'] && !$_GET['add_type'] && !$_GET['edit_type'] )
     // if admin display delete links
     if(  in_array( "10", $_person['role_ids'] ) ) {
       echo "<td>";
-      echo plc_delete_link_button('setting_action.php?del_type='. $type['interface_tag_type_id'],
-                                 $type['name']);
+      echo plc_delete_link_button('setting_action.php?del_type='. $type['tag_type_id'],
+                                 $type['tagname']);
       echo "</td>";
     }
     // if admin, the name is a link to edition
     if (in_array( "10", $_person['role_ids'])) {
-      echo "<td><a href='settings.php?edit_type=". $type['interface_tag_type_id'] . "'>" . $type['name'] . "</a></td>";
+      echo "<td><a href='settings.php?edit_type=". $type['tag_type_id'] . "'>" . $type['tagname'] . "</a></td>";
     } else {
-      echo "<td>" . $type['name'] . "</td>";
+      echo "<td>" . $type['tagname'] . "</td>";
     }
     echo "<td>" . $type['category'] . "</td>";
     echo "<td>" . $type['min_role'] . "</td><td>" . $type['min_role_id'] . "</td><td>" . $type['description'] . "</td>";
@@ -116,13 +116,13 @@ if( !$_GET['id'] && !$_GET['add'] && !$_GET['add_type'] && !$_GET['edit_type'] )
 
 }
 elseif( $_GET['add_type'] || $_GET['edit_type'] ) {
-  // if its edit get the attribute info
+  // if its edit get the tag info
   if( $_GET['edit_type'] ) {
     $type_id= intval( $_GET['edit_type'] );
     $type= $api->GetTagTypes( array( $type_id ) );
     
     $category=$type[0]['category'];
-    $name= $type[0]['name'];
+    $name= $type[0]['tagname'];
     $min_role_id= $type[0]['min_role_id'];
     $description= $type[0]['description'];
     
@@ -151,7 +151,7 @@ elseif( $_GET['add_type'] || $_GET['edit_type'] ) {
   echo "</td></tr>\n";
   echo "<tr><td colspan=2 align=center>";
   if( $_GET['edit_type'] ) {
-    echo "<input type=hidden name='interface_tag_type_id' value='$type_id'>\n";
+    echo "<input type=hidden name='tag_type_id' value='$type_id'>\n";
     echo "<input type=submit name='edit_type' value='Edit Setting Type'>\n";
   } else {
     echo "<input type=submit name='add_type' value='Add Interface Type'>\n";
@@ -170,8 +170,8 @@ elseif( $_GET['add'] ) {
   
   // get all setting types 
   global $person_role;
-  $filter = array (']min_role_id'=>$person_role);
-  $setting_types= $api->GetTagTypes( $filter, array( "interface_tag_type_id", "name" , "category") );
+  $filter = array (']min_role_id'=>$person_role,'category'=>'interface*');
+  $setting_types= $api->GetTagTypes( $filter, array( "tag_type_id", "tagname" , "category") );
   sort_interface_tags($setting_types);
     
   // get interface's settings
@@ -185,10 +185,10 @@ elseif( $_GET['add'] ) {
   
   echo "<table cellpadding='2'> <caption> New Setting </caption>";
 
-  echo "<tr><th>Select</th><td><select name='interface_tag_type_id'><option value=''>Choose a type to add</option>\n";
+  echo "<tr><th>Select</th><td><select name='tag_type_id'><option value=''>Choose a type to add</option>\n";
   
   foreach( $setting_types as $setting_type ) {
-    echo "<option value='". $setting_type['interface_tag_type_id'] ."'>". $setting_type['category'] . ":" . $setting_type['name'] ."</option>\n";
+    echo "<option value='". $setting_type['tag_type_id'] ."'>". $setting_type['category'] . ":" . $setting_type['tagname'] ."</option>\n";
   
   }
   echo "</select></td</tr>\n";
@@ -209,7 +209,7 @@ else {
   // interface info
   $interface= $api->GetInterfaces( array( $setting[0]['interface_id'] ), array( "ip" ) );
   
-  drupal_set_title("Edit setting ". $setting[0]['name'] ." on ". $interface[0]['ip']);
+  drupal_set_title("Edit setting ". $setting[0]['tagname'] ." on ". $interface[0]['ip']);
 
   // start form and put values in to be edited.
   echo "<form action='setting_action.php' method='post'>\n";
@@ -218,7 +218,7 @@ else {
   
   echo "<table cellpadding='2'> <caption> Edit Setting </caption>";
   echo "<tr><th> Category </th> <td>" . $setting[0]['category'] . "</td></tr>";
-  echo "<tr><th> Name </th> <td>" . $setting[0]['name'] . "</td></tr>";
+  echo "<tr><th> Name </th> <td>" . $setting[0]['tagname'] . "</td></tr>";
   echo "<tr><th> Value </th> <td><input type=text name='value' value='" . $setting[0]['value'] . "'> </td></tr>";
   echo "<tr><td colspan=2> <input type=submit value='Edit Setting' name='edit_setting'></td></tr>";
   echo "</table>";
index 262530d..cf0b347 100644 (file)
@@ -24,12 +24,12 @@ $_roles= $_person['role_ids'];
 
 
 if( !empty( $_POST['add_sub'] ) ) {
-  $attrib_type= $_POST['sliver'];
+  $tag_type= $_POST['sliver'];
   $value= $_POST['value'];
   $node_id= $_POST['node_id'];
   $slice_id= $_POST['slice_id'];
 
-  $api->AddSliceTag( intval( $slice_id ), intval( $attrib_type ), $value, intval( $node_id ) );
+  $api->AddSliceTag( intval( $slice_id ), intval( $tag_type ), $value, intval( $node_id ) );
 
   header( "location: slivers.php?slice=$slice_id&node=$node_id" );
   exit();
@@ -39,12 +39,12 @@ if( !empty( $_POST['add_sub'] ) ) {
 
 // 
 if( $_GET['rem_id'] ) {
-  $attrib_id= $_GET['rem_id'];
+  $tag_id= $_GET['rem_id'];
   
   // get the slivers for this node
-  $sliver_info= $api->GetSliceTags( array( "slice_tag_id"=>intval( $attrib_id ) ), array( "slice_id", "node_id" ) );
+  $sliver_info= $api->GetSliceTags( array( "slice_tag_id"=>intval( $tag_id ) ), array( "slice_id", "node_id" ) );
   
-  $api->DeleteSliceTag( intval( $attrib_id ) );
+  $api->DeleteSliceTag( intval( $tag_id ) );
 
   header( "location: slivers.php?slice=". $sliver_info[0]['slice_id'] ."&node=". $sliver_info[0]['node_id'] );
   exit();
index 035a307..7389782 100644 (file)
@@ -34,23 +34,23 @@ if( $_GET['add'] ) {
   // node info
   $node_info= $api->GetNodes( array( intval( $node_id ) ), array( "hostname" ) );
 
-  // get attribute types
-  $type_info= $api->GetSliceTagTypes( NULL, array( "attribute_type_id", "name" ) );
+  // get tag types
+  $type_info= $api->GetTagTypes( NULL, array( "tag_type_id", "tagname" ) );
   
   // get the slivers for this node
-  $sliver_info= $api->GetSliceTags( array( "node_id"=>intval( $node_id ), "slice_id"=>intval( $slice_id ) ), array( "attribute_type_id", "name" ) );
+  $sliver_info= $api->GetSliceTags( array( "node_id"=>intval( $node_id ), "slice_id"=>intval( $slice_id ) ), array( "tag_type_id", "name" ) );
 
     $types_left= $type_info;
 
 
   // start form
   echo "<form action='sliver_action.php' method=post>\n
-  <h2>Add a Sliver Attribute to ". $slice_info[0]['name'] ." on node ". $node_info[0]['hostname'] ."</h2>\n
+  <h2>Add a Sliver Tag to ". $slice_info[0]['name'] ." on node ". $node_info[0]['hostname'] ."</h2>\n
   <table class='list_set' border=0 cellpadding=2><tbody>\n
-  <tr class='list_set'><th class='list_set'>Attribute: </th><td class='list_set'><select name='sliver'>\n";
+  <tr class='list_set'><th class='list_set'>Tag: </th><td class='list_set'><select name='sliver'>\n";
 
   foreach( $types_left as $type ) {
-    echo "<option value='". $type['attribute_type_id'] ."'>". $type['name'] ."</option>\n";
+    echo "<option value='". $type['tag_type_id'] ."'>". $type['name'] ."</option>\n";
 
   }
 
@@ -58,14 +58,14 @@ if( $_GET['add'] ) {
   <tr class='list_set'><th class='list_set'>Value: </th><td class='list_set'><input type=text name='value'></td></tr>\n
   </tbody></table>\n
   <input type=hidden name='node_id' value='$node_id'><input type=hidden name='slice_id' value='$slice_id'>
-  <p><input type=submit name='add_sub' value='Add Sliver Attribute'>\n";
+  <p><input type=submit name='add_sub' value='Add Sliver Tag'>\n";
 
   echo "<p><a href='index.php?id=$node_id'>Back to Node</a>\n</form>\n";
   
 }
 
 
-// if slice and node ids are passed display slivers and attribs
+// if slice and node ids are passed display slivers and tags
 if( $_GET['slice'] && $_GET['node'] ) {
   $slice_id= $_GET['slice'];
   $node_id= $_GET['node'];
@@ -80,16 +80,16 @@ if( $_GET['slice'] && $_GET['node'] ) {
   $sliver_info= $api->GetSliceTags( array( "node_id"=>intval( $node_id ), "slice_id"=>intval( $slice_id ) ), array( "slice_tag_id", "name", "value", "min_role_id", "description" ) );
 
   // get the attrbibutes for this slice
-  $attrib_info= $api->GetSliceTags( array( intval( $slice_id ) ), array( "slice_tag_id", "name", "value", "min_role_id", "description" ) );
+  $tag_info= $api->GetSliceTags( array( intval( $slice_id ) ), array( "slice_tag_id", "name", "value", "min_role_id", "description" ) );
 
 
   // start form
   echo "<form action='slivers.php' method=post>\n<h2>Sliver Details for slice ". $slice_info[0]['name'] ." on node ". $node_info[0]['hostname'] ."</h2>\n";
 
-  // sliver attributes of slice
+  // sliver tags of slice
   if( empty( $sliver_info ) )
     // if no sliver exists tell user
-    echo "No sliver attribute for this node/slice sliver combination.\n";
+    echo "No sliver tag for this node/slice sliver combination.\n";
   else {
     echo "<p><table class='list_set' border=0 cellpadding=2>\n<caption class='list_set'>Slivers</caption>\n<thead><tr class='list_set'><th class='list_set'>Name</th><th class='list_set'>Value</th><th class='list_set'>Min Roll</th><th class='list_set'>Description</th>";
     if ( in_array( 10, $_roles ) || ( in_array( 20, $_roles ) && in_array( $node_info, $_person['site_ids'] ) ) ) echo "<th></th><th></th>";
@@ -97,7 +97,7 @@ if( $_GET['slice'] && $_GET['node'] ) {
 
     foreach( $sliver_info AS $sliver ) {
       echo "<tr><td>". $sliver['name'] ."</td><td>". $sliver['value'] ."</td><td>". $sliver['min_role_id'] ."</td><td>". $sliver['description'] ."</td>";
-      if ( in_array( 10, $_roles ) || ( in_array( 20, $_roles ) && in_array( $node_info, $_person['site_ids'] ) ) ) echo "<td><a href='/db/slices/attributes.php?id=". $sliver['slice_tag_id'] ."'>Edit</a></td><td><a href='sliver_action.php?rem_id=". $sliver['slice_tag_id'] ."' onclick=\"javascript:return confirm('Are you sure you want to remove ". $sliver['name'] ." from node ". $node_info[0]['hostname'] ."?')\">Remove</a></td>";
+      if ( in_array( 10, $_roles ) || ( in_array( 20, $_roles ) && in_array( $node_info, $_person['site_ids'] ) ) ) echo "<td><a href='/db/slices/tags.php?type=slice&id=". $sliver['slice_tag_id'] ."'>Edit</a></td><td><a href='sliver_action.php?rem_id=". $sliver['slice_tag_id'] ."' onclick=\"javascript:return confirm('Are you sure you want to remove ". $sliver['name'] ." from node ". $node_info[0]['hostname'] ."?')\">Remove</a></td>";
       echo "</tr>\n";
 
 
@@ -107,22 +107,22 @@ if( $_GET['slice'] && $_GET['node'] ) {
 
   }
   
-  if ( in_array( 10, $_roles ) || ( in_array( 20, $_roles ) && in_array( $node_info, $_person['site_ids'] ) ) ) echo "<p><a href='slivers.php?add=$node_id&slice=$slice_id'>Add Sliver Attribute</a>\n";
+  if ( in_array( 10, $_roles ) || ( in_array( 20, $_roles ) && in_array( $node_info, $_person['site_ids'] ) ) ) echo "<p><a href='slivers.php?add=$node_id&slice=$slice_id'>Add Sliver Tag</a>\n";
 
   echo "<br /><hr />";
 
-  // regular attributes of slice
-  if( empty( $attrib_info ) )
-    // if no attributes exist tell user
-    echo "No Attributes for this slice.\n";
+  // regular tags of slice
+  if( empty( $tag_info ) )
+    // if no tags exist tell user
+    echo "No Tags for this slice.\n";
   else {
-    echo "<p><table class='list_set' border=0 cellpadding=2>\n<caption class='list_set'>Attributes</caption>\n<thead><tr class='list_set'><th class='list_set'>Name</th><th class='list_set'>Value</th><th class='list_set'>Min Roll</th><th class='list_set'>Description</th>";
+    echo "<p><table class='list_set' border=0 cellpadding=2>\n<caption class='list_set'>Tags</caption>\n<thead><tr class='list_set'><th class='list_set'>Name</th><th class='list_set'>Value</th><th class='list_set'>Min Roll</th><th class='list_set'>Description</th>";
     if ( in_array( 10, $_roles ) || ( in_array( 20, $_roles ) && in_array( $node_info, $_person['site_ids'] ) ) ) echo "<th></th>";
     echo "</tr></thead><tbody>\n";
 
-    foreach( $attrib_info AS $attrib ) {
-      echo "<tr><td>". $attrib['name'] ."</td><td>". $attrib['value'] ."</td><td>". $attrib['min_role_id'] ."</td><td>". $attrib['description'] ."</td>";
-      if ( in_array( 10, $_roles ) || ( in_array( 20, $_roles ) && in_array( $node_info, $_person['site_ids'] ) ) ) echo "<td><a href='attributes.php?id=". $attrib['slice_tag_id'] ."'>Edit</a></td>";
+    foreach( $tag_info AS $tag ) {
+      echo "<tr><td>". $tag['name'] ."</td><td>". $tag['value'] ."</td><td>". $tag['min_role_id'] ."</td><td>". $tag['description'] ."</td>";
+      if ( in_array( 10, $_roles ) || ( in_array( 20, $_roles ) && in_array( $node_info, $_person['site_ids'] ) ) ) echo "<td><a href='tags.php?type=slice&id=". $tag['slice_tag_id'] ."'>Edit</a></td>";
       echo "</tr>\n";
 
 
index e49bc4b..3798b25 100644 (file)
@@ -166,9 +166,9 @@ elseif( !$_GET['id'] ) {
   $persons= array_map(layout_person,$persons);
   sort_persons( $persons );
 
-  drupal_set_html_head('<script type="text/javascript" src="/planetlab/includes/js/bsn.Ajax.js"></script>
-    <script type="text/javascript" src="/planetlab/includes/js/bsn.DOM.js"></script>
-    <script type="text/javascript" src="/planetlab/includes/js/bsn.AutoSuggest.js"></script>');
+  drupal_set_html_head('<script type="text/javascript" src="/planetlab/bsn/bsn.Ajax.js"></script>
+    <script type="text/javascript" src="/planetlab/bsn/bsn.DOM.js"></script>
+    <script type="text/javascript" src="/planetlab/bsn/bsn.AutoSuggest.js"></script>');
 
   echo "<div>\n
         <form method=get action='index.php'>\n";
index 22970d1..c8f5514 100644 (file)
@@ -155,9 +155,9 @@ elseif( !$_GET['id'] ) {
   } else {
     sort_sites( $sites );
 
-    drupal_set_html_head('<script type="text/javascript" src="/planetlab/includes/js/bsn.Ajax.js"></script>
-    <script type="text/javascript" src="/planetlab/includes/js/bsn.DOM.js"></script>
-    <script type="text/javascript" src="/planetlab/includes/js/bsn.AutoSuggest.js"></script>');
+    drupal_set_html_head('<script type="text/javascript" src="/planetlab/bsn/bsn.Ajax.js"></script>
+    <script type="text/javascript" src="/planetlab/bsn/bsn.DOM.js"></script>
+    <script type="text/javascript" src="/planetlab/bsn/bsn.AutoSuggest.js"></script>');
     
     echo "<div>\n
         <form method=get action='index.php'>\n";
diff --git a/planetlab/slices/attrib_action.php b/planetlab/slices/attrib_action.php
deleted file mode 100644 (file)
index 230bb53..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-
-// Require login
-require_once 'plc_login.php';
-
-// Get session and API handles
-require_once 'plc_session.php';
-global $plc, $api;
-
-/*
-// Print header
-require_once 'plc_drupal.php';
-drupal_set_title('Slices');
-include 'plc_header.php';
-*/
-
-// Common functions
-require_once 'plc_functions.php';
-require_once 'plc_sorts.php';
-
-// find person roles
-$_person= $plc->person;
-$_roles= $_person['role_ids'];
-
-
-// ATTRIBUTES -------------------------------------------------
-
-// attribute deletion
-if( $_GET['rem_id'] ) {
-  // get the id of the attrib to remove from GET
-  $attribute_id= intval( $_GET['rem_id'] );
-
-  // get slice_id 
-  $attrib_info= $api->GetSliceTags( array( $attribute_id ), array( "slice_id" ) );
-  $slice_id= $attrib_info[0]['slice_id'];
-  
-  // delete the attribute
-  $api->DeleteSliceTag( $attribute_id );
-
-
-  header( "location: index.php?id=$slice_id" );
-  exit();
-}
-
-
-// attirbute updates
-if( $_POST['edit_attribute'] ) {
-  // get the id of the attrib to update and teh value from POST
-  $attribute_id= intval( $_POST['attribute_id'] );
-  $value= $_POST['value'];
-  $slice_id= $_POST['slice_id'];
-
-  // update it!
-  $api->UpdateSliceTag( $attribute_id, $value );
-
-  header( "location: index.php?id=$slice_id" );
-  exit();
-}
-
-
-// attribute adds
-if( $_POST['add_attribute'] ) {
-  // get the slice_id, attribute_type_id, and value from POST
-  $slice_id= intval( $_POST['slice_id'] );
-  $attribute_type_id= intval( $_POST['attribute_type_id'] );
-  $value= $_POST['value'];
-
-  // add it!
-  $api->AddSliceTag( $slice_id, $attribute_type_id, $value );
-
-  header( "location: index.php?id=$slice_id" );
-  exit();
-}
-
-// ATTRIBUTE TYPES ---------------------------------------------------
-  
-// attribute type adds
-if( $_POST['add_type'] ) {
-  // get post vars 
-  $name= $_POST['name'];
-  $min_role_id= intval( $_POST['min_role_id'] );
-  $description= $_POST['description'];
-  
-  // make the attribute_type_fields dict
-  $attribute_type_fields= array( "min_role_id" => $min_role_id, "name" => $name, "description" => $description );
-  
-  // add it!!
-  $api->AddSliceTagType( $attribute_type_fields );
-
-  header( "location: attributes.php" );
-  exit();
-}
-  
-
-// attribute type updates
-if( $_POST['edit_type'] ) {
-  // get post vars 
-  $name= $_POST['name'];
-  $min_role_id= intval( $_POST['min_role_id'] );
-  $description= $_POST['description'];  
-  $attribute_type_id= intval( $_POST['attribute_type_id'] );
-  
-  // make attribute_type_fields dict
-  $attribute_type_fields= array( "min_role_id" => $min_role_id, "name" => $name, "description" => $description );
-
-  // Update it!
-  $api->UpdateSliceTagType( $attribute_type_id, $attribute_type_fields );
-  
-  header( "location: attributes.php" );
-  exit();
-}
-
-
-// delete attribute types
-if( $_GET['del_type'] ) {
-  // get vars
-  $type_id= intval( $_GET['del_type'] );
-
-  // delete it!
-  $api->DeleteSliceTagType( $type_id );
-  
-  header( "location: attributes.php" );
-  exit();
-}
-
-  
-  
-/*
-// Print footer
-include 'plc_footer.php';
-*/
-
-?>
diff --git a/planetlab/slices/attributes.php b/planetlab/slices/attributes.php
deleted file mode 100644 (file)
index b134a87..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-<?php
-
-// Require login
-require_once 'plc_login.php';
-
-// Get session and API handles
-require_once 'plc_session.php';
-global $plc, $api;
-
-// Print header
-require_once 'plc_drupal.php';
-drupal_set_title('Attributes');
-include 'plc_header.php';
-
-// Common functions
-require_once 'plc_functions.php';
-require_once 'plc_sorts.php';
-
-// find person roles
-$_person= $plc->person;
-$_roles= $_person['role_ids'];
-
-//print_r( $_person );
-
-// if no id, display list of attributes types
-if( !$_GET['id'] && !$_GET['add'] && !$_GET['add_type'] && !$_GET['edit_type'] ) {
-  // get types
-  $attrib_types= $api->GetSliceTagTypes( NULL, array( "attribute_type_id", "name", "description", "min_role_id" ) );
-  
-  // get role names for the min role_ids
-  foreach( $attrib_types as $attrib_type ) {
-    $roles= $api->GetRoles();
-    foreach( $roles as $role ) {
-      if( $attrib_type['min_role_id'] == $role['role_id'] )
-        $role_name= $role['name'];
-    }
-    
-    $attrib_type_info[]= array( "attribute_type_id" => $attrib_type['attribute_type_id'], "name" => $attrib_type['name'], "description" => $attrib_type['description'], "min_role" => $role_name );
-  }
-
-  // list them
-  echo "<h2>Slice Attribute Types</h2>\n";
-  
-  echo "<table cellpadding=2><thead><tr><th>Name</th><th>Min Role</th><th>Description</th>";
-  // if admin we need to more cells
-  if(  in_array( "10", $_person['role_ids'] ) )
-    echo "<th></th><th></th>";
-  echo "</thead><tbody>";
-
-  foreach( $attrib_type_info as $type ) {
-    echo "<tr><td>". $type['name'] ."</td><td>". $type['min_role'] ."</td><td>". $type['description'] ."</td>";
-    // if admin display edit/delet links
-    if(  in_array( "10", $_person['role_ids'] ) ) {
-      echo "<td><a href='attributes.php?edit_type=". $type['attribute_type_id'] ."'>Edit</a></td>";
-      echo "<td>";
-      echo plc_delete_link_button ('attrib_action.php?del_type='. $type['attribute_type_id'],
-                                  $type['name']);
-      echo "</td>";
-    }
-    echo "</tr>\n";
-  
-  }
-  
-  echo "</tbody></table>\n";
-  
-  if( in_array( "10", $_person['role_ids'] ) )
-    echo "<p><a href='attributes.php?add_type=1'>Add an Attribute Type</a>";
-  
-}
-elseif( $_GET['add_type'] || $_GET['edit_type'] ) {
-  // if its edit get the attribute info
-  if( $_GET['edit_type'] ) {
-    $type_id= intval( $_GET['edit_type'] );
-    $type_info= $api->GetSliceTagTypes( array( $type_id ) );
-    
-    $name= $type_info[0]['name'];
-    $min_role_id= $type_info[0]['min_role_id'];
-    $description= $type_info[0]['description'];
-    
-  }
-  
-  // display form for attribute types
-  echo "<form action='attrib_action.php' method='post'>\n";
-  echo "<h2>Add Attribute Type</h2>\n";
-  echo "<p><strong>Name: </strong> <input type=text name='name' size=20 value='$name'>\n";
-  echo "<p><strong>Min Role: </strong><select name='min_role_id'>\n";
-  echo "<option value='10'"; if( $min_role_id == 10 ) echo " selected"; echo ">Admin</option>\n";
-  echo "<option value='20'"; if( $min_role_id == 20 ) echo " selected"; echo ">PI</option>\n";
-  echo "<option value='30'"; if( $min_role_id == 30 ) echo " selected"; echo ">User</option>\n";
-  echo "<option value='40'"; if( $min_role_id == 40 ) echo " selected"; echo ">Tech</option>\n";
-  echo "</select>\n";
-  echo "<p><strong>Description: </strong><br>\n";
-  echo "<textarea name='description' cols=40 rows=5>$description</textarea>\n";
-  echo "<p><input type=submit ";
-  if( $_GET['edit_type'] ) {
-    echo "name='edit_type' value='Edit Attribute Type'>\n";
-    echo "<input type=hidden name='attribute_type_id' value='$type_id'>\n";
-  }
-  else
-    echo "name='add_type' value='Add Attribute Type'>\n";
-
-  echo "</form>\n";
-
-}
-elseif( $_GET['add'] ) {
-  // get slice id from GET
-  $slice_id= intval( $_GET['add'] );
-  
-  // get all attribute types 
-  $attrib_types= $api->GetSliceTagTypes( NULL, array( "attribute_type_id", "name" ) );
-  
-  foreach( $attrib_types as $attrib_type ) {
-    $all_attribs[$attrib_type['attribute_type_id']]= $attrib_type['name'];
-  }
-  
-  // get slice's attribute types
-  $slice_info= $api->GetSlices( array( $slice_id ), array( "slice_tag_ids" ) );
-  $attrib_info= $api->GetSliceTags( $slice_info[0]['slice_tag_ids'], array( "attribute_type_id", "name" ) );
-  
-  foreach( $attrib_info as $info ) {
-    $slice_attrib_types[$info['attribute_type_id']]= $info['name'];
-  }
-
-
-    $attribute_types= $all_attribs;
-  
-  // start form
-  echo "<form action='attrib_action.php' method='post'>\n";
-  echo "<h2>Edit ". $slice_info[0]['name'] ." attribute: ". $attrib_type[0]['name'] ."</h2>\n";
-  
-  echo "<select name='attribute_type_id'><option value=''>Choose a type to add</option>\n";
-  
-  foreach( $attribute_types as $key => $val ) {
-    echo "<option value='". $key ."'>". $val ."</option>\n";
-  
-  }
-  echo "</select>\n";
-  
-  echo "<p><strong>Value: </strong><input type=text name='value'>\n";
-  
-  echo "<p><input type=submit name='add_attribute' value='Add Attribute'>\n";
-  echo "<input type=hidden name='slice_id' value='$slice_id'>\n";
-  echo "</form>\n";
-  
-}
-else {
-  $attribute_id= intval( $_GET['id'] );
-  
-  // get attribute info
-  $slice_attib= $api->GetSliceTags( array( $attribute_id ), array( "slice_id", "slice_tag_id", "attribute_type_id", "value", "description", "min_role_id" ) );
-  
-  // get type info 
-  $attrib_type= $api->GetSliceTagTypes( array( $slice_attib[0]['attribute_type_id'] ), array( "attribute_type_id", "name", "description" ) );
-  
-  // slice info
-  $slice_info= $api->GetSlices( array( $slice_attib[0]['slice_id'] ), array( "name" ) );
-  
-  // start form and put values in to be edited.
-  echo "<form action='attrib_action.php' method='post'>\n";
-  echo "<h2>Edit ". $slice_info[0]['name'] ." attribute: ". $attrib_type[0]['name'] ."</h2>\n";
-  
-  echo $slice_attib[0]['description'] ."<br />\n";
-  echo "<strong>Value:</strong> <input type=text name=value value='". $slice_attib[0]['value'] ."'><br /><br />\n";
-  
-  echo "<input type=submit value='Edit Attribute' name='edit_attribute'>\n";
-  echo "<input type=hidden name='slice_id' value='". $slice_attib[0]['slice_id'] ."'>\n";
-  echo "<input type=hidden name='attribute_id' value='". $attribute_id ."'>\n";
-  echo "</form>\n";
-  
-}
-
-echo "<p><a href='index.php?id=$slice_id'>Back to Slices</a>\n";
-
-// Print footer
-include 'plc_footer.php';
-
-?>
index 1df026a..d97e651 100644 (file)
@@ -47,9 +47,9 @@ if( !$_GET['id'] ) {
   if( in_array( 10, $_roles ) ) {
     // auto complete box for finding a slice
                 
-    drupal_set_html_head('<script type="text/javascript" src="/planetlab/includes/js/bsn.Ajax.js"></script>
-    <script type="text/javascript" src="/planetlab/includes/js/bsn.DOM.js"></script>
-    <script type="text/javascript" src="/planetlab/includes/js/bsn.AutoSuggest.js"></script>');
+    drupal_set_html_head('<script type="text/javascript" src="/planetlab/bsn/bsn.Ajax.js"></script>
+    <script type="text/javascript" src="/planetlab/bsn/bsn.DOM.js"></script>
+    <script type="text/javascript" src="/planetlab/bsn/bsn.AutoSuggest.js"></script>');
 
     echo "<div>\n
           <form method=post action='index.php'>\n";
@@ -122,7 +122,7 @@ if( !$_GET['id'] ) {
     echo "<p><strong>No slice found, or all are expired.</strong>";
   } else {
   
-    $slice_info= $api->GetSlices( $slice_ids, array( "slice_id", "name", "site_id", "state", "person_ids", "expires", "peer_id" ) );
+    $slice_info= $api->GetSlices( $slice_ids, array( "slice_id", "name", "site_id", "person_ids", "expires", "peer_id" ) );
     //print '<pre>'; print_r( $api->trace() ) ; print '</pre>';
 
     if ( ! $slice_info) {
@@ -153,7 +153,6 @@ if( !$_GET['id'] ) {
       foreach( $slice_info as $slice ) {
        $slice_id= $slice['slice_id'];
        $slice_name= $slice['name'];
-       $slice_state= $slice['state'];
        $slice_expires= date( "M j, Y", $slice['expires'] );
        $peer_id = $slice['peer_id'];
       
@@ -252,24 +251,24 @@ else {
     }
   }
 
-  // slice attribute info
+  // slice tag info
   if( !empty( $slice_tag_ids ) )
     $slice_attibs= $api->GetSliceTags( $slice_tag_ids, 
-                                            array( "slice_tag_id", "attribute_type_id", "value", "description", "min_role_id", "node_id" ) );
+                                      array( "slice_tag_id", "tag_type_id", "value", "description", "min_role_id", "node_id" ) );
 
-  // gets attrib type info and combines it to form all attrib info array
+  // gets tag type info and combines it to form all tag info array
   if( $slice_attibs ) {
     foreach( $slice_attibs as $slice_attib ) {
-      $attrib_type= $api->GetSliceTagTypes( array( $slice_attib['attribute_type_id'] ), 
-                                                 array( "attribute_type_id", "name", "description" ) );
+      $tag_type= $api->GetTagTypes( array( $slice_attib['tag_type_id'] ), 
+                                                 array( "tag_type_id", "tagname", "description" ) );
       
-      $attributes[]= array( "slice_tag_id" => $slice_attib['slice_tag_id'], 
-                           "attribute_type_id" => $slice_attib['attribute_type_id'], 
-                           "name" => $attrib_type[0]['name'], 
-                           "value" => $slice_attib['value'], 
-                           "description" => $slice_attib['description'], 
-                           "min_role_id" => $slice_attib['min_role_id'], 
-                           "node_id" => $slice_attib['node_id'] );
+      $tags[]= array( "slice_tag_id" => $slice_attib['slice_tag_id'], 
+                     "tag_type_id" => $slice_attib['tag_type_id'], 
+                     "tagname" => $tag_type[0]['tagname'], 
+                     "value" => $slice_attib['value'], 
+                     "description" => $slice_attib['description'], 
+                     "min_role_id" => $slice_attib['min_role_id'], 
+                     "node_id" => $slice_attib['node_id'] );
     }
 
   }
@@ -361,17 +360,17 @@ else {
   echo "<br /><hr />\n";
 
 
-  // slice attributes
-  if( $attributes ) {
+  // slice tags
+  if( $tags ) {
 
-    // builds 2 arrays, one for attribs,one for slivers
-    foreach( $attributes as $attribute ) {
-      if( empty( $attribute['node_id'] ) ) {
-        $slice_attrib[]= $attribute;
+    // builds 2 arrays, one for tags, one for slivers
+    foreach( $tags as $tag ) {
+      if( empty( $tag['node_id'] ) ) {
+        $slice_tag[]= $tag;
       }
       else {
-        $sliver_attrib[]= $attribute;
-        $sliver_nodes[]= $attribute['node_id'];
+        $sliver_tag[]= $tag;
+        $sliver_nodes[]= $tag['node_id'];
       }
     }
   }
@@ -389,37 +388,37 @@ else {
    echo "<br /></div>\n";
   }
  
-  // slice attributes
+  // slice tags
   $is_admin=in_array( 10, $_roles );
   $is_in_slice=in_array( $slice_id, $_person['slice_ids'] );
   $is_pi=in_array( 20, $_roles );
-  if( $slice_attrib ) {
-    echo "<table cellpadding=3><caption class='list_set'>Slice Attributes</caption>";
+  if( $slice_tag ) {
+    echo "<table cellpadding=3><caption class='list_set'>Slice Tags</caption>";
     echo "<thead><tr>";
     if( $is_admin )
       echo "<th></th>";
-    echo "<th>Attribute</th><th>Value</th><th>Description</th>";
+    echo "<th>Tag</th><th>Value</th><th>Description</th>";
     echo "</tr></thead><tbody>\n";
 
-    foreach( $attributes as $attribute ) {
-      // ignore sliver attributes at this stage
-      if( empty( $attribute['node_id'] ) ) {
+    foreach( $tags as $tag ) {
+      // ignore sliver tags at this stage
+      if( empty( $tag['node_id'] ) ) {
         echo("<tr>");
         if( $is_admin ) {
          printf("<td>");
-         sprintf($label,"\\n [ %s = %s] \\n from %s",$attribute['name'],$attribute['value'],$name);
-         echo plc_delete_link_button ('attrib_action.php?rem_id=' . $attribute['slice_tag_id'],
+         sprintf($label,"\\n [ %s = %s] \\n from %s",$tag['tagname'],$tag['value'],$name);
+         echo plc_delete_link_button ('tag_action.php?rem_id=' . $tag['slice_tag_id'],
                                       $label);
          echo "</td>";
        }
        if( $is_admin || ($is_pi && $is_in_slice) ) {
-          printf ("<td><a href='attributes.php?id=%s'>%s</a></td>",
-                 $attribute['slice_tag_id'],$attribute['name']);
+          printf ("<td><a href='tags.php?type=slice?id=%s'>%s</a></td>",
+                 $tag['slice_tag_id'],$tag['tagname']);
        } else {
-         printf("<td>%s</td>",$attribute['name']);
+         printf("<td>%s</td>",$tag['tagname']);
        }
        printf("<td align=center>%s</td><td>%s</td>",
-              $attribute['value'],$attribute['description']);
+              $tag['value'],$tag['description']);
         echo "</tr>";
       }
     }
@@ -429,39 +428,39 @@ else {
 
   }
   if( $is_admin || ($is_pi && $is_in_slice) )
-    echo "<a href='attributes.php?add=$slice_id'>Add a Slice Attribute\n";    
+    echo "<a href='tags.php?type=slice&add=$slice_id'>Add a Slice Tag</a>\n";    
 
 
 
-  // sliver attributes
-  if( $sliver_attrib ) {
-    echo "<table cellpadding=3><caption class='list_set'>Sliver Attributes</caption>";
+  // sliver tags
+  if( $sliver_tag ) {
+    echo "<table cellpadding=3><caption class='list_set'>Sliver Tags</caption>";
     echo "<thead><tr>";
     if( $is_admin )
       echo "<th></th>";
-    echo "<th>Attribute</th><th>Value</th><th>Description</th><th>Node</th>";
+    echo "<th>Tag</th><th>Value</th><th>Description</th><th>Node</th>";
     echo "</tr></thead><tbody>\n";
 
-    foreach( $attributes as $attribute ) {
-      $nodename=$new_sliver_node_info[$attribute['node_id']]['hostname'];
-      // consider only sliver attributes at this stage
-      if( !empty( $attribute['node_id'] ) ) {
+    foreach( $tags as $tag ) {
+      $nodename=$new_sliver_node_info[$tag['node_id']]['hostname'];
+      // consider only sliver tags at this stage
+      if( !empty( $tag['node_id'] ) ) {
         echo("<tr>");
         if( $is_admin ) {
          echo("<td>");
          $label=sprintf("\\n [ %s = %s ] \\n from %s \\n on node %s",
-                        $attribute['name'],$attribute['value'],$name,$nodename);
-         echo plc_delete_link_label('/db/nodes/sliver_action.php?rem_id=' . $attribute['slice_tag_id'], 
+                        $tag['tagname'],$tag['value'],$name,$nodename);
+         echo plc_delete_link_label('/db/nodes/sliver_action.php?rem_id=' . $tag['slice_tag_id'], 
                                     $label);
          echo "</td>";
        }
         if( $is_admin ) {
-          printf("<td><a href='attributes.php?id=%s'>%s</a></td>",$attribute['slice_tag_id'],$attribute['name']);
+          printf("<td><a href='tags.php?type=slice&id=%s'>%s</a></td>",$tag['slice_tag_id'],$tag['tagname']);
        } else {
-         printf("<td>%s</td>",$attribute['name']);
+         printf("<td>%s</td>",$tag['tagname']);
        }
        printf("<td align=center>%s</td><td>%s</td><td><a href='/db/nodes/index.php?id=%s'>%s</a></td>",
-              $attribute['value'],$attribute['description'],$attribute['node_id'],$nodename);
+              $tag['value'],$tag['description'],$tag['node_id'],$nodename);
        
         echo "</tr>";
       }
index 5a00ab0..916f95d 100644 (file)
@@ -108,29 +108,30 @@ else
 
 // get site nodes for $site_id
 if( $site_id == 'all_site' ) {
-  $full_node_info= $adm->GetNodes( NULL, array( "hostname", "node_id" , "peer_id", "boot_state","last_updated") );
+  $full_node_info= $adm->GetNodes( array("node_type","regular"),
+                                  array( "hostname", "node_id" , "peer_id", "boot_state","last_updated") );
 
-       $snode_info= array();
-       foreach( $full_node_info as $full_node ) {
-               if( !in_array( $full_node['node_id'], $slice_info[0]['node_ids'] ) )
-                       $snode_info[]= $full_node;
-       }
+  $snode_info= array();
+  foreach( $full_node_info as $full_node ) {
+    if( !in_array( $full_node['node_id'], $slice_info[0]['node_ids'] ) )
+      $snode_info[]= $full_node;
+  }
 }
 else {
-       $sid= intval( $site_id );
-       $site_node_info= $adm->GetSites( array( $sid ), array( "node_ids" ) );
-       $site_nodes= $site_node_info[0]['node_ids'];
+  $sid= intval( $site_id );
+  $site_node_info= $adm->GetSites( array( $sid ), array( "node_ids" ) );
+  $site_nodes= $site_node_info[0]['node_ids'];
        
-       // gets all node_ids from site that arent already associated with the slice
-       foreach( $site_nodes as $snode) {
-               if( !in_array( $snode, $slice_info[0]['node_ids'] ) )
-                       $snodes[]= $snode;
-       }
+  // gets all node_ids from site that arent already associated with the slice
+  foreach( $site_nodes as $snode) {
+    if( !in_array( $snode, $slice_info[0]['node_ids'] ) )
+      $snodes[]= $snode;
+  }
        
-       // Get node info from new list
-       if( !empty( $snodes ) )
-         $snode_info= $adm->GetNodes( $snodes, array( "hostname", "node_id" , "peer_id", "boot_state","last_updated" ) );
-
+  // Get node info from new list
+  if( !empty( $snodes ) )
+    $snode_info= $adm->GetNodes( $snodes, array( "hostname", "node_id" , "peer_id", "boot_state","last_updated" ) );
+  
 }
 
 // start form   
@@ -149,10 +150,10 @@ if ( ! $slice_readonly ) {
     echo "</td><td>";
   }
   echo "<select name='site_id' onChange='submit()'>\n";
-       echo "<option value='all_site'";
-       if( $site_id == 'all_site' )
-               echo " selected";
-       echo ">--All Sites--</option>\n";
+  echo "<option value='all_site'";
+  if( $site_id == 'all_site' )
+    echo " selected";
+  echo ">--All Sites--</option>\n";
 
   foreach( $site_info as $site ) {
     echo "<option value=". $site['site_id'];
diff --git a/planetlab/slices/tag_action.php b/planetlab/slices/tag_action.php
new file mode 100644 (file)
index 0000000..170db85
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+
+// Require login
+require_once 'plc_login.php';
+
+// Get session and API handles
+require_once 'plc_session.php';
+global $plc, $api;
+
+/*
+// Print header
+require_once 'plc_drupal.php';
+drupal_set_title('Slices');
+include 'plc_header.php';
+*/
+
+// Common functions
+require_once 'plc_functions.php';
+require_once 'plc_sorts.php';
+
+// find person roles
+$_person= $plc->person;
+$_roles= $_person['role_ids'];
+
+
+// TAGS -------------------------------------------------
+
+// tag deletion
+if( $_GET['rem_id'] ) {
+  // get the id of the tag to remove from GET
+  $tag_id= intval( $_GET['rem_id'] );
+
+  // get slice_id 
+  $tag_info= $api->GetSliceTags( array( $tag_id ), array( "slice_id" ) );
+  $slice_id= $tag_info[0]['slice_id'];
+  
+  // delete the tag
+  $api->DeleteSliceTag( $tag_id );
+
+
+  header( "location: index.php?id=$slice_id" );
+  exit();
+}
+
+
+// tag updates
+if( $_POST['edit_tag'] ) {
+  // get the id of the tag to update and teh value from POST
+  $tag_id= intval( $_POST['tag_id'] );
+  $value= $_POST['value'];
+  $slice_id= $_POST['slice_id'];
+
+  // update it!
+  $api->UpdateSliceTag( $tag_id, $value );
+
+  header( "location: index.php?id=$slice_id" );
+  exit();
+}
+
+
+// tag adds
+if( $_POST['add_tag'] ) {
+  // get the slice_id, tag_type_id, and value from POST
+  $slice_id= intval( $_POST['slice_id'] );
+  $tag_type_id= intval( $_POST['tag_type_id'] );
+  $value= $_POST['value'];
+
+  // add it!
+  $api->AddSliceTag( $slice_id, $tag_type_id, $value );
+
+  header( "location: index.php?id=$slice_id" );
+  exit();
+}
+
+// TAG TYPES ---------------------------------------------------
+  
+// tag type adds
+if( $_POST['add_type'] ) {
+  // get post vars 
+  $name= $_POST['name'];
+  $min_role_id= intval( $_POST['min_role_id'] );
+  $description= $_POST['description'];
+  
+  // make the tag_type_fields dict
+  // xxx misses category
+  $tag_type_fields= array( "min_role_id" => $min_role_id, 
+                          "tagname" => $name, 
+                          "description" => $description );
+  
+  // add it!!
+  $api->AddTagType( $tag_type_fields );
+
+  header( "location: tags.php?type=slice" );
+  exit();
+}
+  
+
+// tag type updates
+if( $_POST['edit_type'] ) {
+  // get post vars 
+  $name= $_POST['name'];
+  $min_role_id= intval( $_POST['min_role_id'] );
+  $description= $_POST['description'];  
+  $tag_type_id= intval( $_POST['tag_type_id'] );
+  
+  // make tag_type_fields dict
+  $tag_type_fields= array( "min_role_id" => $min_role_id, "tagname" => $name, "description" => $description );
+
+  // Update it!
+  $api->UpdateTagType( $tag_type_id, $tag_type_fields );
+  
+  header( "location: tags.php?type=slice" );
+  exit();
+}
+
+
+// delete tag types
+if( $_GET['del_type'] ) {
+  // get vars
+  $type_id= intval( $_GET['del_type'] );
+
+  // delete it!
+  $api->DeleteTagType( $type_id );
+  
+  header( "location: tags.php?type=slice" );
+  exit();
+}
+
+  
+  
+/*
+// Print footer
+include 'plc_footer.php';
+*/
+
+?>
diff --git a/planetlab/slices/tags.php b/planetlab/slices/tags.php
new file mode 100644 (file)
index 0000000..b4a0d34
--- /dev/null
@@ -0,0 +1,198 @@
+<?php
+
+// Require login
+require_once 'plc_login.php';
+
+// Get session and API handles
+require_once 'plc_session.php';
+global $plc, $api;
+
+if (! isset($_GET['type']) || $_GET['type']=='all') {
+  $title="All tag types";
+  $tag_type_filter=array("-SORT"=>"tagname"); 
+ } else {
+  $title="Tag Types for " . $_GET['type'] . "s";
+  $pattern=$_GET['type'] . '*';
+  $tag_type_filter=array("category"=>$pattern,"-SORT"=>"tagname");
+ }
+
+
+// Print header
+require_once 'plc_drupal.php';
+drupal_set_title($title);
+include 'plc_header.php';
+
+// Common functions
+require_once 'plc_functions.php';
+require_once 'plc_sorts.php';
+
+// find person roles
+$_person= $plc->person;
+$_roles= $_person['role_ids'];
+
+//print_r( $_person );
+
+// if no id, display list of tag types
+if( !$_GET['id'] && !$_GET['add'] && !$_GET['add_type'] && !$_GET['edit_type'] ) {
+  // get types
+  $tag_types= $api->GetTagTypes($tag_type_filter,
+                               array( "tag_type_id", "tagname", "category", "description", "min_role_id" ) );
+  
+  // get role names for the min role_ids
+  foreach( $tag_types as $tag_type ) {
+    $roles= $api->GetRoles();
+    foreach( $roles as $role ) {
+      if( $tag_type['min_role_id'] == $role['role_id'] )
+        $role_name= $role['name'];
+    }
+    
+    $tag_type_info[]= array( "tag_type_id" => $tag_type['tag_type_id'], 
+                            "tagname" => $tag_type['tagname'], 
+                            "description" => $tag_type['description'], 
+                            "min_role" => $role_name,
+                            "category" => $tag_type['category']);
+  }
+
+  // list them
+  echo "<h2>Tag Types</h2>\n";
+  
+  echo "<table cellpadding=2><thead><tr><th>Name</th><th>Category></th><th>Min Role</th><th>Description</th>";
+  // if admin we need to more cells
+  if(  in_array( "10", $_person['role_ids'] ) )
+    echo "<th></th><th></th>";
+  echo "</thead><tbody>";
+
+  foreach( $tag_type_info as $type ) {
+    echo "<tr>";
+    echo "<td>". $type['tagname'] ."</td>";
+    echo "<td>". $type['category'] ."</td>";
+    echo "<td>". $type['min_role'] ."</td>";
+    echo "<td>". $type['description'] ."</td>";
+    // if admin display edit/delet links
+    if(  in_array( "10", $_person['role_ids'] ) ) {
+      echo "<td><a href='tags.php?type=slice&edit_type=". $type['tag_type_id'] ."'>Edit</a></td>";
+      echo "<td>";
+      echo plc_delete_link_button ('tag_action.php?del_type='. $type['tag_type_id'],
+                                  $type['tagname']);
+      echo "</td>";
+    }
+    echo "</tr>\n";
+    
+  }
+  
+  echo "</tbody></table>\n";
+  
+  if( in_array( "10", $_person['role_ids'] ) )
+    echo "<p><a href='tags.php?type=slice&add_type=1'>Add a Tag Type</a>";
+  
+ }
+elseif( $_GET['add_type'] || $_GET['edit_type'] ) {
+  // if its edit get the tag info
+  if( $_GET['edit_type'] ) {
+    $type_id= intval( $_GET['edit_type'] );
+    $type_info= $api->GetTagTypes( array( $type_id ) );
+    
+    $tagname= $type_info[0]['tagname'];
+    $min_role_id= $type_info[0]['min_role_id'];
+    $description= $type_info[0]['description'];
+    $category=$type_info[0]['category'];
+    
+  }
+  
+  // display form for tag types
+  echo "<form action='tag_action.php' method='post'>\n";
+  echo "<h2>Add Tag Type</h2>\n";
+  echo "<p><strong>Name: </strong> <input type=text name='name' size=20 value='$tagname'>\n";
+  echo "<p><strong>Category: </strong><input type=text name='category' size=30 value='$category'>\n";
+  echo "<p><strong>Min Role: </strong><select name='min_role_id'>\n";
+  echo "<option value='10'"; if( $min_role_id == 10 ) echo " selected"; echo ">Admin</option>\n";
+  echo "<option value='20'"; if( $min_role_id == 20 ) echo " selected"; echo ">PI</option>\n";
+  echo "<option value='30'"; if( $min_role_id == 30 ) echo " selected"; echo ">User</option>\n";
+  echo "<option value='40'"; if( $min_role_id == 40 ) echo " selected"; echo ">Tech</option>\n";
+  echo "</select>\n";
+  echo "<p><strong>Description: </strong><br>\n";
+  echo "<textarea name='description' cols=40 rows=5>$description</textarea>\n";
+  echo "<p><input type=submit ";
+  if( $_GET['edit_type'] ) {
+    echo "name='edit_type' value='Edit Tag Type'>\n";
+    echo "<input type=hidden name='tag_type_id' value='$type_id'>\n";
+  }
+  else
+    echo "name='add_type' value='Add Tag Type'>\n";
+
+  echo "</form>\n";
+
+}
+elseif( $_GET['add'] ) {
+  // get slice id from GET
+  $slice_id= intval( $_GET['add'] );
+  
+  // get all tag types 
+  $tag_types= $api->GetTagTypes( $tag_type_filter , array( "tag_type_id", "tagname" ) );
+  
+  foreach( $tag_types as $tag_type ) {
+    $all_tags[$tag_type['tag_type_id']]= $tag_type['tagname'];
+  }
+  
+  // get slice's tag types
+  $slice_info= $api->GetSlices( array( $slice_id ), array( "slice_tag_ids" ) );
+  $tag_info= $api->GetSliceTags( $slice_info[0]['slice_tag_ids'], array( "tag_type_id", "tagname" ) );
+  
+  foreach( $tag_info as $info ) {
+    $slice_tag_types[$info['tag_type_id']]= $info['tagname'];
+  }
+
+
+    $tag_types= $all_tags;
+  
+  // start form
+  echo "<form action='tag_action.php' method='post'>\n";
+  echo "<h2>Edit ". $slice_info[0]['name'] ." tag: ". $tag_type[0]['tagname'] ."</h2>\n";
+  
+  echo "<select name='tag_type_id'><option value=''>Choose a type to add</option>\n";
+  
+  foreach( $tag_types as $key => $val ) {
+    echo "<option value='". $key ."'>". $val ."</option>\n";
+  
+  }
+  echo "</select>\n";
+  
+  echo "<p><strong>Value: </strong><input type=text name='value'>\n";
+  
+  echo "<p><input type=submit name='add_tag' value='Add Tag'>\n";
+  echo "<input type=hidden name='slice_id' value='$slice_id'>\n";
+  echo "</form>\n";
+  
+}
+else {
+  $tag_id= intval( $_GET['id'] );
+  
+  // get tag
+  $slice_tag= $api->GetSliceTags( array( $tag_id ), array( "slice_id", "slice_tag_id", "tag_type_id", "value", "description", "min_role_id" ) );
+  
+  // get type info 
+  $tag_type= $api->GetTagTypes( array( $slice_tag[0]['tag_type_id'] ), array( "tag_type_id", "tagname", "description" ) );
+  
+  // slice info
+  $slice_info= $api->GetSlices( array( $slice_tag[0]['slice_id'] ), array( "name" ) );
+  
+  // start form and put values in to be edited.
+  echo "<form action='tag_action.php' method='post'>\n";
+  echo "<h2>Edit ". $slice_info[0]['name'] ." tag: ". $tag_type[0]['tagname'] ."</h2>\n";
+  
+  echo $slice_tag[0]['description'] ."<br />\n";
+  echo "<strong>Value:</strong> <input type=text name=value value='". $slice_tag[0]['value'] ."'><br /><br />\n";
+  
+  echo "<input type=submit value='Edit Tag' name='edit_tag'>\n";
+  echo "<input type=hidden name='slice_id' value='". $slice_tag[0]['slice_id'] ."'>\n";
+  echo "<input type=hidden name='tag_id' value='". $tag_id ."'>\n";
+  echo "</form>\n";
+  
+}
+
+echo "<p><a href='index.php?id=$slice_id'>Back to Slices</a>\n";
+
+// Print footer
+include 'plc_footer.php';
+
+?>
diff --git a/planetlab/tablesort/customsort.js b/planetlab/tablesort/customsort.js
new file mode 100644 (file)
index 0000000..a766898
--- /dev/null
@@ -0,0 +1,356 @@
+/*\r
+    sortEnglishDateTime\r
+    -----------------------\r
+\r
+    This function sorts English dateTime vaues such as:\r
+\r
+    1st January 2003, 23:32:01\r
+    23/03/1972 Ã  10:22:22\r
+    1970/13/03 at 23:22:01\r
+    \r
+    The function is "safe" i.e. non-dateTime data (like the word "Unknown") can be passed in and is sorted properly.\r
+*/\r
+var sortEnglishDateTime = fdTableSort.sortNumeric;\r
+\r
+function sortEnglishDateTimePrepareData(tdNode, innerText) {\r
+        // You can localise the function here\r
+        var months = ['january','february','march','april','may','june','july','august','september','october','november','december','jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'];\r
+\r
+        // Lowercase the text\r
+        var aa = innerText.toLowerCase();\r
+\r
+        // Replace the longhand months with an integer equivalent\r
+        for(var i = 0; i < months.length; i++) {\r
+                aa = aa.replace(months[i], (i+13)%12);\r
+        };\r
+\r
+        // Replace multiple spaces and anything that is not valid in the parsing of the date, then trim\r
+        aa = aa.replace(/\s+/g, " ").replace(/([^\d\s\/-:.])/g, "").replace(/^\s\s*/, '').replace(/\s\s*$/, '');\r
+\r
+        // No timestamp at the end, then return -1\r
+        if(aa.search(/(\d){2}:(\d){2}(:(\d){2})?$/) == -1) { return -1; };\r
+\r
+        // Grab the timestamp\r
+        var timestamp = aa.match(/(\d){2}:(\d){2}(:(\d){2})?$/)[0].replace(/:/g, "");\r
+\r
+        // Make the timestamp 6 characters by default\r
+        if(timestamp.length == 4) { timestamp += "00"; };\r
+\r
+        // Remove it from the string to assist the date parser, then trim\r
+        aa = aa.replace(/(\d){2}:(\d){2}(:(\d){2})?$/, "").replace(/\s\s*$/, '');\r
+\r
+        // If you want the parser to favour the parsing of European dd/mm/yyyy dates then leave this set to "true"\r
+        // If you want the parser to favour the parsing of American mm/dd/yyyy dates then set to "false"\r
+        var favourDMY = true;\r
+\r
+        // If you have a regular expression you wish to add, add the Object to the end of the array\r
+        var dateTest = [\r
+                       { regExp:/^(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])([- \/.])((\d\d)?\d\d)$/, d:3, m:1, y:5 },  // mdy\r
+                       { regExp:/^(0?[1-9]|[12][0-9]|3[01])([- \/.])(0?[1-9]|1[012])([- \/.])((\d\d)?\d\d)$/, d:1, m:3, y:5 },  // dmy\r
+                       { regExp:/^(\d\d\d\d)([- \/.])(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])$/, d:5, m:3, y:1 }      // ymd\r
+                       ];\r
+\r
+        var start,y,m,d;\r
+        var cnt = 0;\r
+        var numFormats = dateTest.length;\r
+        while(cnt < numFormats) {\r
+               start = (cnt + (favourDMY ? numFormats + 1 : numFormats)) % numFormats;\r
+               if(aa.match(dateTest[start].regExp)) {\r
+                       res = aa.match(dateTest[start].regExp);\r
+                       y = res[dateTest[start].y];\r
+                       m = res[dateTest[start].m];\r
+                       d = res[dateTest[start].d];\r
+                       if(m.length == 1) m = "0" + String(m);\r
+                       if(d.length == 1) d = "0" + String(d);\r
+                       if(y.length != 4) y = (parseInt(y) < 50) ? "20" + String(y) : "19" + String(y);\r
+\r
+                       return y+String(m)+d+String(timestamp);\r
+               };\r
+               cnt++;\r
+        };\r
+        return -1;\r
+};\r
+\r
+/*\r
+    sortAlphaNumeric\r
+    -----------------------\r
+\r
+    This function sorts alphaNumeric values e.g. 1, e, 1a, -23c, 54z\r
+    \r
+    Notice how the prepareData function actually returns an Array i.e. you are not limited\r
+    in the type of data you return to the tableSort script.\r
+*/\r
+function sortAlphaNumericPrepareData(tdNode, innerText){\r
+        var aa = innerText.toLowerCase().replace(" ", "");\r
+        var reg = /((\-|\+)?(\s+)?[0-9]+\.([0-9]+)?|(\-|\+)?(\s+)?(\.)?[0-9]+)([a-z]+)/;\r
+\r
+        if(reg.test(aa)) {\r
+                var aaP = aa.match(reg);\r
+                return [aaP[1], aaP[8]];\r
+        };\r
+\r
+        // Return an array\r
+        return isNaN(aa) ? ["",aa] : [aa,""];\r
+}\r
+\r
+function sortAlphaNumeric(a, b){\r
+        // Get the previously prepared array\r
+        var aa = a[fdTableSort.pos];\r
+        var bb = b[fdTableSort.pos];\r
+\r
+        // If they are equal then return 0\r
+        if(aa[0] == bb[0] && aa[1] == bb[1]) { return 0; };\r
+\r
+        // Check numeric parts if not equal\r
+        if(aa[0] != bb[0]) {\r
+                if(aa[0] != "" && bb[0] != "") { return aa[0] - bb[0]; };\r
+                if(aa[0] == "" && bb[0] != "") { return -1; };\r
+                return 1;\r
+        };\r
+        \r
+        // Check alpha parts if numeric parts equal\r
+        if(aa[1] == bb[1]) return 0;\r
+        if(aa[1] < bb[1])  return -1;\r
+        return 1;\r
+}\r
+\r
+/*\r
+    sortDutchCurrencyValues\r
+    -----------------------\r
+\r
+    This function sorts Dutch currency values (of the type 100.000,00)\r
+    The function is "safe" i.e. non-currency data (like the word "Unknown") can be passed in and is sorted properly.\r
+*/\r
+var sortDutchCurrencyValues = fdTableSort.sortNumeric;\r
+\r
+function sortDutchCurrencyValuesPrepareData(tdNode, innerText) {\r
+        innerText = parseInt(innerText.replace(/[^0-9\.,]+/g, "").replace(/\./g,"").replace(",","."));\r
+        return isNaN(innerText) ? "" : innerText;\r
+}\r
+\r
+/*\r
+   sortByTwelveHourTimestamp\r
+   -------------------------\r
+\r
+   This custom sort function sorts 12 hour timestamps of an hour/minute nature.\r
+   The hour/minute dividor can be a full-stop or a colon and it correctly calculates that 12.30am is before 1am etc\r
+   The am/pm part can be written in lower or uppercase and can optionally contain full-stops e.g.\r
+\r
+   am, a.m, a.m., AM, A.M etc\r
+\r
+   Additionally, the values "12 midnight" and "12 noon" are also handled correctly.\r
+\r
+   The question remains... does "12p.m." mean "midnight" or "12 noon"? I've decided here that it's 12 noon.\r
+\r
+   The function is "safe" i.e. non-timestamp data (like the word "Unknown") can be passed in and is sorted properly.\r
+*/\r
+var sortByTwelveHourTimestamp = fdTableSort.sortNumeric;\r
+\r
+function sortByTwelveHourTimestampPrepareData(tdNode, innerText) {\r
+        tmp = innerText\r
+        innerText = innerText.replace(":",".");\r
+\r
+        // Check for the special cases of "12 noon" or "12 midnight"\r
+        if(innerText.search(/12([\s]*)?noon/i) != -1) return "12.00";\r
+        if(innerText.search(/12([\s]*)?midnight/i) != -1) return "24.00";\r
+\r
+        var regExpPM = /^([0-9]{1,2}).([0-9]{2})([\s]*)?(p[\.]?m)/i;\r
+        var regExpAM = /^([0-9]{1,2}).([0-9]{2})([\s]*)?(a[\.]?m)/i;\r
+\r
+        if(innerText.search(regExpPM) != -1) {\r
+                var bits = innerText.match(regExpPM);\r
+                if(parseInt(bits[1]) < 12) { bits[1] = parseInt(bits[1]) + 12; }\r
+        } else if(innerText.search(regExpAM) != -1) {\r
+                var bits = innerText.match(regExpAM);\r
+                if(bits[1] == "12") { bits[1] = "00"; }\r
+        } else return "";\r
+\r
+        if(bits[2].length < 2) { bits[2] = "0" + String(bits[2]); }\r
+\r
+        innerText = bits[1] + "." + String(bits[2]);\r
+\r
+        return isNaN(innerText) ? "" : innerText;\r
+}\r
+/*\r
+   sortEnglishLonghandDateFormat\r
+   -----------------------------\r
+\r
+   This custom sort function sorts dates of the format:\r
+\r
+   "12th April, 2006" or "12 April 2006" or "12-4-2006" or "12 April" or "12 4" or "12 Apr 2006" etc\r
+\r
+   The function expects dates to be in the format day/month/year. Should no year be stipulated,\r
+   the function treats the year as being the current year.\r
+\r
+   The function is "safe" i.e. non-date data (like the word "Unknown") can be passed in and is sorted properly.\r
+*/\r
+var sortEnglishLonghandDateFormat = fdTableSort.sortNumeric;\r
+\r
+function sortEnglishLonghandDateFormatPrepareData(tdNode, innerText) {\r
+        var months = ['january','february','march','april','may','june','july','august','september','october','november','december'];\r
+\r
+        var aa = innerText.toLowerCase();\r
+\r
+        // Replace the longhand months with an integer equivalent\r
+        for(var i = 0; i < 12; i++) {\r
+                aa = aa.replace(months[i], i+1).replace(months[i].substring(0,3), i+1);\r
+        }\r
+\r
+        // If there are still alpha characters then return -1\r
+        if(aa.search(/a-z/) != -1) return -1;\r
+\r
+        // Replace multiple spaces and anything that is not numeric\r
+        aa = aa.replace(/\s+/g, " ").replace(/[^\d\s]/g, "");\r
+\r
+        // If were left with nothing then return -1\r
+        if(aa.replace(" ", "") == "") return -1;\r
+\r
+        // Split on the (now) single spaces\r
+        aa = aa.split(" ");\r
+\r
+        // If something has gone terribly wrong then return -1\r
+        if(aa.length < 2) return -1;\r
+\r
+        // If no year stipulated, then add this year as default\r
+        if(aa.length == 2) {\r
+                aa[2] = String(new Date().getFullYear());\r
+        }\r
+\r
+        // Equalise the day and month\r
+        if(aa[0].length < 2) aa[0] = "0" + String(aa[0]);\r
+        if(aa[1].length < 2) aa[1] = "0" + String(aa[1]);\r
+\r
+        // Deal with Y2K issues\r
+        if(aa[2].length != 4) {\r
+                aa[2] = (parseInt(aa[2]) < 50) ? '20' + aa[2] : '19' + aa[2];\r
+        }\r
+\r
+        // YMD (can be used as integer during comparison)\r
+        return aa[2] + String(aa[1]) + aa[0];\r
+}\r
+/*\r
+   sortIPAddress\r
+   -------------\r
+\r
+   This custom sort function correctly sorts IP addresses i.e. it checks all of the address parts and not just the first.\r
+\r
+   The function is "safe" i.e. non-IP address data (like the word "Unknown") can be passed in and is sorted properly.\r
+*/\r
+var sortIPAddress = fdTableSort.sortNumeric;\r
+\r
+function sortIPAddressPrepareData(tdNode, innerText) {\r
+        // Get the innerText of the TR nodes\r
+        var aa = innerText;\r
+\r
+        // Remove spaces\r
+        aa = aa.replace(" ","");\r
+\r
+        // If not an IP address then return -1\r
+        if(aa.search(/^([0-9]{1,3}).([0-9]{1,3}).([0-9]{1,3}).([0-9]{1,3})$/) == -1) return -1;\r
+\r
+        // Split on the "."\r
+        aa = aa.split(".");\r
+\r
+        // If we don't have 4 parts then return -1\r
+        if(aa.length != 4) return -1;\r
+\r
+        var retVal = "";\r
+\r
+        // Make all the parts an equal length and create a master integer\r
+        for(var i = 0; i < 4; i++) {\r
+                retVal += (String(aa[i]).length < 3) ? "0000".substr(0, 3 - String(aa[i]).length) + String(aa[i]) : aa[i];\r
+        }\r
+\r
+        return retVal;\r
+}\r
+/*\r
+   sortScientificNotation\r
+   ----------------------\r
+\r
+   This custom sort function sorts numbers stipulated in scientific notation\r
+\r
+   The function is "safe" i.e. data like the word "Unknown" can be passed in and is sorted properly.\r
+\r
+   N.B. The only way I can think to really sort scientific notation is to convert\r
+        it to a floating point number and then perform the sort on that. If you can think of\r
+        an easier/better way then please let me know.\r
+*/\r
+var sortScientificNotation = fdTableSort.sortNumeric;\r
+\r
+function sortScientificNotationPrepareData(tdNode, innerText) {\r
+        var aa = innerText;\r
+\r
+        var floatRegExp = /((\-|\+)?(\s+)?[0-9]+\.([0-9]+)?|(\-|\+)?(\s+)?(\.)?[0-9]+)/g;\r
+\r
+        aa = aa.match(floatRegExp);\r
+\r
+        if(!aa || aa.length != 2) return "";\r
+\r
+        var f1 = parseFloat(aa[0].replace(" ",""))*Math.pow(10,parseFloat(aa[1].replace(" ","")));\r
+        return isNaN(f1) ? "" : f1;\r
+}\r
+\r
+/*\r
+        sortImage\r
+        ---------\r
+\r
+        This is the function called in order to sort the data previously prepared by the function\r
+        "sortImagePrepareData". It does a basic case sensitive comparison on the data using the\r
+        tableSort's in-built sortText method.\r
+*/\r
+var sortImage = fdTableSort.sortText;\r
+\r
+/*\r
+        This is the function used to prepare i.e. parse data, to be used during the sort\r
+        of the images within the last table.\r
+\r
+        In this case, we are checking to see if the TD node has any child nodes that are\r
+        images and, if an image exists, return it's "src" attribute.\r
+        If no image exists, then we return an empty string.\r
+\r
+        The "prepareData" functions are passed the actual TD node and also the TD node inner text\r
+        which means you are free to check for child nodes etc and are not just limited to\r
+        sorting on the TD node's inner text.\r
+\r
+        The prepareData functions are not required (only your bespoke sort function is required)\r
+        and only called by the script should they exist.\r
+*/\r
+function sortImagePrepareData(td, innerText) {\r
+        var img = td.getElementsByTagName('img');\r
+        return img.length ? img[0].src: "";\r
+}\r
+\r
+/*\r
+        sortFileSize\r
+        ------------\r
+\r
+        1 Byte = 8 Bit\r
+        1 Kilobyte = 1024 Bytes\r
+        1 Megabyte = 1048576 Bytes\r
+        1 Gigabyte = 1073741824 Bytes\r
+*/\r
+var sortFileSize = fdTableSort.sortNumeric;\r
+\r
+function sortFileSizePrepareData(td, innerText) {\r
+        var regExp = /(kb|mb|gb)/i;\r
+\r
+        var type = innerText.search(regExp) != -1 ? innerText.match(regExp)[0] : "";\r
+\r
+        switch (type.toLowerCase()) {\r
+                case "kb" :\r
+                        mult = 1024;\r
+                        break;\r
+                case "mb" :\r
+                        mult = 1048576;\r
+                        break;\r
+                case "gb" :\r
+                        mult = 1073741824;\r
+                        break;\r
+                default :\r
+                        mult = 1;\r
+        };\r
+\r
+        innerText = parseFloat(innerText.replace(/[^0-9\.\-]/g,''));\r
+\r
+        return isNaN(innerText) ? "" : innerText * mult;\r
+};\r
diff --git a/planetlab/tablesort/paginate.js b/planetlab/tablesort/paginate.js
new file mode 100644 (file)
index 0000000..0dfcc7d
--- /dev/null
@@ -0,0 +1,479 @@
+/*\r
+        paginate table object v2.0 by frequency-decoder.com\r
+\r
+        Released under a creative commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/)\r
+\r
+        Please credit frequency decoder in any derivative work - thanks\r
+\r
+        You are free:\r
+\r
+        * to copy, distribute, display, and perform the work\r
+        * to make derivative works\r
+        * to make commercial use of the work\r
+\r
+        Under the following conditions:\r
+\r
+                by Attribution.\r
+                --------------\r
+                You must attribute the work in the manner specified by the author or licensor.\r
+\r
+                sa\r
+                --\r
+                Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.\r
+\r
+        * For any reuse or distribution, you must make clear to others the license terms of this work.\r
+        * Any of these conditions can be waived if you get permission from the copyright holder.\r
+*/\r
+\r
+var tablePaginater = (function() {\r
+        /*\r
+\r
+        Localise the button titles here...\r
+\r
+        %p is replaced with the appropriate page number\r
+        %t is replaced with the total number of pages\r
+        \r
+        */\r
+        var tableInfo = {},\r
+            uniqueID  = 0,\r
+            text      = ["First Page","Previous Page (Page %p)","Next Page (Page %p)","Last Page (Page %t)","Page %p of %t"];\r
+            \r
+        var addClass = function(e,c) {\r
+                if(new RegExp("(^|\\s)" + c + "(\\s|$)").test(e.className)) return;\r
+                e.className += ( e.className ? " " : "" ) + c;\r
+        }; \r
+        \r
+        /*@cc_on\r
+        /*@if (@_win32)\r
+        var removeClass = function(e,c) {\r
+                e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');\r
+        };\r
+        @else @*/\r
+        var removeClass = function(e,c) {                 \r
+                e.className = !c ? "" : (e.className || "").replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s\s*/, '').replace(/\s\s*$/, '');\r
+        };\r
+        /*@end\r
+        @*/  \r
+        \r
+        var addEvent = function(obj, type, fn) {\r
+                if( obj.attachEvent ) {\r
+                        obj["e"+type+fn] = fn;\r
+                        obj[type+fn] = function(){obj["e"+type+fn]( window.event );};\r
+                        obj.attachEvent( "on"+type, obj[type+fn] );\r
+                } else {\r
+                        obj.addEventListener( type, fn, true );\r
+                };\r
+        };\r
+        var removeEvent = function(obj, type, fn) {\r
+                try {\r
+                        if( obj.detachEvent ) {\r
+                                obj.detachEvent( "on"+type, obj[type+fn] );\r
+                                obj[type+fn] = null;\r
+                        } else {\r
+                                obj.removeEventListener( type, fn, true );\r
+                        };\r
+                } catch(err) {};\r
+        };\r
+        var stopEvent = function(e) {\r
+                e = e || window.event;\r
+                if(e.stopPropagation) {\r
+                        e.stopPropagation();\r
+                        e.preventDefault();\r
+                };\r
+                \r
+                /*@cc_on@*/\r
+                /*@if(@_win32)\r
+                e.cancelBubble = true;\r
+                e.returnValue  = false;\r
+                /*@end@*/\r
+                return false;\r
+        }; \r
+        \r
+        var init = function(tableId) {\r
+                var tables = tableId && typeof(tableId) == "string" ? [document.getElementById(tableId)] : document.getElementsByTagName('table'),\r
+                    hook, maxPages, visibleRows, numPages, cp, cb, rowList;\r
+                \r
+                for(var t = 0, tbl; tbl = tables[t]; t++) {\r
+                        if(tbl.className.search(/paginate-([0-9]+)/) == -1) { continue; };\r
+\r
+                        if(!tbl.id) { tbl.id = "fdUniqueTableId_" + uniqueID++; };\r
+\r
+                        maxPages = tbl.className.search(/max-pages-([0-9]+)/) == -1 ? null : Number(tbl.className.match(/max-pages-([0-9]+)/)[1]);\r
+                        if(maxPages % 2 == 0 && maxPages > 1) { maxPages--; };\r
+                        \r
+                        hook = tbl.getElementsByTagName('tbody');\r
+                        hook = (hook.length) ? hook[0] : tbl;\r
+\r
+                        visibleRows = calculateVisibleRows(hook);\r
+                        \r
+                        if(maxPages > (visibleRows / Number(tbl.className.match(/paginate-([0-9]+)/)[1]))) {\r
+                                maxPages = null;\r
+                        };\r
+                        \r
+                        numPages = Math.ceil(visibleRows / Number(tbl.className.match(/paginate-([0-9]+)/)[1]));\r
+                        \r
+                        if(numPages < 2 && !(tbl.id in tableInfo)) {\r
+                                continue;\r
+                        };\r
+                        \r
+                        cp = (tbl.id in tableInfo) ? Math.min(tableInfo[tbl.id].currentPage, numPages) : 1;\r
+                        \r
+                        tableInfo[tbl.id] = {\r
+                                rowsPerPage:Number(tbl.className.match(/paginate-([0-9]+)/)[1]),\r
+                                currentPage:cp,\r
+                                totalRows:hook.getElementsByTagName('tr').length,\r
+                                hook:hook,\r
+                                maxPages:maxPages,\r
+                                numPages:numPages,\r
+                                rowStyle:tbl.className.search(/rowstyle-([\S]+)/) != -1 ? tbl.className.match(/rowstyle-([\S]+)/)[1] : false,\r
+                                callbacks:parseCallback(/^paginationcallback-/i, /paginationcallback-([\S-]+)/ig, tbl.className)\r
+                        };\r
+                        \r
+                        showPage(tbl.id);\r
+                        hook = null;\r
+                };\r
+        };\r
+        \r
+        var parseCallback = function(head, regExp, cname) {\r
+                var cbs    = [],\r
+                    matchs = cname.match(regExp),\r
+                    parts, obj, func;\r
+                \r
+                if(!matchs) { return []; };\r
+                  \r
+                for(var i = 0, mtch; mtch = matchs[i]; i++) {                         \r
+                        mtch = mtch.replace(head, "").replace(/-/g, ".");\r
+                         \r
+                        try {\r
+                                if(mtch.indexOf(".") != -1) {\r
+                                        parts = mtch.split('.');\r
+                                        obj   = window;\r
+                                        for (var x = 0, part; part = obj[parts[x]]; x++) {\r
+                                                if(part instanceof Function) {\r
+                                                        (function() {\r
+                                                                var method = part;\r
+                                                                func = function (data) { method.apply(obj, [data]) };\r
+                                                        })();\r
+                                                } else {\r
+                                                        obj = part;\r
+                                                };\r
+                                        };\r
+                                } else {\r
+                                        func = window[mtch];\r
+                                };\r
+                            \r
+                                if(!(func instanceof Function)) continue;\r
+                                cbs[cbs.length] = func;                              \r
+                        } catch(err) {};\r
+                };\r
+                \r
+                return cbs;                      \r
+        };\r
+                \r
+        var callback = function(tblId, opts) {                \r
+                if(!(tblId in tableInfo) || !(tableInfo[tblId]["callbacks"].length)) return;                \r
+                for(var i = 0, func; func = tableInfo[tblId]["callbacks"][i]; i++) {\r
+                        func(opts || {});\r
+                };\r
+        };\r
+        \r
+        var calculateVisibleRows = function(hook) {\r
+                var trs = hook.rows,\r
+                    cnt = 0,\r
+                    reg = /(^|\s)invisibleRow(\s|$)/;\r
+                \r
+                for(var i = 0, tr; tr = trs[i]; i++) {\r
+                        if(tr.parentNode != hook || tr.getElementsByTagName("th").length || (tr.parentNode && tr.parentNode.tagName.toLowerCase().search(/thead|tfoot/) != -1)) continue;\r
+                        if(tr.className.search(reg) == -1) { cnt++; };\r
+                };\r
+                \r
+                return cnt;\r
+        };\r
+        \r
+        var createButton = function(details, ul, pseudo) {\r
+                var li   = document.createElement("li"),\r
+                    but  = document.createElement(pseudo ? "div" : "a"),\r
+                    span = document.createElement("span");\r
+\r
+                if(!pseudo) { \r
+                        but.href = "#"; \r
+                        but.title = details.title; \r
+                };\r
+                \r
+                but.className = details.className;\r
+\r
+                ul.appendChild(li);\r
+                li.appendChild(but);\r
+                but.appendChild(span);\r
+                span.appendChild(document.createTextNode(details.text));\r
+\r
+                if(!pseudo) { \r
+                        li.onclick = but.onclick = buttonClick; \r
+                        if(details.id) { but.id = details.id; };\r
+                };                 \r
+                \r
+                li = but = span = null;\r
+        };\r
+        var removePagination = function(tableId) {\r
+                var wrapT = document.getElementById(tableId + "-fdtablePaginaterWrapTop"),\r
+                    wrapB = document.getElementById(tableId + "-fdtablePaginaterWrapBottom");\r
+                if(wrapT) { wrapT.parentNode.removeChild(wrapT); };\r
+                if(wrapB) { wrapB.parentNode.removeChild(wrapB); };\r
+        };\r
+        var buildPagination = function(tblId) {\r
+                if(!(tblId in tableInfo)) { return; };\r
+\r
+                removePagination(tblId);\r
+\r
+                var details = tableInfo[tblId];\r
+                \r
+                if(details.numPages < 2) return;\r
+                \r
+                function resolveText(txt, curr) {\r
+                        curr = curr || details.currentPage;\r
+                        return txt.replace("%p", curr).replace("%t", details.numPages);\r
+                };\r
+\r
+                if(details.maxPages) {\r
+                        findex = Math.max(0, Math.floor(Number(details.currentPage - 1) - (Number(details.maxPages - 1) / 2)));\r
+                        lindex = findex + Number(details.maxPages);\r
+                        if(lindex > details.numPages) {\r
+                                lindex = details.numPages;\r
+                                findex = Math.max(0, details.numPages - Number(details.maxPages));\r
+                        };\r
+                } else {\r
+                        findex = 0;\r
+                        lindex = details.numPages;\r
+                };\r
+                \r
+\r
+                var wrapT = document.createElement("div");\r
+                wrapT.className = "fdtablePaginaterWrap";\r
+                wrapT.id = tblId + "-fdtablePaginaterWrapTop";\r
+\r
+                var wrapB = document.createElement("div");\r
+                wrapB.className = "fdtablePaginaterWrap";\r
+                wrapB.id = tblId + "-fdtablePaginaterWrapBottom";\r
+\r
+                // Create list scaffold\r
+                var ulT = document.createElement("ul");\r
+                ulT.id  = tblId + "-tablePaginater";\r
+\r
+                var ulB = document.createElement("ul");\r
+                ulB.id  = tblId + "-tablePaginaterClone";\r
+                ulT.className = ulB.className = "fdtablePaginater";\r
+\r
+                // Add to the wrapper DIVs\r
+                wrapT.appendChild(ulT);\r
+                wrapB.appendChild(ulB);\r
+\r
+                // FIRST (only created if maxPages set)\r
+                if(details.maxPages) {\r
+                        createButton({title:text[0], className:"first-page", text:"\u00ab"}, ulT, !findex);\r
+                        createButton({title:text[0], className:"first-page", text:"\u00ab"}, ulB, !findex);\r
+                };\r
+                \r
+                // PREVIOUS (only created if there are more than two pages)\r
+                if(details.numPages > 2) {\r
+                        createButton({title:resolveText(text[1], details.currentPage-1), className:"previous-page", text:"\u2039", id:tblId+"-previousPage"}, ulT, details.currentPage == 1);\r
+                        createButton({title:resolveText(text[1], details.currentPage-1), className:"previous-page", text:"\u2039", id:tblId+"-previousPageC"}, ulB, details.currentPage == 1);\r
+                };\r
+                \r
+                // NUMBERED\r
+                for(var i = findex; i < lindex; i++) {\r
+                        createButton({title:resolveText(text[4], i+1), className:i != (details.currentPage-1) ? "page-"+(i+1) : "currentPage page-"+(i+1), text:(i+1), id:i == (details.currentPage-1) ? tblId + "-currentPage" : ""}, ulT);\r
+                        createButton({title:resolveText(text[4], i+1), className:i != (details.currentPage-1) ? "page-"+(i+1) : "currentPage page-"+(i+1), text:(i+1), id:i == (details.currentPage-1) ? tblId + "-currentPageC" : ""}, ulB);\r
+                };\r
+\r
+                // NEXT (only created if there are more than two pages)\r
+                if(details.numPages > 2) {\r
+                        createButton({title:resolveText(text[2], details.currentPage + 1), className:"next-page", text:"\u203a", id:tblId+"-nextPage"}, ulT, details.currentPage == details.numPages);\r
+                        createButton({title:resolveText(text[2], details.currentPage + 1), className:"next-page", text:"\u203a", id:tblId+"-nextPageC"}, ulB, details.currentPage == details.numPages);\r
+                };\r
+                \r
+                // LAST (only created if maxPages set)\r
+                if(details.maxPages) {\r
+                        createButton({title:resolveText(text[3], details.numPages), className:"last-page", text:"\u00bb"}, ulT, lindex == details.numPages);\r
+                        createButton({title:resolveText(text[3], details.numPages), className:"last-page", text:"\u00bb"}, ulB, lindex == details.numPages);\r
+                };\r
+                \r
+                // DOM inject wrapper DIVs (FireFox 2.x Bug: this has to be done here if you use display:table)\r
+                if(document.getElementById(tblId+"-paginationListWrapTop")) {\r
+                        document.getElementById(tblId+"-paginationListWrapTop").appendChild(wrapT);\r
+                } else {\r
+                        document.getElementById(tblId).parentNode.insertBefore(wrapT, document.getElementById(tblId));\r
+                };\r
+\r
+                if(document.getElementById(tblId+"-paginationListWrapBottom")) {\r
+                        document.getElementById(tblId+"-paginationListWrapBottom").appendChild(wrapB);\r
+                } else {\r
+                        document.getElementById(tblId).parentNode.insertBefore(wrapB, document.getElementById(tblId).nextSibling);\r
+                };\r
+        };\r
+        \r
+        // The tableSort script uses this function to redraw.\r
+        var tableSortRedraw = function(tableid, identical) {\r
+                if(!tableid || !(tableid in fdTableSort.tableCache) || !(tableid in tableInfo)) { return; };\r
+                \r
+                var dataObj     = fdTableSort.tableCache[tableid],\r
+                    data        = dataObj.data,\r
+                    len1        = data.length,\r
+                    len2        = len1 ? data[0].length - 1 : 0,\r
+                    hook        = dataObj.hook,\r
+                    colStyle    = dataObj.colStyle,\r
+                    rowStyle    = dataObj.rowStyle,\r
+                    colOrder    = dataObj.colOrder,                 \r
+                    page        = tableInfo[tableid].currentPage - 1,\r
+                    d1          = tableInfo[tableid].rowsPerPage * page,\r
+                    d2          = Math.min(tableInfo[tableid].totalRows, d1 + tableInfo[tableid].rowsPerPage), \r
+                    cnt         = 0,\r
+                    rs          = 0,\r
+                    reg         = /(^|\s)invisibleRow(\s|$)/,                \r
+                    tr, tds, cell, pos;\r
+                \r
+                for(var i = 0; i < len1; i++) {\r
+                        tr = data[i][len2];\r
+                        \r
+                        if(colStyle) {\r
+                                tds = tr.cells;\r
+                                for(thPos in colOrder) {\r
+                                        if(!colOrder[thPos]) removeClass(tds[thPos], colStyle);\r
+                                        else addClass(tds[thPos], colStyle);\r
+                                };\r
+                        };\r
+                        \r
+                        if(tr.className.search(reg) != -1) { continue; };\r
+                        \r
+                        if(!identical) {\r
+                                cnt++;\r
+\r
+                                if(cnt > d1 && cnt <= d2) {\r
+                                        if(rowStyle) {\r
+                                                if(rs++ & 1) addClass(tr, rowStyle);\r
+                                                else removeClass(tr, rowStyle);\r
+                                        };\r
+                                        tr.style.display = "";\r
+                                } else {\r
+                                        tr.style.display = "none";\r
+                                };\r
+\r
+                                // Netscape 8.1.2 requires the removeChild call or it freaks out, so add the line if you want to support this browser\r
+                                // hook.removeChild(tr);\r
+                                hook.appendChild(tr);\r
+                        };\r
+                };\r
+\r
+                tr = tds = hook = null;\r
+        };\r
+        \r
+        var showPage = function(tblId, pageNum) {\r
+                if(!(tblId in tableInfo)) { return; };\r
+\r
+                var page = Math.max(0, !pageNum ? tableInfo[tblId].currentPage - 1 : pageNum - 1),\r
+                    d1  = tableInfo[tblId].rowsPerPage * page,\r
+                    d2  = Math.min(tableInfo[tblId].totalRows, d1 + tableInfo[tblId].rowsPerPage),\r
+                    trs = tableInfo[tblId].hook.rows,\r
+                    cnt = 0,\r
+                    rc  = 0,\r
+                    len = trs.length,\r
+                    rs  = tableInfo[tblId].rowStyle,\r
+                    reg = /(^|\s)invisibleRow(\s|$)/,\r
+                    row = [];\r
+                \r
+                for(var i = 0; i < len; i++) {\r
+                        if(trs[i].className.search(reg) != -1 || trs[i].getElementsByTagName("th").length || (trs[i].parentNode && trs[i].parentNode.tagName.toLowerCase().search(/thead|tfoot/) != -1)) { continue; };\r
+                        \r
+                        cnt++;\r
+                        \r
+                        if(cnt > d1 && cnt <= d2) {\r
+                                if(rs) {\r
+                                        if(rc++ & 1) {\r
+                                                addClass(trs[i], rs);\r
+                                        } else {\r
+                                                removeClass(trs[i], rs);\r
+                                        }\r
+                                };\r
+                                trs[i].style.display = "";\r
+                                row[row.length] = trs[i];\r
+                        } else {\r
+                                trs[i].style.display = "none";\r
+                        };\r
+                };\r
+\r
+                buildPagination(tblId);\r
+                callback(tblId, {"totalRows":len, "currentPage":(page+1), "rowsPerPage":tableInfo[tblId].rowsPerPage, "visibleRows":row});\r
+        };\r
+        \r
+        var buttonClick = function(e) {\r
+                e = e || window.event;\r
+\r
+                var a = this.tagName.toLowerCase() == "a" ? this : this.getElementsByTagName("a")[0];\r
+\r
+                if(a.className.search("currentPage") != -1) return false;\r
+\r
+                var ul = this;\r
+                while(ul.tagName.toLowerCase() != "ul") ul = ul.parentNode;\r
+\r
+                var tblId = ul.id.replace("-tablePaginaterClone","").replace("-tablePaginater", "");\r
+\r
+                tableInfo[tblId].lastPage = tableInfo[tblId].currentPage;\r
+                \r
+                var showPrevNext = 0;\r
+                \r
+                if(a.className.search("previous-page") != -1) {\r
+                        tableInfo[tblId].currentPage = tableInfo[tblId].currentPage > 1 ? tableInfo[tblId].currentPage - 1 : tableInfo[tblId].numPages;\r
+                        showPrevNext = 1;\r
+                } else if(a.className.search("next-page") != -1) {\r
+                        tableInfo[tblId].currentPage = tableInfo[tblId].currentPage < tableInfo[tblId].numPages ? tableInfo[tblId].currentPage + 1 : 1;\r
+                        showPrevNext = 2;\r
+                } else if(a.className.search("first-page") != -1) {\r
+                        tableInfo[tblId].currentPage = 1;\r
+                } else if(a.className.search("last-page") != -1) {\r
+                        tableInfo[tblId].currentPage = tableInfo[tblId].numPages;\r
+                } else {\r
+                        tableInfo[tblId].currentPage = parseInt(a.className.match(/page-([0-9]+)/)[1]) || 1;\r
+                };\r
+\r
+                showPage(tblId);\r
+\r
+                // Focus on the appropriate button (previous, next or the current page)\r
+                // I'm hoping screen readers are savvy enough to indicate the focus event to the user\r
+                if(showPrevNext == 1) {\r
+                        var elem = document.getElementById(ul.id.search("-tablePaginaterClone") != -1 ? tblId + "-previousPageC" : tblId + "-previousPage");\r
+                } else if(showPrevNext == 2) {\r
+                        var elem = document.getElementById(ul.id.search("-tablePaginaterClone") != -1 ? tblId + "-nextPageC" : tblId + "-nextPage");\r
+                } else {\r
+                        var elem = document.getElementById(ul.id.search("-tablePaginaterClone") != -1 ? tblId + "-currentPageC" : tblId + "-currentPage");\r
+                };\r
+                \r
+                if(elem && elem.tagName.toLowerCase() == "a") { elem.focus(); };   \r
+                \r
+                return stopEvent(e);\r
+        };\r
+        \r
+        var onUnLoad = function(e) {\r
+                var tbl, lis, pagination, uls;\r
+                for(tblId in tableInfo) {\r
+                        uls = [tblId + "-tablePaginater", tblId + "-tablePaginaterClone"];\r
+                        for(var z = 0; z < 2; z++) {\r
+                                pagination = document.getElementById(uls[z]);\r
+                                if(!pagination) { continue; };\r
+                                lis = pagination.getElementsByTagName("li");\r
+                                for(var i = 0, li; li = lis[i]; i++) {\r
+                                        li.onclick = null;\r
+                                        if(li.getElementsByTagName("a").length) { li.getElementsByTagName("a")[0].onclick = null; };\r
+                                };\r
+                        };\r
+                };\r
+        };\r
+        \r
+        addEvent(window, "load",   init);\r
+        addEvent(window, "unload", onUnLoad); \r
+        \r
+        return {                  \r
+                init:                   function(tableId) { init(tableId); },                 \r
+                redraw:                 function(tableid, identical) { tableSortRedraw(tableid, identical); },\r
+                tableIsPaginated:       function(tableId) { return (tableId in tableInfo); },\r
+                changeTranslations:     function(translations) { text = translations; }\r
+        };        \r
+})();
\ No newline at end of file
diff --git a/planetlab/tablesort/tablesort.js b/planetlab/tablesort/tablesort.js
new file mode 100644 (file)
index 0000000..27e8c3c
--- /dev/null
@@ -0,0 +1,748 @@
+/*\r
+        TableSort revisited v5.0 by frequency-decoder.com\r
+\r
+        Released under a creative commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/)\r
+\r
+        Please credit frequency decoder in any derivative work - thanks\r
+\r
+        You are free:\r
+\r
+        * to copy, distribute, display, and perform the work\r
+        * to make derivative works\r
+        * to make commercial use of the work\r
+\r
+        Under the following conditions:\r
+\r
+                by Attribution.\r
+                --------------\r
+                You must attribute the work in the manner specified by the author or licensor.\r
+\r
+                sa\r
+                --\r
+                Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.\r
+\r
+        * For any reuse or distribution, you must make clear to others the license terms of this work.\r
+        * Any of these conditions can be waived if you get permission from the copyright holder.\r
+*/\r
+\r
+(function() {\r
+fdTableSort = {\r
+        regExp_Currency:        /^[£$€¥¤]/,\r
+        regExp_Number:          /^(\-)?[0-9]+(\.[0-9]*)?$/,\r
+        pos:                    -1,\r
+        uniqueHash:             1,\r
+        thNode:                 null,\r
+        tableId:                null,\r
+        tableCache:             {},\r
+        tmpCache:               {},\r
+        sortActiveClass:        "sort-active",\r
+        /*@cc_on\r
+        /*@if (@_win32)\r
+        colspan:                "colSpan",\r
+        rowspan:                "rowSpan",\r
+        @else @*/\r
+        colspan:                "colspan",\r
+        rowspan:                "rowspan",\r
+        /*@end\r
+        @*/\r
+        \r
+        addEvent: function(obj, type, fn, tmp) {\r
+                tmp || (tmp = true);\r
+                if( obj.attachEvent ) {\r
+                        obj["e"+type+fn] = fn;\r
+                        obj[type+fn] = function(){obj["e"+type+fn]( window.event );};\r
+                        obj.attachEvent( "on"+type, obj[type+fn] );\r
+                } else {\r
+                        obj.addEventListener( type, fn, true );\r
+                };\r
+        },\r
+        removeEvent: function(obj, type, fn, tmp) {\r
+                tmp || (tmp = true);\r
+                try {\r
+                        if( obj.detachEvent ) {\r
+                                obj.detachEvent( "on"+type, obj[type+fn] );\r
+                                obj[type+fn] = null;\r
+                        } else {\r
+                                obj.removeEventListener( type, fn, true );\r
+                        };\r
+                } catch(err) {};\r
+        },\r
+        stopEvent: function(e) {\r
+                e = e || window.event;\r
+\r
+                if(e.stopPropagation) {\r
+                        e.stopPropagation();\r
+                        e.preventDefault();\r
+                };\r
+                \r
+                /*@cc_on@*/\r
+                /*@if(@_win32)\r
+                e.cancelBubble = true;\r
+                e.returnValue  = false;\r
+                /*@end@*/\r
+                return false;\r
+        },\r
+        parseClassName: function(head, tbl) {\r
+                var colMatch = tbl.className.match(new RegExp(head + "((-[\\d]+([r]){0,1})+)"));\r
+                return colMatch && colMatch.length ? colMatch[0].replace(head, "").split("-") : [];\r
+        },\r
+        disableSelection: function(element) {\r
+                element.onselectstart = function() {\r
+                        return false;\r
+                };\r
+                element.unselectable = "on";\r
+                element.style.MozUserSelect = "none";\r
+        },\r
+        removeTableCache: function(tableId) {\r
+                if(!(tableId in fdTableSort.tableCache)) return;\r
+\r
+                fdTableSort.tableCache[tableId] = null;\r
+                delete fdTableSort.tableCache[tableId];\r
+\r
+                var tbl = document.getElementById(tableId);\r
+                if(!tbl) return;\r
+                var ths = tbl.getElementsByTagName("th");\r
+                var a;\r
+                for(var i = 0, th; th = ths[i]; i++) {\r
+                        a = th.getElementsByTagName("a");\r
+                        if(a.length) a[0].onkeydown = a[0].onclick = null;\r
+                        th.onclick = th.onselectstart = th = a = null;\r
+                };\r
+        },\r
+        removeTmpCache: function(tableId) {\r
+                if(!(tableId in fdTableSort.tmpCache)) return;\r
+                var headers = fdTableSort.tmpCache[tableId].headers;\r
+                var a;\r
+                for(var i = 0, row; row = headers[i]; i++) {\r
+                        for(var j = 0, th; th = row[j]; j++) {\r
+                                a = th.getElementsByTagName("a");\r
+                                if(a.length) a[0].onkeydown = a[0].onclick = null;\r
+                                th.onclick = th.onselectstart = th = a = null;\r
+                        };\r
+                };\r
+                fdTableSort.tmpCache[tableId] = null;\r
+                delete fdTableSort.tmpCache[tableId];\r
+        },\r
+        initEvt: function(e) {\r
+                fdTableSort.init(false);\r
+        },\r
+        init: function(tableId) {\r
+                if (!document.getElementsByTagName || !document.createElement || !document.getElementById) return;\r
+\r
+                var tables = tableId && document.getElementById(tableId) ? [document.getElementById(tableId)] : document.getElementsByTagName("table");\r
+                var c, ii, len, colMatch, showOnly, match, showArrow, columnNumSortObj, obj, workArr, headers, thtext, aclone, multi, colCnt, cel, allRowArr, rowArr, sortableTable, celCount, colspan, rowspan, rowLength;\r
+\r
+                var a          = document.createElement("a");\r
+                a.href         = "#";\r
+                a.className    = "fdTableSortTrigger";\r
+\r
+                var span       = document.createElement("span");\r
+\r
+                for(var k = 0, tbl; tbl = tables[k]; k++) {\r
+\r
+                        if(tbl.id) {\r
+                                fdTableSort.removeTableCache(tbl.id);\r
+                                fdTableSort.removeTmpCache(tbl.id);\r
+                        };\r
+\r
+                        allRowArr     = tbl.getElementsByTagName('thead').length ? tbl.getElementsByTagName('thead')[0].getElementsByTagName('tr') : tbl.getElementsByTagName('tr');\r
+                        rowArr        = [];\r
+                        sortableTable = false;\r
+\r
+                        for(var i = 0, tr; tr = allRowArr[i]; i++) {\r
+                                if(tr.getElementsByTagName('td').length || !tr.getElementsByTagName('th').length) { continue; };\r
+                                rowArr[rowArr.length] = tr.getElementsByTagName('th');\r
+                                for(var j = 0, th; th = rowArr[rowArr.length - 1][j]; j++) {\r
+                                        if(th.className.search(/sortable/) != -1) { sortableTable = true; };\r
+                                };\r
+                        };\r
+\r
+                        if(!sortableTable) continue;\r
+\r
+                        if(!tbl.id) { tbl.id = "fd-table-" + fdTableSort.uniqueHash++; };\r
+\r
+                        showArrow   = tbl.className.search("no-arrow") == -1;\r
+                        showOnly    = tbl.className.search("sortable-onload-show") != -1;\r
+\r
+                        columnNumSortObj = {};\r
+                        colMatch         = fdTableSort.parseClassName(showOnly ? "sortable-onload-show" : "sortable-onload", tbl);\r
+                        for(match = 1; match < colMatch.length; match++) {\r
+                                columnNumSortObj[parseInt(colMatch[match], 10)] = { "reverse":colMatch[match].search("r") != -1 };\r
+                        };\r
+\r
+                        rowLength = rowArr[0].length;\r
+\r
+                        for(c = 0;c < rowArr[0].length;c++){\r
+                                if(rowArr[0][c].getAttribute(fdTableSort.colspan) && rowArr[0][c].getAttribute(fdTableSort.colspan) > 1){\r
+                                        rowLength = rowLength + (rowArr[0][c].getAttribute(fdTableSort.colspan) - 1);\r
+                                };\r
+                        };\r
+\r
+                        workArr = new Array(rowArr.length);\r
+                        for(c = rowArr.length;c--;){ workArr[c]= new Array(rowLength); };\r
+\r
+                        for(c = 0;c < workArr.length;c++){\r
+                                celCount = 0;\r
+                                for(i = 0;i < rowLength;i++){\r
+                                        if(!workArr[c][i]){\r
+                                                cel = rowArr[c][celCount];\r
+                                                colspan = (cel.getAttribute(fdTableSort.colspan) > 1) ? cel.getAttribute(fdTableSort.colspan):1;\r
+                                                rowspan = (cel.getAttribute(fdTableSort.rowspan) > 1) ? cel.getAttribute(fdTableSort.rowspan):1;\r
+                                                for(var t = 0;((t < colspan)&&((i+t) < rowLength));t++){\r
+                                                        for(var n = 0;((n < rowspan)&&((c+n) < workArr.length));n++) {\r
+                                                                workArr[(c+n)][(i+t)] = cel;\r
+                                                        };\r
+                                                };\r
+                                                if(++celCount == rowArr[c].length) break;\r
+                                        };\r
+                                };\r
+                        };\r
+\r
+                        for(c = 0;c < workArr.length;c++) {\r
+                                for(i = 0;i < workArr[c].length;i++){\r
+\r
+                                        if(workArr[c][i].className.search("fd-column-") == -1 && workArr[c][i].className.search("sortable") != -1) workArr[c][i].className = workArr[c][i].className + " fd-column-" + i;\r
+\r
+                                        if(workArr[c][i].className.match('sortable')) {\r
+                                                workArr[c][i].className = workArr[c][i].className.replace(/forwardSort|reverseSort/, "");\r
+\r
+                                                if(i in columnNumSortObj) {\r
+                                                        columnNumSortObj[i]["thNode"] = workArr[c][i];\r
+                                                        columnNumSortObj["active"] = true;\r
+                                                };\r
+\r
+                                                thtext = fdTableSort.getInnerText(workArr[c][i], true);\r
+                                                \r
+                                                for(var cn = workArr[c][i].childNodes.length; cn--;) {\r
+                                                        // Skip image nodes and links created by the filter script.\r
+                                                        if(workArr[c][i].childNodes[cn].nodeType == 1 && (workArr[c][i].childNodes[cn].className == "fdFilterTrigger" || /img/i.test(workArr[c][i].childNodes[cn].nodeName))) {\r
+                                                                continue;\r
+                                                        };\r
+                                                        if(workArr[c][i].childNodes[cn].nodeType == 1 && /^a$/i.test(workArr[c][i].childNodes[cn].nodeName)) {\r
+                                                                workArr[c][i].childNodes[cn].onclick = workArr[c][i].childNodes[cn].onkeydown = null;\r
+                                                        };\r
+                                                        workArr[c][i].removeChild(workArr[c][i].childNodes[cn]);\r
+                                                };\r
+\r
+                                                aclone = a.cloneNode(true);\r
+                                                //aclone.appendChild(document.createTextNode(thtext));\r
+                                                aclone.innerHTML = thtext;\r
+                                                aclone.title = "Sort on \u201c" + thtext.replace('<br />', '') + "\u201d";\r
+                                                aclone.onclick = aclone.onkeydown = workArr[c][i].onclick = fdTableSort.initWrapper;\r
+                                                workArr[c][i].appendChild(aclone);\r
+                                                if(showArrow) workArr[c][i].appendChild(span.cloneNode(false));\r
+                                                workArr[c][i].className = workArr[c][i].className.replace(/fd-identical|fd-not-identical/, "");\r
+                                                fdTableSort.disableSelection(workArr[c][i]);\r
+                                                aclone = null;\r
+                                        };\r
+                                };\r
+                        };\r
+\r
+                        fdTableSort.tmpCache[tbl.id] = {cols:rowLength, headers:workArr};\r
+\r
+                        workArr = null;\r
+                        multi   = 0;\r
+\r
+                        if("active" in columnNumSortObj) {\r
+                                fdTableSort.tableId = tbl.id;\r
+                                fdTableSort.prepareTableData(document.getElementById(fdTableSort.tableId));\r
+\r
+                                delete columnNumSortObj["active"];\r
+\r
+                                for(col in columnNumSortObj) {\r
+                                        obj = columnNumSortObj[col];\r
+                                        if(!("thNode" in obj)) { continue; };\r
+                                        fdTableSort.multi = true;\r
+\r
+                                        len = obj.reverse ? 2 : 1;\r
+\r
+                                        for(ii = 0; ii < len; ii++) {\r
+                                                fdTableSort.thNode = obj.thNode;\r
+                                                if(!showOnly) {\r
+                                                        fdTableSort.initSort(false, true);\r
+                                                } else {\r
+                                                        fdTableSort.addThNode();\r
+                                                };\r
+                                        };\r
+\r
+                                        if(showOnly) {\r
+                                                fdTableSort.removeClass(obj.thNode, "(forwardSort|reverseSort)");\r
+                                                fdTableSort.addClass(obj.thNode, obj.reverse ? "reverseSort" : "forwardSort");\r
+                                                if(showArrow) {\r
+                                                        span = fdTableSort.thNode.getElementsByTagName('span')[0];\r
+                                                        if(span.firstChild) { span.removeChild(span.firstChild); };\r
+                                                        span.appendChild(document.createTextNode(len == 1 ? " \u2193" : " \u2191"));\r
+                                                };\r
+                                        };\r
+                                };\r
+                                if(showOnly && (fdTableSort.tableCache[tbl.id].colStyle || fdTableSort.tableCache[tbl.id].rowStyle)) {\r
+                                        fdTableSort.redraw(tbl.id, false);\r
+                                };\r
+                        } else if(tbl.className.search(/onload-zebra/) != -1) {\r
+                                fdTableSort.tableId = tbl.id;\r
+                                fdTableSort.prepareTableData(tbl);\r
+                                if(fdTableSort.tableCache[tbl.id].rowStyle) { fdTableSort.redraw(tbl.id, false); };\r
+                        };\r
+                };\r
+\r
+                fdTableSort.thNode = aclone = a = span = columnNumSortObj = thNode = tbl = allRowArr = rowArr = null;\r
+        },\r
+        initWrapper: function(e) {\r
+                e = e || window.event;\r
+                var kc = e.type == "keydown" ? e.keyCode != null ? e.keyCode : e.charCode : -1;\r
+                if(fdTableSort.thNode == null && (e.type == "click" || kc == 13)) {\r
+                        var targ = this;\r
+                        while(targ.tagName.toLowerCase() != "th") { targ = targ.parentNode; };\r
+                        fdTableSort.thNode = targ;\r
+                        while(targ.tagName.toLowerCase() != "table") { targ = targ.parentNode; };\r
+                        fdTableSort.tableId = targ.id;\r
+                        fdTableSort.multi = e.shiftKey;\r
+                        fdTableSort.addSortActiveClass();\r
+                        setTimeout(fdTableSort.initSort,5,false);\r
+                        return fdTableSort.stopEvent(e);\r
+                };\r
+                return kc != -1 ? true : fdTableSort.stopEvent(e);\r
+        },\r
+        jsWrapper: function(tableid, colNums) {\r
+                if(!(tableid in fdTableSort.tmpCache)) { return false; };\r
+                if(!(tableid in fdTableSort.tableCache)) { fdTableSort.prepareTableData(document.getElementById(tableid)); };\r
+                if(!(colNums instanceof Array)) { colNums = [colNums]; };\r
+\r
+                fdTableSort.tableId = tableid;\r
+                var len = colNums.length, colNum;\r
+\r
+                if(fdTableSort.tableCache[tableid].thList.length == colNums.length) {\r
+                        var identical = true;\r
+                        var th;\r
+                        for(var i = 0; i < len; i++) {\r
+                                colNum = colNums[i];\r
+                                th = fdTableSort.tmpCache[tableid].headers[0][colNum];\r
+                                if(th != fdTableSort.tableCache[tableid].thList[i]) {\r
+                                        identical = false;\r
+                                        break;\r
+                                };\r
+                        };\r
+                        if(identical) {\r
+                                fdTableSort.thNode = th;\r
+                                fdTableSort.initSort(true);\r
+                                return;\r
+                        };\r
+                };\r
+\r
+                fdTableSort.addSortActiveClass();\r
+\r
+                for(var i = 0; i < len; i++) {\r
+                        fdTableSort.multi = i;\r
+                        colNum = colNums[i];\r
+                        fdTableSort.thNode = fdTableSort.tmpCache[tableid].headers[0][colNum];\r
+                        fdTableSort.initSort(true);\r
+                };\r
+        },\r
+        addSortActiveClass: function() {\r
+                if(fdTableSort.thNode == null) { return; };\r
+                fdTableSort.addClass(fdTableSort.thNode, fdTableSort.sortActiveClass);\r
+                fdTableSort.addClass(document.getElementsByTagName('body')[0], fdTableSort.sortActiveClass);\r
+        },\r
+        removeSortActiveClass: function() {\r
+                if(fdTableSort.thNode == null) return;\r
+                fdTableSort.removeClass(fdTableSort.thNode, fdTableSort.sortActiveClass);\r
+                fdTableSort.removeClass(document.getElementsByTagName('body')[0], fdTableSort.sortActiveClass);\r
+        },\r
+        doCallback: function(init) {\r
+                if(!fdTableSort.tableId || !(fdTableSort.tableId in fdTableSort.tableCache)) { return; };\r
+                fdTableSort.callback(fdTableSort.tableId, init ? fdTableSort.tableCache[fdTableSort.tableId].initiatedCallback : fdTableSort.tableCache[fdTableSort.tableId].completeCallback);\r
+        },\r
+        addClass: function(e,c) {\r
+                if(new RegExp("(^|\\s)" + c + "(\\s|$)").test(e.className)) { return; };\r
+                e.className += ( e.className ? " " : "" ) + c;\r
+        },\r
+        /*@cc_on\r
+        /*@if (@_win32)\r
+        removeClass: function(e,c) {\r
+                e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');\r
+        },\r
+        @else @*/\r
+        removeClass: function(e,c) {\r
+                e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s\s*/, '').replace(/\s\s*$/, '');\r
+        },\r
+        /*@end\r
+        @*/\r
+        callback: function(tblId, cb) {\r
+                var func, parts;\r
+                try {\r
+                        if(cb.indexOf(".") != -1) {                              \r
+                                parts = cb.split('.');\r
+                                obj   = window;\r
+                                for (var x = 0, part; part = obj[parts[x]]; x++) {\r
+                                        if(part instanceof Function) {\r
+                                                (function() {\r
+                                                        var method = part;\r
+                                                        func = function (data) { method.apply(obj, [data]) };\r
+                                                })();\r
+                                        } else {\r
+                                                obj = part;\r
+                                        };\r
+                                };\r
+                        } else if(cb + tblId in window) {\r
+                                func = window[cb + tblId];\r
+                        } else if(cb in window) {\r
+                                func = window[cb];\r
+                        } else {\r
+                                func = null;\r
+                        };\r
+                 } catch(err) {};\r
+                           \r
+                if(!(func instanceof Function)) return;\r
+                func(tblId, fdTableSort.tableCache[tblId].thList);                               \r
+        },\r
+        prepareTableData: function(table) {\r
+                var data = [];\r
+\r
+                var start = table.getElementsByTagName('tbody');\r
+                start = start.length ? start[0] : table;\r
+\r
+                var trs = start.rows;\r
+                var ths = table.getElementsByTagName('th');\r
+\r
+                var numberOfRows = trs.length;\r
+                var numberOfCols = fdTableSort.tmpCache[table.id].cols;\r
+\r
+                var data = [];\r
+                var identical = new Array(numberOfCols);\r
+                var identVal  = new Array(numberOfCols);\r
+\r
+                for(var tmp = 0; tmp < numberOfCols; tmp++) identical[tmp] = true;\r
+\r
+                var tr, td, th, txt, tds, col, row;\r
+\r
+                var re = new RegExp(/fd-column-([0-9]+)/);\r
+                var rowCnt = 0;\r
+\r
+                var sortableColumnNumbers = [];\r
+\r
+                for(var tmp = 0, th; th = ths[tmp]; tmp++) {\r
+                        if(th.className.search(re) == -1) continue;\r
+                        sortableColumnNumbers[sortableColumnNumbers.length] = th;\r
+                };\r
+\r
+                for(row = 0; row < numberOfRows; row++) {\r
+\r
+                        tr              = trs[row];\r
+                        if(tr.parentNode != start || tr.getElementsByTagName("th").length || (tr.parentNode && tr.parentNode.tagName.toLowerCase().search(/thead|tfoot/) != -1)) continue;\r
+                        data[rowCnt]    = [];\r
+                        tds             = tr.cells;\r
+\r
+                        for(var tmp = 0, th; th = sortableColumnNumbers[tmp]; tmp++) {\r
+                                col = th.className.match(re)[1];\r
+\r
+                                td  = tds[col];\r
+                                txt = fdTableSort.getInnerText(td) + " ";\r
+                                txt = txt.replace(/^\s+/,'').replace(/\s+$/,'');\r
+\r
+                                if(th.className.search(/sortable-date/) != -1) {\r
+                                        txt = fdTableSort.dateFormat(txt, th.className.search(/sortable-date-dmy/) != -1);\r
+                                } else if(th.className.search(/sortable-numeric|sortable-currency/) != -1) {\r
+                                        txt = parseFloat(txt.replace(/[^0-9\.\-]/g,''));\r
+                                        if(isNaN(txt)) txt = "";\r
+                                } else if(th.className.search(/sortable-text/) != -1) {\r
+                                        txt = txt.toLowerCase();\r
+                                } else if (th.className.search(/sortable-keep/) != -1) {\r
+                                        txt = rowCnt;\r
+                                } else if(th.className.search(/sortable-([a-zA-Z\_]+)/) != -1) {\r
+                                        if((th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "PrepareData") in window) {\r
+                                                txt = window[th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "PrepareData"](td, txt);\r
+                                        };\r
+                                } else if(txt != "") {\r
+                                        fdTableSort.removeClass(th, "sortable");\r
+                                        if(fdTableSort.dateFormat(txt) != 0) {\r
+                                                fdTableSort.addClass(th, "sortable-date");\r
+                                                txt = fdTableSort.dateFormat(txt);\r
+                                        } else if(txt.search(fdTableSort.regExp_Number) != -1 || txt.search(fdTableSort.regExp_Currency) != -1) {\r
+                                                fdTableSort.addClass(th, "sortable-numeric");\r
+                                                txt = parseFloat(txt.replace(/[^0-9\.\-]/g,''));\r
+                                                if(isNaN(txt)) txt = "";\r
+                                        } else {\r
+                                                fdTableSort.addClass(th, "sortable-text");\r
+                                                txt = txt.toLowerCase();\r
+                                        };\r
+                                };\r
+\r
+                                if(rowCnt > 0 && identical[col] && identVal[col] != txt) { identical[col] = false; };\r
+\r
+                                identVal[col]     = txt;\r
+                                data[rowCnt][col] = txt;\r
+                        };\r
+                        data[rowCnt][numberOfCols] = tr;\r
+                        rowCnt++;\r
+                };\r
+\r
+                var colStyle = table.className.search(/colstyle-([\S]+)/) != -1 ? table.className.match(/colstyle-([\S]+)/)[1] : false;\r
+                var rowStyle = table.className.search(/rowstyle-([\S]+)/) != -1 ? table.className.match(/rowstyle-([\S]+)/)[1] : false;\r
+                var iCBack   = table.className.search(/sortinitiatedcallback-([\S-]+)/) == -1 ? "sortInitiatedCallback" : table.className.match(/sortinitiatedcallback-([\S]+)/)[1];\r
+                var cCBack   = table.className.search(/sortcompletecallback-([\S-]+)/) == -1 ? "sortCompleteCallback" : table.className.match(/sortcompletecallback-([\S]+)/)[1];\r
+                iCBack = iCBack.replace("-", ".");\r
+                cCBack = cCBack.replace("-", ".");\r
+                fdTableSort.tableCache[table.id] = { hook:start, initiatedCallback:iCBack, completeCallback:cCBack, thList:[], colOrder:{}, data:data, identical:identical, colStyle:colStyle, rowStyle:rowStyle, noArrow:table.className.search(/no-arrow/) != -1 };\r
+                sortableColumnNumbers = data = tr = td = th = trs = identical = identVal = null;\r
+        },\r
+        onUnload: function() {\r
+                for(tbl in fdTableSort.tableCache) { fdTableSort.removeTableCache(tbl); };\r
+                for(tbl in fdTableSort.tmpCache) { fdTableSort.removeTmpCache(tbl); };\r
+                fdTableSort.removeEvent(window, "load", fdTableSort.initEvt);\r
+                fdTableSort.removeEvent(window, "unload", fdTableSort.onUnload);\r
+                fdTableSort.tmpCache = fdTableSort.tableCache = null;\r
+        },\r
+        addThNode: function() {\r
+                var dataObj = fdTableSort.tableCache[fdTableSort.tableId];\r
+                var pos     = fdTableSort.thNode.className.match(/fd-column-([0-9]+)/)[1];\r
+                var alt     = false;\r
+\r
+                if(!fdTableSort.multi) {\r
+                        if(dataObj.colStyle) {\r
+                                var len = dataObj.thList.length;\r
+                                for(var i = 0; i < len; i++) {\r
+                                        dataObj.colOrder[dataObj.thList[i].className.match(/fd-column-([0-9]+)/)[1]] = false;\r
+                                };\r
+                        };\r
+                        if(dataObj.thList.length && dataObj.thList[0] == fdTableSort.thNode) alt = true;\r
+                        dataObj.thList = [];\r
+                };\r
+\r
+                var found = false;\r
+                var l = dataObj.thList.length;\r
+\r
+                for(var i = 0, n; n = dataObj.thList[i]; i++) {\r
+                        if(n == fdTableSort.thNode) {\r
+                                found = true;\r
+                                break;\r
+                        };\r
+                };\r
+\r
+                if(!found) {\r
+                        dataObj.thList.push(fdTableSort.thNode);\r
+                        if(dataObj.colStyle) { dataObj.colOrder[pos] = true; };\r
+                };\r
+\r
+                var ths = document.getElementById(fdTableSort.tableId).getElementsByTagName("th");\r
+                for(var i = 0, th; th = ths[i]; i++) {\r
+                        found = false;\r
+                        for(var z = 0, n; n = dataObj.thList[z]; z++) {\r
+                                if(n == th) {\r
+                                        found = true;\r
+                                        break;\r
+                                };\r
+                        };\r
+                        if(!found) {\r
+                                fdTableSort.removeClass(th, "(forwardSort|reverseSort)");\r
+                                if(!dataObj.noArrow) {\r
+                                        span = th.getElementsByTagName('span');\r
+                                        if(span.length) {\r
+                                                span = span[0];\r
+                                                while(span.firstChild) span.removeChild(span.firstChild);\r
+                                        };\r
+                                };\r
+                        };\r
+                };\r
+\r
+                if(dataObj.thList.length > 1) {\r
+                        classToAdd = fdTableSort.thNode.className.search(/forwardSort/) != -1 ? "reverseSort" : "forwardSort";\r
+                        fdTableSort.removeClass(fdTableSort.thNode, "(forwardSort|reverseSort)");\r
+                        fdTableSort.addClass(fdTableSort.thNode, classToAdd);\r
+                        dataObj.pos = -1\r
+                } else if(alt) { dataObj.pos = fdTableSort.thNode };\r
+        },\r
+        initSort: function(noCallback, ident) {\r
+                var thNode      = fdTableSort.thNode;\r
+                var tableElem   = document.getElementById(fdTableSort.tableId);\r
+\r
+                if(!(fdTableSort.tableId in fdTableSort.tableCache)) { fdTableSort.prepareTableData(document.getElementById(fdTableSort.tableId)); };\r
+\r
+                fdTableSort.addThNode();\r
+\r
+                if(!noCallback) { fdTableSort.doCallback(true); };\r
+\r
+                fdTableSort.pos = thNode.className.match(/fd-column-([0-9]+)/)[1];\r
+                var dataObj     = fdTableSort.tableCache[tableElem.id];\r
+                var lastPos     = dataObj.pos && dataObj.pos.className ? dataObj.pos.className.match(/fd-column-([0-9]+)/)[1] : -1;\r
+                var len1        = dataObj.data.length;\r
+                var len2        = dataObj.data.length > 0 ? dataObj.data[0].length - 1 : 0;\r
+                var identical   = dataObj.identical[fdTableSort.pos];\r
+                var classToAdd  = "forwardSort";\r
+\r
+                if(dataObj.thList.length > 1) {\r
+                        var js  = "var sortWrapper = function(a,b) {\n";\r
+                        var l   = dataObj.thList.length;\r
+                        var cnt = 0;\r
+                        var e,d,th,p,f;\r
+\r
+                        for(var i=0; i < l; i++) {\r
+                                th = dataObj.thList[i];\r
+                                p  = th.className.match(/fd-column-([0-9]+)/)[1];\r
+                                if(dataObj.identical[p]) { continue; };\r
+                                cnt++;\r
+\r
+                                if(th.className.match(/sortable-(numeric|currency|date|keep)/)) {\r
+                                        f = "fdTableSort.sortNumeric";\r
+                                } else if(th.className.match('sortable-text')) {\r
+                                        f = "fdTableSort.sortText";\r
+                                } else if(th.className.search(/sortable-([a-zA-Z\_]+)/) != -1 && th.className.match(/sortable-([a-zA-Z\_]+)/)[1] in window) {\r
+                                        f = "window['" + th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "']";\r
+                                } else  f = "fdTableSort.sortText";\r
+\r
+                                e = "e" + i;\r
+                                d = th.className.search('forwardSort') != -1 ? "a,b" : "b,a";\r
+                                js += "fdTableSort.pos   = " + p + ";\n";\r
+                                js += "var " + e + " = "+f+"(" + d +");\n";\r
+                                js += "if(" + e + ") return " + e + ";\n";\r
+                                js += "else { \n";\r
+                        };\r
+\r
+                        js += "return 0;\n";\r
+\r
+                        for(var i=0; i < cnt; i++) {\r
+                                js += "};\n";\r
+                        };\r
+\r
+                        if(cnt) js += "return 0;\n";\r
+                        js += "};\n";\r
+\r
+                        eval(js);\r
+                        dataObj.data.sort(sortWrapper);\r
+                        identical = false;\r
+                } else if((lastPos == fdTableSort.pos && !identical) || (thNode.className.search(/sortable-keep/) != -1 && lastPos == -1)) {\r
+                        dataObj.data.reverse();\r
+                        classToAdd = thNode.className.search(/reverseSort/) != -1 ? "forwardSort" : "reverseSort";\r
+                        if(thNode.className.search(/sortable-keep/) != -1 && lastPos == -1) fdTableSort.tableCache[tableElem.id].pos = thNode;\r
+                } else {\r
+                        fdTableSort.tableCache[tableElem.id].pos = thNode;\r
+                        classToAdd = thNode.className.search(/forwardSort/) != -1 ? "reverseSort" : "forwardSort";\r
+                        if(!identical) {\r
+                                if(thNode.className.match(/sortable-(numeric|currency|date|keep)/)) {\r
+                                        dataObj.data.sort(fdTableSort.sortNumeric);\r
+                                } else if(thNode.className.match('sortable-text')) {\r
+                                        dataObj.data.sort(fdTableSort.sortText);\r
+                                } else if(thNode.className.search(/sortable-([a-zA-Z\_]+)/) != -1 && thNode.className.match(/sortable-([a-zA-Z\_]+)/)[1] in window) {\r
+                                        dataObj.data.sort(window[thNode.className.match(/sortable-([a-zA-Z\_]+)/)[1]]);\r
+                                };\r
+\r
+                                if(thNode.className.search(/(^|\s)favour-reverse($|\s)/) != -1) {\r
+                                        classToAdd = classToAdd == "forwardSort" ? "reverseSort" : "forwardSort";\r
+                                        dataObj.data.reverse();\r
+                                };\r
+                        };\r
+                };\r
+                if(ident) { identical = false; };\r
+                if(dataObj.thList.length == 1) {\r
+                        fdTableSort.removeClass(thNode, "(forwardSort|reverseSort)");\r
+                        fdTableSort.addClass(thNode, classToAdd);\r
+                };\r
+                if(!dataObj.noArrow) {\r
+                        var span = fdTableSort.thNode.getElementsByTagName('span')[0];\r
+                        if(span.firstChild) span.removeChild(span.firstChild);\r
+                        span.appendChild(document.createTextNode(fdTableSort.thNode.className.search(/forwardSort/) != -1 ? " \u2193" : " \u2191"));\r
+                };\r
+                if(!dataObj.rowStyle && !dataObj.colStyle && identical) {\r
+                        fdTableSort.removeSortActiveClass();\r
+                        if(!noCallback) { fdTableSort.doCallback(false); };\r
+                        fdTableSort.thNode = null;\r
+                        return;\r
+                };\r
+                if("tablePaginater" in window && tablePaginater.tableIsPaginated(fdTableSort.tableId)) {\r
+                        tablePaginater.redraw(fdTableSort.tableId, identical);\r
+                } else {\r
+                        fdTableSort.redraw(fdTableSort.tableId, identical);\r
+                };\r
+                fdTableSort.removeSortActiveClass();\r
+                if(!noCallback) { fdTableSort.doCallback(false); };\r
+                fdTableSort.thNode = null;\r
+        },\r
+        redraw: function(tableid, identical) {\r
+                if(!tableid || !(tableid in fdTableSort.tableCache)) { return; };\r
+                var dataObj     = fdTableSort.tableCache[tableid];\r
+                var data        = dataObj.data;\r
+                var len1        = data.length;\r
+                var len2        = len1 ? data[0].length - 1 : 0;\r
+                var hook        = dataObj.hook;\r
+                var colStyle    = dataObj.colStyle;\r
+                var rowStyle    = dataObj.rowStyle;\r
+                var colOrder    = dataObj.colOrder;\r
+                var highLight   = 0;\r
+                var reg         = /(^|\s)invisibleRow(\s|$)/;\r
+                var tr, tds;\r
+\r
+                for(var i = 0; i < len1; i++) {\r
+                        tr = data[i][len2];\r
+                        if(colStyle) {\r
+                                tds = tr.cells;\r
+                                for(thPos in colOrder) {\r
+                                        if(!colOrder[thPos]) fdTableSort.removeClass(tds[thPos], colStyle);\r
+                                        else fdTableSort.addClass(tds[thPos], colStyle);\r
+                                };\r
+                        };\r
+                        if(!identical) {\r
+                                if(rowStyle && tr.className.search(reg) == -1) {\r
+                                        if(highLight++ & 1) fdTableSort.addClass(tr, rowStyle);\r
+                                        else fdTableSort.removeClass(tr, rowStyle);\r
+                                };\r
+\r
+                                // Netscape 8.1.2 requires the removeChild call or it freaks out, so add the line if you want to support this browser\r
+                                // hook.removeChild(tr);\r
+                                hook.appendChild(tr);\r
+                        };\r
+                };\r
+                tr = tds = hook = null;\r
+        },\r
+        getInnerText: function(el, allowBrTags) {\r
+                if (typeof el == "string" || typeof el == "undefined") return el;\r
+                if(el.innerText) return el.innerText;\r
+                var txt = '', i;\r
+                for(i = el.firstChild; i; i = i.nextSibling) {\r
+                        if(allowBrTags && i.nodeName && i.nodeName == "BR") txt += "<br />";\r
+                        else if(i.nodeType == 3)       txt += i.nodeValue;\r
+                        else if(i.nodeType == 1)       txt += fdTableSort.getInnerText(i);\r
+                };\r
+                return txt;\r
+        },\r
+        dateFormat: function(dateIn, favourDMY) {\r
+                var dateTest = [\r
+                        { regExp:/^(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])([- \/.])((\d\d)?\d\d)$/, d:3, m:1, y:5 },  // mdy\r
+                        { regExp:/^(0?[1-9]|[12][0-9]|3[01])([- \/.])(0?[1-9]|1[012])([- \/.])((\d\d)?\d\d)$/, d:1, m:3, y:5 },  // dmy\r
+                        { regExp:/^(\d\d\d\d)([- \/.])(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])$/, d:5, m:3, y:1 }      // ymd\r
+                        ];\r
+                var start, cnt = 0, numFormats = dateTest.length;\r
+                while(cnt < numFormats) {\r
+                        start = (cnt + (favourDMY ? numFormats + 1 : numFormats)) % numFormats;\r
+                        if(dateIn.match(dateTest[start].regExp)) {\r
+                                res = dateIn.match(dateTest[start].regExp);\r
+                                y = res[dateTest[start].y];\r
+                                m = res[dateTest[start].m];\r
+                                d = res[dateTest[start].d];\r
+                                if(m.length == 1) m = "0" + String(m);\r
+                                if(d.length == 1) d = "0" + String(d);\r
+                                if(y.length != 4) y = (parseInt(y) < 50) ? "20" + String(y) : "19" + String(y);\r
+\r
+                                return y+String(m)+d;\r
+                        };\r
+                        cnt++;\r
+                };\r
+                return 0;\r
+        },\r
+        sortNumeric:function(a,b) {\r
+                var aa = a[fdTableSort.pos];\r
+                var bb = b[fdTableSort.pos];\r
+                if(aa == bb) return 0;\r
+                if(aa === "" && !isNaN(bb)) return -1;\r
+                if(bb === "" && !isNaN(aa)) return 1;\r
+                return aa - bb;\r
+        },\r
+        sortText:function(a,b) {\r
+                var aa = a[fdTableSort.pos];\r
+                var bb = b[fdTableSort.pos];\r
+                if(aa == bb) return 0;\r
+                if(aa < bb)  return -1;\r
+                return 1;\r
+        }\r
+};\r
+})();\r
+fdTableSort.addEvent(window, "load",   fdTableSort.initEvt);\r
+fdTableSort.addEvent(window, "unload", fdTableSort.onUnload);\r